현실감각 0% :: '컴퓨터 관련' 카테고리의 글 목록 (8 Page)

컴퓨터 관련 2011. 3. 29. 11:37

Inverse Polar Transform




요즘 되지도 않을 것 같은 360도 카메라 프로젝트인가 뭔가때문에 골치아프다.
이놈의 주 목적은 사방 360도를 n개의 카메라로 촬영해서 방범용으로 쓴다는 건데...
꼭 n개가 필요할까 싶어 좌우상하 180도 화각을 갖는 어안렌즈 검색하던 도중 이런 영상을 만났다.



 



오~ 카메라 하나로 360도 다 보이기는 하는데.. 눈 건강에 그리 좋을것 같진 않다. 어린왕자의 소혹성 B612도 아니고.. 이게 뭐임;;
이 카메라 만드는 회사에서 소프트웨어를 하나 주길래 다운받아보니 바로 저놈을 평평하게 펴주는 프로그램!!!

그래서...

그 친구들도 만드는데 못만들까 싶어 함 만들어봤다. 평평하게 펴주는 변환!!!
정식명칭은 Inverse Polar Transform
억지로 번역하자면 역 극변환 정도?;;

이것저것 잡다한건 openCV로 처리했다. (잡다할것도 없지만...)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
#include <math.h>

IplImage* ComputeInversePolarTransformUsingOpenCV(IplImage* srcImg, int srcWidth, int srcHeight, int dstWidth, int dstHeight)
{
 int centerX = srcWidth / 2;
 int centerY = srcHeight / 2;
 double radius = sqrt((double)(centerX - 0) * (double)(centerX - 0) + (double)(centerY - 0) * (double)(centerY - 0));
 double radiusTemp = sqrt((double)(centerX - srcWidth) * (double)(centerX - srcWidth) + (double)(centerY - 0) * (double)(centerY - 0));
 if(radiusTemp > radius)
  radius = radiusTemp;
 radiusTemp = sqrt((double)(centerX - 0) * (double)(centerX - 0) + (double)(centerY - srcHeight) * (double)(centerY - srcHeight));
 if(radiusTemp > radius)
  radius = radiusTemp;
 radiusTemp = sqrt((double)(centerX - srcWidth) * (double)(centerX - srcWidth) + (double)(centerY - srcHeight) * (double)(centerY - srcHeight));
 if (radiusTemp > radius)
  radius = radiusTemp;
 int radiusInt = (int)radius;
 if(dstWidth < 0)
  dstWidth = radiusInt;
 
 CvSize dstSize;
 dstSize.width = dstWidth;
 dstSize.height = dstHeight;
 IplImage* dstImg = cvCreateImage(dstSize, IPL_DEPTH_8U, 3);
 
 for(int yy = 0; yy < dstHeight; yy++)
 {
  for(int xx = 0; xx < dstWidth; xx++)
  {
   double r = xx;
   double angle = (yy / (double)dstHeight) * CV_PI * 2;

   double y = (r * cos(angle)) + centerX;
   double x = abs((r * sin(angle)) + centerY);
   
   if((yy < 0 || xx < 0 || yy >= dstHeight || xx >= dstWidth) || (int)y < 0 || (int)x < 0 || (int)y >= srcHeight || (int)x >= srcWidth)
   {}
   else
   {
    dstImg->imageData[(yy * dstWidth + xx) * 3] = srcImg->imageData[((int)y * srcWidth + (int)x) * 3];
    dstImg->imageData[(yy * dstWidth + xx) * 3 + 1] = srcImg->imageData[((int)y * srcWidth + (int)x) * 3 + 1];
    dstImg->imageData[(yy * dstWidth + xx) * 3 + 2] = srcImg->imageData[((int)y * srcWidth + (int)x) * 3 + 2];
   }
  }
 }
 //cvNamedWindow("Test", CV_WINDOW_AUTOSIZE);
 //cvShowImage("Test", dstImg);
 //cvWaitKey(0);

 return dstImg;
}


우선... 매개변수로 입력영상, 입력영상의 가로세로 길이, 그리고 출력영상의 가로세로 길이를 받는다.
그담엔... 중점을 구하고... (정사각형이라고 가정하면 꼭 x, y 다받을 필요는 없다)
그담엔 반지름을 구하고...(물론 정사각형이라면 위 코드처럼 복잡하게 쓸 필요 없이 더 간단하게...)
IplImage 부분은 출력영상 생성하는 부분이니까 그냥 뛰어넘고...
앵글 구해준 다음에 입력영상부의 x, y 픽셀 데이터를 앵글을 이용해 새로 생성한 출력영상의 x, y 픽셀에 복사
마지막으로 출력영상 리턴해주면 끝.

출력영상의 너비를 모르기 때문에 출력 영상을 매개변수로 안받고 리턴 처리했다.
(물론 출력영상의 너비를 입력할 수 있지만, 0보다 작은 값을 넣으면 알아서 계산하게끔 했다.)

Interpolation.. 뭐 그런건 귀찮아서 안함-_-;;;
회전도 귀찮아서 안함-_-;;

아래는 결과 영상(회전은 알씨로...-_-;;)




깜장부분 있으면 지저분하니까 크롭!!


당연한 얘기겠지만... 하늘을 바라보고 찍은 사진도 가능하다.(위 사진은 지면을 바라보고 찍은 것)


영상 변환 시 중점에 가까울수록 별로 있지도 않은 픽셀을 억지로 늘리다보니 왜곡이 생긴다.
좌측의 첨탑 꼭대기가 살짝 휜 왜곡은 촬영시의 실제 중심과 영상의 중심이 조금 어긋나서 생긴듯 하다.


참고문헌

http://www.cs.columbia.edu/CAVE/ (영문)
http://en.wikipedia.org/wiki/Polar_coordinate_system (영문-_-;;)



컴퓨터 관련 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;
}




템플릿



실험영상



결과영상



로그 출력 결과