Search This Blog

Thursday, April 23, 2015

Image Retrieval

ဓာတ်ပုံ တပုံ (test image) ပေးလိုက်ရင် သိမ်းထားတဲ့ ပုံတွေထဲက အတူဆုံး ပုံတွေ ကို ပြန်ရွေးထုတ်ပေးတဲ့ program ကို MatLab နဲ့ ရေးကြည့်ထားပါတယ်။

Dataset

နမူနာ အနေနဲ့ Graz dataset ထဲက စက်ဘီးပုံတွေ နဲ့ လူပုံတွေကို သုံးပါမယ်။ အဲဒီ ဝဘ်ဆိုက် က ပုံတွေကို download လုပ်ပြီးတဲ့ အခါ စက်ဘီးပုံ ၂၀၀ နဲ့ လူပုံ ၂၀၀ကို training လုပ်ဖို့ training ဆိုတဲ့ folder ထဲမှာ သိမ်းပြီး၊ ကျန်တာကို test လုပ်ဖို့ test ဆိုတဲ့ folder ထဲမှာသိမ်းလိုက်ပါမယ်။ အဲဒီက ပုံတွေက bmp ဖိုင်တွေ ဖြစ်နေတဲ့ အတွက် ပုံတွေ အားလုံးကို ကျွန်တော်တို့ လိုချင်တဲ့ png ဖိုင် format ဖြစ်အောင် ပြောင်းဖို့လိုပါတယ်။ အဲလို ပုံအားလုံးကို တခါတည်း ပြောင်းဖို့ အတွက် အောက်မှာပြထားတဲ့ အတိုင်း MatLab program လေးတပုဒ်ကို ရေးပြီး training folder အတွက်တခါ၊ test folder အတွက်တစ်ခါ လွယ်လွယ်ကူကူ မြန်မြန်ဆန်ဆန် ပြောင်းလိုက်ပါတယ်။

%File name: bmp2png.m
%-------------------------------------------------------------------------
sDir=[pwd,'/test/']; %source folder
sFile=[sDir, '*.bmp']; %source files
dFile=[pwd,'/test_png/test_']; %destination
%-------------------------------------------------------------------------
sList=dir(sFile); %Get file list
nFiles=size(sList,1) %number of files
%-------------------------------------------------------------------------
for i=1:nFiles 
    Img=imread([sDir,sList(i).name]);
    imwrite(Img,[dFile,sprintf('%03d',i),'.png']);
    i %output progress
end


ကျွန်တော့် စက်ထဲမှာ MatLab မရှိတဲ့ အတွက် အဲဒီအစား အလကားရတဲ့ free software တစ်ခုဖြစ်တဲ့ Octave ကို သုံးပါမယ်။ Ubuntu 14.04 LTS Linux စက် အဟောင်းလေးတစ်လုံးကို သံုးခဲ့ ပါတယ်။ Octave မှာ image processing package မရှိသေးရင်အောက်က ပြထားတဲ့ အတိုင်း terminal မှာ ရိုက်ပြီး ထည့်လို့ရပါတယ်။ ကျွန်တော်က တော့ statistics package ကိုပါ ထည့်လိုက်ပါတယ်။

sudo apt-get install octave-image
sudo apt-get install octave-statistics


ပြီးရင် Octave ကို ဖွင့်လိုက်ပါမယ်။ နောက် Octave ရဲ့ terminal ထဲမှာ အောက်က command တွေကို ရိုက်ထည့်ပြီး package တွေကို တင်ဖို့လိုပါတယ်။

pkg load image
pkg load statistics


နောက်တခါ ပြောင်းလို့ ရတဲ့ ပုံတွေကို သိမ်းဖို့ အတွက် training_png နဲ့ test_png ဆိုပြီး folder အသစ်တွေ ဖွဲ့လိုက်ပါမယ်။ Octave ရဲ့ terminal ထဲမှာပဲ chdir ဆိုတဲ့ command နဲ့ လက်ရှိ directory ကို ပြောင်း၊ ခုနက MatLab progarm ကို run ဖို့ သူ့ရဲ့ ဖိုင်နာမည် bmp2png; ဆိုပြီးရိုက်လိုက်မယ် ဆိုရင် ပုံတွေကို png ပြောင်းပြီး သတ်မှတ်တဲ့ folder ထဲမှာ သိမ်းသွားပါမယ်။


Feature Extraction

SIFT features တွေကို ရှာဖို့ ဒီ ဝဘ်ဆိုက် ကနေ Harris & Hessian ဆိုတဲ့ feature extractor ကိုသုံးပါမယ်။ အဲဒီ feature extractor ကို မသုံးချင်ရင် တခြားရှိတဲ့ ဟာတွေထဲက ကြိုက်တာကို ယူသုံးလို့ရပါတယ်။ အဲဒီ extract_features.tar.gz ဆိုတဲ့ ဖိုင်ကို ဒေါင်းလုပ် လုပ်၊ extract လုပ်ပြီးရင်၊ feature တွေ ထုတ်ချင်တဲ့ folder တွေထဲမှာ extract_features.ln နဲ့ harhessift.basis ဆိုတဲ့ ဖိုင်တွေကို ထားလိုက်ပါမယ်။ ပြီးရင် ဖိုင်အားလုံးကို တခုချင်းစီ extract လုပ်ဖို့ အောက်မှာ ပြထားတဲ့ အတိုင်း xall.sh ဆိုပြီး script ဖိုင်လေး ရေးပြီး feature တွေကို ထုတ်လိုက်ပါမယ်။

#!/bin/bash
FILES=./*.png
for f in $FILES
do
 echo "Processing - $f ..."
 ./extract_features.ln -harhes -i "$f" -sift -pca harhessift.basis
done


Scrpit ဖိုင်ရေးပြီးထဲအခါ terminal မှာ သူ့ကို run လို့ရအောင် အောက်ကအတိုင်း ပြင်ဖို့လိုပါတယ်။ ပြီးရင် ./xall.sh ရိုက်လိုက်ရင် ပုံတွေ တစ်ပုံ ခြင်းစီ အတွက် feature file တွေရလာပါလိမ့်မယ်။

chmod 755 xall.sh


ထုတ်ထားတဲ့ feature တွေကို ပုံနဲ့ တွဲကြည့်ချင်ရင် ခုနက ဝက်ဘ်ဆိုဒ် ကပဲ display_features.m ဆိုတဲ့ MatLab code ကို ယူနိုင်ပါတယ်။ နောက် Octave terminal မှာ နမူနာ အနေနဲ့ ဒုတိယမြောက် စက်ဘီးပုံနဲ့ သူ့ရဲ့ features တွေကို အောက်က အတိုင်း command ရိုက်ထည့်ပြီး ကြည့်လို့ ရပါတယ်။

display_features('bike_002.png.harhes.sift','bike_002.png',0,0);


Feature file တွေရဲ့ extension က .sift ဖြစ်ပြီး ပထမဆုံး အကြောင်းက feature vector တခုခြင်းစီရဲ့ အရွယ်အစား ကို ဖော်ပြထားတာ ဖြစ်ပြီး ၁၂၈ လို့ တွေ့နိုင်ပါတယ်။ ဒုတိယ အကြောင်းက feature အရေအတွက်ကို ဖော်ပြထားတာ ဖြစ်ပါတယ်။ တပုံ တပုံ စီမှာ feature အရေအတွက် ထောင်ချီပါတာကို တွေ့ရပါတယ်။ တတိယ အကြောင်းက စပြီး တတန်းစီမှာ ဂဏန်း ၁၃၃ လုံးပါပြီး ၆ ခုမြောက်ကစပြီး နောက်ဆုံး ၁၃၃ လုံး အထိက dimension 128 ခုရှိတဲ့ feature vector တွေဖြစ်ပါတယ်။

Classification

ပထမ အဆင့် အနေနဲ့ ရထားတဲ့ features တွေကို အမျိုးအစားခွဲပြီး အုပ်စု နှစ်ရာလောက် ခွဲထားလိုက်ပါမယ်။ အဲဒီလို တူရာတူရာ အုပ်စု ဖွဲဖို့ အတွက် k-means clustering algorithm ကို သုံးပါမယ်။ Feature vectors တွေ တစ်ခု နဲ့ တစ်ခု ဘယ်လောက်နီးစပ်လည်း ဆိုတာ တိုင်းဖို့ ကြိုက်တဲ့ distance အမျိုးအစားကို သုံးနိုင်ပါတယ်။ ကျွန်တော်တို့က နောက်ပိုင်းမှာ histogram တွေကို ပါ ဘယ်လောက်နီးစပ်လည်း တိုင်းမှာမို့ သူတို့အတွက် သင့်တော်တဲ့ χ2 distance ကို တလက်စတည်း သုံးလိုက်ပါမယ်။ Vector နှစ်ခုကြားက χ2 distance ရှာတဲ့ MatLab ဖန်ရှင်တစ်ခုကို အောက်မှာ ပြထားပါတယ်။

function d=ChiDist(v1,v2)
%Find Chi Square distance between two vectors
%Input: 2 vectors 
%Output: a scalar distance
%File name: ChiDist.m


    dv=(v1-v2).^2;
    sv=abs(v1)+abs(v2);
    sv(sv==0)=1e-9; %eliminate zero denominator
    d=sum(dv./sv)./2;    
end


MatLab မှာ kmeans ဆိုတဲ့ ဖန်ရှင်ပါပေမယ့် အဲဒီမှာ χ2 distance ကို သုံးလို့မရတဲ့ အတွက် k-means ရှာတဲ့ ကုဒ်ကို ကိုယ့်ဟာကို ရေး ပြီးသုံးခဲ့ ပါတယ်။ အမှန်တော့ features တွေကို အုပ်စုဖွဲ့တာ ရိုးရိုး kmeans ကို သုံးလိုက်လည်း ရပါတယ်။ ဒါပေမယ့် kmeans အတွက် statistics package လိုပါတယ်။

function [Idx,C]=KMeansCustom(X,k)
%KMeansCustom partitions the points in the n-by-d data matrix X into k clusters.
%[Idx,C]= KMeansCustom(X,k) returns 
%n-by-1 vector IDX containing the cluster indices of each point and 
%k-by-d matrix C containing the k cluster centroid locations.
%For n sample points with d dimensions in each point, X has n rows and d columns.
%File name: KMeansCustom.m
%Author: Yan Naing Aye
%Website: http://cool-emerald.blogspot.com/


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Define maximum number of iterations
MaxIter=100;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[n,d]=size(X);
k=round(k);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%step1 :arbitrarily choose k samples as the initial cluster centers
p=randperm(n);
Mu=X(p(1:k),:);
D=zeros(k,n);
for t=1:MaxIter
 %step2:distribute the samples X  to the clusters 
 for j=1:k
        for i=1:n
            D(j,i)=ChiDist(X(i,:),Mu(j,:));%Use custom distance
        end
 end
 [ValMin,IndexMin]=min(D);
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %step 3: update the cluster centers
 OldMu=Mu;
 for i=1:k
        Mu(i,:)=mean(X(IndexMin==i,:),1);
 end
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %step4 :check convergence
 if sum(sum(abs(OldMu-Mu))) == 0
        break
 end
 t %output progress
end
Idx=IndexMin';
C=Mu;



တပုံစီမှာ feature vector တွေ ထောင်ချီပါတာမို့ ကျွန်တော်တို့ train လုပ်မယ့် ပုံ ၄၀၀ အတွက် ဆိုရင် တွက်ချက်မှု တွေ အများကြီး လုပ်ရမှာမို့။ တပုံချင်းစီ ကနေ feature အားလုံးကို မယူတော့ပဲ ပုံအားလုံးကနေ ကြုံရာကျဘမ်း feature အခု ၁၀၀ စီယူလိုက်ပါမယ်။ ပြီးမှ ရလာတဲ့ ကျဘမ်း features တွေကို k-means သုံးပြီး အုပ်စု ၂၀၀ ဖွဲ့လိုက်ပါမယ်။ အဲဒီလို အုပ်စုဖွဲ့ပေးပြီး အုပ်စုတွေရဲ့ အလယ် (centers) တွေကို ရှာပေးတဲ့ MatLab ပရိုဂရမ်ကို အောက်မှာ ပြထားပါတယ်။ ထောင်ချီရှိတဲ့ feature တွေ အကုန်မယူပဲ တစ်ရာစီပဲ ယူတွက်တာတောင်မှ နာရီ နဲ့ ချီပြီးကြာတာမို့ ပစ်ထားလိုက်ပြီး နောက်နေ့ကျမှ ကွန်ပြူတာဖွင့်ကြည့်လိုက်တဲ့ အခါ visual token vocabulary လို့ခေါ်တဲ့ feature centers တွေကို FeatureCtrs.dat ဆိုတဲ့ ဖိုင်ထဲမှာ သိမ်းထားတာကို ရပါတယ်။

%File name: IF1_Find_VisualTokens.m
%Author: Yan Naing Aye
%Website: http://cool-emerald.blogspot.com/
%-------------------------------------------------------------------------
%parameter settings
NRF=100; %number of random features taken from each training image
NVT=200; %number of visual tokens to find
FV_Size=128; %feature vector size
%-------------------------------------------------------------------------
sDir=[pwd,'/training_png/']; %source directory
sFile=[sDir,'*.sift']; %source files
fList=dir(sFile); %get file list
nFiles=size(fList,1); %number of files
%-------------------------------------------------------------------------
allFeatures=zeros(NRF*nFiles,FV_Size); %allocate and initialize for all features 
%(e.g. 100 random features each from 400 files and each feature has 128 dimensions)
selFeatures=zeros(NRF,FV_Size); %allocate and initialize for random features 
si=1; ei=100; %starting index and ending index for each file
for i=1:nFiles
    i %output progress
    sFile=[sDir,fList(i).name]
    %read all features from each file
    readFeatures=textread(sFile,'','headerlines',2);

    %each row has 133 elements and first 5 elements are u,v,a,b,c; feature vectors starts from 6th column
    readFeatures=readFeatures(:,6:end);

    %select random features
    nFeatures=size(readFeatures,1);
    p=randperm(nFeatures,NRF);
    selFeatures=readFeatures(p,:);

    %put the selected features to all features pool
    ei=i*NRF; si=ei-NRF+1;
    allFeatures(si:ei,:)=selFeatures;
end

%find feature centers by k-means clustering
[idx,FeatureCtrs] = KMeansCustom(allFeatures,NVT);

%save the feature centers to a file
save -ascii -double -tabs FeatureCtrs.dat FeatureCtrs;



အဲဒီ ပရိုဂရမ်ကို Octave terminal မှာ IF1_Find_VisualTokens; လို့ရိုက်ထည့်ပြီး run နိုင်ပါတယ်။

Histogram of Visual Tokens

ဒုတိယ အဆင့် အနေနဲ့ training ပုံတပုံ စီက features အားလုံးကို ဖတ်ပြီး ဘယ် visual token တွေဘယ်နှစ်ခု ပါတယ် ဆိုတဲ့ histogram တခုစီ ဆောက်ပါမယ်။ token frequency feature လို့လည်း ခေါ်ပါတယ်။

function h=GetHistOfVT(sFile)
%Get histogram of visual tokens
%Input: feature file path
%Output: a row (1D array) of histogram
%File name: GetHistOfVT.m
%Author: Yan Naing Aye
%Website: http://cool-emerald.blogspot.com/


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %read all features from each file
    readFeatures=textread(sFile,'','headerlines',2);

    %each row has 133 elements and first 5 elements are u,v,a,b,c; feature vectors starts from 6th column
    readFeatures=readFeatures(:,6:end);

    %load feature centers (visual tokens)
    load('FeatureCtrs.dat');
    
    nf=size(readFeatures,1);
    nc=size(FeatureCtrs,1);
    %----------------------------------------------------------------
    %initialize histogram
    h=zeros(1,nc);
    for i=1:nf
        minD=ChiDist(readFeatures(i,:),FeatureCtrs(1,:));
        minC=1;
        for j=2:nc
            d=ChiDist(readFeatures(i,:),FeatureCtrs(j,:));
            if (d<minD)
                minD=d;
                minC=j; 
            end
        end
        h(minC)=h(minC)+1;
    end
end



အဲဒီလို histogram တွေရှာပြီး ပုံအားလုံးရဲ့ histogram တွေ အားလုံးကို AllHistVT.dat ဆိုတဲ့ ဖိုင်တဖိုင် ဖန်တီးပြီး သိမ်းပေးတဲ့ ပရိုဂရမ်ကို အောက်မှာ ပြထားပါတယ်။ Octave terminal မှာ IF2_Build_Histogram_of_VisualTokens; လို့ ရိုက်ထည့်ပြီး run နိုင်ပါတယ်။ သူလည်း နာရီနဲ့ ချီပြီးကြာပါတယ်။

%File name: IF2_Build_Histogram_of_VisualTokens.m
%Author: Yan Naing Aye
%Website: http://cool-emerald.blogspot.com/
%-------------------------------------------------------------------------
NVT=200; %number of visual tokens
sDir=[pwd,'/training_png/']; %source directory
sFile=[sDir, '*.sift']; %source files
sList=dir(sFile); %source file list
nFiles=size(sList,1); %number of files
AllHistVT=zeros(nFiles,NVT); %histograms of visual tokens of all files
%-------------------------------------------------------------------------
for i=1:nFiles 
    AllHistVT(i,:)=GetHistOfVT([sDir,sList(i).name]);
    i%output progress
end
save -ascii -double -tabs AllHistVT.dat AllHistVT;





Image Retrieval

နောက်ဆုံးအဆင့်ကတော့ query image လို့ ခေါ်တဲ့ test image တပုံကို ရွေးပေးလိုက်ရင် သူနဲ့ အတူဆုံး ပုံ ၉ ပုံကို training image တွေထဲက ရွေးထုတ်ဖို့ပါ။ အဲလို လုပ်ပေးမယ့် ပရိုဂရမ်ကို အောက်မှာ ပြထားပြီး Octave terminal မှာ IF3_Retrieve; လို့ ရိုက်ထည့်ပြီး run နိုင်ပါတယ်။

%File name: IF3_Retrieve.m
%Author: Yan Naing Aye
%Website: http://cool-emerald.blogspot.com/
%-------------------------------------------------------------------------
%Get test file
featureDir=[pwd,'/test_png/'];
[uFileName,uPathName] = uigetfile('*.png','Select a test file',featureDir);
[pathstr, fileNameWithoutExt, ext, versn] = fileparts(uFileName);
featurefile=[featureDir, fileNameWithoutExt,'.png.harhes.sift'];
%-------------------------------------------------------------------------
%show test image
figure;
imshow([pwd,'/test_png/', fileNameWithoutExt,'.png']);
title('Test image');
%-------------------------------------------------------------------------
%Get histogram for test image
figure;
h=GetHistOfVT(featurefile);
bar(h);
title('Test image histogram');
%-------------------------------------------------------------------------
%Load trained data
load('AllHistVT.dat');
n=size(AllHistVT,1);
cDist=zeros(n,1);
for i=1:n; cDist(i)=ChiDist(h,AllHistVT(i,:)); end
[B, IX] = sort(cDist);
%-------------------------------------------------------------------------
%Get folder path and wildcard
imgDir=[pwd,'/training_png/'];
imgFile=[imgDir, '*.png'];
%Get file list and number of files
fileList=dir(imgFile);
%-------------------------------------------------------------------------
%Show results
nr=9;%number of results to show
%settings
cs=3;
rs=ceil(nr./cs);
figure;
for i=1:nr
    imgFileName=fileList(IX(i)).name;
    subplot(rs,cs,i);
    imshow([imgDir,imgFileName]);
    title(['Rank ',num2str(i)]);    
end
%-------------------------------------------------------------------------



သပ်သပ်ဖယ်ထားတဲ့ test_png ထဲက နမူနာ စက်ဘီးပုံ test image တစ်ခု ပေးလိုက်တဲ့ အခါ training_png ထဲက စက်ဘီးပုံ တွေကို စက္ကန့် အနည်းငယ် အတွင်း မှန်ကန်စွာ ထုတ်ပ​ေးနိုင်တာ တွေ့ရပြီး၊ လူပုံ ပေးကြည့်တဲ့ အခါမှာ လည်း ခပ်ဆင်ဆင် လူပုံတွေကို ထုတ်ပေးနိုင်တာကို တွေ့ရပါတယ်။


ရေးထားတဲ့ ကုဒ်ဖိုင်တွေကို ဒီမှာ download လုပ်နိုင်ပါတယ်။

No comments:

Post a Comment