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 (영문-_-;;)