참고: https://www.csie.ntu.edu.tw/~cjlin/libsvm/


마지막 과제로 머신 러닝을 이용한 특징점 검출을 했다.

보고서를 따로 작성한 내용이 있어서 목차와 표지, 파일 설명 부분을 제외하고 그대로 옮겼다.

파이어폭스로 글을 옮기면 자꾸만 브라우저가 꺼진다..

줄간격 맞추기도 어렵다.


코드는 맨 하단에.




1. 개요

LBP를 이용하여 특징점에 대한 Training database를 만든 후, 머신 러닝을 이용하여 학습을 통해 적절한 특징점을 찾아낼 수 있는 모델을 완성하는 것을 목표로 한다.


2. Training database 생성

1) SIFT를 이용한 특징점 검출

먼저 Training database를 만들기 전에 특징점을 검출할 수 있는 기능이 있어야 한다. 기존에 과제에서 작성했던 SIFT Detector를 이용해서 특징점을 검출한다.

다만, 기존 SIFT Detector는 검출되는 특징점의 양이 많아서 활용하기에 적절하지 못하므로 약간의 수정을 가한다.

기존 SIFT Detector2개의 Octave에 각각 6장의 Gaussian Filter, 5장의 DoG(Difference of Gaussian)을 사용했지만 특징점의 개수를 줄이기 위하여 1 Octave에서 4번째 DoG에서 검출되는 특징점만 이용하기로 한다.

기존 코드를 바꾸는 것도 좋지만 검출 시간에 큰 영향을 미치지 않기에 기존 제출된 과제물에서 약간의 수정을 한다.


극점을 판단 하는 위치에

 

if (1.6*pow(2, (o + d) / 3.0) > 3.2)

 

줄여서 다시 작성하면  

 

if (((o + d) / 3.0)>1)

 

위와 같은 코드를 작성함으로써 특징점 검출 개수를 줄일 수 있다.


수정된 SIFT 이미지(좌측), 기존 SIFT 이미지(우측)


database에 삽입될 LBP 정보는 특징점을 기준으로 11x11의 패턴을 LBP를 이용하여 자신을 제외한 총 120개의 값을 받아서 저장한다.

LBP 패턴의 값을 뽑아올 때는 특징점을 중심으로 일정 거리의 값들을 비교하여 저장하기 때문에 이미지의 일정 범위의 테두리는 검토하지 않는다. Padding을 시도할 경우, 그만큼 저장할 feature이 늘어나기 때문에 사용하지 않는다.

여러가지 LBP 크기를 검토해보았으나 3x3, 5x5는 크기가 너무 작아서 제외하고 7x7은 각 픽셀에 대한 모든 LBP feature를 저장했을 때 이미지 10장 기준 약 300MB database가 나왔다. 500장 기준 15GB의 용량을 차지하며, 15x15의 경우 18GB의 용량을 차지한다.

11 x 11을 사용한 이유는 크기가 너무 작거나 크지 않고 넓이가 128에 가장 근접했기 때문이다. 특징점 중복 제거를 위한 값을 이용할 때, 사용하기 적합하다고 판단했다.

BSD500의 이미지를 전부 사용할 경우, Positive feature: 25,921, Negative feature: 73,214,579개가 나온다.

 

2) 특징점 중복 제거

특징점이 가질 수 있는 경우의 수는 2(LBP의 넓이-1) 이다

11 x 11의 경우,1,329,227,995,784,915,872,903,807,060,280,344,576 만큼의 경우의 수가 존재한다. 경우의 수가 많기 때문에 각 패턴이 같을 경우는 거의 없지만 같은 경우가 있다면 가치가 떨어진다고 판단했다.

처음 시도한 방법은 각 특징점의 패턴을 기록하기 전에 Vector 컨테이너에 존재하는 값들과 비교를 한 후, 중복이 없으면 Vector 컨테이너에 추가 삽입을 하면서 모든 패턴을 비교한 후에 training database를 생성하는 것을 시도했다.

LBP는 특징점의 픽셀과 주변 픽셀과의 크기 비교를 통해 1 또는 0의 값만 보관하므로 bitset을 이용하여 값을 보관하도록 했다. Bitset은 각 1비트씩 값을 저장하기 때문에 11x11LBP 패턴을 보관할 비트 배열을 생성 및 저장하도록 한다.

Vector 를 이용해서 새로 들어올 패턴과 기존의 패턴들을 비교할 때, Vector는 입력된 자료의 순서대로 저장을 하기 때문에 값을 비교할 때 Vector에 저장된 양이 많아질 수록 느리다. 한 이미지의 특징점 검출 및 Vector에 삽입하기까지 처음에 1초가 걸렸다면 그 다음엔 2, …., 10초로 점점 숫자가 늘어나더니 한 이미지 당 1분이 넘게 걸리게 된다.

Map 컨테이너는 중복 체크 및 정렬에 특화된 컨테이너이다. 특정 값의 삽입을 시도하면 중복 여부를 체크하고 자동으로 정렬해준다. <key, value>의 형식으로 이루어져 있기 때문에 key가 있어야 한다. value에는 LBP 패턴이 들어가고, key에는 중복 및 순서를 확인하기 위한 정보가 들어간다. 앞서 말했듯이 LBP 패턴의 종류는 2(LBP의 넓이-1) 이다. 이를 구분하기 위해서는 120bit를 넘어가는 값을 저장할 수 있어야 한다. 따라서 별도의 구조체를 생성한다.

 

typedef struct int256 { __int64 a = 0; __int64 b = 0;__int64 c = 0;__int64 d = 0;

           bool operator < (const int256 &rhs) const {

                     if (d != rhs.d) return (d < rhs.d);

                     if (c != rhs.c) return (c< rhs.c);

                     if (a != rhs.a) return (a < rhs.a); return (a < rhs.a); } };

 

64bit 변수를 4개를 가지고 있기 때문에 최대 256bit까지 값을 보관할 수 있다. oprator < 함수는 map 컨테이너가 구조체를 가지고서 비교 및 정렬을 할 수 있게 해주는 연산자 오버로딩 함수이다. 최대 256bit까지 허용한 이유는 15x15를 테스트하기 위함이다.

Map 컨테이너를 사용할 경우, feature 검출에 이미지 별로 약 400~600ms의 시간이 소요된다. 많은 양의 이미지를 집어넣어도 빠르게 중복 검출 및 정렬을 할 수 있다는 점이 큰 장점이다.


feature 검출 소요 시간 및 정보


중복 제거를 할 경우, Positive feature: 23,578, Negative feature: 54,438,407개가 나온다. Positive feature의 경우 2,343개의 중복 feature를 제외했으며, Negative feature의 경우 18,776,172개의 중복 feature를 제거했다. 이를 통해 제한된 용량의 database에 들어갈 feature의 종류를 더욱 다양하게 해준다.

 

3) Database에 삽입될 특징점 선정

중복을 제거했지만 database에 전부 집어넣기엔 많은 용량이 필요하다. 모든 feature를 집어넣었다고 해도 Train에 많은 시간을 필요로 하게 된다. 검출된 feature 중에서 적절한 feature를 선정해서 적은 용량과 짧은 시간으로 model을 만들어야 한다.

Positive Feature는 약 5000개를 이용하기로 한다. 25000개를 넣고, Negative Featue3배 정도 넣었을 때도 상당한 시간이 걸렸기 때문에 1/5로 축소한 값이다. 

Negative FeaturePositive Feature의 약 2000배지만 이를 다 집어넣을 수 없기 때문에 Positive3배에 해당하는 양을 database에 삽입한다.

Database에 삽입할 때, 삽입 기준은 Random 및 임계값 사용 등 다양한 방법이 있지만 map이 자동 정렬을 해주기 때문에 조금 더 단순한 방법을 사용한다.

MapKey 값에 따라 삽입을 할 때마다 정렬이 된다. Key값은 LBP feature의 값을 나타낸다. LBP의 넓이가 n일 때,

LBP Feature Key값은 p[0]*20+p[1]*21+p[2]*22+…+p[n-1]*2n-1 이 된다.

, LBP 패턴에 따라 Key값이 정해지며 이를 통해 특정 Key값에서의 LBP 패턴 모양을 알 수 있다. MapKey값을 자동으로 정렬해주므로 LBP Feature도 점진적인 형태로 정렬이 되어있다. 3x3 LBP의 경우, 00000001(2), 00000010(2), 00000011(2), …., 11111111(2) 와 같이 정렬이 된다.

따라서 일정 간격마다 Negative Featuredatabase에 삽입하면 고른 분포로 database에 삽입이 된다.

MapIndex를 사용할 수 없기 때문에 정렬된 Map 내의 ValueVector 컨테이너로 옮긴 후, index를 따라 일정 간격 단위로 값을 database에 저장한다.


map에서 vector로 옮기는 중에 출력된 MapKey

 

Vector 컨테이너에서 Negative Featuredatabase에 삽입하는 조건은 다음과 같다.

 

distanceVal = negaSize / posSize; distanceSmallVal = distanceVal / negativeMulti;

negaVector.reserve(vectorMap_Negative.size());

for (auto elem : vectorMap_Negative) negaVector.push_back(elem.second);

for (int mn = 0; mn < negativeMulti; ++mn)

for (int d = mn * distanceSmallVal; d < negaVector.size(); d += distanceVal){}

 

distanceValnegative vector positive vector의 몇 배인지 확인하는 변수이다. distanceSmallVal는 몇 배인지 알았을 때 negative vectorpositive의 몇 배 만큼 집어넣을지 확인해서 간격을 다시 나눠서 가지는 변수이다.

여기까지 해서 Positive feature 5,128, Negative feature: 15,385개를 database에 삽입했다.

 

3. Feature classification by machine learning

1) LIBSVM

Feature 분류를 위한 라이브러리는 LIBSVM을 사용한다. MLP보다 간단하며 사용 예가 많다는 점에서 적절하다고 판단했다.

목차 2.의 모든 과정은 LIBSVM에서 사용할 적절한 DB를 만들기 위함이다. 용량이 적으면서 적절한 양을 가진 DB가 있으면 빠르게 처리할 수 있고 많은 테스트를 시도해볼 수 있다.

위에서 만든 feature databaseLIBSVM을 이용하여 Train을 하면 다음과 같은 결과와 함께 model이 완성된다.


Feature database의 Train 결과


위의 feature databasetrain했을 때, 11,030 줄의 파일이 하나 생성된다.

이미지 측정을 위해서는 Predict 과정을 거쳐야 하는데, 다음과 같은 방법으로 진행하였다.

Sample에 특정 이미지에 내의 모든 LBP feature값을 기록한다. 특징점의 값은 모두 Negative , 0으로 설정한다. 이러면 모든 픽셀의 LBP PatternPredict를 통해 Negative인지 아닌지 측정을 하며 Negative일 경우 1, Positive일 경우 0을 출력한다. 이를 통해서 샘플 이미지에 대한 특징점을 찾아낼 수 있다.


Predict 결과 99%라는 것은 해당 이미지의 Negative99% 있음을 나타낸다.

 

4. Feature detection from test image

1) Lenna 128 x 128

빠른 시간에 테스트를 해보기 위하여 Lenna 128 x 128 이미지를 사용하였다.

비교 이미지는 SIFT를 사용하며 Train을 하기 위한 조건과 마찬가지로

if (((o + d) / 3.0)>1) 라는 조건을 설정해준다.

 

Predict 결과(좌측), SIFT 수정 결과(가운데), 기존 SIFT 결과(우측)


조금 당황스러운 결과가 나왔는데, 이미지가 작아서 위의 조건을 달면 Lenna 128 x128에서는 특징점이 거의 검출되지 않았다. 따라서 기존 SIFT 결과물과의 비교를 진행한다.

기존 SIFT의 결과는 특징점이 140개가 검출되었고, SVM을 통한 결과는 97개가 검출되었다. 개수는 조금 다르지만 Traindatabase를 생성할 때 이미지별로 각각 100개가 안되는 Positive를 추출해서 입력했던 것을 감안하고 볼 때, 기존 SIFT보다 더 괜찮을 결과가 나왔다.

Predict 결과물은 눈을 인지하고 벽과 같은 빈 공간에 점이 배치되어 있지 않지만 SIFT 결과물은 눈을 특징점으로 인지하지 않고 죄측 상단의 벽에 적절하지 못한 특징점이 존재한다.


 

2) Lenna 512 x 512


Predict 결과(좌측), SIFT 수정 결과(가운데), 기존 SIFT 결과(우측)

 

Lenna 512 x 512에서 재밌는 결과가 나왔다. SIFT 코드를 수정한 이미지는 102개의 특징점, 기존 SIFT 코드로 나온 특징점은 2,123, SVM으로 나온 특징점은 1,447개이다.

개수는 기존 SIFT보다 적지만 특징점의 위치가 벽면에 있는 개수가 훨씬 적고, 눈이나 다린, 머리카락 경계 등, 더욱 정확한 위치를 찾고 있는 것을 보게 된다.


 

3) Trained Image

Predict 결과(좌측), SIFT 수정 결과(가운데), 기존 SIFT 결과(우측)


4) Other Image

Predict 결과(좌측), SIFT 수정 결과(가운데), 기존 SIFT 결과(우측)


5. 평가

SIFT 결과보다 대체적으로 만족스러운 결과를 보여주었다. OpenCV에 내장된 SIFT와 비교를 하진 못했지만 기존에 만들었던 SIFT에 비해서 훨씬 적절한 결과를 제공해준다.

용량과 속도의 제한이 있어서 multi-core LIBLIBRARY 같은 것을 사용해보고자 했지만 정상적으로 작동하지 않아서 용량을 축소하였기에 많은 특징점을 삽입하지 못한 점은 아쉬운 점으로 남는다.



+ Recent posts