현실감각 0% :: '분류 전체보기' 카테고리의 글 목록 (14 Page)

컴퓨터 관련 2011. 3. 16. 14:22

2D영상 오브젝트의 회전각 산출하기





컴퓨터비전에서 영상처리를 하는 목적은 여러가지가 있겠지만 가장 큰 목적 중 하나가 바로 오브젝트의 추출이다.
이런저런 알고리즘을 써서 힘들게 오브젝트를 추출했는데.. 아니 글쎄 이놈들이 인식하기 어렵게 지들 마음대로 회전되어 있다면??
회전에 강건한 인식 알고리즘도 많지만 템플릿매칭이나 Haar-like 특징을 사용한 알고리즘 같이 회전되어있으면 좀 골치아픈 알고리즘에서는 이놈들을 다시 차렷자세로 돌려놓을 필요가 있다.

그럴때 쓰는 유용한 수식.




이 수식을 어디서 찾았는지는 분명하지 않다...-_-;; 아마 곤잘레스의 이미지프로세싱 책에서 본듯싶은데...
뭐 아무튼 수식을 간단히 설명하자면, 뮤는 수식에서 보면 쉽게 알 수 있듯이 뭐.. 분산이라고 보면 된다.
즉 뮤20은 x축의 분산, 뮤02는 y축의 분산, 그리고 뮤11은 그냥 분산...(?) 공분산!!

아무튼 x축 분산과 y축 분산을 빼면 어디로 더 분산이 큰지 알수있는데 그놈을 공분산값으로 나눠주면 기울기가 나온다.
그 기울기를 아크탄젠트 해주면 각도 생성.
언어에 따라 다르겠지만 혹 degree값이 필요한데 radian값으로 나왔다면 180/π 를 곱해주는 작업을 추가하면 된다.
반대인 경우 나눠주면 되고-_-;


                    
                     영상 -> 이진화 -> 레이블링 -> 각도 추출 -> MBR로 장축 길이 추출 -> 장축을 중심으로 각도만큼 회전


위 사진은 열쇠 영상을 이진화 한 후, 레이블링하여 각각의 오브젝트로 만든 후, 각각의 회전각을 산출하고 차렷자세로 만든 모습.
귀찮아서 인터폴레이션 안했더니 오브젝트에 구멍이 슝슝...-_-;


소스코드는 아래..(클래스화 시킨다고 시키고는 안돌려봐서 잘되는지는 모름-_-;)



컴퓨터 관련 2010. 10. 21. 16:05

openCV를 이용한 회전/스케일링에 강건한(-_-;) 이진 템플릿 매칭(Template Matching)




템플릿 매칭 방식으로만 만들어야해서 SIFT, SURF, ANN, ADABOOST 등 여러 좋은 알고리즘 놔두고 이렇게 만들었음.ㅠㅠ 엉엉

openCV 함수 제외하곤 함수 만들어 쓴것도 없고 main에 다 때려박은데다가 워낙 알아보기 쉽게 코딩하는 스타일이 아니라서

좀 그지같지만, 나름 잘돌아감.

아.. 참고로 코드를 보면 알겠지만 가장 무식한 방법으로 짬.-_-;

스케일변환용 for문 2개, 회전용 for문 2개. 토탈 4중 for문..ㅡ,ㅡ;;
영상 회전을 왜 저렇게 했을까...싶은데 트랜스폼매트릭스 쓰는 방법 기억안나서
저렇게 코딩함-_-; 검색하기 귀찮...;



//---------------------------------------------------------
// 개요      : 템플릿 매칭
// Library   : OpenCV for MS-Windows 2.1
//---------------------------------------------------------

#include <stdio.h>
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include <math.h>

void rotation(IplImage *rotimg, double Angle, double Scale);

int main()
{
 int i, j, x, y, key;
 double minVal;
 char windowNameSource[] = "Original Image";
 char windowNameDestination[] = "Result Image";
 char windowNameCoefficientOfCorrelation[] = "Coefficient of Correlation Image";
  
 CvPoint minLoc;
 CvPoint tempLoc;
 
 IplImage *sourceImage = cvLoadImage("template_source.bmp", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);
 IplImage *templateImage = cvLoadImage("template.bmp", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);

 if (sourceImage == NULL)
 {
  printf( "소스 영상이 발견되지 않습니다\n");
  return -1;
 }
 if(templateImage == NULL)
 {
  printf( "템플릿 영상이 발견되지 않습니다\n");
  return -1;
 }
 
 IplImage *graySourceImage = cvCreateImage(cvGetSize(sourceImage), IPL_DEPTH_8U, 1);
 IplImage *grayTemplateImage = cvCreateImage(cvGetSize(templateImage), IPL_DEPTH_8U, 1);
 IplImage *binarySourceImage = cvCreateImage(cvGetSize(sourceImage), IPL_DEPTH_8U, 1);
 IplImage *binaryTemplateImage = cvCreateImage(cvGetSize(templateImage), IPL_DEPTH_8U, 1);
 IplImage *destinationImage = cvCreateImage(cvGetSize(sourceImage), IPL_DEPTH_8U, 3);

 cvCopy(sourceImage, destinationImage);
 cvCvtColor(sourceImage, graySourceImage, CV_RGB2GRAY);
 cvCvtColor(templateImage, grayTemplateImage, CV_RGB2GRAY);

 cvThreshold(graySourceImage, binarySourceImage, 70, 255, CV_THRESH_BINARY);
 cvThreshold(grayTemplateImage, binaryTemplateImage, 70, 255, CV_THRESH_BINARY);
 
 int templateHeight = templateImage->height;
 int templateWidth = templateImage->width;

 float templateScale = 0.5f;
 // 템플릿 매칭 수행부분
 for(i = 2; i <= 3; i++)  // 스케일(100%부터 150%까지 50%간격으로 축소 및 확대) ,i 및 templateScale 값 수정하여 조정 가능
 {
  int tempTemplateHeight = (int)(templateWidth * (i * templateScale));
  int tempTemplateWidth = (int)(templateHeight * (i * templateScale));
  IplImage *tempBinaryTemplateImage = cvCreateImage(cvSize(tempTemplateWidth, tempTemplateHeight), IPL_DEPTH_8U, 1);
  // W - w + 1, H - h + 1
  IplImage *result = cvCreateImage(cvSize(sourceImage->width - tempTemplateWidth + 1, sourceImage->height - tempTemplateHeight + 1), IPL_DEPTH_32F, 1);
  cvResize(binaryTemplateImage, tempBinaryTemplateImage, CV_INTER_LINEAR);
  
  float degree = 20.0f;
  for(j = 0; j <= 9; j++) // 회전(0도부터 180도까지 20도 간격으로 회전) => j * 20 = degree ,j 및 degree 값 수정하여 조정 가능
  {
   IplImage *rotateBinaryTemplateImage = cvCreateImage(cvSize(tempBinaryTemplateImage->width, tempBinaryTemplateImage->height), IPL_DEPTH_8U, 1);
   //cvShowImage(windowNameSource, tempBinaryTemplateImage);  // 템플릿 확인용
   //cvWaitKey(0);             // 템플릿 확인용
   //회전
   
   // 회전, 스케일링 적용할 템플릿 변수 255로 초기화
   for(y = 0; y < tempTemplateHeight; y++)
   {
    for(x = 0; x < tempTemplateWidth; x++)
    {
     rotateBinaryTemplateImage->imageData[y * tempTemplateWidth + x] = 255;
    }
   }
   
   // 템플릿 회전
   for(y = 0; y < tempTemplateHeight; y++)
   {
    for(x = 0; x < tempTemplateWidth; x++)
    {
     float radian = (float)j * degree * CV_PI / 180.0f;
     int scale = y * tempTemplateWidth + x;
     int rotateY = - sin(radian) * ((float)x - (float)tempTemplateWidth / 2.0f) + cos(radian) * ((float)y - (float)tempTemplateHeight / 2.0f) + tempTemplateHeight / 2;
     int rotateX = cos(radian) * ((float)x - (float)tempTemplateWidth / 2.0f) + sin(radian) * ((float)y - (float)tempTemplateHeight / 2.0f) + tempTemplateWidth / 2;

     if(rotateY < tempTemplateHeight && rotateX < tempTemplateWidth && rotateY >= 0 && rotateX  >= 0)
      rotateBinaryTemplateImage->imageData[scale] = tempBinaryTemplateImage->imageData[rotateY * tempTemplateWidth + rotateX];
    }
   }
   // 회전확인용
   //cvShowImage(windowNameSource, rotateBinaryTemplateImage);
   //cvWaitKey(0);

   cvMatchTemplate(binarySourceImage, rotateBinaryTemplateImage, result, CV_TM_SQDIFF_NORMED); // 정규화 O (0~1 사이 값으로 산출)
   //cvMatchTemplate(binarySourceImage, rotateBinaryTemplateImage, result, CV_TM_SQDIFF);  // 정규화 X (템플릿 크기에 따라 다른 값 산출)
   cvMinMaxLoc(result, &minVal, NULL, &minLoc, NULL, NULL);
   printf("템플릿 스케일 %d%%, 회전각도 %d˚의 최대 유사도 : %f%%\n", (int)(i * 0.5 * 100), j * 20, (1 - minVal) * 100);    // 최대 유사도값 로그 출력
   if(minVal < 0.065) // 1 - 0.065 = 0.935 : 93.5% 이상 유사한 경우 탐지하였다고 가정
   {
    tempLoc.x = minLoc.x + tempTemplateWidth;
    tempLoc.y = minLoc.y + tempTemplateHeight;
    cvRectangle(destinationImage, minLoc, tempLoc, CV_RGB(255, 0, 0), 1, 8, 0);
   }
  }
  //cvShowImage(windowNameSource, result);
  //cvWaitKey(0);
  cvReleaseImage(&tempBinaryTemplateImage);
  cvReleaseImage(&result);
 }
 // 템플릿 매칭 수행부분 끝

 cvShowImage(windowNameSource, sourceImage);
// cvShowImage(windowNameCoefficientOfCorrelation, result); // Coefficient of Correlation 출력
 cvShowImage(windowNameDestination, destinationImage);
 key = cvWaitKey(0);
 
 // 메모리를 해방한다
 cvReleaseImage(&sourceImage);
 cvReleaseImage(&templateImage);
 cvReleaseImage(&graySourceImage);
 cvReleaseImage(&grayTemplateImage);
 cvReleaseImage(&binarySourceImage);
 cvReleaseImage(&binaryTemplateImage);
 cvReleaseImage(&destinationImage);

 // 윈도우를 파기한다
 cvDestroyWindow(windowNameSource);
 cvDestroyWindow(windowNameDestination);
 cvDestroyWindow(windowNameCoefficientOfCorrelation);

 return 0;
}




템플릿



실험영상



결과영상



로그 출력 결과



컴퓨터 관련 2010. 10. 18. 16:29

벡터의 사영(projection)



사영(projection), 직교(orthogonal), 정규 직교(orthonormal)...
수업시간에 죽어라 들어도 잘 이해가 안가던것들이었는데 이 그림 한방으로 이해되었다.
머리가 원체 나빠서 그림으로 보여줘야 잘 이해함.-_-;


출처 : 패턴인식개론(개정판), page 50, 한빛미디어


별거 아니다. 사영이란 쉽게 말해 벡터 y를 벡터 x와 수직인 방향에서 올려다봤을때 보여지는 벡터 y라고나 할까.
즉 방향은 벡터 x와 동일하고 길이는 사영되어 나타난 값 (그림의 y'ux)만큼 되는 것이다.

그런데 여기서 y와 x의 내적이 0이 된다면? 쉬운 예로 x = |-2 2|, y = |2, 2| 이면 dot(x,y) = 0인데
이러한 경우는 그래프를 그려봐도 알겠지만 y가 x에 대해 수직이다.
즉 y'ux의 길이가 0이다. 이런경우를 직교(orthogonal)한다고 한다.

그리고 x와 y의 값이 동일할 때, 즉 |x| = |y| 인 직교이면서도 길이까지 같은 경우를 정규 직교(orthonormal)이라고 한다.

아 정말 개쉬운걸 가지고 고생했다.