[Learn opencv by examples] 5. 영상 이진화, Threshold operation

오늘은 http://opencvexamples.blogspot.com/p/learning-opencv-functions-step-by-step.html에 있는 5번째 예제를 배워보겠습니다. 이 예제를 공부하는데 있어서 http://docs.opencv.org/2.4/doc/tutorials/imgproc/threshold/threshold.html도 큰 도움이 되었습니다.


5. Threshold operation

Threshold operation은 가장 간단한 분할 방법입니다. 분석하고자하는 물체와 배경을 픽셀 세기의 변화에 기반해서 분할합니다. 간단히 하나의 역치값(threshold value)을 설정해서 이것보다 크면 물체로, 이것보다 작으면 배경으로 판단합니다.  

opencv에서는 5가지 유형의 threshold operation을 제공합니다.


1) Threshold Binary 

이 유형은 이미지 내의 픽셀 값이 역치값보다 크면 픽셀세기의 최대값으로, 역치값보다 작으면 0으로 변환해주는 것입니다. 


2) Threshold Binary, Inverted

첫번째 유형과는 반대로, 픽셀 값이 역치값보다 크면 0으로, 작으면 픽셀세기의 최대값으로 변환해주는 것입니다. 


3) Truncate

이 유형은 픽셀 값이 역치값보다 크면 그 픽셀 값들은 모두 역치값으로 변환해주고, 작으면 그대로 나둡니다. 


4) Threshold to Zero

이 유형은 픽셀 값이 역치값보다 큰 부분은 그대로 나두고, 작은 부분은 모두 0으로 변환해주는 것입니다.


5) Threshold to Zero, Inverted

네번째 유형의 반대 경우로, 픽셀 값이 역치값보다 크면 0으로, 작으면 그대로 나둡니다.


소스코드입니다.


#include "opencv2/imgproc/imgproc.hpp"

#include "opencv2/highgui/highgui.hpp"

#include <stdlib.h>

#include <stdio.h>


using namespace cv;


int threshold_value = 0;

int threshold_type = 3;;

int const max_value = 255;

int const max_type = 4;

int const max_BINARY_value = 255;


Mat src, src_gray, dst;

char* window_name = "Threshold Demo";


char* trackbar_type = "Type: \n 0: Binary \n 1: Binary Inverted \n 2: Truncate \n 3: To Zero \n 4: To Zero Inverted";

char* trackbar_value = "Value";


void Threshold_Demo(int, void*);


int main(int argc, char** argv)

{

/// Load an image

src = imread("apple.jpg", 1);


/// Convert the image to Gray

cvtColor(src, src_gray, CV_RGB2GRAY); //RGB 영상을 그레이 영상으로 변환


/// Create a window to display results

namedWindow(window_name, CV_WINDOW_AUTOSIZE); //결과를 나타내기 위한 창을 만든다.


/// Create Trackbar to choose type of Threshold

createTrackbar(trackbar_type,

window_name, &threshold_type,

max_type, Threshold_Demo); //Threshold의 타입을 선택하기 위한 트랙바를 만든다.


createTrackbar(trackbar_value,

window_name, &threshold_value,

max_value, Threshold_Demo);


/// Call the function to initialize

Threshold_Demo(0, 0); 


/// Wait until user finishes program

while (true)

{

int c;

c = waitKey(20);

if ((char)c == 'e') // e를 타이핑하면 프로그램 종료. 

{

break;

}

}

}



void Threshold_Demo(int, void*)

{

/* 0: Binary

1: Binary Inverted

2: Threshold Truncated

3: Threshold to Zero

4: Threshold to Zero Inverted

*/


threshold(src_gray, dst, threshold_value, max_BINARY_value, threshold_type); // Threshold operation 함수 실행


imshow(window_name, dst);

}


threshold 함수들의 입력인수들을 설명하겠습니다.

  • 1번째 인수: 처리를 해줄 이미지입니다.
  • 2번째 인수: 처리가 된 이미지를 대입해줄 변수를 넣어줍니다.
  • 3번째 인수: 역치값을 설정합니다.
  • 4번째 인수: threshold 타입 중에서 THRESH_BINARY와 THRESH_BINARY_INV를 사용할 때 필요한 최대값입니다. (maximum value to use with the THRESH_BINARY and THRESH_BINARY_INV thresholding types.)
  • 5번째 인수: threshold 타입을 결정합니다. THRESH_BINARY, THRESH_BINARY_INV, THRESH_TRUNC, THRESH_TOZERO, THREH_TOZERO_INV 이렇게 다섯가지가 있습니다. 


참고로 트랙바(track bar)란 일정한 범위 내의 특정한 값을 선택하고자 할 때 사용하는 일종의 스크롤 바입니다. 

(참고: http://terms.naver.com/entry.nhn?docId=864874&cid=50371&categoryId=50371)


실행된 결과입니다 (그림 1). 사과들을 잘 분할해낸 것을 볼 수 있습니다. 


그림1. Threshold binary, inverted 타입으로 역치 연산을 한 결과


이것을 응용해서 이번에는 초록색 사과와 빨간 색 사과를 구별해보겠습니다. 그냥 그레이 영상의 픽셀 세기로 역치값 연산을 시행하면 어려울 것 같습니다. 그래서 RGB중에 G이미지를 추출해서 역치값 연산을 해보겠습니다. 

코드는 아래와 같습니다.


#include "opencv2/imgproc/imgproc.hpp"

#include "opencv2/highgui/highgui.hpp"

#include <stdlib.h>

#include <stdio.h>


using namespace cv;


int threshold_value = 0;

int threshold_type = 3;;

int const max_value = 255;

int const max_type = 4;

int const max_BINARY_value = 255;


Mat src, src_g, dst;

char* window_name = "Threshold Demo";


char* trackbar_type = "Type: \n 0: Binary \n 1: Binary Inverted \n 2: Truncate \n 3: To Zero \n 4: To Zero Inverted";

char* trackbar_value = "Value";


void Threshold_Demo(int, void*);


int main(int argc, char** argv)

{

/// Load an image

src = imread("apples.jpg", 1);


namedWindow("Original image", CV_WINDOW_AUTOSIZE); //원본 이미지를 나타내기 위한 창을 만든다.

imshow("Original image", src); // 원본 이미지를 보여줍니다. 


/// RGB영상을 R채널, G채널, B채널로 분리합니다. 

Mat image_r(src.rows, src.cols, CV_8UC1);

Mat image_g(src.rows, src.cols, CV_8UC1);

Mat image_b(src.rows, src.cols, CV_8UC1);


Mat out[] = { image_r, image_g, image_b };

int from_to[] = { 0,2, 1,1, 2,0 };    // BGR 이미지를 RGB로 순서를 바꾸겠다는 의미. 

mixChannels(&src, 1, out, 3, from_to, 3);


src_g = image_g;


namedWindow("G image", CV_WINDOW_AUTOSIZE); //G 이미지를 나타내기 위한 창을 만든다.

imshow("G image", src_g); // G 이미지를 보여줍니다. 


/// Create a window to display results

namedWindow(window_name, CV_WINDOW_AUTOSIZE); //결과를 나타내기 위한 창을 만든다.


/// Create Trackbar to choose type of Threshold

createTrackbar(trackbar_type,

window_name, &threshold_type,

max_type, Threshold_Demo); //Threshold의 타입을 선택하기 위한 트랙바를 만든다.


createTrackbar(trackbar_value,

window_name, &threshold_value,

max_value, Threshold_Demo);


/// Call the function to initialize

Threshold_Demo(0, 0); 


/// Wait until user finishes program

while (true)

{

int c;

c = waitKey(20);

if ((char)c == 'e') // e를 타이핑하면 프로그램 종료. 

{

break;

}

}

}



void Threshold_Demo(int, void*)

{

/* 0: Binary

1: Binary Inverted

2: Threshold Truncated

3: Threshold to Zero

4: Threshold to Zero Inverted

*/


threshold(src_g, dst, threshold_value, max_BINARY_value, threshold_type); // Threshold operation 함수 실행


imshow(window_name, dst);

}


RGB영상을 각 채널로 분리시키는 것은 mixChannels 함수로 가능합니다. opencv에서 칼라이미지는 BGR 순서로 저장되어 있기 때문에, RGB로 순서를 바꾸겠습니다. 

(참고: http://docs.opencv.org/master/d2/de8/group__core__array.html#ga51d768c270a1cdd3497255017c4504be)


mixChannels(const Mat *src, int nsrc, Mat* dst, int ndst, const int *fromTo, size_t npairs);

  • src: 행렬들의 인풋 배열

  • nsrc: src의 행렬의 갯수 

  • dst: 행렬들의 아웃풋 배열

  • ndst: dst의 행렬의 갯수

  • fromTo: 인덱스 쌍들의 배열, 즉 어떻게 채널들을 구성해줄지 설정.

  • npairs: fromTo 내의 인덱스 쌍의 갯수 

실행된 결과입니다 (그림 2). 중간에 있는 Green 채널 이미지를 보시면 초록색 사과가 전반적으로 빨간 색 사과보다 밝습니다. 그렇기 때문에 초록색 사과와 빨간 색 사과를 역치값 연산으로 어느 정도 구별해 낼수 있습니다.


그림 2. 왼쪽부터 차례로 원본 이미지, Green 채널 이미지, 역치값 연산을 실행한 이미지





댓글()

[Learn opencv by examples] 4. RGB 이미지를 그레이 영상 또는 다른 컬러 공간으로 전환하기

오늘은 http://opencvexamples.blogspot.com/p/learning-opencv-functions-step-by-step.html에 있는 네번째 예제를 따라해보겠습니다. 


4. Convert RGB to gray / other color spaces


저는 RGB이미지를 그레이 이미지와 HSV 이미지로 변환해봤습니다. 

그레이 이미지는 밝기 정보로만 이미지를 나타내는 것입니다. 

HSV 이미지는 Hue(색조 or 색상), Saturation(채도),  Value(명도), 이렇게 3가지 성분으로 이미지를 표현하는 것입니다. 왜냐하면 이 3가지가 빛의 3속성이기 때문입니다. 

색조는 색상 자체를 의미하고, 빛의 파장의 길이에 의해 결정됩니다. 눈으로 볼 수 있는 빛인 가시광선에서 파장이 길면 빨간색, 짧으면 보라색 계열로 보입니다. 채도는 색의 순도로 색의 맑고 탁한 정도를 의미합니다. 순색의 경우 채도가 높고, 색을 섞을수록 채도가 낮아져서 탁색 or 회색이 됩니다. 명도는 색의 밝고 어두운 정도입니다. 

먼셀의 표색계인 그림 1을 보시면 색상, 명도, 채도에 대해서 쉽게 이해하실 수 있습니다.

그림 1. 먼셀 표색계 http://tip.daum.net/question/51764357


구현을 위한 소스코드는 아래와 같습니다.

#include <opencv2/core/core.hpp>

#include <opencv2/highgui/highgui.hpp>

#include <opencv2/imgproc/imgproc.hpp>

#include <iostream>


using namespace cv;

using namespace std;


int main()

{

Mat image;

image = imread("hee.jpg", CV_LOAD_IMAGE_COLOR);


if (!image.data)

{

cout << "Could not open or find the image" << std::endl;

return -1;

}


Mat gray_image;

Mat HVS_image;


cvtColor(image, gray_image, CV_BGR2GRAY); // RGB이미지에서 그레이 이미지로 변환

cvtColor(image, HVS_image, CV_BGR2HSV); // RGB이미지에서 HSV 이미지로 변환


namedWindow("RGB image", CV_WINDOW_AUTOSIZE);

imshow("RGB image", image);


namedWindow("Gray image", CV_WINDOW_AUTOSIZE);

imshow("Gray image", gray_image);


namedWindow("HVS image", CV_WINDOW_AUTOSIZE);

imshow("HVS image", HVS_image);


waitKey(0);

return 0;


}


컬러 공간 변화에 사용되는 함수는 바로 cvtColor입니다. 

  • 1번째 인수: 변환할 이미지를 입력해줍니다.

  • 2번째 인수: 변환된 이미지를 대입해줄 변수를 입력해줍니다.

  • 3번째 인수: 어떤 컬러 공간에서 어떤 컬러 공간으로 변환해줄지 설정해줍니다. opencv에서는 매우 다양한 컬러 공간의 변환을 지원하는데, 그것은 http://docs.opencv.org/master/de/d25/imgproc_color_conversions.html#color_convert_rgb_xyz를 참고하세요. 


전시된 이미지 결과는 그림 2에 있습니다. 영상처리에서 자주 사용되는 레나 대신 제 여자친구 사진을 사용했습니다. 

그림 2. 왼쪽부터 순서대로 원본 RGB이미지, 그레이 이미지, HSV 이미지



<참고 자료>

[1] http://darkpgmr.tistory.com/66 => HSV에 관해 참고

[2] http://driz2le.tistory.com/188 => HSV에 관해 참고

댓글()

SIFT (Scale Invariant Feature Transform)의 원리

늦게나마 SIFT에 매료된 후 2004년에 나온 논문[2]을 비롯한 여러 자료들을 살펴보던 와중에, [1]에 포스팅 된 글과 [3]에 한글로 한 대학생이 SIFT에 대해 정리한 PDF 파일이 개인적으로 이해하는데 큰 도움이 되었습니다. 아직 완벽하게 이해된 것은 아니지만 나름대로 최선으로 정리해보겠습니다. 조금 길긴하지만 끝까지 인내심을 갖고 읽어나가신다면 무언가 얻으실 수 있을 것입니다. 혹시 궁금점이 있으시거나 틀린 내용이 있거나 이 글이 도움이 되셨다면, 댓글을 남겨주시거나 공감을 눌러주세요! 


SIFT (Scale-Invariant Feature Transform)은 이미지의 크기와 회전에 불변하는 특징을 추출하는 알고리즘입니다. 서로 다른 두 이미지에서 SIFT 특징을 각각 추출한 다음에 서로 가장 비슷한 특징끼리 매칭해주면 두 이미지에서 대응되는 부분을 찾을 수 있다는 것이 기본 원리입니다. 즉, 그림 1과 같이 크기와 회전은 다르지만 일치하는 내용을 갖고 이미지에서 동일한 물체를 찾아서 매칭해줄 수 있는 알고리즘입니다. 


그림 1. SIFT로 이미지 매칭한 예


위에 두 이미지에서 동일한 부분인 책을 찾아서 매칭해준 것을 확인할 수 있습니다. 중요한 것은 이미지의 크기도 다르고, 책의 회전? 방향도 다르고, 다른 물체들에 조금씩 가려져 있는데도 일치되는 부분을 잘 찾아서 매칭해주었습니다. 이것이 바로 SIFT의 장점입니다. SIFT는 크기나 회전에 불변하는 특징을 찾아내줍니다. 위의 이미지들은 흑백이라 조금 보기 안 이쁘니 다른 예를 하나 더 살펴보겠습니다. 그림 2와 그림 3을 보시죠 (참고로 이 사진은 부산에 아는 형 결혼식 갔다가 찍은 사진입니다.) 그림 2에서는 왼쪽 아래에 있는 빨간 색 차를 매칭해봤습니다. 차 사진이 회전되었고, 좀 더 흐려졌음에도 잘 매칭되었음을 확인할 수 있습니다.  


그림 2. SIFT를 이용해서 두 이미지 안에 있는 빨간 색 차를 매칭


그림 3에서는 표지판을 매칭했는데, 두 포인트 정도 완전히 엇나간 것을 제외하고는 비교적 잘 매칭되었습니다.


그림 3. SIFT를 이용해서 두 이미지 안에 있는 표지판을 매칭

  

이런 식으로 작동하는 SIFT는 파노라마 영상을 만들 때도 사용됩니다. 파노라마 사진 만드는 알고리즘을 보진 않았지만 아마도 여러 장 조금 겹치게 찍은 사진에서 서로 겹치는 부분을 찾아서 옆에 붙이고 붙여주는 원리를 갖고 있지 않을까 싶네요. 그리고 SIFT는 두 장의 이미지가 좌우로 조금 다른 수평 디스패리티를 갖고 있는 스테레오 이미지를 매칭할 때도 사용될 수 있습니다. 


그렇다면, 도대체 SIFT 알고리즘은 어떤 순서를 갖고 있기에 회전과 크기 변화에도 robust하게 서로 매칭되는 부분을 잘 찾아낼 수 있는 것일까요? 절차를 하나씩 잘 살펴보겠습니다. 대략적으로 아래와 같은 순서로 진행됩니다. 


1. "Scale space" 만들기

2. Difference of Gaussian (DoG) 연산

3. keypoint들 찾기

4. 나쁜 Keypoint들 제거하기

5. keypoint들에 방향 할당해주기

6. 최종적으로 SIFT 특징들 산출하기


자, 그러면 하나씩 살펴보도록 하겠습니다. 



1. "Scale space" 만들기 


첫번째로 "Scale space" 만들기입니다. Scale space에 대한 개념은 아래 링크를 참고하세요.(http://bskyvision.com/144) 먼저 원본 이미지를 두 배로 크게 만든 다음에 점진적으로 블러되게 만듭니다. 그리고 원본 이미지를 점진적으로 블러되게 만듭니다. 그 다음에는 원본 이미지를 반으로 축소시킨 이미지에서 또한 점진적으로 블러된 이미지들을 만들어냅니다. 그리고 또 반으로 축소시킨 다음에 점진적으로 블러된 이미지들을 만들어냅니다. (원본 이미지를 일단 2배로 키우는 이유는 나중에 Difference of Gaussian (DoG) 이미지를 만들 때 같은 옥타브 내에서 인접한 2개의 블러 이미지를 활용해서 만들고, 또 그렇게 생성된 DoG 이미지들 중에서 인접한 세 개의 DoG 이미지를 활용해서 Keypoint들을 찾기 때문입니다.) 그림 4에 구현한 결과를 나타냈습니다. 


그림 4. Scale space 만든 결과


blurring, 즉 흐리게 만드는 것은 가우시안 연산자와 이미지를 컨볼루션 연산해줌으로 가능합니다. 공식으로 나타내면 아래와 같습니다. 


...(1)


여기서 L은 블러된 이미지이고, G는 가우시안 필터이고, I는 이미지입니다. x, y는 이미지 픽셀의 좌표값이고, 는 "scale" 파라미터입니다. 이거 이름을 왜 scale 파라미터라고 했는지는 잘 모르겠지만, 아무튼 블러의 정도를 결정짓는 파라미터입니다. 값이 클수록 더 흐려집니다. 아래는 가우시안 필터의 공식입니다. 


...(2)

점차적으로 블러된 이미지를 얻기 위해서 값을 k배씩 높여갔습니다. 처음 값을 으로, k 는 로 설정했습니다. 2배로 키운 원본이미지가 처음에는 의 값으로 블러되었다면, 그 다음에는 1이 되고, 그 다음에는 , 또 그 다음에는 2의 값으로 블러가 되는 방식입니다. 점점 더 흐려진 결과를 볼 수 있습니다. 원본 이미지에서는 에서 시작해서 k배씩 점차적으로 블러되게 해줍니다. 그리고 원본 이미지의 크기를 가로, 세로 각각 반씩 줄인 다음에는 에서 시작해서 k배씩 점차적으로 블러되게 해줍니다. 같은 사이즈를 갖지만 다른 블러의 정도를 갖는 이미지가 각각 5장씩 존재하고, 총 4개의 그룹을 형성하게 됩니다. 여기서 같은 사이즈의 이미지들의 그룹을 octave라고 명칭합니다. 즉, 세로로 배열되어 있는 이미지들이 한 옥타브를 형성합니다. 아마도 우리가 어떤 물체를 볼 때 거리에 따라 명확하거나 흐리게 보이기도 하고, 크거나 작게 보이기도 하는 현상을 담으려고 이런 방법을 쓰는 것이 아닐까 싶습니다. 이 과정 덕분에 SIFT 특징은 이미지의 크기에 불변하는 특성을 갖게 됩니다. 



2. DoG 연산


전 단계에서 저희는 이미지의 scale space를 만들었습니다. 다양한 scale값으로 Gaussian 연산처리된 4 그룹의 옥타브, 총 20장의 이미지를 얻었습니다. 이제 Laplacian of Gaussian (LoG)를 사용하면 이미지 내에서 흥미로운 점들, 즉 keypoint들을 찾을 수 있습니다. LoG 연산자의 작동 원리를 간단히 말하자면, 먼저 이미지를 살짝 블러한 다음에 2차 미분을 계산합니다. 식으로 나타내면 다음과 같습니다.


...(3)


LoG 연산을 통해서 이미지 내에 있는 엣지들과 코너들이 두드러지게 됩니다. 이러한 엣지들과 코너들은 keypoint들을 찾는데 큰 도움을 줍니다. 하지만 LoG는 많은 연산을 요구하기 때문에, 비교적 간단하면서도 비슷한 성능을 낼 수 있는 Difference of Gaussian (DoG)로 대체합니다. LoG, DoG 모두 이미지에서 엣지 정보, 코너 정보를 도출할 때 널리 사용되는 방법들입니다. DoG는 매우 간단합니다. 전 단계에서 얻은 같은 옥타브 내에서 인접한 두 개의 블러 이미지들끼리 빼주면 됩니다. 그림 5는 그 과정을 설명해줍니다. 

그림 5. SIFT 내에서의 DoG 연산 [2]


이 과정을 실제 이미지로 구현한 결과는 그림 6에 있습니다. 


그림 6. DoG 연산을 실제로 구현한 결과


중간에 있는 DoG 이미지들 그냥 검은색 이미지 아닙니다. 컴퓨터 화면이 얼룩진건가 하시는 분들도 있을 텐데 자세히 봐보세요. 얼룩말의 형상이 보일 것입니다. 그래도 잘 안보이는 것 같아서, 정규화한 버전의 DoG 이미지들도 오른쪽에 추가했습니다. 이미지의 엣지 정보들이 잘 도출된 것을 확인할 수 있습니다. 이 과정이 모든 octave에서 동일하게 진행됩니다. 그러면 결과적으로 4개씩, 16장의 DoG이미지를 얻겠죠?? 

여기서 잠깐 LoG와 DoG에 대해 좀 더 깊이 살펴보고 지나가겠습니다. LoG를 DoG로 대체할 때 시간이 적게 걸린다는 것 외에도 장점이 더 있습니다. LoG는 scale 불변성을 위해 로 라플라시안 연산자를 정규화(normalization)해줘야합니다. 즉, LoG연산자가 아래와 같이 scale-normalized LoG로 변합니다. 


...(4)


scale-normalized LoG의 극대값, 극소값은 매우 안정적으로 이미지 특징을 나타냅니다. 그래서 이 극대값, 극소값들은 keypoint의 후보가 되는 것입니다. 그러면 도대체 어떻게 LoG가 DoG로 대체될 수 있을까요? 이것을 증명하기 위해서 열 확산 방정식이 응용됩니다. 


...(5)


열 확산 방정식에 의해 이런 관계가 형성된다고 합니다. Gaussian을 에 대해 미분한 것은 LoG에 를 곱한 것과 같다는 의미죠. 이것은 미분함수의 성질을 이용하면 다음과 같이 전개될 수 있습니다.


...(6)


양변에 우변의 분모를 각각 곱해주면, 아래와 같은 식이 나오게 되죠.


...(7)


결국 다른 scale을 갖고 있는 Gaussian 이미지들끼리의 합, 즉 DoG는 scale-normalized LoG에 (k-1)을 곱한 것과 거의 같습니다. 따라서 DoG는 scale 불변성을 보장하는 scale 정규화 과정을 자연스럽게 포함하게 됩니다. 그리고 곱해진 (k-1)은 극대값, 극소값을 찾는데는 아무런 영향을 주지 않기 때문에 무시해도 괜찮습니다. 암튼 이러한 방식으로 LoG는 DoG로 무리없이 잘 대체됩니다. (오히려 상당부분 이득을 보면서 대체됩니다.) 

이제 이 DoG 이미지들을 활용해서 흥미있는 keypoint들을 찾아낼 것입니다. 



3. keypoint들 찾기


이제 DoG 이미지들에서 keypoint들을 찾을 차례입니다. 먼저 DoG 이미지들 내에서 극대값, 극소값들의 대략적인 위치를 찾습니다. 그림 7에 극대값, 극소값의 위치를 찾는 방법이 설명되어 있습니다. 


그림 7. DoG이미지에서 극소값, 극대값 찾는 원리


한 픽셀에서의 극대값, 극소값을 결정할 때는 동일한 octave내의 세 장의 DoG 이미지가 필요합니다. 체크할 DoG이미지와 scale이 한 단계씩 크고 작은 DoG 이미지들이 필요합니다. 지금 체크할 픽셀 주변의 8개 픽셀과, scale이 한단계씩 다른 위 아래 두 DoG 이미지에서 체크하려고 하는 픽셀과 가까운 9개씩, 총 26개를 검사합니다. 만약 지금 체크하는 픽셀의 값이 26개의 이웃 픽셀값 중에 가장 작거나 가장 클 때 keypoint로 인정이 됩니다. 그러므로 4장의 DoG 이미지들 중에서 가장 위에 있고 가장 아래에 있는 DoG 이미지들에서는 극대값, 극소값을 찾을 수 없습니다. 이런 방식으로 DoG 이미지의 모든 픽셀에서 Keypoint들을 찾습니다. 좀 더 확실한 이해를 위해 이 과정을 실제 이미지로 구현해보겠습니다. 이번에도 octave1에서의 결과만 보이도록 하겠습니다. 그림 8을 확인하세요. 나머지 octave들에서도 동일한 연산이 이루어진다는 것을 잊지 마세요. 


그림 8. 극대값, 극소값 찾기


흰색 점들로 표현된 곳이 극대값, 극소값들입니다. 역시 잘 안보이지만 모니터의 각도를 좀 바꿔보면 잘 보이는 각도가 있을거에요. 암튼 이 점들이 일차적으로 keypoint들이 됩니다. 여기서 보면 두 장의 극값 이미지를 얻기 위해서는 4장의 DoG이미지가 필요하다는 것을 알 수 있습니다. 이를 위해서 scale space를 만들 때 5 단계의 가우시안 블러 이미지를 만든 것입니다. 5장의 블러 이미지에서 4장의 DoG 이미지가 나오고, 또 4장의 DoG 이미지에서 2장의 극값 이미지가 나오는 거죠. 네 그룹의 octave를 갖고 있으니, 8장의 극값 이미지를 얻게 됩니다. 그런데, 이렇게 얻은 극값들은 대략적인 것입니다. 왜냐하면, 그림 9에 설명한 것과 같이 진짜 극소값, 극대값들은 픽셀들 사이의 공간에 위치할 가능성이 많기 때문이죠. 


그림 9. 진짜 극값의 위치


그런데 우리는 이 진짜 극소값, 극대값들의 위치에 접근할 수 없습니다. 그래서 subpixel 위치를 수학적으로 찾아내야 합니다. subpixel은 이렇게 픽셀들 사이에 위치한 것을 의미하는 것 같습니다. 좀 더 정확한 극값들을 어떻게 찾아내는가 하면, 바로 테일러 2차 전개를 사용합니다. (여기 테일러 전개에 대한 이론적인 내용은 저도 잘 모르겠네요. 일단 대략적인 개념만 이해하고 넘어가셔도 좋습니다.) 


...(8)


여기서 D는 DoG이미지이고, x는입니다. 이걸 x로 미분해서 0이 되게 하는 x의 값, 즉이 극값의 위치가 됩니다. 계산하면 아래와 같이 구해진다고 합니다. 


...(9)


이 과정은 알고리즘이 좀 더 안정적인 성능을 낼 수 있게 도와줍니다. 



4. 나쁜 Keypoint들 제거하기


이제 전 단계에서 극값들로 찾은 keypoint들 중에서 활용가치가 떨어지는 것들은 제거해줘야 합니다. 두 가지 기준으로 제거해주는데요, 첫번째는 낮은 콘트라스트를 갖고 있는 것들을 제거해줍니다. 그 다음 엣지 위에 존재하는 것들을 제거해줍니다. 

먼저 낮은 콘트라스트의 keypoint들을 제거해주는 과정을 살펴보겠습니다. 간단히 DoG 이미지에서 keypoint들의 픽셀의 값이 특정 값(threshold)보다 작으면 제거해줍니다. 저희는 지금 subpixel 위치에서 keypoint들을 갖고 있기 때문에 이번에도 역시 테일러 전개를 활용해서 subpixel 위치에서의 픽셀값을 구합니다. 간단히 위에서 구한 을 (8)에 대입해주면 됩니다. 그 결과는 (10)과 같습니다.


...(10)


이 값의 절대값이 특정 값보다 작으면 그 위치는 keypoint에서 제외됩니다. 

이제 두번째로 엣지 위에 존재하는 keypoint들을 제거해줍니다. 그 이유를 저는 다음과 같이 이해했습니다. DoG가 엣지를 찾아낼 때 매우 민감하게 찾아내기 때문에, 약간의 노이즈에도 반응할 수 있는 위험이 있습니다. 즉, 노이즈를 엣지로 찾아낼 수도 있다는 것입니다. 그래서 엣지 위에 있는 극값들을 keypoint로 사용하기에는 조금 위험성이 따릅니다. 따라서 좀 더 확실하고 안전한 코너점들만 keypoint로 남겨주는 것이죠. 이를 위해서 keypoint에서 수직, 수평 그레디언트를 계산해줍니다. 수평 방향으로는 픽셀 값이 어떻게 변화하는지, 수직 방향으로는 픽셀 값이 어떻게 변화하는지 변화량을 계산하는 것입니다. 만약 양 방향으로 변화가 거의 없다면 평탄한 지역(flat region)이라고 생각할 수 있습니다. 근데 수직 방향(or 수평 방향)으로는 변화가 큰데, 수평 방향(or 수직 방향)으로는 변화가 적으면 엣지(가장자리)라고 판단할 수 있습니다. 또 수직, 수평 방향 모두 변화가 크면 코너(모서리)라고 판단할 수 있구요. 설명이 부족하다면, 아래 그림10을 보세요.


그림 10. flat 지역, 엣지, 코너 설명


이 중에서 코너들이 좋은 keypoint들입니다. 따라서 우리의 목적은 코너에 위치한 keypoint들만 남기는 것입니다. Hessian Matrix를 활용하면 코너인지 아닌지 판별할 수 있다고 합니다. 이 두 제거 과정을 통해 그림 11에서 확인할 수 있듯이 keypoint의 갯수는 상당히 줄어듭니다. 흰 점들이 상당히 많이 줄어들었죠. 


그림 11. 나쁜 keypoint들 제거한 결과


이제는 이 keypoint들에게 방향을 할당해줄 차례입니다. 



5. keypoint들에 방향 할당해주기


전 단계에서 우리는 적당한 keypoint들을 찾았습니다. 이 keypoint들은 scale invariance(스케일 불변성)를 만족시킵니다. 이제는 keypoint들에 방향을 할당해줘서 rotation invariance(회전 불변성)를 갖게 하려고 합니다. 방법은 각 keypoint 주변의 그레디언트 방향과 크기를 모으는 것입니다. 그런 다음 가장 두드러지는 방향을 찾아내어 keypoint의 방향으로 할당해줍니다. 그림 12와 같이 하나의 keypoint 주변에 윈도우를 만들어준 다음 가우시안 블러링을 해줍니다. 이 keypoint의 scale값(블러의 정도를 결정해주는 파라미터)으로 가우시안 블러링을 해줍니다. 


그림 12. keypoint 주변에서 그레디언트 크기와 방향에 대한 값들 모으기위해 윈도우 설정 [1]


그 다음에 그 안에 있는 모든 픽셀들의 그레디언트 방향과 크기를 다음의 공식들을 이용해서 구합니다.


...(11)


그 결과는 그림 13과 같습니다. 


그림 13. 그레디언트 크기, 방향 계산 결과 [3]


여기서 그레디언트 크기에는 가우시안 가중함수를 이용해서 keypoint에 가까울수록 좀 더 큰 값을, 멀수록 좀 더 작은 값을 갖게 해줍니다. 이 과정은 그림 14에 있습니다. 


그림 14. 그레디언트 크기에 가우시안 가중함수를 곱해준 결과 [3]


이제 각 keypoint에 어떻게 방향을 할당해주는지 살펴보도록 하겠습니다. 먼저 36개의 bin을 가진 히스토그램을 만듭니다. 360도의 방향을 0~9도, 10~19도, ... , 350~359도로 10도씩 36개로 쪼갭니다. 그래서 만약 28도면 3번째 bin을 그레디언트 크기만큼 채우는 것입니다. 여기서 그레디언트 크기는 가중처리된 값입니다. 윈도우 내의 모든 픽셀에서 그레디언트 방향의 값을 이런 식으로 해당하는 bin에 채워줍니다. 그러면 그레디언트 방향에 대한 히스토그램이 완성됩니다. 그림 15를 보시죠. 


그림 15. keypoint 주변 그레디언트 방향 히스토그램 [1]


이런 히스토그램이 만들어집니다. 가장 높은 bin의 방향이 keypoint의 방향으로 할당됩니다. 또 여기서 만약 가장 높은 bin의 80% 이상의 높이를 갖는 bin이 있다면 그 방향도 keypoint의 방향으로 인정됩니다. 한 keypoint가 두 개의 keypoint로 분리되는 것입니다. 마찬가지로 만약 3개의 bin이 가장 높은 bin의 80% 이상의 높이를 갖는다면 총 4개의 keypoint로 분리됩니다. 이제 keypoint들에 방향을 할당해주는 작업이 완료되었습니다. 



6. 최종적으로 SIFT 특징들 산출하기


지금까지 keypoint들을 결정해왔습니다. 우리는 지금 keypoint들의 위치와 스케일과 방향을 알고 있습니다. 그리고 keypoint들은 스케일과 회전 불변성을 갖고 있습니다. 이제 이 keypoint들을 식별하기 위해 지문(fingerprint)과 같은 특별한 정보를 각각 부여해줘야 합니다. 각각의 keypoint의 특징을 128개의 숫자로 표현을 합니다. 이를 위해 keypoint 주변의 모양변화에 대한 경향을 파악합니다. keypoint 주변에 16x16 윈도우를 세팅하는데, 이 윈도우는 작은 16개의 4x4 윈도우로 구성됩니다. 그림 16을 확인하세요.


그림 16. 하나의 keypoint의 특징을 나타내는 128개의 숫자를 얻는 과정


16개의 작은 윈도우에 속한 픽셀들의 그레디언트 크기와 방향을 계산해줍니다. 전 단계에서 했던 것과 비슷하게 히스토그램을 만들어주는데, 이번에는 bin을 8개만 세팅합니다. 360도를 8개로 쪼갤 것입니다. 0~44, 45~89, 90~134, ... , 320~359로 나눠지겠죠? 역시 그레디언트 방향와 크기를 이용해서 bin들을 채웁니다. 결국 16개의 작은 윈도우들 모두 자신만의 히스토그램을 갖게 됩니다. 16개의 작은 윈도우마다 8개의 bin값들이 있으므로, 16 x 8을 해주면 128개의 숫자(feature vector)를 얻게 됩니다. 이것이 바로 keypoint의 지문이 되는 것입니다. 아직 회전 의존성과 밝기 의존성을 해결해야합니다. 128개의 숫자로 구성된 feature vector는 이미지가 회전하면 모든 그레디언트 방향은 변해버립니다. 회전 의존문제를 해결하기 위해 keypoint의 방향을 각각의 그레디언트 방향에서 빼줍니다. 그러면 각각의 그레디언트 방향은 keypoint의 방향에 상대적이게 됩니다.  (예를 들어 keypoint의 방향을 구한 것이 20-29도라면 24.5도를 keypoint 주변 16개의 4x4 윈도우의 방향에서 빼주면, 16개의 윈도우의 방향은 keypoint 방향에 상대적이게 됩니다.) 그리고 밝기 의존성을 해결해주기 위해서 정규화를 해줍니다. 이렇게 최종적으로 keypoint들에게 지문(fingerprint)을 할당해줬습니다. 이것이 바로 SIFT 특징입니다. 


그렇다면 SIFT로 이미지 매칭을 어떻게 하는 것일까요? 먼저 두 이미지에서 각각 keypoint들을 찾고 지문을 달아줬다면, 이 지문값들의 차이가 가장 작은 곳이 서로 매칭되는 위치인 것입니다. 



▶ 글을 마무리 하며..


이제 글을 마무리하려고 합니다. 여전히 부족함이 많다는 생각이 드네요. 이해가 안 되는 부분에 대해서는 질문을 남겨주시면, 좀 더 공부해서라도 답변해드리도록 노력하겠습니다. 또 잘 아시는 분은 지적해주시기 부탁드립니다. 긴 글을 보시느라 수고 많으셨습니다! 



<참고 자료>

[1] http://aishack.in/tutorials/sift-scale-invariant-feature-transform-introduction/

[2] Lowe D G. Distinctive Image Features from Scale-Invariant Keypoints[M]. Kluwer Academic Publishers, 2004.

[3] https://salkuma.files.wordpress.com/2014/04/sifteca095eba6ac.pdf



댓글()
  1. 이전 댓글 더보기
  2. 6학기생 2017.10.20 23:41 댓글주소  수정/삭제  댓글쓰기

    SIFT 이해 하려고 여러글 찾아다녔는데
    제일 쉽게 설명된글 같아요!!
    감사합니다 ㅠㅠ

  3. 재영 2018.02.07 14:10 댓글주소  수정/삭제  댓글쓰기

    안녕하세요, 영상처리쪽을 혼자 공부하고있는 학부생입니다.
    SIFT가 어떤 알고리즘으로 동작하는지 궁금하여 찾던중에 이렇게 좋은 글을 볼 수 있어서 정말 다행입니다.
    근데 읽던 도중 제가 아직 많이 모잘라서 궁금한 점이 생겼는데.
    LoG 가 DoG를 대체 할 수 있다는것을 증명하는 과정에서,
    K 의 값이 너무 크다면, LoG가 DoG를 대체하는데 있어 한계가 있는것인가요??

    • BlogIcon bskyvision 심교훈 2018.02.07 14:28 신고 댓글주소  수정/삭제

      부족한 글 읽어주셔서 제가 감사한걸요^^ 일단 LoG를 DoG로 대체하는 것이구요.. 둘다 하는 일은 엣지를 찾아내는 것입니다. 원래는 LoG가 먼저 나온 개념인데 DoG가 LoG의 역할을 거의 완벽히 대체하고 더 계산적으로도 간단하기 때문에 LoG 대신에 DoG를 주로 사용합니다.^^ 답이 되었는지 모르겠네요 ㅎㅎ

  4. 오로로 2018.03.24 12:05 댓글주소  수정/삭제  댓글쓰기

    SIFT에대한 설명을 찾고있었는데 정말 잘 설명해 놓으신거 같아서 감사의 말이라도 전하고 싶어서 글남깁니다. ㅎㅎ
    설명 정말 잘보고 갑니다.

  5. 잘읽었습니다 2018.05.09 01:10 댓글주소  수정/삭제  댓글쓰기

    상세한 설명감사합니다.

    이해가 안가는 게 몇가지 있어 질문합니다.

    1.Octave 별로 총 8개의 극대,극소 영상들을 얻었는데 나머지 프로세스들을 각각에 동일하게 적용하는 것인가요?


    2. 만약 그렇다면 다 적용해서 각각의 영상들끼리 어떻게 활용하나요?

    3. 글 3번 문단에서 2차 테일러 하는 부분이 겉으로만 보면 이해가 가는데 이걸 구현하려면 어떻게 해야하는 지 난감하네요. 어떤 식으로 hat of x 를 구하도록 구현할지 설명해주실 수 있나요?

    +추가질문
    -> 왜 극대,극소 두 가지 성분 모두를 구하나요? 보통 극댓값을 활용하지 않는지요?



    • BlogIcon bskyvision 심교훈 2018.06.04 16:34 신고 댓글주소  수정/삭제

      질문 감사합니다.^^ 답변이 너무 늦었네요 ㅜㅜ

      1. 옥타브 별로 4장의 DoG 영상들을 얻습니다. 그리고 그 4장의 DoG 영상들을 이용해서 2장의 극값 이미지들을 얻습니다. 총 네 그룹의 옥타브를 갖고 있으니 총 8장의 극값 이미지를 얻습니다.

      2. 8장의 극값 이미지에 있는 모든 극대값, 극소값들이 모두 다 keypoint는 아닙니다. 극대값, 극소값들 중에서 코너에 존재하는 점들 즉, 코너점들만을 keypoint로 남겨줍니다.

      3. 저도 수식적으로 자세히 접근해보지 않아서 이 부분은 설명하기 어려울 것 같습니다. 나중에 좀 더 공부하게 되면 포스팅해야겠네요. 테일러 2차 전개에 대해서..ㅎㅎ

      + 극소값, 극대값 모두를 찾는 이유는 다른 점들에 비해 튀는 점들을 찾는 것이라고 생각하시면 될 것 같습니다.

      충분한 답변이 됬는지 모르겠습니다. 한번 잘 읽어보시고 이해안되시면 다시 질문해주세요.^^

  6. niteZ 2018.05.20 06:20 댓글주소  수정/삭제  댓글쓰기

    영상처리를 공부하는 대학생입니다! 좋은 글 많은 참고가 되었습니다. 쉽게 풀이된 글과 예시가 많은 도움이 되었습니다.
    혹시 keypoint가 서브픽셀 위에 위치하게 하도록 하는 3번의 마지막 부분을 어떻게 구현해야 할 지 식부터도 잘 이해가 되지 않아서 고민입니다 ㅜㅜ 혹시 설명해 주실 수 있으실까요

    • BlogIcon bskyvision 심교훈 2018.06.04 16:35 신고 댓글주소  수정/삭제

      저도 테일러 2차 전개를 수식적으로는 자세히 공부해보지 않아서 지금 답변해드리기는 어려울 것 같습니다. 기회가 되는대로 공부하고 포스팅해야겠네요..^^

  7. 동탄왕서방 2018.05.31 10:40 댓글주소  수정/삭제  댓글쓰기

    좋은 글 감사합니다
    3. Keypoint찾기에서 픽셀을 총 26개를 검사한다고 하셨고, 픽셀의 값이 26개의 이웃 픽셀값 중에 가장 작거나 클 때 keypoint로 인정된다고 하셨는데 여기서 말씀하시는 '픽셀값'이 픽셀의 밝기값인가요?

  8. BlogIcon 정지수 2018.07.09 15:50 댓글주소  수정/삭제  댓글쓰기

    안녕하세요 영상처리를 공부하는 학부연구생입니다. 한가지 질문이 있는데, SIFT 특징점 후보들의 방향을 할당해 주는 과정에서 만드는 Window 사이즈는 어떻게 정해지는 것인지 궁금합니다. 도움을 절실히 바라고 있습니다! 감사합니다.

  9. 궁금합니다 2018.07.09 18:46 댓글주소  수정/삭제  댓글쓰기

    안녕하세요. 이미지 처리를 공부하는 대학생입니다.
    궁금한 사항이 생겨서 댓글 남겨봅니다.
    6.최종적으로 SIFT 특징들 산출하기 단락에서
    `360도를 8개로 쪼갤 것입니다. 0~44, 45~89, 90~134, ... , 320~359로 나눠지겠죠? 역시 그레디언트 방향와 크기를 이용해서 bin들을 채웁니다.`라는 문장이 있는데.

    그레디언트 방향과 크기를 이용해서 bin들을 채우는 것이 정확히 어떠한 과정을 거치는 것인지 궁금합니다.

    예제가 있다면 부탁드리겠습니다.

    • BlogIcon bskyvision 심교훈 2018.08.05 14:38 신고 댓글주소  수정/삭제

      만약에 0~44도라면 0~44도에 해당하는 bin, 즉 통에 그레디언트 크기만큼을 채워줍니다. 이런 방식으로 윈도우 내의 모든 픽셀들에 대해서 처리해주면 8개의 bin은 각기 다른 크기를 갖게 될 것입니다.

      현재 예제를 따로 가지고 있진 않습니다.^^;

  10. 공대생 2018.10.31 10:55 댓글주소  수정/삭제  댓글쓰기

    다양한 그림으로 글보다는 이해가 쉽게 설명이 되어있고 작성자가 완벽하게 이해했다는 느낌이 드네요 많이 배워갑니다. 앞으로 포스팅이 기대되네요

  11. 권용혜 2018.12.02 17:39 댓글주소  수정/삭제  댓글쓰기

    좋은 설명 감사드립니다.

  12. ... 2018.12.09 15:56 댓글주소  수정/삭제  댓글쓰기

    비슷한 feature 끼리 매칭하는 과정이
    대응하는 모든 feature와 distance를 구한 다음 그 중 가장 유사한 걸 매칭시키는 거라고 이해했는데, 맞는 건가요?

    • BlogIcon bskyvision 심교훈 2018.12.12 10:53 신고 댓글주소  수정/삭제

      질문이 정확하게 이해가 안되는데 제가 이해한대로 일단 답변해드리겠습니다^^; SIFT를 간단하게 말하면, 우선 가장 괜찮은 keypoint들을 찾아낸 다음에 각 keypoint들에 지문을 부여해서 지문이 가장 비슷한 애들을 매칭시켜줍니다.

  13. 김준호 2019.03.18 10:37 댓글주소  수정/삭제  댓글쓰기

    안녕하세요 좋은 강의 잘 보았습니다~~ 한가지 의문점이 있어 질문드립니다.
    SIFT로 특징점들을 찾고 나면 객체를 인식할때는 어떤 방식을 쓰는건지 궁금합니다.
    위의 알고리즘대로라면 Key point들만 찾아내는 알고리즘인데 객체를 인식하려면 Key point 간의 관계를 알아야 하지 않나요?

    • BlogIcon bskyvision 심교훈 2019.03.18 22:42 신고 댓글주소  수정/삭제

      질문 감사합니다. :D

      <6. 최종적으로 SIFT 특징들 산출하기>에 그에 대한 설명이 있습니다. 찾아낸 keypoint들에 지문과 같은 특별한 정보를 부여해줍니다. 각 keypoint의 특징을 128개의 숫자로 표현하는 것이죠. 이 숫자가 서로 비슷하면 매칭되는 부분이라고 볼 수 있는 것입니다.

      답이 되셨는지 모르겠습니다^^;

    • 김준호 2019.03.20 14:20 댓글주소  수정/삭제

      한 물체를 인식하고 싶을때 여러개의 Key point 들이 Match가 될텐데 물체를 확인하기 위해서는 몇개정도의 Key point가 벗어나도 되는지에 대한 허용치는 사용자가 정하는 parameter인건가요? 위의 예시중 표지판의 경우 2개정도의 Key point가 벗어났음에도 인식했다 하는것 처럼요. 감사합니다

    • BlogIcon bskyvision 심교훈 2019.03.20 16:18 신고 댓글주소  수정/삭제

      sift를 이용해서 두 영상에서 키포인트를 찾아내서 비슷한 지문값을 가진 것들끼리 매칭해줄뿐 그 이상도 그 이하도 아닙니다. 인식 알고리즘이라고 보긴 어렵고 매칭 알고리즘으로 보는 것이 적당할 듯 싶네요^^

    • 김준호 2019.03.21 10:14 댓글주소  수정/삭제

      좋은 설명 감사합니다~

    • BlogIcon bskyvision 심교훈 2019.03.21 10:19 신고 댓글주소  수정/삭제

      질문해주셔서 감사합니다!

  14. 우서현 2019.05.21 01:10 댓글주소  수정/삭제  댓글쓰기

    컴싸 학부생 한명 살리셨습니다..ㅠㅠ다른글들은 봐도 이해안가던데 이글보고 드디어 이해갔어요 감사합니다!!

  15. 2019.05.26 04:20 댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • BlogIcon bskyvision 심교훈 2019.05.30 12:04 신고 댓글주소  수정/삭제

      좋은 질문 감사합니다. 덕분에 저도 오랜만에 다시 SIFT의 original 논문을 살펴봤습니다.

      SIFT의 original 논문에 의하면, 엣지 위에 있는 keypoint들을 제거해주는 이유를 다음과 같이 설명하고 있습니다.

      DoG가 엣지를 찾아낼때 매우 민감하게 찾아내기 때문에, 약간의 노이즈에도 반응을 할 수 있기 때문이라고 말하고 있습니다. 그러니까 DoG는 노이즈를 엣지라고 찾아낼 수도 있다는 것이죠. 그래서 엣지를 keypoint로 사용하기에는 조금 위험성이 따릅니다. 좀 더 확실하고 안전한 corner들만 남기는 것이죠.

      저의 이해는 이렇습니다.^^

    • 2019.06.09 13:18 댓글주소  수정/삭제

      비밀댓글입니다

    • BlogIcon bskyvision 심교훈 2019.06.09 22:26 신고 댓글주소  수정/삭제

      덕분에 글을 좀더 업그레이드 할수있었습니다 ㅎㅎ 감사합니다^^

  16. BlogIcon ㅇㅇ 2019.06.04 00:23 댓글주소  수정/삭제  댓글쓰기

    테일러 시리즈 학부 1학년 내용 아닌가;;;

  17. BlogIcon ㅇㅇ 2019.06.04 00:51 댓글주소  수정/삭제  댓글쓰기

    헤시안 매트릭스도 곡률이니까 학부 1학년 2학기 때 나오지 않았나? 어메이징하네

  18. 감사합니다 2019.06.20 17:59 댓글주소  수정/삭제  댓글쓰기

    위에 어떤분 배배 꼬이셨는데, 남이 모르는걸 지적하는건 좋지만 꼽주는건 웃기네요. 본인은 얼마나 잘났기에 ^^
    아무튼 글쓴이님께 정말 감사합니다. 덕분에 SIFT 이해에 많은 도움이 되었습니다. 자주 방문할게요~~

  19. 엉뚱이 2019.07.02 17:54 댓글주소  수정/삭제  댓글쓰기

    안녕하세요. 첫 댓글에 달아주신 분이랑 동일한 질문이기는 한데 답변을 읽어도 아직 잘 이해가 안되네요. ㅠㅠ
    제가 여러번 읽어서 이해한 바로는 원본 이미지에서 keypoint의 방향을 빼서 만든 4*4 gradient 행렬과 동일한 이미지를 회전시켰을때 같은 keypoint에서 같은 과정으로 만든 4*4 행렬이 일치하게 되는건가요?

    • BlogIcon bskyvision 심교훈 2019.07.03 15:51 신고 댓글주소  수정/삭제

      질문이 정확하게 이해가 안되긴 하지만, 아래의 문장이 답이 될 것 같습니다.

      예를 들어 keypoint의 방향을 구한 것이 20-29도라면 24.5도를 keypoint 주변 16개의 4x4 윈도우의 방향에서 빼주면, 16개의 윈도우의 방향은 keypoint 방향에 상대적이게 됩니다.

  20. 김정환 2019.08.26 23:40 댓글주소  수정/삭제  댓글쓰기

    안냥하세요. 물체 탐색과 인식에 대해서 혼자 공부하고 있는 늦깍이 학생입니다 ㅎㅎ
    Metric learning과 angular classification를 알아보다가 물체 인식 역사가 복잡해서 또 알아보다가 SIFT까지 오게 되었습니다... (다음은 one-shot learning...)

    keypoints 찾기에서 궁금한 부분이 있어서 이렇게 글을 남기게 되었습니다.

    "그림 9에 설명한 것과 같이 진짜 극소값, 극대값들은 픽셀들 사이의 공간에 위치할 가능성이 많기 때문이죠. " 라는 부분입니다. 그림9을 제시하시면서 극값이 픽셀들 사이의 공간에 존재할 가능성이 많다고 하셨는데요. 제가 이해한 내용은, '이미지는 픽셀들로 구성되는데 픽셀들 사이 공간을 고려하는 것은 불필요하다' 입니다. 마치 오목을 두는데, '+' 표시에 바둑알을 두는 것이 아니라 '+'와 '+' 사이에 바둑알 을 둘 수 있다고 우기고 두는 것 같다는 생각이 들었습니다.


    • BlogIcon bskyvision 심교훈 2019.08.26 23:49 신고 댓글주소  수정/삭제

      좀 더 정확한 위치의 키포인트를 찾기 위해 SIFT 개발자는 이러한 전략을 취했다고 합니다.^^ 이산적인 값은 실제 값의 대략적인 값일 수 밖에 없으니까요.

  21. 박상제 2019.10.07 20:43 댓글주소  수정/삭제  댓글쓰기

    정말 설명 잘하시는것 같습니다!! 감사합니다. 덕분에 궁금증이 해결되었습니다.

제한선을 지키기, 왕상 2:36-45

삶/말씀과 찬양|2017. 5. 23. 11:12

오늘 묵상한 본문은 열왕기상 2:36-46입니다.

왕이 사람을 보내어 시므이를 불러서 이르되 너는 예루살렘에서 너를 위하여 집을 짓고 거기서 살고 어디든지 나가지 말라

너는 분명히 알라 네가 나가서 기드론 시내를 건너는 날에는 반드시 죽임을 당하리니 네 피가 네 머리로 돌아가리라

시므이가 왕께 대답하되 이 말씀이 좋사오니 내 주 왕의 말씀대로 종이 그리 하겠나이다 하고 이에 날이 오래도록 예루살렘에 머무니라

삼 년 후에 시므이의 두 종이 가드 왕 마아가의 아들 아기스에게로 도망하여 간지라 어떤 사람이 시므이에게 말하여 이르되 당신의 종이 가드에 있나이다

시므이가 그 종을 찾으려고 일어나 그의 나귀에 안장을 지우고 가드로 가서 아기스에게 나아가 그의 종을 가드에서 데려왔더니

시므이가 예루살렘에서부터 가드에 갔다가 돌아온 일을 어떤 사람이 솔로몬에게 말한지라

왕이 사람을 보내어 시므이를 불러서 이르되 내가 너에게 여호와를 두고 맹세하게 하고 경고하여 이르기를 너는 분명히 알라 네가 밖으로 나가서 어디든지 가는 날에는 죽임을 당하리라 하지 아니하였느냐 너도 내게 말하기를 내가 들은 말씀이 좋으니이다 하였거늘

네가 어찌하여 여호와를 두고 한 맹세와 내가 네게 이른 명령을 지키지 아니하였느냐

왕이 또 시므이에게 이르되 네가 네 마음으로 아는 모든 악 곧 내 아버지에게 행한 바를 네가 스스로 아나니 여호와께서 네 악을 네 머리로 돌려보내시리라 

그러나 솔로몬 왕은 복을 받고 다윗의 왕위는 영원히 여호와 앞에서 견고히 서리라 하고

여호야다의 아들 브나야에게 명령하매 그가 나가서 시므이를 치니 그가 죽은지라 이에 나라가 솔로몬의 손에 견고하여지니라

출처: 개역개정 성경

시므이는 다윗이 압살롬에게 반역을 당해 도망갈 때 다윗을 모욕하고 저주했었습니다. 그리고 다윗의 군대가 압살롬 군대를 이기고 다시 귀환할 때 목숨만은 살려달라고 나와 애걸했었습니다. 그때 다윗은 살려주겠다고 약속을 하고 돌려보냈습니다. 솔로몬은 이런 시므이에게 활동영역을 제한하는 징계를 내립니다. 예루살렘을 벗어나지 말라고 엄중히 명령했습니다. 시므이는 이 명령을 3년 동안은 잘 지켰습니다. 하지만 종이 가드로 도망 갔다는 소식을 듣고 직접 잡으러 갑니다. 그리고 이 사실이 솔로몬 왕에게 전해지고 결국 사형에 처해집니다. 

어찌보면 왕을 모욕하고 저주했던 시므이에게 내려진 명령은 은혜였습니다. 예루살렘 내에서는 모든 자유를 누릴 수 있었습니다. 종도 여럿 있었던 사람이기에 다른 지역에 볼 일이 있으면 충분히 종들에게 임무를 줄 수도 있었습니다. 그런데도 그는 왕의 명령을 저버리고 제한선을 넘어갑니다. 혹시 왕에게 안 들킬 것이라고 자만했던 것일까요? 아니면 왕의 명령을 우습게 여겼던 것일까요? 아니면 예루살렘에만 있는 것이 너무 답답했던 것일까요? 

저에게도 시므이와 비슷한 죄성이 있습니다. 주님이 분명히 하지 말라고 명령해놓으신 것들을 때로는 너무 쉽게 어깁니다. 죄의 삯은 사망(롬6:23)이라고 분명히 말씀하셨는데도 말이죠. 예루살렘을 벗어나지 말라는 명령을 주님을 떠나지 말라는 것으로 해석할 수 있을지도 모르겠네요. 하나님은 저에게 헤아릴 수 없는 자유를 선물해주셨습니다. 울타리만 벗어나지 않으면 됩니다. 그 울타리 밖은 사망의 길입니다. 다른 관점으로 보면 허락된 자유와 은혜를 누리기에도 인생은 짧습니다. 요즘 많은 사람들이 해서는 안되는 것이 있는 것이 무슨 자유라고 말하기도 하지만, 잘 생각해보십시오. 해서는 안되는 것이 없는 사회에 진정한 안정과 행복이 있겠습니까? 예수님을 믿고 있지 않더라도 우리의 양심이 하지 말아야 할 행동에 대해 우리에게 말하고 있지 않습니까? 하나님의 권위를 인정하고 또 자신에게 주어진 한계를 겸손히 인정하고 주어진 울타리 내에서 맘껏 즐기는 것이 진정한 자유를 누리는 것입니다. 저도 이제 그만 반역해야겠습니다. 용서해주신 그분의 은혜를 헛되이하지 않아야겠습니다. 

태그 : 말씀공부

댓글()

호롭터(horopter)와 파눔융합역(Panum's fusion area)

▶호롭터와 파눔융합역이란?


호롭터와 파눔융합역은 모두 양안 비젼(두 눈을 사용해서 세상을 바라봄)에서 흔히 사용되는 용어들입니다. 우리의 두 눈은 수평으로 일정한 거리를 두고 분리되어 있습니다. 그래서 두 눈이 바라보는 장면은 거의 같지만 수평적으로 약간 다릅니다. 이 다름(시차, disparity)을 통해 뇌는 깊이에 대한 정보를 얻습니다. 


호롭터는 관찰자로부터 동일한 거리에 위치한 점들을 잇는 곡선을 의미합니다. 즉, 두 눈으로부터 같은 거리에 위치하여 디스패리티가 존재하지 않는 점들을 이은 곡선입니다. 따라서 호롭터 위에 있는 점들은 깊이감 없이 융합됩니다. 그리고 파눔융합역은 두 망막 이미지들이 하나로 융합될 수 있는 호롭터 주변의 구역을 말합니다. 호롭터 위를 제외한 나머지 파눔융합역에서는 디스패리티가 있기 때문에 깊이감을 가지면서 융합됩니다. 디스패리티가 너무 커서 파눔융합역을 벗어나면, 뇌는 융합에 실패하여 복시현상(diplopia)이 나타나고 눈은 상당한 피로를 느끼게 됩니다 [1][2][3]


그림1. 호롭터와 파눔융합역을 설명하는 그림 [1]

  


<참고 자료>

[1] http://www1.appstate.edu/~kms/classes/psy3203/Depth/horopter2.htm

[2] Lambooij M, Fortuin M, Heynderickx I, et al. Visual Discomfort and Visual Fatigue of Stereoscopic Displays: A Review[J]. Journal of Imaging Science & Technology, 2009, volume 53(3):30201-1-30201-14(14).

[3] Park J, Lee S, Bovik A C. 3D Visual Discomfort Prediction: Vergence, Foveation, and the Physiological Optics of Accommodation[J]. IEEE Journal of Selected Topics in Signal Processing, 2014, 8(3):415-427.


댓글()