해당 포스트는 "OpenCV로 배우는 영상 처리 및 응용", "C++ API OpenCV 프로그래밍" 책의 내용을 요약한 것이다.
※ DFT 를 이용한 회선(convolution)
: f(x,y)와 g(x,y)의 회선은 각각의 푸리에 변환 F(u,v)와 G(u,v)의 곱셈을 수행한 결과를 역방향 푸리에 변환을 적용하여 계산한다.
※ DFT 를 이용한 상관관계(convolution)
: f(x,y)와 g(x,y)의 상관관계는 푸리에 변환 F(u,v)와 G(u,v)의 켤레 복소수의 곱셈을 역푸리에 변환을 하여 계산한다.
- Point2d phaseCorrelate(InputArray src1, InputArray src2, InputArray window, double* response=0)
: src1과 src2의 상관관계를 subpixel 수준으로 계산하고 즉, 픽셀 안 채널 수준으로 계산하고 상관관계가 최대값인 위치를 이용하여 영상의 중심으로부터의 위치를 반환한다. src1과 src2는 크기가 같아야 하며 CV_32FC1 또는 CV_64FC1 행렬이다. window는 경계 부분에서 에지를 약화시키기 위해 사용하는 윈도우이다. response는 상관관계 최대값 주변의 5*5 이웃값을 가지고 근사시킨 0과 1 사이의 상관계수 값이다. 이 함수는 최종적으로 역푸리에 변환을 수행하고 난 행렬을 저주파의 위치가 중심으로 가게 고주파의 위치가 바깥쪽으로 가도록 4개의 부분영역을 교환한다. 그리고 영상의 중심에 대한 상대 위치를 반환한다. 따라서 반환 값 즉, 상관관계가 최대값인 위치를 조정해줘야 한다. 글로는 이해하기 힘든데 다음의 예를 보면서 이해해보자.
#include "opencv2\opencv.hpp" #include <iomanip> using namespace cv; using namespace std; Point PhaseCorr(Mat &_A, Mat &_B, double *maxloc); void Printmat(const char *strName, Mat m); int main() { float dataA[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 1, 1, 1, 1, 9, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; float dataB1[] = { 9, 9, 1, 9, 9, 1, 1, 1, 1 }; Mat A(6, 6, CV_32F, dataA); Mat B(3, 3, CV_32F, dataB1); double phaseCorr; Point peakLoc = PhaseCorr(A, B, &phaseCorr); cout << "phaseCorr = " << phaseCorr << endl; cout << "peakLoc = " << peakLoc << endl; return 0; } Point PhaseCorr(Mat &_A, Mat &_B, double *maxValue) { // calculate convolution and correlation by DFT int nW = getOptimalDFTSize(_A.cols + _B.cols - 1); int nH = getOptimalDFTSize(_A.rows + _B.rows - 1); Mat A; copyMakeBorder(_A, A, 0, nH - (_A.rows), 0, nW - (_A.cols), BORDER_CONSTANT, 0); Mat B; copyMakeBorder(_B, B, 0, nH - (_B.rows), 0, nW - (_B.cols), BORDER_CONSTANT, 0); // correlation: magF = IDFT(DFT(A)*conj(DFT(B))) Mat dftA; dft(A, dftA, DFT_COMPLEX_OUTPUT); Mat dftB; dft(B, dftB, DFT_COMPLEX_OUTPUT); Mat dftD; mulSpectrums(dftA, dftB, dftD, 0, true);// conjB = true Mat matCmplx[2]; split(dftD, matCmplx); // normalize by magnitude Mat magF; magnitude(matCmplx[0], matCmplx[1], magF); divide(matCmplx[0], magF, matCmplx[0]); divide(matCmplx[1], magF, matCmplx[1]); merge(matCmplx, 2, dftD); dft(dftD, magF, DFT_INVERSE | DFT_SCALE | DFT_REAL_OUTPUT); // magF : phase correlation Printmat("Corr=", magF); Point peakLoc; minMaxLoc(magF, NULL, maxValue, NULL, &peakLoc); return peakLoc; } void Printmat(const char *strName, Mat m) { int x, y; float fValue; cout << endl << strName << endl; cout << setiosflags(ios::fixed); for (y = 0; y<m.rows; y++) { for (x = 0; x<m.cols; x++) { fValue = m.at<float>(y, x); cout << setprecision(2) << setw(8) << fValue; } cout << endl; } cout << endl; }
위 코드는 phaseCorrelate를 사용하지 않고 직접 코딩해서 상관관계를 최대값을 구하는 프로그램이다. 위 결과 사진을 보면 실제로 [2,2]의 상관관계가 제일 높은 것을 알 수 있다. 다음은 phaseCorrelate 함수를 사용한 코드이다.
#include "opencv2\opencv.hpp" using namespace cv; using namespace std; int main() { Mat srcImage = imread("alphabet.bmp", IMREAD_GRAYSCALE); if (srcImage.empty()) return -1; String tName[] = { "A.bmp", "s.bmp", "b.bmp", "m.bmp" }; Mat dstImage; cvtColor(srcImage, dstImage, COLOR_GRAY2BGR); Mat src32f; srcImage.convertTo(src32f, CV_32F); for (int i = 0; i < 4; i++) { Mat tImage = imread(tName[i], IMREAD_GRAYSCALE); if (tImage.empty()) return -1; Mat tmpl32f; tImage.convertTo(tmpl32f, CV_32F); copyMakeBorder(tmpl32f, tmpl32f, 0, src32f.rows - tmpl32f.rows, 0, src32f.cols - tmpl32f.cols, BORDER_CONSTANT, 0); double phaseCorr; Point2d peakLoc = phaseCorrelate(src32f, tmpl32f, noArray(), &phaseCorr); Point2d center(src32f.cols / 2.0, src32f.rows / 2.0); /////////////////////////// // region1 // region2 // /////////////////////////// // region3 // region4 // /////////////////////////// peakLoc = (center - peakLoc); // phaseCorrelate 함수의 반환 포인트가 영상의
//중심을 기준으로 반환한다. peakLoc = center - t
// inverse of fftShift if (peakLoc.x < center.x) { if (peakLoc.y < center.y) // region 1 peakLoc += center; // to region 4 else // region 3 { peakLoc.x += center.x; // to region 2 peakLoc.y -= center.y; } } else { if (peakLoc.y < center.y) // region 2 { peakLoc.x -= center.x; // to region 3 peakLoc.y += center.y; } else // region 4 peakLoc -= center; // to region 1 } rectangle(dstImage, Point((int)peakLoc.x, (int)peakLoc.y), Point(peakLoc.x + tImage.cols, peakLoc.y + tImage.rows), Scalar(255, 0, 0), 2); } imshow("dstImage", dstImage); waitKey(); return 0; }
'OpenCV 프로그래밍' 카테고리의 다른 글
kmeans, 코너점 검출 함수, 윤곽선 관련 특징 및 매칭 함수, 블록 껍질 (0) | 2017.06.28 |
---|---|
Canny,HoughLines, findContours, drawContours (0) | 2017.06.27 |
dft, idft, mulSpectrums, 저주파/고주파 통과 필터 (0) | 2017.06.26 |
getStructuringElement, erode, dilate, morphologyEx (0) | 2017.06.26 |
getDerivKernels, Sobel, Scharr, Laplacian, LoG (0) | 2017.06.26 |