Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Upcoming SlideShare
캐빈머피 머신러닝 Kevin Murphy Machine Learning Statistic
Next
Download to read offline and view in fullscreen.

Share

VLFeat SIFT MATLAB application 테크니컬 리포트

Download to read offline

VLFeat SIFT MATLAB application 테크니컬 리포트

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all

VLFeat SIFT MATLAB application 테크니컬 리포트

  1. 1. 1 TECHNICAL REPORT & SIFT 알고리즘을 이용한 플리커 이미지 자동분류. Flickr Image Classification using SIFT Algorism AUTHOR: Jang HyunWoong
  2. 2. 2 SIFT 알고리즘을 이용한 플리커 이미지 자동분류 TITLE SIFT 알고리즘을 이용한 플리커 이미지 자동분류. Flickr Image Classification using SIFT Algorism Executive Summary SIFT 알고리즘은 이미지의 특징점을 추출하는 크기와 기울기 변화에 영향을 안받고 이미지의 특징점을 추출할 수 있는 강력한 알고리즘이다. 이번 연구에서는 이미지를 분류하는 SIFT 알고리즘을 통해 추출된 BoVW(Bag of Visual Words)를 사용하여 플리커 이미지를 분류하는 방법을 제안한다. SIFT 로부터 추출된 대표 features 를 k- means 클러스터링을 수행하여 codebook 즉 BoVW 를 구성한다. 각 이미지의 BoVW 를 히스토그램으로 만든다. 매칭은 유클리드 거리를 통해 매칭한다. 유클리드 거리계산을 고속화하는 kd-tree 를 통해 히스토그램에 들어갈 bin 크기를 계산한다. tree 를 사용하면 빠르고 쉽게 계산이 가능하다. 히스토그램들을 벡터로 보고 SVM 으로 학습시켜서 SVM classifier 를 구성하여 매칭을 한다. 이미지 분류의 이미지 데이터는 이전 연구에서 분류된 데이터베이스에 있는 이미지를 사용한다. 이전 연구에서는 이미지의 태그에서 의미적 연관성을 이용하여 이미지 검색 순위를 Re-ranking 하여 정확도를 높였다. 1 차 분류된 데이터베이스에 SIFT 알고리즘을 사용하여 더욱 정확하게 이미지의 특징점을 추출하고 데이터베이스의 이미지들을 분류하는 기법을 제안한다. Table of Contents 1 Introduction …………………………………………………………………………. 1 1.1 VLFeat - SIFT Algorithm ………………………………………………………. 2 2 Methodology …………………….…………………………………………………… 5 2.1. Setting ………………………………………………………………………... 5 2.2. Experimental_phow_caltech101.m test ……………………………………. 6 2.3. Experimental_phow_caltech101.m (classifier) …………………………..... 7 2.4. Experimental_classifier.m (view) ………………………………………...… 14 2.5. Data analysis ……………………………………………………………….... 19 3 Results ………………………………………………………………………………... 20 4 Discussions …………………………………………………………………………… 21 5 Conclusions …………………………………………………………………………... 21 6 Recommendations …………………………………………………………………… 21 References ……………………………………………………………………………. 21 1: Introduction
  3. 3. 3 기존에 태그 연관성을 이용해서 분류한 데이터 베이스에 이미지의 변형에 강한 SIFT 알고리즘을 적용해서 이미지를 분류한다. 2004 년에 쓰인 David G.Lowe 교수님의 논문을 참고 했고 www.vlfeat.org 에 있는 vlfeat 라이브러리를 사용한다. VLFeat 는 컴퓨터 시각처리 분야에서 유명한 오픈 소스 라이브러리로 HOG, SIFT, MSER, k-means, hierarchical k-means, agglomerative information bottleneck, SLIC superpixels, and quick shift 등의 알고리즘을 매트랩에서 쉽게 구현할 수 있도록 지원하고 있다. 먼저 보편적인 이미지 분류를 테스트할 때 사용되는 데이터 집합인 Caltech101 dataset 을 이용하여 VLFeat 에 있는 SIFT 알고리즘의 성능을 평가했다. http://www.vision.caltech.edu/Image_Datasets/Caltech101/ 에서 dataset 을 다운 받을 수 있다. Caltech101 dataset 은 California Institute of Technology 에서 2003 년 9 월에 Computer Vision 기술개발에 기여하기 위해 만들어진 정제된 이미지 집합이다. Caltech101 에는 총 9146 개의 이미지와 101 개로 구별되는 카테고리가 포함되어 있다. 1.1. VLFeat - SIFT Algorithm 먼저 VLFeat 라이브러리에 있는 vl_phow() 함수의 매칭 단계는 1. 특징점 찾기 2. 표현하기의 단계로 나눌 수 있다고 생각한다. 1. 특징점 찾기 vlfeat 라이브러리를 사용해서 1.특징점 찾기 와 2. 표현하기 단계가 한번에 이루어 진다. This is an example from "An open implementation of the SIFT detector and descriptor - Andrea Vedaldi" Example1. I = imread('test.png'); I = double(rgb2gray(I)/256); [frames, descriptors] = sift(I, 'Verbosity', 1); - http://www.vlfeat.org/overview/sift.html 참고
  4. 4. 4 figure1. 인터페이스를 만들어서 50 개의 특징점만 표현한 예제 이 sift 함수는 4 x K matrix 의 frames , 128 x K matrix 의 descriptors 를 리턴한다. 여기서 frames 는 특징점을 말하는데 (x1, x2)의 센터 ①②위치와 ③시그마(σ) 특징점의 scale, ④세타(θ) 각도 이렇게 4 가지의 정보를 가지고 있다. ①x1 ②x2 ③σ ④θ 그리고 보통 128bit 의 demension 을 가지고 있다. SIFT detector and descriptor 는 이미지의 Gaussian scale space 로 찾아질 수 있다. 이 Gaussian scale space 함수에는 SIFT 에 필요한 수학적인 개념들이 다 들어가 있다. The Gaussian scale space is the function 인데 이다. 일단 가우시안 함수는 통계학에서 배운 정규분포로 볼 수 있다. 가우시안 필터는 이미지 프로세싱에서 이미지를 blur, 흐리게 만들어 주는데 사용한다. 그 이유는 이미지에서 특징을 잡아내야 하는데 이미지 특성상 불필요한 특징들이 있기 때문에 blur 로 중요치 않은 이미지는 흐리게 만들어서 주요한 특징만 잡아내는 것이다. VL_PHOW 함수에는 VL_IMSMOOTH()와 VL_DSIFT()가 함께 있어서 이미지를 blur 하게 하고 특징점을 잡아낸다. 이 함수에는 가우시안과 디텍터가 다 있어서 한번에 다 추출하고 원본 이미지는 유지한다. - http://www.vlfeat.org/matlab/vl_phow.html 참조 이미지의 특징을 분류하는데는 이미지의 경계선을 특징으로 삼을 수 있다. 밝기를 사용해서 밝았다가 갑자기 어두워지는 그 경계를 특징으로 잡을 수 있다. 그러한 변화를 미분으로 뽑아 낼 수 있다. 미분이라는 것은 기울기를 나타내는데 밝기가 달라지는 부분의 기울기를 구할 수 있다.
  5. 5. 5 예를 들어 이미지를 gray 변화 시키고 그렇게 되면 흰색과 검은색으로 구분되는데 흰 부분에서 검은 부분으로 갈 때 그 경계에서 기울기가 발생한다. 미분을 통해 그 기울기를 알 수 있다. 2 차 미분을 통해 다시 검은 부분에서 흰 부분으로 갈 때의 기울기를 구하고 이것으로 시그마를 잘 조절한다면 밝은 점 or 어두운 점을 특징점으로 잡을 수 있다. LoG, 가우시안의 라플라시안은 2 차 미분의 선형 연산이다. 영상을 컨볼루션시키는 것은 먼저 영상을 스무딩 함수로 컴볼루션시킨 후, 그 결과의 라플라시안을 계산하는 것과 같다. 이것이 LoG 의 핵심 개념이다. 영상을 부드럽게 만들어 노이즈를 줄이고, 에지를 검출할 수 있다. SIFT 에서는 DOG(Difference of Gaussian)을 사용하는데 LOG(Laplacian of Gaussian)과 큰 차이가 나지 않는다. DoG 는 가우시안의 차 영상이다. DoG 를 사용하면 LoG 가 사용하는 2 번 미분하는데 계산하는 시간을 줄 일 수 있고 LoG 와 비슷한 효과를 나타낸다. 그래서 DoG 를 사용한다. figure2. DOG 가 만들어지는 과정, LOG 의 결과와 큰 차이가 없지만 LOG 보다 빠르다. 2. 표현하기 [Descriptor] detector(검출기)를 통해 검출된 keypoints(the SIFT frames) 키포인트들은 주변에 키포인트 gradients(기울기)를 검출할 수 있는데 keypoint 주변 4x4 배열의 히스토그램이
  6. 6. 6 8 개의 방향을 가지는 결과를 보여주기 때문에 4x4x8 = 128 이다. 이것 때문에 128bit 를 사용한다. 앞에서 본 128 x K(특징점) 로 표현된다. figure3. 검출된 키포인트 주변 Image gradients 를 Keypoint Descriptor 로 변환. 8 개의 방향으로 나눠진 벡터들을 합하면 Keypoint descriptor 가 나오게 된다. 왜 방향을 8 개로 하는가? 16 개로 하면 더 많이 검출 하지 않을까? 하지만 Lowe 교수님의 논문에 보면 실험해 본 결과 8 개 일 때가 Correct nearest descriptor 의 수치가 가장 높다. 2: Methodology 2.1. Setting - Setup VLFeat Library http://www.vlfeat.org/ 에서 Download 에 있는 'VLFeat binary package' 를 다운 받는다. 다운 받은 압축을 풀고 MATLAB2010 을 실행한다. 매트랩 명령어 창에서 설치 한다. >> run('VLFEATROOT/toolbox/vl_setup') >> run('C:VLFEAT/toolbox/vl_setup') >> vl_version verbose VLFeat version 0.9.16 Static config: X64, little_endian, Microsoft Visual C++ 1500 LP64, Windows_threads, SSE2 4 CPU(s): GenuineIntel MMX SSE SSE2 SSE3 SSE41 SSE42 Debug: no SIMD enabled: yes - 현재 컴퓨터의 환경, 버전을 나타내고 있다. - Phow_Caltech101.m
  7. 7. 7 http://www.vlfeat.org/applications/caltech-101-code.html 에 접속하면 Caltech101 을 테스트 할 수 있는 소스코드가 있다. 이것을 이용해서 이미지 분류를 한다. (버전이 업데이트 되면서 코드가 약간씩 바뀌지만 기본적인 진행은 같다) 2.2. Experimental_phow_caltech101.m test - Caltech101 dataset 에 phow_caltech101 함수를 사용해서 VLFeat 의 SIFT 알고리즘 정확도 측정 phow_caltech101.m 파일 안에 conf.calDir 을 다운받은 Caltech101 dataset 폴더의 이름을 입력한다. conf.calDir = 'D:Caltech101' ; < Confusion Mtrix > 결과는 Confusion Matrix 로 표현 할 수 있다. 먼저 confusion matrix 는 분류기를 통해 분류된 값들의 정확도를 시각적으로 표현한 표이다. 각 표의 원소들은 확률을 나타내는데 그 표의 색을 넣어 표현할 수 있다. confusion matrix 를 표현한 예이다. 빨강색으로 표현될수록 1 에 가까운 확률을 나타내고 파란색에 가까울수록 0 에 가까운 확률을 나타낸다. 이번 연구에서 사용된 것은 matlab 에 내장되어 있는 imagesc 함수로 수치들의 colormap 을 보여준다.
  8. 8. 8 figure4. caltech101 dataset 에 있는 이미지에 phow_caltech101 함수를 사용해서 카테고리를 분류한 결과 (88.00%정확성) conf.numWords = 700 ; selTrainFeats = vl_colsubset(selTrain, 400) ; descrs = vl_colsubset(cat(2, descrs{:}), 10e5) ; phow_caltech101 함수의 셋팅을 바꾼다. figure5. phow_caltech101 함수의 셋팅을 바꾼 후 카테고리를 분류한 결과 (94.00%정확성) VLFeat 의 SIFT 알고리즘의 정확도를 확인할 수 있었다. 2.3. Experimental_phow_caltech101.m (classifier) - 소스코드와 해설 <phow_caltech101.m> source - phow_caltech101 함수의 옵션 셋팅 function phow_caltech101
  9. 9. 9 conf.calDir = 'D:img_test' ; conf.dataDir = 'C:VLFEATdata' ; conf.autoDownloadData = false ; conf.numTrain = 700 ; conf.numTest = 30 ; conf.numClasses = 3 ; conf.numWords = 700 ; conf.numSpatialX = [2 4] ; conf.numSpatialY = [2 4] ; conf.quantizer = 'kdtree' ; conf.svm.C = 10 ; conf.svm.solver = 'pegasos' ; conf.svm.biasMultiplier = 1 ; conf.phowOpts = {'Step', 3} ; conf.clobber = false ; conf.tinyProblem = false ; conf.prefix = 'baseline' ; conf.randSeed = 1 ; conf.vocabPath = fullfile(conf.dataDir, [conf.prefix '-vocab.mat']) ; conf.histPath = fullfile(conf.dataDir, [conf.prefix '-hists.mat']) ; conf.modelPath = fullfile(conf.dataDir, [conf.prefix '-model.mat']) ; conf.resultPath = fullfile(conf.dataDir, [conf.prefix '-result']) ; comment - 프로그램 데이터 기본 셋팅 conf.calDir = 'D:img_test' ; 이미지 데이터가 있는 위치 지정 conf.dataDir = 'C:VLFEATdata' ; 결과 데이터가 저장될 위치 지정 conf.numTrain = 700 ; conf.numTest = 30 ; conf.numClasses = 3 ; train 이미지 수 700 개, test 이미지 수 30 개 지정, 이미지를 가져올 폴더 수 3 개(카테고리) conf.vocabPath = fullfile(conf.dataDir, [conf.prefix '-vocab.mat']) ; vocabulary 데이터 저장 위치는 'c:vlfeat/data/.' conf.histPath = fullfile(conf.dataDir, [conf.prefix '-hists.mat']) ; histogram 데이터 저장 conf.modelPath = fullfile(conf.dataDir, [conf.prefix '-model.mat']) ; model 정보 저장 conf.resultPath = fullfile(conf.dataDir, [conf.prefix '-result']) ; 결과 저장 source - 디렉토리에서 이미지를 불러온다. % -------------------------------------------------------------------- % Setup data % -------------------------------------------------------------------- classes = dir(conf.calDir) ; classes = classes([classes.isdir]) ; classes = {classes(3:conf.numClasses+2).name} ; images = {} ; imageClass = {} ;
  10. 10. 10 for ci = 1:length(classes) ims = dir(fullfile(conf.calDir, classes{ci}, '*.jpg'))' ; ims = vl_colsubset(ims, conf.numTrain + conf.numTest) ; ims = cellfun(@(x)fullfile(classes{ci},x),{ims.name},'UniformOutput',false) ; images = {images{:}, ims{:}} ; imageClass{end+1} = ci * ones(1,length(ims)) ; end selTrain = find(mod(0:length(images)-1, conf.numTrain+conf.numTest) < conf.numTrain) ; selTest = setdiff(1:length(images), selTrain) ; imageClass = cat(2, imageClass{:}) ; save('data_images', 'images'); % GUI 로 images list cell 전달 save('data_selTest', 'selTest'); save('data_selTrain', 'selTrain'); model.classes = classes ; model.phowOpts = conf.phowOpts ; model.numSpatialX = conf.numSpatialX ; model.numSpatialY = conf.numSpatialY ; model.quantizer = conf.quantizer ; model.vocab = [] ; model.w = [] ; model.b = [] ; model.classify = @classify ; comment classes = dir(conf.calDir) ; classes = classes([classes.isdir]) ; classes = {classes(3:conf.numClasses+2).name} ; 이미지 디렉토리에서 첫 번째부터 세 번째 까지 디렉토리를 classes 에 넣는다. images = {} ; - 모든 이미지들의 인덱스가 들어간다. imageClass = {} ;- 이미지의 카테고리가 들어간다. selTrain = find(mod(0:length(images)-1, conf.numTrain+conf.numTest) < conf.numTrain) ; selTest = setdiff(1:length(images), selTrain) ; imageClass = cat(2, imageClass{:}) ; selTest 에는 701~730 인덱스… 30 개의 테스트 이미지의 인덱스가 들어가고 selTrain 에는 701~730..selTest 인덱스를 제외한 2100 개의 이미지의 인덱스가 들어간다. save('data_images', 'images'); % GUI 로 images list cell 전달 save('data_selTest', 'selTest'); save('data_selTrain', 'selTrain'); GUI 로 데이터를 불러오기 위해 이미지 list 를 따로 저장해서 전달한다. vl_phow() 함수를 사용해서 descrs 를 추출했다. 이 특징점을 BoVW(Bag of Visual Words)를 만들기 위해 kmeans 클러스터링을 해야 한다. 먼저 k-means 클러스터링이란 k 개의 센터를 정하고 센터에서 가까운 데이터들을 하나의 집합으로 만들어 가는 것이다. [1]
  11. 11. 11 위의 예는 5 개의 중심점으로 클러스터링 한 결과이다. k-means 의 클러스터의 개수를 적절하게 잘 조절하느냐에 따라서 성능이 결정 될 수 있다. 너무 많거나 적은 클러스터 개수를 설정하면 클러스터가 여러 개로 나뉘어서 정확한 분류가 되지 않을 수 있다. 아래는 vl_kmeans 함수의 설명이다[2] VL_KMEANS(X, 10, 'verbose', 'distance', 'l1', 'algorithm', 'elkan') clusters the data point X using 10 centers, l1 distance, and the Elkan's algorithm. [CBout,esq,j] = kmeans(X,7,CBin); kmeans 함수에 입력데이터 X 를 넣고 7 로 클러스터링하고, 초기값 7 개는 이와 같다. 그러면 반환하는 값은 CBout 은 최종 평균값이 되며, j 에 클러스터링 라벨이 들어가게 된다. vocab = vl_kmeans(descrs, conf.numWords, 'verbose', 'algorithm', 'elkan') ; vl_kmeans 함수에 입력데이터 descrs 를 넣고, conf.numWords(600)으로 클러스터링하고 Elkan's aligorithm 사용한다. vocab 에 저장한다. source - SIFT, K-means 를 사용해서 visual words 단어들을 만든다. % -------------------------------------------------------------------- % Train vocabulary % -------------------------------------------------------------------- if ~exist(conf.vocabPath) || conf.clobber % Get some PHOW descriptors to train the dictionary selTrainFeats = vl_colsubset(selTrain, 600) ; descrs = {} ; %for ii = 1:length(selTrainFeats) parfor ii = 1:length(selTrainFeats) im = imread(fullfile(conf.calDir, images{selTrainFeats(ii)})) ; im = standarizeImage(im) ; [drop, descrs{ii}] = vl_phow(im, model.phowOpts{:}) ;
  12. 12. 12 %phow 는 vl_imsmooth 와 vl_dsift 로 wrapped, 특징점 end % descrs = 1~700 <128x64980> <128x64520> ... %test = cat(2, descrs{:}); %128x28427798 (엑셀에서 합한 값과 같다) descrs = vl_colsubset(cat(2, descrs{:}), 10e5) ; descrs = single(descrs) ; % Quantize the descriptors to get the visual words %centers = vl_kmeans(descrs, K, ‘Algorithm’, ‘Elkan’); vocab = vl_kmeans(descrs, conf.numWords, 'verbose', 'algorithm', 'elkan') ; save(conf.vocabPath, 'vocab') ; save('data_vocab', 'vocab'); else load(conf.vocabPath) ; save('data_vocab', 'vocab'); end model.vocab = vocab ; if strcmp(model.quantizer, 'kdtree') model.kdtree = vl_kdtreebuild(vocab) ; end comment - vocab 만드는과정 selTrainFeats = vl_colsubset(selTrain, 600) ; [ vl_colsubset(x, n) x 의 개수가 n 보다 작으면 랜덤이 아닌 컬럼을 그대로 반영하지만 x 의 컬럼이 더 많다면 랜덤으로 n 의 개수로 랜덤 반영한다. ] 700 개중 600 개를 특징점 트레인 이미지로 뽑았다. (400~selTrain)개수로 여러 번 맞춰봤지만 현재 컴퓨터에서는 600 이 가장 확률이 높았다.) descrs = {} ; 128 x K matrix 의 descriptors 를 리턴 받을 저장소 parfor ii = 1:length(selTrainFeats) im = imread(fullfile(conf.calDir, images{selTrainFeats(ii)})) ; im = standarizeImage(im) ; [drop, descrs{ii}] = vl_phow(im, model.phowOpts{:}) ; %phow 는 vl_imsmooth 와 vl_dsift 로 wrapped, 특징점 end 600 개의 이미지 인덱스를 사용해 im 에 이미지를 하나 하나 불러온 후 standarizeImage()함수로 이미지를 같은 타입으로 만든다.(vlfeat 는 single form 의 이미지를 요구한다). 그 후 vl_phow 함수를 사용해서 model.phowOpts{:} 옵션으로 im 에 있는 이미지의 features(frames 로 구성 - frames(1:2, : )는 특징점의 x, y 좌표(센터값) )와 descriptors 를 구한다. % descrs = 1~700 <128x64980> <128x64520> ... %test = cat(2, descrs{:}); %128x28427798 (엑셀에서 합한 값과 같다) descrs = vl_colsubset(cat(2, descrs{:}), 10e5) ; descrs = single(descrs) ;
  13. 13. 13 descrs 는 1~700 <128x64980> <128x64520> ... 특징점을 가지고 있는데 모든 64980+64520.. 값은 128x28427798 (엑셀에서 합한 값과 같다). descrs = vl_colsubset(cat(2, descrs{:}), 10e5) ; 10e5 개 만큼 뽑는다. descrs = single(descrs) ; single 화 % Quantize the descriptors to get the visual words %centers = vl_kmeans(descrs, K, ‘Algorithm’, ‘Elkan’); vocab = vl_kmeans(descrs, conf.numWords, 'verbose', 'algorithm', 'elkan') ; save(conf.vocabPath, 'vocab') ; save('data_vocab', 'vocab'); 코드북(visual words) 만들기 위해 양자화 한다. vl_kmeas 함수를 사용해 센터 값을 구한다. save(conf.vocabPath, 'vocab') ; 구한 결과 저장 save('data_vocab', 'vocab'); GUI 에 사용하기 위한 결과 저장 if strcmp(model.quantizer, 'kdtree') model.kdtree = vl_kdtreebuild(vocab) ; end vl_kdtreebuild(x) x 데이터의 인덱스로 kd-tree forest 를 만든다. Bag of Visual Words 를 만든다. 먼저 phow features 를 통해 이미지의 특징점을 추출한다. Vector Quantization 양자화 작업을 한다. (vl_kmeans, vl_kdtreebuild, vl_kdtreequery) Spatial histograms 공간 히스토그램을 만들어 BOVW 를 만든다. [4] kd-tree 와 VLFeat vl_binsearch, vl_binsum 은 각 visual words 를 spatial histogram 에 축적한다. kd-tree 로 위에서 만든 vocab 를 vl_kdtreebuild(vocab)함수로 vocab 데이터의 인덱스로 kd-tree forest 를 만들었다. kd-tree 를 만드는 이유는 빠르고 쉽게 visual descriptors 들을 지도화 시킬 수 있기 때문이다. source % -------------------------------------------------------------------- % Compute spatial histograms % -------------------------------------------------------------------- if ~exist(conf.histPath) || conf.clobber hists = {} ; parfor ii = 1:length(images) %모든 카테고리 images % for ii = 1:length(images) fprintf('Processing %s (%.2f %%)n', images{ii}, 100 * ii / length(images)) ; im = imread(fullfile(conf.calDir, images{ii})) ; hists{ii} = getImageDescriptor(model, im);
  14. 14. 14 end hists = cat(2, hists{:}) ; save(conf.histPath, 'hists') ; else load(conf.histPath) ; end save('data_hists', 'hists'); comment - 히스토그램 만드는 과정 hists = {} ; histogram 저장할 hists 만들고 parfor ii = 1:length(images) %모든 카테고리 images % for ii = 1:length(images) fprintf('Processing %s (%.2f %%)n', images{ii}, 100 * ii / length(images)) ; im = imread(fullfile(conf.calDir, images{ii})) ; hists{ii} = getImageDescriptor(model, im); getImageDescriptor 함수에 im 를 넘긴다. 위에서 만든 kd-tree 로 가장 가까운 값을 찾아 경계 영역을 만든다. 양자화시켜서 영역을 나눈다. 히스토그램을 만든다. getImageDescriptor(model, im); 함수안에 standarizeImage(im) 함수를 호출해서 이미지를 표준화 시킨다. Vector Quantization 양자화 작업을 한다. (vl_kmeans, vl_kdtreebuild, vl_kdtreequery) 히스토그램을 만드는 과정이다. source - getImageDescriptor 함수 % ------------------------------------------------------------------------- function hist = getImageDescriptor(model, im) % ------------------------------------------------------------------------- im = standarizeImage(im) ; width = size(im,2) ; %500 height = size(im,1) ; %400 numWords = size(model.vocab, 2) ; %600 % get PHOW features [frames, descrs] = vl_phow(im, model.phowOpts{:}) ; get_frames = frames(:); % quantize appearance switch model.quantizer case 'vq' [drop, binsa] = min(vl_alldist(model.vocab, single(descrs)), [], 1) ; case 'kdtree' binsa = double(vl_kdtreequery(model.kdtree, model.vocab, ... single(descrs), ... 'MaxComparisons', 15)) ; end
  15. 15. 15 for i = 1:length(model.numSpatialX) binsx = vl_binsearch(linspace(1,width,model.numSpatialX(i)+1), frames(1,:)) ; binsy = vl_binsearch(linspace(1,height,model.numSpatialY(i)+1), frames(2,:)) ; % combined quantization bins = sub2ind([model.numSpatialY(i), model.numSpatialX(i), numWords], ... binsy,binsx,binsa) ; hist = zeros(model.numSpatialY(i) * model.numSpatialX(i) * numWords, 1) ; hist = vl_binsum(hist, ones(size(bins)), bins) ; hists{i} = single(hist / sum(hist)) ; end hist = cat(1,hists{:}) ; hist = hist / sum(hist) ; comment - 이미지 히스토그램을 만든다. % get PHOW features [frames, descrs] = vl_phow(im, model.phowOpts{:}) ; - 이미지 im 의 f, d 키포인트의 위치와 속성을 나타 낸다. descrs 는 4x4x128 방향 나타낸다. case 'kdtree' binsa = double(vl_kdtreequery(model.kdtree, model.vocab, ... single(descrs), ... 'MaxComparisons', 15)) ; kdtree 사용 source - standarizeImage 함수 % ------------------------------------------------------------------------- function im = standarizeImage(im) % ------------------------------------------------------------------------- im = im2single(im) ; if size(im,1) > 480, im = imresize(im, [480 NaN]) ; end %convert image to single precision, 이미지 크기가 크면 크기를 줄인다. %size(im,1) 크기가 보통 333. 이미지의 height comment - 이미지 type single 로 변환 이미지 double 을 single 로 만든다. 이미지의 크기가 480 을 넘으면 resize 한다. source % -------------------------------------------------------------------- % Compute feature map % -------------------------------------------------------------------- psix = vl_homkermap(hists, 1, 'kchi2', 'gamma', .5) ; comment - hists 로 커널 맵 구성 (linear SVM solver PEGASOS 를 트레인 하기 위해)
  16. 16. 16 Support Vector Machine (SVM) 을 훈련 SVM 을 훈련한다는 것은 vocab 즉 k-means 로 만들어진 클래스 들의 경계(초평면)을 구하고 테스트하는 데이터가 경계 사이에 들어올 때 값을 판별함으로써 Classification 을 할 수 있다. support vector 라는 것은 두 경계를 나누고 초평면을 이루는 최대값에 있는 트레이닝 데이터라고 할 수 있다. 여기서 보이는 두 점선(경계- 가운데 선분(초평면))을 만드는 트레이닝 데이터가 support vector 라고 할 수 있다. 두 경계를 구하는 것은 수학적인 요소가 있다. 초평면 식 : W*X + b = 0 여기서 W 는 weight vector 이고, b 는 bias 이다. X 가 이루는 초평면이 classification 의 기준이 된다. 새로운 테스트 데이터가 들어오게 되면 에서, 로 판별하는 것이다. support vector 를 구하는 식은 기본적으로 평면과 점의 거리를 구하는 식으로 시작할 수 있다. 고등학교 수학에 나왔던 거 같다. 하지만 SVM 에서 커널이라는 함수를 적용하게 되므로 직접 수학적으로 구하기에는 굉장히 어렵고 vlfeat 라이브러리가 계산을 다 해준다. linear kernel 이 사용되었다. [2] source % -------------------------------------------------------------------- % Train SVM % --------------------------------------------------------------------
  17. 17. 17 if ~exist(conf.modelPath) || conf.clobber switch conf.svm.solver case 'pegasos' lambda = 1 / (conf.svm.C * length(selTrain)) ; w = [] ; %for ci = 1:length(classes) parfor ci = 1:length(classes) perm = randperm(length(selTrain)) ; fprintf('Training model for class %sn', classes{ci}) ; y = 2 * (imageClass(selTrain) == ci) - 1 ; data = vl_maketrainingset(psix(:,selTrain(perm)), int8(y(perm))) ; [w(:,ci) b(ci)] = vl_svmpegasos(data, lambda, ... 'MaxIterations', 50/lambda, ... 'BiasMultiplier', conf.svm.biasMultiplier) ; end % The training struct DATA is created using the function % VL_MAKETRAININGSET. case 'liblinear' svm = train(imageClass(selTrain)', ... sparse(double(psix(:,selTrain))), ... sprintf(' -s 3 -B %f -c %f', ... conf.svm.biasMultiplier, conf.svm.C), ... 'col') ; w = svm.w' ; end model.b = conf.svm.biasMultiplier * b ; model.w = w ; save(conf.modelPath, 'model') ; else load(conf.modelPath) ; end comment source % -------------------------------------------------------------------- % Test SVM and evaluate % -------------------------------------------------------------------- % Estimate the class of the test images scores = model.w' * psix + model.b' * ones(1,size(psix,2)) ; [drop, imageEstClass] = max(scores, [], 1) ; % Compute the confusion matrix idx = sub2ind([length(classes), length(classes)], ... imageClass(selTest), imageEstClass(selTest)) ; confus = zeros(length(classes)) ; confus = vl_binsum(confus, ones(size(idx)), idx) ; save('data_idx', 'idx'); save('data_confus', 'confus'); save('data_scores', 'scores');
  18. 18. 18 save('data_classes', 'classes'); % Plots % figure(1) ; clf; % subplot(1,2,1) ; % imagesc(scores(:,[selTrain selTest])) ; title('Scores') ; % set(gca, 'ytick', 1:length(classes), 'yticklabel', classes) ; % subplot(1,2,2) ; % imagesc(confus) ; % title(sprintf('Confusion matrix (%.2f %% accuracy)', ... % 100 * mean(diag(confus)/conf.numTest) )) ; % print('-depsc2', [conf.resultPath '.ps']) ; save([conf.resultPath '.mat'], 'confus', 'conf') ; comment scores = model.w' * psix + model.b' * ones(1,size(psix,2)) ; [drop, imageEstClass] = max(scores, [], 1) ; scores 에서 SVM 을 사용한 결과가 나온다. 거리가 나오는데 3 카테고리 중 양수로 해당되는 것이 correct 값이다. 1~700 은 첫 번째(bird)가 양수, 701~730 은 테스트되어서 나온다. 이러한 값들을 max 값으로 단순화 시킬 수 있다. (1, 2, 3)이라는 카테고리 수로 단순하게 만든다. idx = sub2ind([length(classes), length(classes)], ... imageClass(selTest), imageEstClass(selTest)) ; sub2ind - 인덱스 값으로 행렬의 같은 위치를 접근 할 수 있다. confus = zeros(length(classes)) ; confus = vl_binsum(confus, ones(size(idx)), idx) ; 3x3 결과를 만든다. 2.4. Experimental_classifier.m (view) <classifier.m> source % --- Executes just before classifier is made visible. function classifier_OpeningFcn(hObject, eventdata, handles, varargin) % This function has no output args, see OutputFcn. % hObject handle to figure % eventdata reserved - to be defined in a future version of MATLAB % handles structure with handles and user data (see GUIDATA) % varargin command line arguments to classifier (see VARARGIN) % Choose default command line output for classifier handles.output = hObject; running = ['run(' '''C:VLFEAT/toolbox/vl_setup''' ')']; eval(running); % Update handles structure guidata(hObject, handles); comment
  19. 19. 19 handles.output = hObject; running = ['run(' '''C:VLFEAT/toolbox/vl_setup''' ')']; eval(running); 처음 어플리케이션이 실행되면서 vl_setup 을 설치한다. guidata(hObject, handles); 결과 반영 source % --- Executes on button press in btn_start. function btn_start_Callback(hObject, eventdata, handles) phow_caltech101 load('data_images'); load('data_selTest'); load('data_selTrain'); load('data_idx'); handles.trainSet = images(selTrain); handles.testSet = images(selTest); handles.idx = idx; set(handles.popupmenu1, 'String', images); load('data_scores'); load('data_classes'); load('data_confus'); load('data_vocab'); axes(handles.axes3); bar(vocab(1:128,1:700),'DisplayName','vocab(1:128,1:700)'); axes(handles.axes4); imagesc(confus) ; title(sprintf('Confusion matrix (%.2f %% accuracy)', ... 100 * mean(diag(confus)/30) )) ; %result table result_sum = confus(1, 1) + confus(2, 2) + confus(3, 3); result_table = [confus(1, 1),confus(1, 1)/30 * 100 ; confus(2, 2), confus(2, 2)/30 * 100; confus(3, 3), confus(3, 3)/30 * 100; result_sum, result_sum/90 * 100]; set(handles.result_table, 'Data', result_table); guidata(hObject, handles); comment - start 버튼을 눌렀을 때 이벤트 phow_caltech101 을 실행 한 후 필요한 파일을 로드한다. phow_caltech101 load('data_images'); load('data_selTest'); load('data_selTrain'); load('data_idx'); load('data_scores'); load('data_classes'); load('data_confus'); load('data_vocab'); handles.trainSet = images(selTrain); handles.testSet = images(selTest);
  20. 20. 20 handles.idx = idx; set(handles.popupmenu1, 'String', images); selTrain 인덱스에 맞는 이미지 데이터를 handles.trainSet 으로 할당한다. popupmenu1 에 이미지 리스트를 셋팅한다. axes(handles.axes3); bar(vocab(1:128,1:700),'DisplayName','vocab(1:128,1:700)'); axes3 에는 히스토그램을 만들어서 나타낸다. axes(handles.axes4); imagesc(confus) ; title(sprintf('Confusion matrix (%.2f %% accuracy)', ... 100 * mean(diag(confus)/30) )) ; axes4 에는 confusion matrix 결과 출력 result_sum = confus(1, 1) + confus(2, 2) + confus(3, 3); result_table = [confus(1, 1),confus(1, 1)/30 * 100 ; confus(2, 2), confus(2, 2)/30 * 100; confus(3, 3), confus(3, 3)/30 * 100; result_sum, result_sum/90 * 100]; set(handles.result_table, 'Data', result_table); guidata(hObject, handles); result 테이블에는 confus 의 값들을 셋팅한다.(정확도(%) 추가) source % --- Executes on button press in btn_show. function btn_show_Callback(hObject, eventdata, handles) img = handles.image; I = single(rgb2gray(img)) ; [f, d] = vl_sift(I); perm = randperm(size(f, 2)); sel = perm(1:50); h1 = vl_plotframe(f(:,sel)); h2 = vl_plotframe(f(:,sel)) ; set(h1,'color','k','linewidth',3) ; set(h2,'color','y','linewidth',2) ; h3 = vl_plotsiftdescriptor(d(:,sel),f(:,sel)) ; set(h3,'color','g') ; %make histogram load('data_hists'); selected_num = single(handles.selected_num); axes(handles.axes2); bar(hists(:, selected_num)); guidata(hObject, handles); comment - show features 버튼을 눌렀을 때 이벤트 img = handles.image; 모든 이미지를 img 로 할당한다. I = single(rgb2gray(img)) ; 이미지를 single 로 하고 gray 로 변환시킨다. (sift 를 하기 위해서) [f, d] = vl_sift(I);
  21. 21. 21 sift 한 후 결과 [frames, descrs]가 나오게 된다. frames 에는 특징점 좌표가 있고 descrs 에는 차원을 표현하는 값이 있다. perm = randperm(size(f, 2)); x, y 위치 값을 랜덤으로 perm 에 나열한다. sel = perm(1:50); 모든 특징점을 나타내면 보기 힘들기 때문에 1~50 까지의 특징점을 sel 에 넣는다. h1 = vl_plotframe(f(:,sel)); h2 = vl_plotframe(f(:,sel)) ; 특징점 표현 set(h1,'color','k','linewidth',3) ; set(h2,'color','y','linewidth',2) ; h3 = vl_plotsiftdescriptor(d(:,sel),f(:,sel)) ; set(h3,'color','g') ; 차원 표현 selected_num = single(handles.selected_num); axes(handles.axes2); bar(hists(:, selected_num)); guidata(hObject, handles); axes2 를 선택 후 선택된 셀의 히스토그램을 표현 source % --- Executes on selection change in listbox1. function listbox1_Callback(hObject, eventdata, handles) contents = cellstr(get(handles.listbox1, 'String')); selected_cell = contents{get(handles.listbox1, 'Value')}; selected_num = get(handles.listbox1, 'Value'); handles.selected_num = selected_num; %img = imread([pathname, filename]); img = imread(['D:img_test', selected_cell]); handles.image = img; axes(handles.axes1); imshow(img); guidata(hObject, handles); comment listbox1 callback 함수 contents = cellstr(get(handles.listbox1, 'String')); 이미지 리스트를 이름으로 채운다. selected_cell = contents{get(handles.listbox1, 'Value')}; selected_num = get(handles.listbox1, 'Value'); handles.selected_num = selected_num; %img = imread([pathname, filename]); img = imread(['D:img_test', selected_cell]); handles.image = img;
  22. 22. 22 선택된 셀의 값을 얻어 그 값이 가리키는 이름으로 이미지를 읽어 img 에 저장한다. axes(handles.axes1); imshow(img); axes1 을 선택한 후 선택된 img 를 출력 source % --- Executes on selection change in listbox2. function listbox2_Callback(hObject, eventdata, handles) contents = cellstr(get(handles.listbox2, 'String')); selected_cell = contents{get(handles.listbox2, 'Value')}; selected_num = get(handles.listbox2, 'Value'); handles.selected_num = selected_num; %img = imread([pathname, filename]); img = imread(['D:img_test', selected_cell]); handles.image = img; axes(handles.axes1); imshow(img); guidata(hObject, handles); comment listbox2 callback 함수 contents = cellstr(get(handles.listbox2, 'String')); 이미지 리스트를 이름으로 채운다. selected_cell = contents{get(handles.listbox2, 'Value')}; selected_num = get(handles.listbox2, 'Value'); handles.selected_num = selected_num; %img = imread([pathname, filename]); img = imread(['D:img_test', selected_cell]); handles.image = img; 선택된 셀의 값을 얻어 그 값이 가리키는 이름으로 이미지를 읽어 img 에 저장한다. axes(handles.axes1); imshow(img); axes1 을 선택한 후 선택된 img 를 출력 source % --- Executes on selection change in popupmenu1. function popupmenu1_Callback(hObject, eventdata, handles) contents = cellstr(get(handles.popupmenu1, 'String')); selected_cell = contents{get(handles.popupmenu1, 'Value')}; selected_num = get(handles.popupmenu1, 'Value'); handles.selected_num = selected_num; %img = imread([pathname, filename]); img = imread(['D:img_test', selected_cell]); handles.image = img; axes(handles.axes1); imshow(img); guidata(hObject, handles);
  23. 23. 23 comment contents = cellstr(get(handles.popupmenu1, 'String')); 이미지 리스트를 이름으로 채운다. selected_cell = contents{get(handles.popupmenu1, 'Value')}; selected_num = get(handles.popupmenu1, 'Value'); handles.selected_num = selected_num; %img = imread([pathname, filename]); img = imread(['D:img_test', selected_cell]); handles.image = img; 선택된 셀의 값을 얻어 그 값이 가리키는 이름으로 이미지를 읽어 img 에 저장한다. axes(handles.axes1); imshow(img); axes1 을 선택한 후 선택된 img 를 출력 source % --- Executes when selected object is changed in uipanel5. function uipanel5_SelectionChangeFcn(hObject, eventdata, handles) switch hObject case handles.rbtn_training %something set(handles.listbox1, 'String', handles.trainSet); case handles.rbtn_test %something set(handles.listbox1, 'String', handles.testSet); end guidata(hObject, handles); % --- Executes when selected cell(s) is changed in result_table. function result_table_CellSelectionCallback(hObject, eventdata, handles) row = eventdata.Indices(1); switch row case 1 row_idx = 1; idx_list = find(handles.idx == 1); set(handles.listbox2, 'String', handles.testSet(idx_list)); case 2 row_idx = 5; idx_list = find(handles.idx == 5); set(handles.listbox2, 'String', handles.testSet(idx_list)); case 3 row_idx = 9; idx_list = find(handles.idx == 9); set(handles.listbox2, 'String', handles.testSet(idx_list)); end guidata(hObject, handles); comment 자동 생성된다.
  24. 24. 24 2.5. Data analysis figure6. 이전 연구에서 수집해서 re-ranking 된 플리커 이미지 카테고리(car) bird, car sea 카테고리를 phow_caltech101 에 넣어서 결과 데이터 파일을 얻는다. 얻어진 결과데이터를 가지고 사용자가 볼 수 있도록 view 를 만든다. figure7. 전체적인 과정
  25. 25. 25 3: Results 카테고리 적합이미지 수 정확도 bird 21 70% car 22 73.33% sea 19 63.33% 합계 62 68.89% table1. 최종결과 데이터 data_scores figure8. 결과 4: Discussion figure4, 5 는 confusion matrix 를 이용해서 시각적으로 매칭된 결과를 알려준다. 빨강색이 강할수록 정확한 매칭이 되었다는 의미이다. figure6 은 이전 연구에서 수집한 이미지 데이터이다. HDD 에 저장되어 있어서 받아서 사용했다. table1 은 data_scores 를 표로 나타냈다. figure8 은 단지 phow_caltech101 로부터 나온 데이터 파일을 받아서 시각적으로 표현 한 것이다. Viewer 에는 50 개만 랜덤으로 뽑아서 나타냈다. 그 이유는 Dense SIFT 로 특징점을 뽑아서 모든 특징점을 다 표현한다면 굉장히 빽빽하고 많아서 구분이 안 간다. 사용자가 편하게 이해할 수 있도록 랜덤으로 50 개만 뽑아서 표현했다. Start 버튼을 클릭하면 vlfeat 라이브러리 가동과 phow_caltech101 이 동작하면서 데이터를 불러온다. 그 후에 원하는 이미지 목록을 클릭하고 Show features 버튼을 클릭하면 50 개의 특징점을 볼 수 있다.
  26. 26. 26 5: Conclusions phow_caltech101 이라는 샘플 코드가 생각보다 굉장히 잘 정리되어있었다. vlfeat 에서 지속적인 업데이트로 정확도나 속도가 빨라질 것이라 생각한다. 6: Recommendations 정확도를 높이는 것이 중요하다. 1. 좀 더 높은 정확도를 얻기 위해 특징점 선택하는 것을 세밀하게 해 볼 필요가 있을 것 같다. 예를 들어 400 개에서 450 개로 지금까지는 100 단위로 해봤다. 2. 새롭게 업데이트 된 vlfeat 버전을 받아서 해보는 것 3. training 이미지 수를 늘려보는 것. 4. 앞으로 해볼 것은 카테고리를 3 개에서 늘려서 4~5 개로 해볼 것 References 1. Andrea Vedaldi, Brian Fulkerson," Vlfeat: an open and portable library of computer vision algorithms", Proceedings of the international conference on Multimedia, New York, NY, USA, 1469– 1472 2. www.vlfeat.org/overview/kmeans.html 3. blog.naver.com/dunopiorg. 4. bagwords.ppt <Bag of Words>. 5. SIFT 정리.ppt <Distinctive Image Features from Scale-Invariant Keypoints>.
  27. 27. 27 전체적인 Summary 시각단어 어휘집(시각어휘 사전) = Visual Library 특징점 = keypoint = Salient image patch Visual word 들의 집합 1. 구성 - 구축 imgA, imgB, imgC, … 여러 개의 이미지에서 image patches 곧 keypoints 를 detect 할 수 있다. 여기서 우리가 사용했던 SIFT or SURF 알고리즘을 통해 keypoints 를 detect & description 한다. image 당 128bit vectors 를 나타낸다. 예를 들어 실험에 사용한 실제 데이터는 이미지를 <128x64990>크기의 vector 로 표현한다. 이러한 데이터가 이미지 개수만큼 있다. 1~500 개의 이미지라고 하면 <128x64990>, <128x82490>, …. , <128x67930> 500 개 모든 이미지 features(keypoint)를 모으면 수십 만개 128bit vectors 가 된다. 이것을 k-means 알고리즘을 가지고 visual word 를 만든다.
  28. 28. 28 vocabulary(word) 수를 600 으로 정하고 싶다면, k-means 의 k 수는 600 이된다. (단어의 수를 정하는 것은 휴리스탁한 방법으로 실험을 통해 정할 수 있다. 정리한 논문이 있음) 이렇게 만들어진 600 개의 words 는 Library(Lib)가 되는 것이다. Lib 를 통해 이미지를 분류한다. 2. 훈련 - 테스트 Bag of Visual word 는 image 를 표현하는 방법이다. 앞에서 만든 codebook(Library)을 가지고 각 image 마다 histogram 을 만든다. Lib 를 가지고 각 이미지에 해당하는 히스토그램을 만든다.
  29. 29. 29 Lib 로 기반한 각 이미지의 histogram 으로 SVM 을 훈련한다. SVM 으로 훈련하고 테스트 데이터를 자동으로 분류한다.
  • kuchangmo

    Apr. 12, 2020
  • changyullee5

    Feb. 18, 2016

VLFeat SIFT MATLAB application 테크니컬 리포트

Views

Total views

2,853

On Slideshare

0

From embeds

0

Number of embeds

622

Actions

Downloads

52

Shares

0

Comments

0

Likes

2

×