1. 程式人生 > >Log-Gabor濾波器構造,opencv實現

Log-Gabor濾波器構造,opencv實現

參照連結:https://github.com/carandraug/PeterKovesiImage/blob/master/PhaseCongruency/gaborconvolve.m點選開啟連結

畢設要用Log-Gabor濾波器來實現視網膜血管增強,這東西真是折騰了我好一段時間。。。。Matlab程式碼轉到C++程式碼沒動什麼腦子,基本就是按邏輯翻譯過來的

Log-Gabor濾波器的概念本文暫不表述了,待後期新增

首先是一些與濾波器無關的matlab轉C++用 的程式碼:

void circshift(Mat& out, const Point& delta)
{
	Size sz = out.size();


	// 錯誤檢查
	assert(sz.height > 0 && sz.width > 0);
	// 此種情況不需要移動
	if ((sz.height == 1 && sz.width == 1) || (delta.x == 0 && delta.y == 0))
		return;


	// 需要移動引數的變換,這樣就能輸入各種整數了
	int x = delta.x;
	int y = delta.y;
	if (x > 0) x = x % sz.width;
	if (y > 0) y = y % sz.height;
	if (x < 0) x = x % sz.width + sz.width;
	if (y < 0) y = y % sz.height + sz.height;




	// 多維的Mat也能移動
	vector<Mat> planes;
	split(out, planes);


	for (size_t i = 0; i < planes.size(); i++)
	{
		// 豎直方向移動
		Mat tmp0, tmp1, tmp2, tmp3;
		Mat q0(planes[i], Rect(0, 0, sz.width, sz.height - y));
		Mat q1(planes[i], Rect(0, sz.height - y, sz.width, y));
		q0.copyTo(tmp0);
		q1.copyTo(tmp1);
		tmp0.copyTo(planes[i](Rect(0, y, sz.width, sz.height - y)));
		tmp1.copyTo(planes[i](Rect(0, 0, sz.width, y)));


		// 水平方向移動
		Mat q2(planes[i], Rect(0, 0, sz.width - x, sz.height));
		Mat q3(planes[i], Rect(sz.width - x, 0, x, sz.height));
		q2.copyTo(tmp2);
		q3.copyTo(tmp3);
		tmp2.copyTo(planes[i](Rect(x, 0, sz.width - x, sz.height)));
		tmp3.copyTo(planes[i](Rect(0, 0, x, sz.height)));
	}


	merge(planes, out);
}


void fftshift(Mat& out)
{
	Size sz = out.size();
	Point pt(0, 0);
	pt.x = (int)floor(sz.width / 2.0);
	pt.y = (int)floor(sz.height / 2.0);
	circshift(out, pt);
}


void ifftshift(Mat& out)
{
	Size sz = out.size();
	Point pt(0, 0);
	pt.x = (int)ceil(sz.width / 2.0);
	pt.y = (int)ceil(sz.height / 2.0);
	circshift(out, pt);
}


void meshgrid(double xStart, double xEnd, double yStart, double yEnd,
              Mat& X, Mat& Y)
{
	std::vector<double> t_x, t_y;
	while (xStart <= xEnd)
	{
		t_x.push_back(xStart);


		++xStart;
	}
	while (yStart <= yEnd)
	{
		t_y.push_back(yStart);


		++yStart;
	}


	repeat(Mat(t_x).t(), t_y.size(), 1, X);
	repeat(Mat(t_y), 1, t_x.size(), Y);
}

下文需要用到的是meshgrid函式、fftshift、ifftshift函式,這兩個函式是幹嘛用的自行百度

mathlab裡[x,y] = meshgrid(xrange, yrange); 等價於上述的 meshgrid(xrange.lower,xrange.upper,yrange.lower,yrange.upper)//大意虛擬碼

然後是Log-Gabor核構造需要的低通濾波器

Mat lowpassFilter(Size size, double cutOff, int n)
{
	int rows, cols;


	double xStart, xEnd, yStart, yEnd;


	if (cutOff < 0 || cutOff > 0.5)
		throw std::exception("cutoff frequency must be between 0 and 0.5");

	if (size.width == 1)
	{
		rows = size.width;
		cols = size.width;
	}
	else
	{
		rows = size.height;
		cols = size.width;
	}


	Mat x(cols, rows, CV_64FC1);
	Mat y(cols, rows, CV_64FC1);

	bool isRowsOdd = (rows % 2 == 1);
	bool isColsOdd = (cols % 2 == 1);
	if (isRowsOdd)
	{
		yStart = -(rows - 1) / 2.0;
		yEnd = (rows - 1) / 2.0;
	}
	else
	{
		yStart = -(rows / 2.0);
		yEnd = (rows / 2.0 - 1);
	}

	if (isColsOdd)
	{
		xStart = -(cols - 1) / 2.0;
		xEnd = (cols - 1) / 2.0;
	}
	else
	{
		xStart = -(cols / 2.0);
		xEnd = (cols / 2.0 - 1);
	}


	meshgrid(xStart, xEnd, yStart, yEnd, x, y);
	if (isColsOdd)
		x /= (cols - 1);
	else
		x /= cols;

	if (isRowsOdd)
		y /= (rows - 1);
	else
		y /= rows;


	Mat radius;
	Mat x2;
	Mat y2;


	pow(x, 2, x2);
	pow(y, 2, y2);
	sqrt(x2 + y2, radius);
	Mat f;
	Mat temp;
	pow((radius / cutOff), 2 * n, temp);
	divide(Mat::ones(radius.rows, radius.cols, CV_64F), 1.0 + temp, f);
	ifftshift(f);
	return f;
}

返回的mat即為構造的低通濾波器核

然後是一些矩陣運算操作

void complexMul(Mat left[2], Mat right[2], Mat* result)
{
	Mat real1;
	Mat real2;
	Mat imag1;
	Mat imag2;

	real1 = left[0].mul(right[0]);
	real2 = (left[1].mul(right[1]));
	imag1 = left[1].mul(right[0]);
	imag2 = left[0].mul(right[1]);

	result[0] = real1 - real2;
	result[1] = imag1 + imag2;
}

void complexDiv(Mat left[2], Mat right[2], Mat* result)
{
	Mat negRight[2];
	negRight[0] = right[0];
	negRight[1] = -right[1];

	Mat upper[2];
	Mat lower[2];
	complexMul(left, negRight, upper);
	complexMul(right, negRight, lower);


	divide(upper[0], lower[0], result[0]);
	divide(upper[1], lower[0], result[1]);
}

void complexAbs(Mat mat[2], Mat& result)
{
	Mat left, right;
	cv::pow(mat[0], 2, left);
	pow(mat[1], 2, right);
	sqrt(left + right, result);
}

注意,之所以是Mat[2]是因為 傳入的應是複數,mat[0] [1]分別為實部和虛部

上述三個函式分別是計算 × ÷ 和  強度的(平方和開根)

最後是返回結果的結構體的定義

struct GaborConvolveResult
{

	vector<vector<Mat>>EO;
	vector<Mat>BP;
};

EO為 EO[nOrient,nScale],分別代表了不同角度和不同尺度的處理結果

BP為BP[nScale] ,分別表示了不同尺度下的處理結果(方向已融合)

下面就是Log-Gabor核的具體構造了

GaborConvolveResult garborConvolve(const Mat& mat, int nScale, int nOrient, double minWaveLength, double mult, double sigmaOnf,
                    double dThetaSigma, int Lnorm = 0, double feedback = 0)
{
	//mat應已確認是灰度圖,並且是double
	int rows = mat.rows;
	int cols = mat.cols;
	

	Mat matDft;
	toFre(mat, matDft);
	
	

	vector<vector<Mat>>EO(nOrient, vector<Mat>(nScale, Mat(cols, rows, CV_64F,Scalar(0))));
	vector<Mat>BP(nScale, Mat(cols, rows, CV_64F, Scalar(0)));

	Mat x;
	Mat y;
	double xStart, xEnd, yStart, yEnd;
	bool isRowsOdd = (rows % 2 == 1);
	bool isColsOdd = (cols % 2 == 1);
	if (isRowsOdd)
	{
		yStart = -(rows - 1) / 2.0;
		yEnd = (rows - 1) / 2.0;
	}
	else
	{
		yStart = -(rows / 2.0);
		yEnd = (rows / 2.0 - 1);
	}

	if (isColsOdd)
	{
		xStart = -(cols - 1) / 2.0;
		xEnd = (cols - 1) / 2.0;
	}
	else
	{
		xStart = -(cols / 2.0);
		xEnd = (cols / 2.0 - 1);
	}




	meshgrid(xStart, xEnd, yStart, yEnd, x, y);

	if (isColsOdd)
		x /= (cols - 1);
	else
		x /= cols;

	if (isRowsOdd)
		y /= (rows - 1);
	else
		y /= rows;

	Mat radius;
	Mat x2;
	Mat y2;


	pow(x, 2, x2);
	pow(y, 2, y2);
	sqrt(x2 + y2, radius);

	Mat theta(rows, cols, CV_64FC1);

	for (int i = 0; i < rows; ++i)
		for (int j = 0; j < cols; ++j)
			theta.at<double>(i, j) = atan2(y.at<double>(i, j), x.at<double>(i, j));

	ifftshift(radius);
	ifftshift(theta);

	radius.at<double>(0, 0) = 1;

	Mat sinTheta(rows, cols, CV_64FC1);
	Mat cosTheta(rows, cols, CV_64FC1);

	for (int i = 0; i < rows; ++i)
		for (int j = 0; j < cols; ++j)
			sinTheta.at<double>(i, j) = sin(theta.at<double>(i, j));

	for (int i = 0; i < rows; ++i)
		for (int j = 0; j < cols; ++j)
			cosTheta.at<double>(i, j) = cos(theta.at<double>(i, j));

	Mat lp = lowpassFilter(Size(cols,rows), 0.45, 15);

	

	//vector<Mat>logGabor(nScale, Mat(rows, cols, CV_64F,Scalar(0,0)));
	vector<Mat>logGabor;
	for(int  s = 0;s<nScale;++s)
	{
		logGabor.push_back(Mat(rows, cols, CV_64F));
		double waveLength = minWaveLength* pow(mult, s );
		double fo = 1.0 / waveLength;

		

		Mat tempUpper;

		log(radius / fo, tempUpper);
		pow(tempUpper, 2, tempUpper);

		double tempLower = pow(log(sigmaOnf), 2);

		double factory = -1 / 2.0;
		tempUpper = tempUpper / tempLower * factory;



		exp(tempUpper, logGabor[s]);

		logGabor[s] = logGabor[s].mul(lp);
		logGabor[s].at<double>(0, 0) = 0;

		double L = 0;
		switch (Lnorm)
		{
		case 0:
			L = 1;
			break;
		case 1:
			{
			Mat planes[2] = { Mat(rows,cols,CV_64F),Mat(rows,cols,CV_64F) };
			Mat complex;
			idft(logGabor[s], complex, DFT_COMPLEX_OUTPUT + DFT_SCALE);
			split(complex, planes);
			Mat realPart = planes[0];

			L = sum(abs(realPart))[0];
			break;
			}
			
		case 2:
		{
			Mat temp;
			pow(logGabor[s], 2, temp);

			L = sqrt(sum(temp)[0]);

		}

			break;
		default:
			break;
		}

		logGabor[s] = logGabor[s] / L;
		//cout << logGabor[s] << endl;
		//cout << curLogGabor;
		

		Mat complex;
		Mat planes[2] = { Mat(rows,cols,CV_64F),Mat(rows,cols,CV_64F) };
		split(matDft, planes);

		planes[0] = planes[0].mul(logGabor[s]);
		planes[1] = planes[1].mul(logGabor[s]);

		//idft(complex, EO, DFT_COMPLEX_OUTPUT + DFT_SCALE);
		

		//cout << temp.depth() << "  " << temp.channels();
		//split(temp, planes);
		
		Mat complexd;
		merge(planes, 2, complexd);
		 idft(complexd,BP[s], DFT_COMPLEX_OUTPUT + DFT_SCALE);

	}
	//cout << logGabor[0] << endl;

	for(int o = 0 ; o < nOrient;++o)
	{
		double angl = o*CV_PI / nOrient;
		double waveLength = minWaveLength;


		Mat ds = sinTheta * cos(angl) - cosTheta * sin(angl);
	Mat dc = cosTheta * cos(angl) + sinTheta * sin(angl);

	Mat dTheta(rows, cols, CV_64F);
	for (int i = 0; i < rows; ++i)
		for (int j = 0; j < cols; ++j)
			dTheta.at<double>(i, j) = abs(atan2(ds.at<double>(i, j), dc.at<double>(i, j)));

	Mat temp;
	pow(dTheta, 2, temp);
	temp = -temp;
	Mat spread;
	double thetaSigma = CV_PI / nOrient / dThetaSigma;
	exp(temp / (2 * pow(thetaSigma, 2)), spread);

		for(int s =0 ;s<nScale;++s)
		{
			Mat filter = spread.mul(logGabor[s]);
			double L = 0;
			switch (Lnorm)
			{
			case 0: L = 1;
				break;
			case 1:
				{
				Mat planes[2] = { Mat(rows,cols,CV_64F),Mat(rows,cols,CV_64F) };
				Mat complex;
				idft(filter, complex, DFT_COMPLEX_OUTPUT + DFT_SCALE);
				split(complex, planes);
				Mat realPart = planes[0];
				L = sum(abs(realPart))[0];
				}
				break;
			case 2:
				{
				
				
				Mat planes[2] = { Mat(rows,cols,CV_64F),Mat(rows,cols,CV_64F) };
				
				split(temp, planes);
				Mat realPart = planes[0];
				
					
				Mat imagPart = planes[1];
				pow(realPart, 2, realPart);
				pow(imagPart, 2, imagPart);
				

				L = sqrt(sum(realPart)[0] + sum(imagPart)[0]);
				}
				break;
			default:
				break;
			}
			filter = filter / L;

			Mat complex;
			Mat planes[2] = { Mat(rows,cols,CV_64F),Mat(rows,cols,CV_64F) };
			cv::split(matDft, planes);
		
			planes[0] = planes[0].mul(filter);
			planes[1] = planes[1].mul(filter);
		
			merge(planes,2, complex);

			//here
			//Mat  multed = matDft.mul(filter);
			//cout << filter << endl;
			idft(complex, EO[o][s], DFT_COMPLEX_OUTPUT + DFT_SCALE);
			//cout << EO[s][o].cols << " " << EO[s][o].rows << EO[s][o].channels() << " " << EO[s][o].depth() << endl;

			Mat EOPlanes[2] = { Mat(rows,cols,CV_64F),Mat(rows,cols,CV_64F) };
			split(EO[o][s], EOPlanes);
		
			
			waveLength = waveLength *mult;
		}

		
	}



	GaborConvolveResult result;
	result.BP = BP;
	result.EO = EO;
	return result;
}

\