현실감각 0% :: Inverse Polar Transform

컴퓨터 관련 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 (영문-_-;;)