해당 포스트는 "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; }


+ Recent posts