Computer Vision2009. 3. 10. 19:14

(주) 글을 읽기 전에
이 글은 저와 같은 OpenCV초보자를 위해서 정리하는 것입니다.
전문가시라면 굳이 읽을 필요가 없습니다.
공부하면서 정리하는 글이라서 서툰 부분이 많이많이 보입니다.


OpenCV로 하는 Rotate를 해보도록 하겠습니다.

1) 공간 확보와 정리
먼저 생성될 공간을 확보해야 하는데요
IplImage *image = cvCreateImage(cvSize(width, height), depth, nChannels);
size는 말 그대로 창의 크기를 말합니다.
depth는 영상을 표현하는 정보의 내용을 의미합니다.
 • IPL_DEPTH_1U : 1비트 양의 정수영상
 • IPL_DEPTH_8U : 8비트 양의 정수영상
 • IPL_DEPTH_16U : 16비트 양의 정수영상
 • IPL_DEPTH_32U : 32비트 양의 정수영상

이 됩니다.

nChannels는 색의 종류를 의미하는 것으로 1이면 흑백 3이면 컬러가 됩니다.

사용을 다 하면 정리해줘야 합니다.
물론 프로그램을 빠져나갈때 정리를 하지 않아도 O/S가 뒤져서 찾아가는 경우도 있지만
원칙은 사용한 프로그램이 다시 그것을 O/S에 돌려줘야 한다는 것입니다.

cvReleaseImage(&image);

를 하여서 정리합니다.

(2) cvPoint2D32f
 2차원 좌표 상에서 32비트 플로팅 포인트로 x와 y좌표를 설정합니다.


(3) WrapAffine
affine transform을 수행합니다.
인자는 다음과 같습니다.

void cvWarpAffine(
 const CvArr* src,  당연히 원본 이미지
 CvArr* dst,    이것도 당연히 결과를 받아내는 이미지
 const CvMat* map_matrix, affine transform을 하는 매트릭스
    int flags=CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS, 이것은 인터폴레이션을 선택
    CvScalar fillval=cvScalarAll(0)     채워지는 값
);

결과 그림은 아래와 같습니다.

사용자 삽입 이미지

끝으로 아래 사이트에 가시면 몇가지 다른 예제를 볼 수 있습니다.
공부에 참고하시기 바랍니다.
http://dasl.mem.drexel.edu/~noahKuntz/openCVTut5.html


아래 코드는 완성된 버전입니다.



 

 #include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include <opencv/cv.h>
#include <opencv/highgui.h>



int main(int argc , char * argv[] ){

    // 이미지 읽기
  IplImage *imgA = cvLoadImage( "4390m.jpg", 1);
  // 대상 이미지 확정
  IplImage *imgB = cvCreateImage( cvGetSize( imgA), IPL_DEPTH_8U, 3);

  const double angle  = 80.0; // 회전 각도를 설정
  const double scale  = 0.8;  // 확대 크기를 설정
 
  // 중심 축은 가운데로...
  CvPoint2D32f center = cvPoint2D32f( imgA->width/2.0, imgA->height/2.0);//회전중심설정
 
  // 그에 따른 매트릭스 만들기
  CvMat *rot_mat = cvCreateMat( 2, 3, CV_32FC1);

  //매트릭스 계산
  cv2DRotationMatrix(
       center, // Source Image의 센터를 정한다.
       angle,  // 이것은 각도 + 값은 시계 반대 반대 방향을 의미한다.
       scale,  // 이미지 크기...
       rot_mat) // 결과를 저장하는 매트릭스 이다.
    ; // 메트릭스 변환


  // affine transform
  cvWarpAffine( imgA,
  imgB,
  rot_mat,
  CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,
  cvScalarAll(0)); // 선형보간
  cvSaveImage( "image_result8.bmp", imgB);

  cvNamedWindow("Rotated Image",0);
  cvShowImage("Rotated Image",(CvArr*)imgB);
  cvWaitKey(0);
  cvDestroyWindow("Roated Image");

  cvReleaseImage( &imgA );
  cvReleaseImage( &imgB );
  cvReleaseMat( &rot_mat);
  return 0;
}

'Computer Vision' 카테고리의 다른 글

OpenCV - Image Mask  (0) 2009.03.16
OpenCV - Extract  (2) 2009.03.12
Rob Hess의 SIFT [5]  (2) 2009.03.02
Rob Hess의 SIFT [4]  (0) 2009.02.25
Rob Hess의 SIFT [3]  (2) 2009.02.23
Posted by GUNDAM_IM
MAC Life2009. 3. 5. 14:02


http://homepage.mac.com/bradster/iarchitect/shame.htm


제목이 특이한데요.. 예전부터 유저 인터페이스에 쬐끔 관심을 가지고 있어서 이것 저것 살펴보다 발견한 사이트입니다.

제목 그대로 그렇게 좋은 인터페이스가 아닌 것들만 모아서 발표한 것입니다. 사이트는 mac.com에 있습니다.

역쉬 맥이라서 그런지 인터페이스에 자신감을 팍팍 가지고 있으니까.. 이런 사이트도 만들어 두는 것 같습니다.


1. 예제로 찾아보면 인터페이스 가이드라인에 따르면, 화면상의 버튼은 활성화가 안되면 활성화가 안되어 있다는 것이 버튼의 이미지로

나타나는 것이 좋다고 합니다. 그래야 유저가 아예 그 버튼 근처로 마우스를 움직이지 않고 다른 해결책을 찾는다는 것입니다.


그런데 아래 그림에서 처럼 MSOffice 95에서는 이런 기능을 아예 고려하지 않았습니다.

“아마 MS의  GUI 디자이너가 이런 비 활성화 그림을 싫어한것 같아서 이렇게 했다” 라는 추측성 글까지 달아놓을 정도로

안좋은 인터페이스의 예가 되었습니다.


사용자 삽입 이미지





2. 아래 글을 읽을 수 있곘습니까 ?


사용자 삽입 이미지



폰트의 가독성이 얼마나 중요한지 나타내는 예제입니다.

위의 그림은 큰 조직의 커다란 소프트웨어 관리 시스템을 위한 교육 자료에 나오는 예제입니다.

이것은 배경에서 부터 폰트 , 색깔 등으 완벽하게 어우러져 가장 안좋은 자료의 예제가 됩니다.


3. 잘못된 GUI


아래 그림을 한번 보시고 잘못된 부분을 찾아보시기 바랍니다.


사용자 삽입 이미지


어디가 잘못되었을까요 ?


바로 탭 윈도우 ( 다이얼로그 박스 위에 있는 선택 탭을 말합니다.) 내에 OK와 Cancel같은 버튼을 배치하였습니다.

그럼 만약 유저가 Character Spacing에가서 이리저리 조절한 뒤에 다시 Font로 와서 OK를 눌러야 하는

불상사가 발생합니다.


아니면 이런 불상사를 방지하기 위해서는 OK 버튼을 양쪽 탭에 모두 배치해야 합니다.


이 예제는 바로 MSWord 6.0에 있던 것입니다.

신기하죠..


그런데 이런 버튼 배열이 없어지지 않고 Office 95까지 내려옵니다.


사용자 삽입 이미지


이런 오류(?)가 지속적으로 살아남는 것은

뭔가 내부에 정확하고 지조 있는 관리 지침이 있으니까 이런 것도 가능하겠구나 생각되는 예제이죠.


4. 탭 윈도우의 또다른 예제


다음 그림은 어디가 잘못되었을까요 ?


사용자 삽입 이미지



어디가 대분류고 어디가 소분류인지 아예 모르도록 디자인 된것이 잘못입니다.

또한 화면의 크기를 고려하면 TAB이 너무 많이 배열되어 있어서 정작 필요한 내용 채우기는

부족해진다는 것이 잘못된 부분입니다.


이것은 IBM에서 만든 주소록 관리 프로그램입니다.


대기업도 이런 실수를 한다는 전형적인 예입니다.

- 음.. 에전에 이 프로그램을 써본적이 있는데



마지막으로


마지막은 인터페이스가 아니고 프레젠테이션에서 수치스러운 / 가장 못난 작품을 이야기할때 항상 나오는 사진입니다.


유명한 빌게이츠의 졸음을 부르는 프레젠테이션 사진입니다.

뭘 설명하는지 본인은 알까요 ?

사용자 삽입 이미지


'MAC Life' 카테고리의 다른 글

나의 다섯번째 애플~~  (1) 2009.03.30
맥에서 개발한다는 것은..  (0) 2009.03.19
Culture Code의 개발 이미지  (0) 2009.01.28
맥에서 히든파일 보기  (0) 2009.01.23
패러럴즈 사용하면서 emacs 키 바꾸기~  (0) 2009.01.17
Posted by GUNDAM_IM
Computer Vision2009. 3. 2. 11:05

유클리드 제곱 거리


이 부분의 구성은 이곳에서 인용하였습니다.


http://www.cherrynet.co.kr/bbs_view.php?s=37&pseq=11&mnid=1



다차원 공간 상에서 두점 사이의 거리를 구하는 것을 말합니다.

다차원이라고 해도 2차원상의 거리를 구하는 것과 비슷한 방법입니다.


점을 (p1, p2, p3, p4,...) (q1, q2, q3, q4, ...) 표기한 경우 유클리디안 거리 공식은 아래와 같습니다.


사용자 삽입 이미지

def euclidean(p,q) :

     sumSq = 0.0


     # 차의 제곱을 더함

       for i in range( len(p) ) :

                sumSq += ( p[i] - q[i] ) **2


     # 루트를 취함

       return ( sumSq**0.5 )


'Computer Vision' 카테고리의 다른 글

OpenCV - Extract  (2) 2009.03.12
OpenCV - Image Rotation & Scale  (0) 2009.03.10
Rob Hess의 SIFT [4]  (0) 2009.02.25
Rob Hess의 SIFT [3]  (2) 2009.02.23
Rob Hess의 SIFT [2]  (0) 2009.02.18
Posted by GUNDAM_IM
Computer Vision2009. 2. 25. 10:20

Rob Hess의 SIFT 


1. kd-tree 란..


http://snisni.net/98

http://zupet.tistory.com/272  - Kd-Tree 구현 참고 사항

http://zupet.tistory.com/395   kd-tree <- 꼭 참고 ~~ 


에 있는 자료를 기반으로 하여서 정리하였습니다. 

개인적으로 이해를 위해서 정리한 자료이니, 이해가 가지 않으시는 분들은 위의 사이트에서

참고하여주시기 바랍니다.



제목인 kd-tree에서 k란 다차원 인 k차원의 공간을 의미합니다.

즉 다차원 트리를 만들고 정리하는 것을 말합니다.


실제로는 binary-tree의 확장 버전으로 이해하시면 됩니다.


공간을 다루는 dB로서 문자 기반의  dB와는 다르게 됩니다.

key 값을 matching 시키는 문제이고, 공간 기반에서 검색합니다.


공간 기반의 검색이란 range 로 표현되는 질의어를 기반으로 검색한다는 뜻입니다.


spatial data는 (공간 데이터는) 점과 선, 다각형 등을 포함하고 있습니다. 이러한 공간상의 데이터를 뒤져서 원하는 값을 찾아내어야 하는데

공간 데이터를 효율적으로 처리하기 위해서는 다음과 같은 특징적 어려움을 알아야 합니다.


① First, spatial data are latge in quantity, complex in structures and relationships

    첫번쨰, 공간 데이터는  크고, 복잡한 구조와 관계를 가지고 있습니다.


② Second, the retrieval process employs complex spatial operators like intersection, adjacency, and containment

   둘째로, 탐색 과정은 복잡한 공간 연산을 기반으로 구현됩니다. 예를들어서

   교차점 탐색, 인접 물 탐색, 묶어 탐색 등등..


③ Third, it is difficult to define a spatial ordering, so conventional techniques cannot be employed for spatial operations

→ spatial indices 필요!

3번째는, 공간상에 순서를 정하는 것이 어렵다는 점이다. 그래서 일반적인 방식으로는 공간상에서 dB를 구성하는 것이 쉽지 않다.

- 공간 인덱스가 필요해진다.




1.1  kd-tree : Binary-tree based indexing


kd-tree는 multi-attribute data index를 binary search하는 tree로, 

k는 표현될 데이터 공간의 차원을 가리킵니다.


각 depth에서, 어떤 branch로 갈 것인가를 결정하는 데에 다른 attribute가 사용이 됩니다.

예를 들어, 2-d (x,y) tree에서는 짝수 depth에서 x 좌표가 branch 기준이라면, 

홀수 depth에서는 y 좌표가 branch 방향의 기준이 됩니다.. 

마지막으로, 이 tree는 꼭 균형을 이룰 필요는 없습니다. 


사용자 삽입 이미지


위의 그림을 보면 제일 먼저 X값을 기준으로 중앙에 있는 A를 선택합니다.

따라서 최초 분리자는 ROOT이며 이것이 A가 됩니다.



이제 다시 A를 가지고 Y축으로 값을  분리합니다. 그럼 B와 E가 선택이 됩니다.

NOTE : 

의문 사항 : A다음에 B와 E를 택하는 이유는 무엇일까 ?

F와 C가 되지 않고 말이지...

답   : B 가 선택이 되는 이유는 A를 중심으로 좌우를 분리하면

좌측에 B/C/D가 존재한다. 이 점에서 Y값을 기준으로 정렬해보면

B가 가운데 온다. 따라서 B가 선택이 되는 것이다.

우측은 그냥 E와 F가 있다 2개의 값이므로 아무값이나 하나 선택

한것이다.


의문사항    : 정말 아무값이나 선택해서 E일까 ?? 


}


B와 E에서 다시 X 축 기반으로 각각 C/D 및 F를 분리합니다.


위의 사항을 그림으로 표현하면 다음과 같습니다.


사용자 삽입 이미지




1.2  Principle


각각의 Node는 각각의 실제 data point를 나타내고, 검색 방향의 지표가 됩니다. 

(left, right 두 개의 child가 있습니다.→ 현재 노드 값 보다 작은가? 큰가?)

한 Node는 5개의 field로 구성되어 있습니다. 


LEFT  Node를 가리키는 포인터 : LOSON(작은 값을 가진 left 자식), 

Right Node를 가리키는 포인터  : HISON(큰 값을 가진 right 자식), 

좌표 값 (x,y,...), : 차원에 따라서 그 차원의 개수만큼 늘어납니다. 

NAME(해당 노드에 대한 정보. 포인터가 들어갈 수도 있다.), 

DISC(좌표 이름을 가리킨다. 즉, 이 노드는 x를 기준으로 했느냐 y를 기준으로 했느냐. 등. ).


1.3  삽입 Insertion


Tree Build에 해당합니다.


Binary search tree에서의 insertion 방법과 유사합니다.

다른 점이 있다면, 앞에서 설명 했듯이, depth마다 비교하는 attribute가 다르다는 것입니다.

 tree의 bottom에 도달하면, 거기에 새 node를 삽입(Insertion)합니다. 


2개의 함수를 먼저 정의합니다.

1) 비교 주어진 노드의 값을 비교하여 값을 반환합니다.

2) 삽입

      2개의 노드 포인터가 주어집니다. 

      하나는 삽입해야할 즉 추가 해야 할 노드 이며,

      다른 하나는 그때 참조하는 값에 해당하는 노드입니다.



Procedure KD_COMPARE(P,Q)

/* 주어진 노드 Q를 기준으로 P가 붙어야할 위치가 어딘지 판단한다.*/


value pointer node P,Q

direction D

기준값을 X축 값을 가지고 해야 한다면 

{

P의 X 좌표가 Q의 X 좌표 보다 작다면

노드의 왼쪽에 붙어야 하고

아니면 오른쪽에 붙어야 한다.

}

아니면

{

P의 Y 좌표가 Q의  Y 좌표 보다 작다면

노드의 왼쪽에 붙어야 하고

아니면 오른쪽에 부터야 한다.

}


붙어야 할 방향 정보를 반환한다.



삽입 ( Insertion)


KD_INSERT(P,R)


value pointer note P : 삽입해야할 노드 이다.

reference pointer note R :  기준 노드를 의미한다.


pointer note F,T

direction Q


만약 R이 NULL 이면,  즉 기준 노드가 없다면 

{

R <- P // 기준 노드는 P가 되고

DISC(P) <- 'X'; // P의 기준 값은 X축 값으로 한다고 표시한다.

}

아니면 즉 기준 노드가 있다면, 

{

   T <- R  /* 

기준 노드인 R을 임시 값으로 복사한다. 이것은

아래 트리 탐색을 위한 반복문에서 반복시 사용되는 인자를

세팅하기 위함이다.

*/


   임시 노드 T가 NULL이 아니고, EQUAL_COORD(P,T) 가 아닌 동안 계속 해서

   {

          F <- T ; /* 직전 노드값을 기억하여 두고 */

  Q <- KD_COMPARE(P,T); /* 삽입할 값과 비교하여 방향을 

    찾아낸다.*/

  T <- SON(T,Q); /* 찾아낸 방향의 노드를 다시 꺼낸다. */

....

/* 꺼낸 노드가  NULL이 아닐때 까지 반복...*/

   }


만약 T가 NULL 이면, /* 즉 트리최종 말단까지 도착하면  */

  {

SON(F,Q) <- P; /* 직전노드의 찾아낸 방향에 P를 삽입한다.

DISC(P) <- NEXT_DISC(F); /* P의 참조 기준 값은 직전 노드의 다음 값이 된다. 

    즉 F의 참조 기준 값이 X였다면, Y가 되고     Y였다면, X (혹은 3차원일 경우 Z) 가 된다.

*/

  }

}



차근 차근 따라가면서 읽어보면 이해 할수 있는 내용입니다.


1.4 탐색 Search


Search 역시 매우 straightforward하다. 흐흐 BST 처럼 찾아 내려가면 된다. 

여기에서 역시, 다른점은 depth마다 비교 기준의 attribute이 다르다는 점 뿐. 


참 그리고, 질의가, 조건 질의가 가능하다는 것이 또 하나 틀린데, 

예를 들어, 'Y>20인 점들을 찾아라' 라든지, '(75,15)에 가까운 점들을 찾아라' 등이 가능하다는 것이다. 


Region Search는 Euclidean distance를 이용하여 구할 수있다. 

node (a,b) 근처 거리 d안에 있는 점들을 다 구하라는 질의가 들어오면, 

우선 (a-d) ≤ x ≤ (a+d), (b-d) ≤ y ≤ (b+d) 안의 점들을 다 구한다. 

그런데 이렇게 구한 점은, 반지름 d인 원 안에 있는 점이 아니라, 

한 변이 2d인 정사각형 안에 들어오는 점이므로, 점들을 구한 다음에 range안에 들어오는 게 

맞는지 확인이 필요하다. 


우선 다음과 같이 트리가 준비되어 있다고 가정하고요


사용자 삽입 이미지


(88,6)을 기준으로 3개의 점 범위 내에 있는 모든 도시를 찾아라 라고 한다면

(85~91 , 3~9)까지가 된다.

따라서 좌측 상단은 ( 85,3 ) 이 되고 우측 하단은 (91,9)가 된다.


우선 처음에 찾는 값은 85,3으로 찾는다. 기준값과 비교해서 오른쪽에 해당함을 확인할 수 있다.


사용자 삽입 이미지



91,9를 가지고 MOBILE을 참조값으로 하여 찾으면 왼쪽 값이 나온다. 

이때 사용되는 참조 변수는  Y축 값인 9이다.


사용자 삽입 이미지

마지막 노드의 값이 NULL이고, 

좌우 범위 내에 들어간다면, OK이다.

사용자 삽입 이미지


최종 그림은 아래와 같이 된다.

붉은색 점선 원이 탐색 범위이고, 마이애미가 범위내에 들어감을 확인할 수 있다.




사용자 삽입 이미지




1.5  삭제 : Deletion

앞에 insert나 search는 BST와 비슷하기 때문에 쉽지만 , deletion은 복잡합니다. 

각 depth마다, DISC가 달라서, 원래 tree의 root는 DISC가 x 지만, subtree의 root는 y가 될 수도 있기 때문에, 

모든 subtree가 kd-tree가 아니다. 그래서 그냥 마구 삭제해 버리면 문제가 생긴다. 

kd-tree로 부터 (a,b) 노드를 삭제한다고 할 때, (a,b)의 두 subtree가 empty tree이면 그냥 삭제하면 되고, 

아니면 (a,b)의 subtree들 중에서 가장 적절한 대체 노드 (c,d)를 찾아서, replace시킨킵니다. 

이 (c,d)는 recursive하게 다시 삭제되는 것이다. 그렇다면, 이 대체 노드는 어떻게 찾는 것일까? 

오른쪽 subtree에서 가장 작은 값을 가진 노드나, 왼쪽 subtree에서 가장 큰 값을 고르면 된다. 

그러면 다음의 예를 보자. 


사용자 삽입 이미지


그림과 같이 A가 최상위 노드로 되어 있는 상태이다.

여기서 하나의 노드를 삭제한다. 하필이면 A를 삭제하기로 한다.

그럼 A가 삭제된 상태에서 노드를 구성하는 동작을 하게 된다.



사용자 삽입 이미지



우선 A가 삭제되었으므로, A의 X축 값에서 가장 가까운 노드의 값을 선택한다. 

예제의 경우 C가 되고 우연히도 C가 A의 바로 밑에 있으므로 개념상 C를 올린다.


{

의문 사항 :

최소값이라고 하였는데 이것을 가장 가까운 값으로 해석해야 하는것인지 궁굼하다.

}


그럼 오른쪽 개념처럼 기준선이 움직인 셈이 된다.



사용자 삽입 이미지



그럼 C를 기준으로 최소  Y값은 우연히도 D이므로 D를 한단계 끌어올린다.

그럼 개념상 오른쪽 그림처럼 된다.



사용자 삽입 이미지




D에서 X 값으로 최소값은 H이므로 이것을 선택한다. 이때 H는 한단계 더 하위 계층이므로 이것을 끌러 올린다.

사용자 삽입 이미지



이때 I가 G보다 X축 값이 작으므로 왼쪽으로 붙는다.


 



1.6  장단점

kd-tree는 partial matching search에 유용하지만, empty space가 없고, 

point search와 region search가 다 가능하다. 하지만, 균형이 심~하게 안맞으면 performance가 안좋다. 

kd-tree는 메모리 기반 index이다. File 기반에서는... 안좋다



1.7 응용처

응용하는데 중에서 쉽게 찾을 수 있는 것은 폴리곤의 분류이다.

3차원 그래픽도 당연히 다차원이므로, 공간세어 객체를 뿌려놓고 찾아서, 렌더링을 해야 하는데 이럴때

kd-tree를 사용한다.


당연히 속도 문제가 이슈가 되기 때문에 여러가지 방법과 논문이 제안되고 있다.

삭은이 님의 블로그에서 보면

논문을 하나 추천하고 있다.


http://www.cgg.cvut.cz/members/havran/ARTICLES/ingo06rtKdtree.pdf



'Computer Vision' 카테고리의 다른 글

OpenCV - Image Rotation & Scale  (0) 2009.03.10
Rob Hess의 SIFT [5]  (2) 2009.03.02
Rob Hess의 SIFT [3]  (2) 2009.02.23
Rob Hess의 SIFT [2]  (0) 2009.02.18
Rob Hess의 SIFT [1]  (1) 2009.02.16
Posted by GUNDAM_IM
Computer Vision2009. 2. 23. 08:23

Rob Hess의 SIFT


앞서 두번에 걸쳐서 Rob의 코드를 컴파일해서 정상적으로 움직이는 것을 확인하였습니다.

이제 코드 내부가 어떻게 동작하는지 확인을 해보겠습니다.

SIFT자체는 나중에 따로 정리하기로 하고 전체 맥락을 한번 따라가는 형식으로 정리하였습니다.



이번에는 전체 코드를 한번 찬찬히 따라가면서 정리하였습니다.

그냥 코드를 읽는 수준이므로 큰 무리 없이 따라가면 됩니다.

OpenCV에 기반하므로, 그냥 이런 함수가 있다고 보고 읽어 가면 됩니다.


1. dspFeat의 분석


dspFeat를 먼저 코드를 보곘습니다.

이 프로그램은 그냥 SIFT를 대상 이미지에 올리는 그림입니다.

별다른 어려운 부분이 없읍니다.


시작 부분에서 두개의 파일을 먼저 설정하고 있습니다.


char* feat_file = "..\\beaver.sift";

char* img_file = "..\\beaver.png";


앞서의 것은 beaver.sift 의 결과물입니다. 텍스트 파일로 되어 있네요

두번째 것은 png format 으로 되어 있는 이미지 파일입니다.



int main( int argc, char** argv )

{

IplImage* img;

struct feature* feat;

char* name;

int n;


img = cvLoadImage( img_file, 1 );  // 먼저 이미지 파일을 로딩한다.


if( ! img )

fatal_error( "unable to load image from %s", img_file );


n = import_features( feat_file, feat_type, &feat ); // 피쳐 파일을 로딩한다.

if( n == -1 )

fatal_error( "unable to import features from %s", feat_file );


name = feat_file;


draw_features( img, feat, n );  // SIFT를 그린다.

cvNamedWindow( name, 1 );  // 창을 열고~~~

cvShowImage( name, img );   // 그림을 그린다. 이때 그림은  draw_feature로 그린것이다.

cvWaitKey( 0 );   // 글고 키 값을 기다린다. 

return 0;

}


이다. 큰 문제점은... 은 없네요~ 


draw_feature()함수를 나중에 따로 정리하여야 하겠습니다.

이것은 SIFT를 정리하면서 하면 되므로 여기서는  SKIP...



2. match의 코드 따라가기


그럼 이제 두번째로 match를 한번 따라가 보겠습니다.



/******************************** Globals ************************************/


char img1_file[] = "..\\beaver.png"; // 찾고자 하는 이미지  파일이다.

char img2_file[] = "..\\beaver_xform.png"; // 비교 대상 파일이다.


/********************************** Main *************************************/



int main( int argc, char** argv )

{

IplImage* img1, * img2, * stacked;

struct feature* feat1, * feat2, * feat;

struct feature** nbrs;

struct kd_node* kd_root;

CvPoint pt1, pt2;

double d0, d1;

int n1, n2, k, i, m = 0;


img1 = cvLoadImage( img1_file, 1 ); // 찾는 이미지를 먼저 로딩한다.

if( ! img1 )

fatal_error( "unable to load image from %s", img1_file );


img2 = cvLoadImage( img2_file, 1 ); // 대상 이미지를 로딩한다.

if( ! img2 )

fatal_error( "unable to load image from %s", img2_file );


stacked = stack_imgs( img1, img2 ); // 두개의 이미지를 합쳐서 하나의 이미지로 만든다.

    // 그렇다고 이미지를 겹치는 것이 아니라 상하로 포개는 것이다.


fprintf( stderr, "Finding features in %s...\n", img1_file );

n1 = sift_features( img1, &feat1 ); // 이제 FEATURE를 찾기...


fprintf( stderr, "Finding features in %s...\n", img2_file );

n2 = sift_features( img2, &feat2 ); // 피쳐를 더 찾기...


kd_root = kdtree_build( feat2, n2 ); // 키포인트 배열에서 원하는 kd tree로 구성한다.


for( i = 0; i < n1; i++ ) // 찾고자 하는 이미지 1의 특징점을 대상으로 반복한다.

{

feat = feat1 + i;

k = kdtree_bbf_knn( kd_root, feat, 2, &nbrs, KDTREE_BBF_MAX_NN_CHKS );

// 특징점에서 NN방식으로 하여 찾아낸다.

if( k == 2 ) // k가 2라면.. 찾아낸 것이므로.. 

{

d0 = descr_dist_sq( feat, nbrs[0] ); // nn과 두번째 nn사이의 

      // 유클리드 제곱 거리를 구한다.

d1 = descr_dist_sq( feat, nbrs[1] );

if( d0 < d1 * NN_SQ_DIST_RATIO_THR ) // 임계치보다 크다면.. 

{

// 두개의 포인트로 선을 그린다. 

pt1 = cvPoint( cvRound( feat->x ), cvRound( feat->y ) );

pt2 = cvPoint( cvRound( nbrs[0]->x ), cvRound( nbrs[0]->y ) );

pt2.y += img1->height;

cvLine( stacked, pt1, pt2, CV_RGB(255,0,255), 1, 8, 0 );

m++;

feat1[i].fwd_match = nbrs[0];

}

}

free( nbrs );

}


// 다 찾았으면 이미지를 그린다.

fprintf( stderr, "Found %d total matches\n", m );

cvNamedWindow( "Matches", 1 );

cvShowImage( "Matches", stacked );

cvWaitKey( 0 );



/*  

       이 것은 RANSAC함수가 어떻게 움직이는지 보기 위해서 만든 것입니다.

       아래 주석 처리가 된 블럭을 풀면 됩니다.

UNCOMMENT BELOW TO SEE HOW RANSAC FUNCTION WORKS


Note that this line above:


feat1[i].fwd_match = nbrs[0];


is important for the RANSAC function to work.

*/

/*

{

CvMat* H;

H = ransac_xform( feat1, n1, FEATURE_FWD_MATCH, lsq_homog, 4, 0.01,

homog_xfer_err, 3.0, NULL, NULL );

if( H )

{

IplImage* xformed;

xformed = cvCreateImage( cvGetSize( img2 ), IPL_DEPTH_8U, 3 );

cvWarpPerspective( img1, xformed, H, 

CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS,

cvScalarAll( 0 ) );

cvNamedWindow( "Xformed", 1 );

cvShowImage( "Xformed", xformed );

cvWaitKey( 0 );

cvReleaseImage( &xformed );

cvReleaseMat( &H );

}

}

*/


//모두 해제합니다.

cvReleaseImage( &stacked );

cvReleaseImage( &img1 );

cvReleaseImage( &img2 );

kdtree_release( kd_root );

free( feat1 );

free( feat2 );

return 0;


여기서는 KDTree를 이용해서 공간상에서 dB를 구축하였음을 알수 있습니다.

다음에는 이것을 좀더 자세하게 따라가 보겠습니다.


'Computer Vision' 카테고리의 다른 글

Rob Hess의 SIFT [5]  (2) 2009.03.02
Rob Hess의 SIFT [4]  (0) 2009.02.25
Rob Hess의 SIFT [2]  (0) 2009.02.18
Rob Hess의 SIFT [1]  (1) 2009.02.16
MAC에서 OpenCV를 컴파일 하기  (0) 2009.01.06
Posted by GUNDAM_IM