shape_based_matching代码解读0401

文章目录

  • 算法旋转角度
  • 一、代码解读
    • 1. 图像预处理(高斯滤波、Sobel采样)
    • 2.量化像素梯度方向
  • 后续


写作本系列文章旨在就个人学习该论文及其开源项目做一个学习分享和交流。

原论文篇名:Gradient Response Maps for Real-TimeDetection of Textureless Objects
原论文地址:https://www.researchgate.net/publication/312945559_Gradient_Response_Maps_for_Real-Time_Detection_of_Textureless_Objects
原项目地址:https://github.com/meiqua/shape_based_matching
本人于VS2017+OpenCV3.4.16编译的工程项目:https://download.csdn.net/download/weixin_41864918/85189757

算法旋转角度

一、代码解读

本篇代码解读和注释都是个人理解,如同原文献有所出入,请以原文为准。

1. 图像预处理(高斯滤波、Sobel采样)

代码如下(示例):

static void quantizedOrientations(const Mat &src, Mat &magnitude,Mat &angle, Mat& angle_ori, float threshold)
{
        Mat smoothed;
		static const int KERNEL_SIZE = 7;
		GaussianBlur(src, smoothed, Size(KERNEL_SIZE, KERNEL_SIZE), 0, 0, BORDER_REPLICATE);           //高斯滤波

		//单通道和三通道图像进行分开处理
		if (src.channels() == 1) 
		{
			Mat sobel_dx, sobel_dy, sobel_ag;
			Sobel(smoothed, sobel_dx, CV_32F, 1, 0, 3, 1.0, 0.0, BORDER_REPLICATE);
			Sobel(smoothed, sobel_dy, CV_32F, 0, 1, 3, 1.0, 0.0, BORDER_REPLICATE);
			magnitude = sobel_dx.mul(sobel_dx) + sobel_dy.mul(sobel_dy);
			phase(sobel_dx, sobel_dy, sobel_ag, true);
			hysteresisGradient(magnitude, angle, sobel_ag, threshold * threshold);
			angle_ori = sobel_ag;

		}
		else 
		{
			magnitude.create(src.size(), CV_32F);
			Size size = src.size();
			Mat sobel_3dx;                                           // 每通道水平导数
			Mat sobel_3dy;                                           // 每通道垂直倒数
			Mat sobel_dx(size, CV_32F);                              // 最大水平导数
			Mat sobel_dy(size, CV_32F);                              // 最大垂直倒数
			Mat sobel_ag;                                            // 最终梯度方向(未量化)

			Sobel(smoothed, sobel_3dx, CV_16S, 1, 0, 3, 1.0, 0.0, BORDER_REPLICATE);
			Sobel(smoothed, sobel_3dy, CV_16S, 0, 1, 3, 1.0, 0.0, BORDER_REPLICATE);

			short *ptrx = (short *)sobel_3dx.data;
			short *ptry = (short *)sobel_3dy.data;
			float *ptr0x = (float *)sobel_dx.data;
			float *ptr0y = (float *)sobel_dy.data;
			float *ptrmg = (float *)magnitude.data;

			const int length1 = static_cast<const int>(sobel_3dx.step1());
			const int length2 = static_cast<const int>(sobel_3dy.step1());
			const int length3 = static_cast<const int>(sobel_dx.step1());
			const int length4 = static_cast<const int>(sobel_dy.step1());
			const int length5 = static_cast<const int>(magnitude.step1());
			const int length0 = sobel_3dy.cols * 3;

			for (int r = 0; r < sobel_3dy.rows; ++r)
			{
				int ind = 0;
				for (int i = 0; i < length0; i += 3)
				{
					//计算每个通道的幅值;幅值大小为pow(ptrx,2)+pow(ptry,2)
					int mag1 = ptrx[i + 0] * ptrx[i + 0] + ptry[i + 0] * ptry[i + 0];
					int mag2 = ptrx[i + 1] * ptrx[i + 1] + ptry[i + 1] * ptry[i + 1];
					int mag3 = ptrx[i + 2] * ptrx[i + 2] + ptry[i + 2] * ptry[i + 2];

					// 找到像素三通道中幅值最大为像素最终梯度方向并记录参数到sobel_dx、sobel_dy和Ptrmg中
					if (mag1 >= mag2 && mag1 >= mag3) 
					{
						ptr0x[ind] = ptrx[i];
						ptr0y[ind] = ptry[i];
						ptrmg[ind] = (float)mag1;        
					}
					else if (mag2 >= mag1 && mag2 >= mag3)
					{
						ptr0x[ind] = ptrx[i + 1];
						ptr0y[ind] = ptry[i + 1];
						ptrmg[ind] = (float)mag2;
					}
					else
					{
						ptr0x[ind] = ptrx[i + 2];
						ptr0y[ind] = ptry[i + 2];
						ptrmg[ind] = (float)mag3;
					}
					++ind;
				}
				
				ptrx += length1;
				ptry += length2;
				ptr0x += length3;
				ptr0y += length4;
				ptrmg += length5;
			}                                                                                      

			phase(sobel_dx, sobel_dy, sobel_ag, true);                     //计算图像sobel_ag的未量化角度
			hysteresisGradient(magnitude, angle, sobel_ag, threshold * threshold);
			angle_ori = sobel_ag;
		}
		}

该部分代码中,src图像经过高斯滤波后进行sobel图像采样,当图片是单通道的可以直接获取sobel_dx、sobel_dy图像,但是如果是三通道图像在sobel采样后获取的是sobel_3dx、sobel_3dy图像,为获取sobel_dx、sobel_dy图像,程序通过for循环的嵌套对像素的每一个通道进行幅值计算,将幅值最大的通道的参数记录到sobel_dx、sobel_dy和Ptrmg中。
phase计算sobel_dx、sobel_dy图像的sobel_ag值,即未量化的图像角度。

2.量化像素梯度方向

代码如下(示例):

void hysteresisGradient(Mat &magnitude, Mat &quantized_angle,Mat &angle, float threshold)
	{
		//将360度划分为16个区间,每个区间22.5度
		// [0, 11.25), [348.75, 360)作为区间0,逆时针进行排序
		Mat_<unsigned char> quantized_unfiltered;
		angle.convertTo(quantized_unfiltered, CV_8U, 16.0 / 360.0);                                    //将角度图像量化到0-16之间保存到q_u,缩放因子:16.0 / 360.0
		memset(quantized_unfiltered.ptr(), 0, quantized_unfiltered.cols);                              //图像首行设置为零 
		memset(quantized_unfiltered.ptr(quantized_unfiltered.rows - 1), 0, quantized_unfiltered.cols); //图像末行设置为零
		
		//图像首列末列设置为零
		for (int r = 0; r < quantized_unfiltered.rows; ++r)
		{
			quantized_unfiltered(r, 0) = 0;
			quantized_unfiltered(r, quantized_unfiltered.cols - 1) = 0;
		}

		//将图像除边缘一个像素外所有量化成7个方向
		for (int r = 1; r < angle.rows - 1; ++r)
		{
			uchar *quant_r = quantized_unfiltered.ptr<uchar>(r);
			for (int c = 1; c < angle.cols - 1; ++c)
			{
				quant_r[c] &= 7;
			}
		}

		//过滤原始量化图像。仅接受幅值高于某个值的像素
		//阈值,并且在量化上存在局部一致性。
		quantized_angle = Mat::zeros(angle.size(), CV_8U);
		for (int r = 0; r < angle.rows - 1; ++r)
		{
			float *mag_r = magnitude.ptr<float>(r);

			for (int c = 0; c < angle.cols - 1; ++c)
			{
				if (mag_r[c] > threshold)
				{
					//计算像素周围3*3领域内量化的直方图
					int histogram[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

					uchar *patch3x3_row = &quantized_unfiltered(r - 1, c - 1);
					histogram[patch3x3_row[0]]++;
					histogram[patch3x3_row[1]]++;
					histogram[patch3x3_row[2]]++;

					patch3x3_row += quantized_unfiltered.step1();
					histogram[patch3x3_row[0]]++;
					histogram[patch3x3_row[1]]++;
					histogram[patch3x3_row[2]]++;

					patch3x3_row += quantized_unfiltered.step1();
					histogram[patch3x3_row[0]]++;
					histogram[patch3x3_row[1]]++;
					histogram[patch3x3_row[2]]++;

					//记录投票值最大的量化方向
					int max_votes = 0;
					int index = -1;
					for (int i = 0; i < 8; ++i)
					{
						if (max_votes < histogram[i])
						{
							index = i;
							max_votes = histogram[i];
						}
					}

					// 对大于阈值的量化方向保存到quantized_angle对应像素中去
					static const int NEIGHBOR_THRESHOLD = 5;
					if (max_votes >= NEIGHBOR_THRESHOLD)
						quantized_angle.at<uchar>(r, c) = uchar(1 << index);
				}
			}
		}
	}

对包含像素角度的图像进行量化
1、将360度量化成16个区间,所有像素值为:0~16
angle.convertTo(quantized_unfiltered, CV_8U, 16.0 / 360.0);
2、将16个梯度方向通过与运算量化成8个方向,所有像素值为:0~7
quant_r[c] &= 7;
3、将图像quantized_angle像素值量化成1、2、4、8、16、32、64


后续

见后续相关文章。
本文内容为个人观点欢迎同行相互交流进步。

你可能感兴趣的:(模板匹配,opencv,图像处理)