Zernik矩亚像素边缘检测

Zernik矩亚像素边缘检测

在网上看到有人写的Zernike矩亚像素边缘检测,发现存在很大的问题,赌气自己写了一个版本,力求精简,没有做过多优化。模板采用(7,7),借用Opencv的数据结构。如有问题欢迎指出。
关于Zernike矩求亚像素的原理,可以百度,有很多资源,下面是本人参考的文献:
Zernike矩亚像素原理
下面直接上代码:

#include 
#include 
#include 
using namespace cv;
using namespace std;



const double PI = 3.14159265358979323846;
const int g_N = 7;
const int h_N = 3;
Mat M00 = (Mat_<float>(7, 7) <<
	0, 0.0287, 0.0686, 0.0807, 0.0686, 0.0287, 0,
	0.0287, 0.0815, 0.0816, 0.0816, 0.0816, 0.0815, 0.0287,
	0.0686, 0.0816, 0.0816, 0.0816, 0.0816, 0.0816, 0.0686,
	0.0807, 0.0816, 0.0816, 0.0816, 0.0816, 0.0816, 0.0807,
	0.0686, 0.0816, 0.0816, 0.0816, 0.0816, 0.0816, 0.0686,
	0.0287, 0.0815, 0.0816, 0.0816, 0.0816, 0.0815, 0.0287,
	0, 0.0287, 0.0686, 0.0807, 0.0686, 0.0287, 0);

Mat M11R = (Mat_<float>(7, 7) <<
	0, -0.015, -0.019, 0, 0.019, 0.015, 0,
	-0.0224, -0.0466, -0.0233, 0, 0.0233, 0.0466, 0.0224,
	-0.0573, -0.0466, -0.0233, 0, 0.0233, 0.0466, 0.0573,
	-0.069, -0.0466, -0.0233, 0, 0.0233, 0.0466, 0.069,
	-0.0573, -0.0466, -0.0233, 0, 0.0233, 0.0466, 0.0573,
	-0.0224, -0.0466, -0.0233, 0, 0.0233, 0.0466, 0.0224,
	0, -0.015, -0.019, 0, 0.019, 0.015, 0);

Mat M11I = (Mat_<float>(7, 7) <<
	0, -0.0224, -0.0573, -0.069, -0.0573, -0.0224, 0,
	-0.015, -0.0466, -0.0466, -0.0466, -0.0466, -0.0466, -0.015,
	-0.019, -0.0233, -0.0233, -0.0233, -0.0233, -0.0233, -0.019,
	0, 0, 0, 0, 0, 0, 0,
	0.019, 0.0233, 0.0233, 0.0233, 0.0233, 0.0233, 0.019,
	0.015, 0.0466, 0.0466, 0.0466, 0.0466, 0.0466, 0.015,
	0, 0.0224, 0.0573, 0.069, 0.0573, 0.0224, 0);

Mat M20 = (Mat_<float>(7, 7) <<
	0, 0.0225, 0.0394, 0.0396, 0.0394, 0.0225, 0,
	0.0225, 0.0271, -0.0128, -0.0261, -0.0128, 0.0271, 0.0225,
	0.0394, -0.0128, -0.0528, -0.0661, -0.0528, -0.0128, 0.0394,
	0.0396, -0.0261, -0.0661, -0.0794, -0.0661, -0.0261, 0.0396,
	0.0394, -0.0128, -0.0528, -0.0661, -0.0528, -0.0128, 0.0394,
	0.0225, 0.0271, -0.0128, -0.0261, -0.0128, 0.0271, 0.0225,
	0, 0.0225, 0.0394, 0.0396, 0.0394, 0.0225, 0);

Mat M31R = (Mat_<float>(7, 7) <<
	0, -0.0103, -0.0073, 0, 0.0073, 0.0103, 0,
	-0.0153, -0.0018, 0.0162, 0, -0.0162, 0.0018, 0.0153,
	-0.0223, 0.0324, 0.0333, 0, -0.0333, -0.0324, 0.0223,
	-0.0190, 0.0438, 0.0390, 0, -0.0390, -0.0438, 0.0190,
	-0.0223, 0.0324, 0.0333, 0, -0.0333, -0.0324, 0.0223,
	-0.0153, -0.0018, 0.0162, 0, -0.0162, 0.0018, 0.0153,
	0, -0.0103, -0.0073, 0, 0.0073, 0.0103, 0);

Mat M31I = (Mat_<float>(7, 7) <<
	0, -0.0153, -0.0223, -0.019, -0.0223, -0.0153, 0,
	-0.0103, -0.0018, 0.0324, 0.0438, 0.0324, -0.0018, -0.0103,
	-0.0073, 0.0162, 0.0333, 0.039, 0.0333, 0.0162, -0.0073,
	0, 0, 0, 0, 0, 0, 0,
	0.0073, -0.0162, -0.0333, -0.039, -0.0333, -0.0162, 0.0073,
	0.0103, 0.0018, -0.0324, -0.0438, -0.0324, 0.0018, 0.0103,
	0, 0.0153, 0.0223, 0.0190, 0.0223, 0.0153, 0);

Mat M40 = (Mat_<float>(7, 7) <<
	0, 0.013, 0.0056, -0.0018, 0.0056, 0.013, 0,
	0.0130, -0.0186, -0.0323, -0.0239, -0.0323, -0.0186, 0.0130,
	0.0056, -0.0323, 0.0125, 0.0406, 0.0125, -0.0323, 0.0056,
	-0.0018, -0.0239, 0.0406, 0.0751, 0.0406, -0.0239, -0.0018,
	0.0056, -0.0323, 0.0125, 0.0406, 0.0125, -0.0323, 0.0056,
	0.0130, -0.0186, -0.0323, -0.0239, -0.0323, -0.0186, 0.0130,
	0, 0.013, 0.0056, -0.0018, 0.0056, 0.013, 0);


int main()
{
	cv::Mat gray_img, med_img, canny_img, border_img, border_edge;
	string image_path = "lena.jpg";
	gray_img = cv::imread(image_path, 0);                                                         //以灰度图形式读取
	cv::medianBlur(gray_img, med_img, 5);                                                         //中值滤波,不是必须,只为消除部分椒盐噪声
	cv::Canny(med_img, canny_img, 80, 120);                                                       //canny边缘检测,进行初定位,获取初始边缘
	//由于图像用到的模板为7*7的,所以先对图像进行边缘扩展
	copyMakeBorder(med_img, border_img, h_N, h_N, h_N, h_N, cv::BORDER_REPLICATE);
	copyMakeBorder(canny_img, border_edge, h_N, h_N, h_N, h_N, cv::BORDER_REPLICATE);
	float Z00 = 0, Z11_I = 0, Z11_R = 0, Z20 = 0, Z31_I = 0, Z31_R = 0, Z40 = 0;                 //不同模板的卷积值
	cv::Mat roi_img;
	float theta1, theta3,Zz11, Zz31, Zz20, Zz40, l, r, t;
	float x, y;
	vector<cv::Point2f> sub_pixel_edge;
	//对初始边缘进行亚像素边缘求取
	for (int i = h_N; i < border_img.rows - h_N; i++)
	{
		for (int j = h_N; j < border_img.cols - h_N; j++)
		{
			if (border_edge.at<uchar>(i, j) == 255)
			{	
				//卷积
				border_img(cv::Rect(j - h_N, i - h_N, g_N, g_N)).convertTo(roi_img, CV_32FC1);
				Z00 = roi_img.dot(M00);
				Z11_I = roi_img.dot(M11I);
				Z11_R = roi_img.dot(M11R);
				Z20 = roi_img.dot(M20);
				/*Z31_I = roi_img.dot(M31I);
				Z31_R = roi_img.dot(M31R);
				Z40 = roi_img.dot(M40);	*/
				theta1 = atan(Z11_I / Z11_R);                                                       //旋转角度计算          
				Zz11 = Z11_R * cos(theta1) + Z11_I * sin(theta1);                                   //各阶Zernike矩旋转后结果

				l = Z20 / Zz11;                                                                     //边缘距离圆心的距离
				r = 1.5 * Zz11 / sqrt(pow(1 - pow(l, 2), 3));                                       //阶跃幅度
				t = (Z00 - r * PI / 2 + r * asin(l) + r * l * sqrt(1 - pow(l, 2))) / PI;            //背景灰度值计算

				//计算亚像素位置
				x = j + g_N / 2.0 * l * cos(theta1) - h_N;                                          //根据公式计算即可,因扩充了边缘需要减去                
				y = i + g_N / 2.0 * l * sin(theta1) - h_N;
				cv::Point2f sub_pixel_point(x, y);
				sub_pixel_edge.push_back(sub_pixel_point);                                          //存储亚像素边缘位置
			}			
		}
	}

	cv::Mat draw_image;
	cv::cvtColor(canny_img, draw_image, cv::COLOR_GRAY2RGB);
	//绘制亚像素边缘图
	for (int i = 0; i < sub_pixel_edge.size(); i++)
	{
		circle(draw_image, sub_pixel_edge[i], 0, Scalar(255, 0, 255), 1, 0, 0);
	}
	imshow("sub pixel edge", draw_image);
	cv::waitKey();
	return 0;
}

原图如下:
Zernik矩亚像素边缘检测_第1张图片Canny结果如下
Zernik矩亚像素边缘检测_第2张图片
亚像素结果如下:
Zernik矩亚像素边缘检测_第3张图片
图中白色为Canny的像素位置,红色为亚像素的位置。因为图像像素只能画出整像素位置,所以仅供显示,看一下效果。若要准确对比相对位置,可以把亚像素坐标存储起来,用matlab画出来进行对比。

你可能感兴趣的:(图像处理,算法,c++,亚像素)