'Computer Vision'에 해당되는 글 34건

  1. 2009.04.22 OpenCV - cvSmooth 2
  2. 2009.04.18 Rob Hess의 SIFT [6]
  3. 2009.04.11 OpenCV - Convert Image
  4. 2009.03.16 OpenCV - Image Mask
  5. 2009.03.12 OpenCV - Extract 2
  6. 2009.03.10 OpenCV - Image Rotation & Scale
  7. 2009.03.02 Rob Hess의 SIFT [5] 2
  8. 2009.02.25 Rob Hess의 SIFT [4]
  9. 2009.02.23 Rob Hess의 SIFT [3] 2
  10. 2009.02.18 Rob Hess의 SIFT [2]
Computer Vision2009. 4. 22. 19:55

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


SIFT의 내용을 따라가다 보면 Smooth함수를 호출합니다.

그래서 이번에 OpenCV Smooth함수를 아래와 같이 정리하고 테스트를 해보았습니다.

중요한 포인트는 번역을 했지만 억지로 번역하여 넣지 않았습니다.  

예제는 인터넷에서 다운 받은것을 조금 수정하였습니다.

( 인터넷에서 다운 받았는데, 나중에 페이지를 찾을려고 하니, 시간이 지난 뒤여서  찾지 못하였습니다.

   그때 그때 북마크를 걸어두어야 하는데, 나중에 보니 못찾겠더군요.. 쩝.. )

파라미터를 바꾸어가면서 테스트 할 수 있도록 수정하였기 때문에

필터 효과를 눈으로 확인을 할 수 있습니다.

덕분에 TrackBar의 사용법도 알게 되었습니다.

궁굼하신 분은 코드를 보시면 쉽게 이해가실 것입니다.

아래 그림은 실행시켰을때 나오는 그림입니다.

사용자 삽입 이미지
트랙바가 2개인데 위 트랙바는 필터의 종류를

두번째 트랙바는 필터의 인자를 뜻합니다.

프린트로 덤프하므로 하나씩 움직여가면서 테스트 해보시면 쉽게 결과를 보실 수 있습니다.


Smooth

OpenCV에서는 몇가지 방법으로 Smooth를 할 수 있게 하여 줍니다.

함수의 원형은 다음과 같습니다.

void cvSmooth( const CvArr* src, CvArr* dst,

               int smoothtype=CV_GAUSSIAN,

               int param1=3, int param2=0, double param3=0 );


smoothing 타입에 따라서 파라미터 인자의 역활이 바뀝니다.

요 부분만 알고 있으면 나머진 쉽습니다.

src

원본 이미지


dst

결과 이미지


smoothtype

smoothing 타입

  • CV_BLUR_NO_SCALE (simple blur with no scaling)
    • summation over a pixel param1×param2 neighborhood.
    • If the neighborhood size may vary, one may precompute integral image with cvIntegral function.
  • CV_BLUR (simple blur)
    • summation over a pixel param1×param2 neighborhood with subsequent scaling by 1/(param1param2).
  • CV_GAUSSIAN (gaussian blur)
    • convolving image with param1×param2 Gaussian kernel.
  • CV_MEDIAN (median blur)
    • finding median of param1×param1 neighborhood (i.e. the neighborhood is square).
  • CV_BILATERAL (bilateral filter)


param1

첫번째 인자

param2

두번째 인자

In case of simple scaled/non-scaled and Gaussian blur if param2 is zero, it is set to param1.

param3

         가우시안 방식일 경우 시그마를 넣어야 하지만,

이 파라미터가 0이면 커널 사이즈에서 자동으로 만들어진다.  수식은 아래와 같습니다.

              sigma = (n/2 - 1)*0.3 + 0.8,

                  where n=param1 for horizontal kernel,

                                                

                        n=param2 for vertical kernel.


  • Using standard sigma for small kernels (3×3 to 7×7) gives better speed.
  •       If param3 is not zero, while param1 and param2 are zeros, the kernel size is calculated from the sigma (to provide accurate enough operation).

보통의 시그마를 쓸 경우 그냥 좀 빨라진다. (차이는 ???)

         - 사실 차이라는게 크게 없는듯 합니다. 많이 하면 차이가 있겠지만 제 머쉰에서는 금방 결과가 나왔습니다.

         커널 사이즈를 안주고 (0으로 주고) 호출하면 역으로 시그마에서 커널 사이즈를 만듭니다.


The function cvSmooth smooths image using one of several methods. Every of the methods has some features and restrictions listed below

Blur with no scaling works with single-channel images only and supports accumulation of 8-bit to 16-bit format (similar to cvSobel and cvLaplace) and 32-bit floating point to 32-bit floating-point format.

Simple blur and Gaussian blur support 1- or 3-channel, 8-bit and 32-bit floating point images. These two methods can process images in-place.

Median and bilateral filters work with 1- or 3-channel 8-bit images and can not process images in-place.


아래는 소스 코드입니다.


#include <stdio.h>

#include <unistd.h>

#include <string.h>

#include <errno.h>


#include <opencv/cv.h>

#include <opencv/highgui.h>




int g_switch_value = 0;

int sigma_value  = 0;

int filterInt = 0;

int lastfilterInt = -1;

int lastsigma = -1;


void switch_callback( int position ){

filterInt = position;

}


void switch_sigma_callback( int position ){

  if(position < 3 )

    position = 4;


  sigma_value = position;

}



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

{

const char* name = "Filters Window";

float f_sigma ;

IplImage* img = cvLoadImage( "4390m.jpg",1 );

IplImage* out = cvCreateImage( cvGetSize(img), IPL_DEPTH_8U, 3 );


cvNamedWindow( name, 1 );

cvShowImage(name, out);

// Other Variables

CvPoint seed_point = cvPoint(305,195);

CvScalar color = CV_RGB(250,0,0);


// Create trackbar

cvCreateTrackbar( "FILTER", name, &g_switch_value, 5, switch_callback );

cvCreateTrackbar( "parameter", name, &sigma_value, 11, switch_sigma_callback );


while( 1 ) {


switch( filterInt ){

case 0:

 cvSmooth( img, out, CV_BLUR, 7, 7 , 0 ,0 );

break;

case 1:

 f_sigma = ( sigma_value / 2 - 1 ) * 0.3 + 0.8 ;

 cvSmooth( img, out, CV_GAUSSIAN, 7 , 7 , f_sigma , f_sigma);

 

break;

case 2:

 if( sigma_value < 3 )  sigma_value = 3 ;

 if( sigma_value % 2  == 0)  sigma_value +=1 ;

 cvSmooth( img, out, CV_MEDIAN,  sigma_value  ,  sigma_value , 0 , 0);

 break;

case 3:

cvErode( img, out, NULL, 1);

break;

case 4:

cvDilate( img, out, NULL, 1);

break;

case 5:

cvFloodFill( out, seed_point, color, cvScalarAll(5.0), cvScalarAll(5.0), NULL, 4, NULL );

break;

}


if(

  (filterInt != lastfilterInt)||

  (sigma_value != lastsigma)

 )

 {

cvShowImage(name, out);

lastfilterInt = filterInt;

lastsigma     = sigma_value;


switch( filterInt ){

   case 0:

     printf("Blur Smooth\n");

     break;

   case 1:

     printf("Gaussian Smooth\n");

       printf(" Sigma %d\n",  sigma_value );  

     break;

   case 2:

     printf("Median Smooth : Filter Size = %d x %d\n" , sigma_value , sigma_value);

     break;

   case 3:

     printf("Erode\n");

     break;

   case 4:

          printf("Dilate\n");

     break;

   case 5:

       printf("Flood Fill\n");

     break;

           }

 }


if( cvWaitKey( 15 ) == 27 )

break;

}


cvReleaseImage( &img );

cvReleaseImage( &out );

cvDestroyWindow( name );

return 0;

}





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

OpenCV - Alpha Blending cvAddWeighted , cvFillPoly  (2) 2009.04.25
JPEG2AVI 를 맥에서 빌드하기  (0) 2009.04.24
Rob Hess의 SIFT [6]  (0) 2009.04.18
OpenCV - Convert Image  (0) 2009.04.11
OpenCV - Image Mask  (0) 2009.03.16
Posted by GUNDAM_IM
Computer Vision2009. 4. 18. 15:08

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

틈틈이 새로운 것을 알게 되면 그때 그때 업데이트 하겠습니다.



(주) 시작하기 전에

실제 코드를 한번 따라가기 전에 SIFT 알고리즘을 간략하게 정리하여둡니다.

코드를 기계적으로 따라가는것 보다는 알고리즘을 이해하고 따라가는 것이 유용하기 때문입니다.

물론 그렇다고 제가 알고리즘을 다 이해하는 것은 아닙니다.

그냥 초보 수준에서 아는 것들만 정리하였습니다.



PPT 발표 자료는 이 페이지를 참조 하세요 : SIFT정리.ppt

발표자료를 기반으로 하여서 추가적으로 정리한 내용입니다.



1. Harris Corner Detector

특징으로는 다음과 같습니다.

  Rotation 에 대해 검출이 가능하다.

  부분 이미지로도 검출이 가능하다.

{

해석 :

이상의 말을 풀이하면

H.C.Detector는 영상이  회전된 것 그리고 밝기의 변화와 이미지 상에서 노이즈가 있어도

어느정도 이를 견디어 내며 원하는 포인트를 찾아낼 수 있다는 의미가 됩니다.

H.C.Detector는 Local Auto Correlation Function에 기반합니다.

기본적인 아이디어는 전체 이미지를 한방에 뒤지는 것이 아니고

주어진 이미지 영역에서 작은 이미지 영역 (부분 영역)을 설정하여 이 윈도우를 주변 영역과 비교하여

커다란 차이점이 있는 부분을 찾아내는 방식입니다.

설정된 이미지에 대해서 뒤지는 방향은 아무 방향이나 상관이 없습니다. 다만 주어진 부분 영역에 대해서

큰 변화가 생기는 방향으로 이동해야 하곘지요


아래 그림을 보면 쉽게 이해됩니다.


사용자 삽입 이미지

 

제일 왼쪽 그림은 뒤져서 변화가 없으므로 “Flat”으로 보게 됩니다.

가운데 그림은 상하로만 선분이 있다는 것을 알게 됩니다.

- 에지 방향으로 변화가 없다는 것은 에지가 존재한다는 의미가 됩니다.

오른쪽 그림은 모든 방향으로 변화가 존재하기 때문에

“Conor”라고 보는 것 입니다.


- 수학적인 의미는 다음에 하기로 하고 여기선 패스 합니다.

- SIFT가 우선이므로, H.C.Detector는 이정도에서 정리합니다.


매트랩에 대한 코드는 아래를 참고하시기 바랍니다.

http://ipl.cnu.ac.kr/mayadata/harris/harris_corner_detector.htm


}


사용자 삽입 이미지

상기 그림을 실제 H.C.Detector에 걸면 다음과 같은 영상을 얻을 수 있습니다.

사용자 삽입 이미지


그리고 이 그림의 임계치 이상만을 검출하면 다음과 같습니다.


사용자 삽입 이미지


임계치 이상 영역에서 최대값만을 표시하면 다음과 같습니다.

사용자 삽입 이미지

요것이 바로 코너 포인트가 되는 것입니다.


  이 코너포인트를 원본 그림과 매핑하면 다음과 같습니다.


사용자 삽입 이미지

문제점 :

크기 변화에대해 대처하지 못한다.

사용자 삽입 이미지
위의 그림처럼 원래 에지로 인식하는 것이 크기가 줄어들면서 Coner로 인식이 되는 경우가 발생합니다.

혹은 그 역으로도 발생합니다.


따라서 H.C.Detector는 크기의 변경에 따라서 검출되는 정보가 변하므로,

        크기 변화에 대처하지 못하는 것입니다.

   

       SIFT는 이러한 문제점을 개선하기 위해서 나온 것입니다.

       본격적인 이야긴 다음편에서~~      



    이번 내용에 대해서 추가적인 자료는 다음과 같습니다.


      매트랩등등의 코드를 함께 제공하기 때문에 공부하실 분들은 이 페이지를 참고하여 주세요


http://ipl.cnu.ac.kr/mayadata/harris/harris_corner_detector.htm

그리고 위에 설명에 사용된 그림과 수식등은


lect9-slides.pdf


      을 참고하시기 바랍니다.


      ----------

      자료 정리하던 프로그램이 Expire되어 버렸습니다.

      잘 정리(?) 된 줄 알았는데 근본적으로 Time Limit이 되어 있더군요

      좋은 프로그램인데 넘 고가여서.. .쩝..


      그래서 한단계 낮은 프로그램을 구입했습니다.

      그바람에 그동안 정리했던 것 옮기느라 많이 늦어졌네요

      

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

JPEG2AVI 를 맥에서 빌드하기  (0) 2009.04.24
OpenCV - cvSmooth  (2) 2009.04.22
OpenCV - Convert Image  (0) 2009.04.11
OpenCV - Image Mask  (0) 2009.03.16
OpenCV - Extract  (2) 2009.03.12
Posted by GUNDAM_IM
Computer Vision2009. 4. 11. 17:41

OpenCV Convert Image


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


영상 처리를 하기 위해서는 때때로 컬러 공간을 바꾸어 줘야 할 때가 많습니다.

영상에서 필요한 정보를 찾아서 추출하기 위해서는 RGB공간에서 얻을 수 없는 정보를

다른 컬러 공간에서 얻어낼 수 있기 때문입니다.

예를들어서 사진에서 휘도를 바꾸고 싶을 경우

YCbCr로 바꾼후에 Y를 변경하고 다시 이것을 RGB로 바꾸는 과정을 거치게 됩니다.

이럴 경우 컬러 공간을 변환하는 함수가 있어야 하는데

OpenCV에서는 아래 함수가 그 동작을 도와줍니다.

cvCvtColor( 입력 이미지 , 출력 이미지 , 옵션 ); 


옵션은 아주 많이 있지만 아래에 몇가지 중요한 옵션을 설명하였습니다.


  CV_RGB2GRAY  : 흑백으로 바꾸기

                            수식은 다음과 같다.

RGB[A]->Gray: Y<-0.299*R + 0.587*G + 0.114*B


  CV_RGB2YCrCb : YCrCb로 바꾸기

    Y <- 0.299*R + 0.587*G + 0.114*B

  Cr <- (R-Y)*0.713 + delta

  Cb <- (B-Y)*0.564 + delta


  CV_RGB2HLS    : ( Hue Lightness Saturation ) 으로 변환한다.

   // In case of 8-bit and 16-bit images

  // R, G and B are converted to floating-point format and scaled to fit 0..1 range

  V,,max,, <- max(R,G,B)

  V,,min,, <- min(R,G,B)

  L <- (V,,max,, + V,,min,,)/2

  S <- (V,,max,, - V,,min,,)/(V,,max,, + V,,min,,)  if L < 0.5

        (V,,max,, - V,,min,,)/(2 - (V,,max,, + V,,min,,))  if L = 0.5

           (G - B)*60/S,  if V,,max,,=R

  H <- 180+(B - R)*60/S,  if V,,max,,=G

        240+(R - G)*60/S,  if V,,max,,=B

  if H<0 then H<-H+360

  On output 0=L=1, 0=S=1, 0=H=360.

  The values are then converted to the destination data type:

      8-bit images:

           L <- L*255, S <- S*255, H <- H/2

      16-bit images (currently not supported):

          L <- L*65535, S <- S*65535, H <- H

      32-bit images:

          H, L, S are left as is


  CV_RGB2HSV : RGB 값을 HSV로 바꾼다. HSV란 Hue Saturation Value를 의미한다.


  // In case of 8-bit and 16-bit images

  // R, G and B are converted to floating-point format and scaled to fit 0..1 range

  V <- max(R,G,B)

  S <- (V-min(R,G,B))/V   if V?0, 0 otherwise

           (G - B)*60/S,  if V=R

  H <- 120+(B - R)*60/S,  if V=G

       240+(R - G)*60/S,  if V=B

  if H<0 then H<-H+360

  On output 0=V=1, 0=S=1, 0=H=360.

  The values are then converted to the destination data type:

      8-bit images:

          V <- V*255, S <- S*255, H <- H/2 (to fit to 0..255)

      16-bit images (currently not supported):

          V <- V*65535, S <- S*65535, H <- H

      32-bit images:

          H, S, V are left as is

  CV_RGB2Lab : CIE Lab Color로 변환한다.

  // In case of 8-bit and 16-bit images

  // R, G and B are converted to floating-point format and scaled to fit 0..1 range

  // convert R,G,B to CIE XYZ

  |X|    |0.412453  0.357580  0.180423| |R|

  |Y| <- |0.212671  0.715160  0.072169|*|G|

  |Z|    |0.019334  0.119193  0.950227| |B|

  X <- X/Xn, where Xn = 0.950456

  Z <- Z/Zn, where Zn = 1.088754

  L <- 116*Y^1/3-16      for Y>0.008856

  L <- 903.3*Y           for Y<=0.008856

  a <- 500*(f(X)-f(Y)) + delta

  b <- 200*(f(Y)-f(Z)) + delta

  where f(t)=t^1/3^              for t>0.008856

        f(t)=7.787*t+16/116   for t<=0.008856

  where delta = 128 for 8-bit images,

                0 for floating-point images

  On output 0=L=100, -127=a=127, -127=b=127

  The values are then converted to the destination data type:

      8-bit images:

          L <- L*255/100, a <- a + 128, b <- b + 128

       16-bit images are currently not supported

       32-bit images:

          L, a, b are left as is


  •   CV_RGB2Luv  CIE Luv Color로 변환한다.

  // In case of 8-bit and 16-bit images

  // R, G and B are converted to floating-point format and scaled to fit 0..1 range

  // convert R,G,B to CIE XYZ

  |X|    |0.412453  0.357580  0.180423| |R|

  |Y| <- |0.212671  0.715160  0.072169|*|G|

  |Z|    |0.019334  0.119193  0.950227| |B|

  L <- 116*Y^1/3^      for Y>0.008856

  L <- 903.3*Y      for Y<=0.008856

  u' <- 4*X/(X + 15*Y + 3*Z)

  v' <- 9*Y/(X + 15*Y + 3*Z)

  u <- 13*L*(u' - u,,n,,), where u,,n,,=0.19793943

  v <- 13*L*(v' - v,,n,,), where v,,n,,=0.46831096

  On output 0=L=100, -134=u=220, -140=v=122

  The values are then converted to the destination data type:

      8-bit images:

          L <- L*255/100, u <- (u + 134)*255/354, v <- (v + 140)*255/256

      16-bit images are currently not supported

      32-bit images:

          L, u, v are left as is


코드는 아래와 같습니다.


#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 *imgB ;





  if (argc < 3 )

        {

                printf("usage : colorcvt.elf srcimage dstimage option \n");

                printf("      srcimage : source image file name\n");

                printf("      dstimage : destination image file name\n");

                printf("      option   : 0 : CV_RGB2GRAY \n");

                printf("                 1 : CV_RGB2YCrCb \n");

                printf("                 2 : CV_RGB2HLS \n");

                printf("                 3 : CV_RGB2HSV \n");

                printf("                 4 : CV_RGB2Lab \n");

                printf("                 5 : CV_RGB2Luv \n");

                exit(0);

        }


  // 이미지 읽기

  IplImage *imgA = cvLoadImage( argv[1] , 1);


  // 대상 이미지 확정

  switch( *argv[3] )

  {

   case '0' :

              imgB = cvCreateImage( cvGetSize( imgA), IPL_DEPTH_8U, 1);

              cvCvtColor(imgA , imgB , CV_RGB2GRAY );

              break;

   case '1' :

              imgB = cvCreateImage( cvGetSize( imgA), IPL_DEPTH_8U, 3);

              cvCvtColor(imgA , imgB , CV_RGB2YCrCb  );

              break;

   case '2' :

              imgB = cvCreateImage( cvGetSize( imgA), IPL_DEPTH_8U, 3);

              cvCvtColor(imgA , imgB , CV_RGB2HLS  );

              break;

   case '3' :

              imgB = cvCreateImage( cvGetSize( imgA), IPL_DEPTH_8U, 3);

              cvCvtColor(imgA , imgB , CV_RGB2HSV  );

              break;

   case '4' :

              imgB = cvCreateImage( cvGetSize( imgA), IPL_DEPTH_8U, 3);

              cvCvtColor(imgA , imgB , CV_RGB2Lab  );

              break;

   case '5' :

              imgB = cvCreateImage( cvGetSize( imgA), IPL_DEPTH_8U, 3);

              cvCvtColor(imgA , imgB , CV_RGB2Luv  );

              break;


  }



  cvNamedWindow("Converted Image",0);

  cvShowImage("Converted Image",(CvArr*)imgB);

  cvWaitKey(0);

  cvDestroyWindow("Converted Image");


  cvSaveImage( argv[2] , imgB);

  cvReleaseImage( &imgA );

  cvReleaseImage( &imgB );

  return 0;

}


아래는 각각의 옵션으로 했을때의 그림입니다.

컬러 스페이스가 틀린것을 강제로 출력한 형태이므로 다른 컬러 공간의 그림은 정확하게 표현되지는 않습니다. 그냥 이렇게 되었다는 것만 보면 될 듯 합니다.


원본 이미지


사용자 삽입 이미지


흑백으로 변환 이미지

사용자 삽입 이미지



사용자 삽입 이미지

사용자 삽입 이미지


사용자 삽입 이미지
사용자 삽입 이미지










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

OpenCV - cvSmooth  (2) 2009.04.22
Rob Hess의 SIFT [6]  (0) 2009.04.18
OpenCV - Image Mask  (0) 2009.03.16
OpenCV - Extract  (2) 2009.03.12
OpenCV - Image Rotation & Scale  (0) 2009.03.10
Posted by GUNDAM_IM
Computer Vision2009. 3. 16. 08:37

() 글을 읽기 전에 

 글은 저와 같은 OpenCV초보자를 위해서 정리하는 것입니다.

전문가시라면 굳이 읽을 필요가 없습니다.

공부하면서 정리하는 글이라서 서툰 부분이 많이많이 보입니다.


이번에는 이미지 마스킹을 수행합니다.


마스킹이라는 것은  한장의 이미지에서 원하는 부분만을 추출해 내는 역활을 합니다.

이것은 앞서의 이미지 추출과는 조금 틀립니다.


이미지 추출은 정해진 영역에서 그냥 추출해 내는 것이고

마스킹은 필요없는 정보를 걸러내어서 원하는 이미지를 찾는 것입니다.


마스킹을 한다면 당연히 AND연산입니다.


OpenCV에서는 And연산을 2가지 종류로 제공해 줍니다.


cvAnd와

cvAndS입니다. 끝에 S가 붙어 있다는 점이 틀린것 이외에도 두 함수의 차이는

전자는 이미지와 이미지를 대상으로 하지만, 후자는 이미지와 Scalar값을 대상으로 한다는 점이 틀립니다.


일단 이번에는 cvAnd를 가지고 하여 보도록 하겠습니다.



코드는 갈수록 단순해지네요..

프로젝트 따라서 필요한 기능을 찾아가면서 정리하는 거라.. 복잡한 쪽으로 이동하지 않고 단순한 쪽으로 이동해서 그렇습니다.



===========


#include <stdio.h>

#include <unistd.h>

#include <string.h>

#include <errno.h>


#include <opencv/cv.h>

#include <opencv/highgui.h>


#define IMG_WIDTH 400

#define IMG_HEIGHT 400


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




  // 이미지 읽기

  IplImage *imgA = cvLoadImage( "4390m.jpg", 1);

  // Mask Iamge읽기

  IplImage * imgB = cvLoadImage("4390m1.jpg", 1);


  // 대상 이미지 확정

  IplImage *imgC = cvCreateImage( cvGetSize(imgA) ,

                                  IPL_DEPTH_8U, 3);


  cvAnd(imgA,imgB,imgC,0);


  ////////////////////////////////////////////////////////////


  cvSaveImage( "image_result.jpg", imgC);

  cvNamedWindow("Masked Image",0);

  cvShowImage("Masked Image",(CvArr*)imgC);

  cvWaitKey(0);

  cvDestroyWindow("Masked Image");


  cvReleaseImage( &imgA );

  cvReleaseImage( &imgB );

  cvReleaseImage( &imgC );


  return 0;

}


결과 이미지는 아래에 올려둡니다.


사용자 삽입 이미지


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

Rob Hess의 SIFT [6]  (0) 2009.04.18
OpenCV - Convert Image  (0) 2009.04.11
OpenCV - Extract  (2) 2009.03.12
OpenCV - Image Rotation & Scale  (0) 2009.03.10
Rob Hess의 SIFT [5]  (2) 2009.03.02
Posted by GUNDAM_IM
Computer Vision2009. 3. 12. 09:17

() 글을 읽기 전에 

 글은 저와 같은 OpenCV초보자를 위해서 정리하는 것입니다.

전문가시라면 굳이 읽을 필요가 없습니다.

공부하면서 정리하는 글이라서 서툰 부분이 많이많이 보입니다.


이미지에서 특정 부분 추출하기


cvCopy(

const CvArr * src,

CvArr *          dst,

const CvArr * mask = NULL

);


이것은 원본과 복사 대상이 같은 크기와 , 같은 타입의 이미지라는 전제하에서 이루어 집니다.



이미지의 크기가 틀리다면, 다음 함수를 써서 원본의 이미지중에 일부를 추출할 수 있습니다.


void cvSetImageROI( IplImage* image, CvRect rect );


CvRect는 사각형 영역을 설정하는 것입니다.


cvRect는 다음과 같이 정의되어 있습니다.


 typedef struct CvRect

    {

        int x; /* x-coordinate of the left-most rectangle corner[s] */

        int y; /* y-coordinate of the top-most or bottom-most

                  rectangle corner[s] */

        int width; /* width of the rectangle */

        int height; /* height of the rectangle */

    }

    CvRect;


그럼 이제 이미지를 복사하여 만들어 보는 것을 해보겠습니다.


소스는 간단해서 그닥 어려운 부분이 없습니다.

원본에서 ROI를 지정한 뒤에 복사하면 잘 됩니다.


--------------------------------------------------------------------------------


#include <stdio.h>

#include <unistd.h>

#include <string.h>

#include <errno.h>


#include <opencv/cv.h>

#include <opencv/highgui.h>


#define IMG_WIDTH 400

#define IMG_HEIGHT 400


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




  // 이미지 읽기 

  IplImage *imgA = cvLoadImage( "4390m.jpg", 1);

  // 대상 이미지 확정

  IplImage *imgB = cvCreateImage( cvSize(IMG_WIDTH ,IMG_HEIGHT ),

  IPL_DEPTH_8U, 3);


  // ROI 설정한다.

  cvSetImageROI( imgA , cvRect(200,200,IMG_WIDTH,IMG_HEIGHT ));

  // 이미지를 복사한다.

  cvCopy(imgA,imgB,0);


  ////////////////////////////////////////////////////////////


  cvSaveImage( "image_result.jpg", imgB);

  cvNamedWindow("Extract Image",0);

  cvShowImage("Extract Image",(CvArr*)imgB);

  cvWaitKey(0);

  cvDestroyWindow("Extract Image");


  cvReleaseImage( &imgA );

  cvReleaseImage( &imgB );


  return 0;

}


아래 그림은 결과 이미지입니다


사용자 삽입 이미지

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

OpenCV - Convert Image  (0) 2009.04.11
OpenCV - Image Mask  (0) 2009.03.16
OpenCV - Image Rotation & Scale  (0) 2009.03.10
Rob Hess의 SIFT [5]  (2) 2009.03.02
Rob Hess의 SIFT [4]  (0) 2009.02.25
Posted by GUNDAM_IM
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
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
Computer Vision2009. 2. 18. 10:08

Rob Hess의 SIFT 2nd


이번에는 Rob Hess의 SIFT  프로그램 두번째에 대한 내용입니다.

언제나 그렇듯 빌드해 나가면서 발생하는 문제점들을 해결하는 형식으로 진행합니다.

이 과정은 제가 작업하는 내용을 기록하는 방식입니다. 

이렇게 고단한 역사(?) 를 기록하면 예전에 어떻게 했었지 하는 것을 기억하기가 쉽고

어떤 방법으로 이리 저리 했는지 따라하기가 쉽습니다.


RobHess의 SIFT는 크게 3개의 파일로 구성되어 있습니다.


1. siftFeat로서 앞서 컴파일 하였던 SIFT Feature를 Detect 하는 부분이며,

2. match   두개의 이미지를 비교하여 찾아내는 것입니다.

3. 마지막으로 찾아낸 특징점들을 화면상에 보여주는 dspFeat 부분입니다.


이번에는 두번째인 match를 빌드해보겠습니다.


1. Match의 빌드


VC에서 컴파일하면 다음과 같은 오류가 나타납니다.


Cannot open include file: 'gsl/gsl_sf.h': No such file or directory


원래 이 프로그램을 컴파일 하는데는 gsl 이 필요하다고 하였으니 이것을 구해서 컴파일 해야 합니다.

- 당연한 이야기죠~


2. GSL Build


자세한 홈피는 이곳을 참조하였습니다.

http://wiki.originlab.com/~originla/wiki/index.php?title=OriginC:How_to_build_GSL_DLL



http://www.gnu.org/software/gsl/


홈페이지는 위와 같습니다.



윈도우즈 용으로 Config된 소스 코드를 다운 받습니다.


http://gnuwin32.sourceforge.net/packages/gsl.htm


현재 버전은 1.8까지 올라와 있습니다. 음.. 최신 버전이 1.9인데.. 일단 컴파일만 하면 되는것이니

그냥 이것을 쓰겠습니다. 


c:\gsl에 설치한다고 가정하고 소스를 압축을 풀어서 카피해 둡니다.


VC에서 다음 파일을 읽어들여서


c:\gsl\src\gsl\1.8\gsl-1.8\VC8\libgsl.sln


빌드를 합니다.


그러면 차근차근 빌드하고 lib파일들이 만들어진다.


(source code path)\VC8\libgslcblas\Release-DLL

(source code path)\VC8\libgsl\Release-DLL


그럼 이제 이것들을 모두 VC에 등록합니다.

include path : C:\gsl\src\gsl\1.8\gsl-1.8

lib path        : 

C:\gsl\src\gsl\1.8\gsl-1.8\VC8\libgsl\Debug-DLL

C:\gsl\src\gsl\1.8\gsl-1.8\VC8\libgsl\Release-DLL

C:\gsl\src\gsl\1.8\gsl-1.8\VC8\libgslcblas\Debug-DLL

C:\gsl\src\gsl\1.8\gsl-1.8\VC8\libgslcblas\Release-DLL


그리고 빌드를 하면

잘 되네요


아래 그림처럼 나옵니다.


1) 찾고자 하는 이미지의 그림

사용자 삽입 이미지


2) 대상 그림


사용자 삽입 이미지

3) 실행 결과


사용자 삽입 이미지




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

Rob Hess의 SIFT [4]  (0) 2009.02.25
Rob Hess의 SIFT [3]  (2) 2009.02.23
Rob Hess의 SIFT [1]  (1) 2009.02.16
MAC에서 OpenCV를 컴파일 하기  (0) 2009.01.06
XcodeでOpenCV開発  (0) 2009.01.03
Posted by GUNDAM_IM