图像处理(九)——Harris角点检测

实现Harris角点检测算法,并与OpenCV的cornerHarris函数的结果进行比较。

特征点在图像中一般有具体的坐标,并具有某些数学特征,如局部最大或最小灰度、以及某些梯度特征等。角点可以简单的认为是两条边的交点。如下图所示:

图像处理(九)——Harris角点检测_第1张图片
在各个方向上移动小窗口,如果在所有方向上移动,窗口内灰度都发生变化,则认为是角点;如果任何方向都不变化,则是均匀区域;如果灰度只在一个方向上变化,则可能是图像边缘。
而Harris角点检测算法就是基于图像的这种特点来进行计算的。该算法的主要步骤为:

  1. 计算图像I(x,y)I(x,y)在XX方向和YY方向的梯度
    图像处理(九)——Harris角点检测_第2张图片
  2. 计算图像两个方向梯度的乘积在这里插入图片描述
  3. 使用窗口高斯函数分别对I_x2、I_y2、IxIy进行高斯加权,生成矩阵M。
    图像处理(九)——Harris角点检测_第3张图片
    图像处理(九)——Harris角点检测_第4张图片
  4. 计算每个像素的Harris响应值R,并设定一阈值T,对小于阈值T的R置零。
    图像处理(九)——Harris角点检测_第5张图片
    图像处理(九)——Harris角点检测_第6张图片
  5. 在一个固定窗口大小的邻域内(5×55×5)进行非极大值抑制,局部极大值点即为图像中的角点。
    图像处理(九)——Harris角点检测_第7张图片

运行结果为:
图像处理(九)——Harris角点检测_第8张图片
改了一下响应函数的参数值:
图像处理(九)——Harris角点检测_第9张图片
但是发现这种方法做出来的效果并不是很好,虽然找出来了一些角点,但是还有很多角点没有找到,而且找到了很多错误的或者在当前阈值下不应该出现的角点。

之后找到了opencv中自带的角点检测函数,并用其对相同的图片进行了处理,结果如下:
图像处理(九)——Harris角点检测_第10张图片
图像处理(九)——Harris角点检测_第11张图片
图像处理(九)——Harris角点检测_第12张图片
图像处理(九)——Harris角点检测_第13张图片
图像处理(九)——Harris角点检测_第14张图片

后来又查了些资料,使用cornerEigenValsAndVecs()函数和minMaxLoc()函数来重新写了一下角点检测算法,测试结果还不错:
图像处理(九)——Harris角点检测_第15张图片
图像处理(九)——Harris角点检测_第16张图片
图像处理(九)——Harris角点检测_第17张图片
图像处理(九)——Harris角点检测_第18张图片

角点检测(Corner Detection)也称为特征点检测,是图像处理和计算机视觉中用来获取图像局部特征点的一类方法,广泛应用于运动检测、图像匹配、视频跟踪、三维建模以及目标识别等领域中。
在实验过程中,也发现了Harris角点的一些性质:
参数α对角点检测的影响:增大α的值,将减小角点响应值R,减少被检测角点的数量;减小α的值,将增大角点响应值R,增加被检测角点的数量。
此外,该方法还可以扩展到多尺度Harris角点检测,但是在此次实验中没有实现,可以考虑在之后加以实现。


同时也欢迎各位关注我的微信的公众号 南木的下午茶

在这里插入图片描述


代码:

// CVE8.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
using namespace cv;

/*
RGB转换成灰度图像的一个常用公式是:
Gray = R*0.299 + G*0.587 + B*0.114
*/
//******************灰度转换函数*************************  
//第一个参数image输入的彩色RGB图像的引用;  
//第二个参数imageGray是转换后输出的灰度图像的引用;  
//*******************************************************
void ConvertRGB2GRAY(const Mat &image, Mat &imageGray);

//******************Sobel卷积因子计算X、Y方向梯度和梯度方向角********************  
//第一个参数imageSourc原始灰度图像;  
//第二个参数imageSobelX是X方向梯度图像;  
//第三个参数imageSobelY是Y方向梯度图像;  
//第四个参数pointDrection是梯度方向角数组指针  
//*************************************************************  
void SobelGradDirction(Mat &imageSource, Mat &imageSobelX, Mat &imageSobelY);

//******************计算Sobel的X方向梯度幅值的平方*************************  
//第一个参数imageGradX是X方向梯度图像;    
//第二个参数SobelAmpXX是输出的X方向梯度图像的平方  
//*************************************************************  
void SobelXX(const Mat imageGradX, Mat_<float> &SobelAmpXX);

//******************计算Sobel的Y方向梯度幅值的平方*************************    
//第一个参数imageGradY是Y方向梯度图像;  
//第二个参数SobelAmpXX是输出的Y方向梯度图像的平方  
//*************************************************************  
void SobelYY(const Mat imageGradY, Mat_<float> &SobelAmpYY);

//******************计算Sobel的XY方向梯度幅值的乘积*************************    
//第一个参数imageGradX是X方向梯度图像;
//第二个参数imageGradY是Y方向梯度图像;
//第二个参数SobelAmpXY是输出的XY方向梯度图像 
//*************************************************************  
void SobelXY(const Mat imageGradX, const Mat imageGradY, Mat_<float> &SobelAmpXY);

//****************计算一维高斯的权值数组*****************
//第一个参数size是代表的卷积核的边长的大小
//第二个参数sigma表示的是sigma的大小
//*******************************************************
double *getOneGuassionArray(int size, double sigma);

//****************高斯滤波函数的实现*****************
//第一个参数srcImage是代表的输入的原图
//第二个参数dst表示的是输出的图
//第三个参数size表示的是卷积核的边长的大小
//*******************************************************
void MyGaussianBlur(Mat_<float> &srcImage, Mat_<float> &dst, int size);

//****计算局部特涨结果矩阵M的特征值和响应函数H = (A*B - C) - k*(A+B)^2******
//M
//A  C
//C  B
//Tr(M)=a+b=A+B
//Det(M)=a*b=A*B-C^2
//计算输出响应函数的值得矩阵
//****************************************************************************
void harrisResponse(Mat_<float> &GaussXX, Mat_<float> &GaussYY, Mat_<float> &GaussXY, Mat_<float> &resultData, float k);


//***********非极大值抑制和满足阈值及某邻域内的局部极大值为角点**************
//第一个参数是响应函数的矩阵
//第二个参数是输入的灰度图像
//第三个参数表示的是输出的角点检测到的结果图
void LocalMaxValue(Mat_<float> &resultData, Mat &srcGray, Mat &ResultImage, int kSize);

int main()
{
	const Mat srcImage = imread("E:/C++/CVE8/img.jpg");
	if (!srcImage.data)
	{
		printf("could not load image...\n");
		return -1;
	}
	imshow("srcImage", srcImage);
	Mat srcGray;
	ConvertRGB2GRAY(srcImage, srcGray);
	Mat imageSobelX;
	Mat imageSobelY;
	Mat resultImage;
	Mat_<float> imageSobelXX;
	Mat_<float> imageSobelYY;
	Mat_<float> imageSobelXY;
	Mat_<float> GaussianXX;
	Mat_<float> GaussianYY;
	Mat_<float> GaussianXY;
	Mat_<float> HarrisRespond;
	//计算Soble的XY梯度
	SobelGradDirction(srcGray, imageSobelX, imageSobelY);
	//计算X方向的梯度的平方
	SobelXX(imageSobelX, imageSobelXX);
	SobelYY(imageSobelY, imageSobelYY);
	SobelXY(imageSobelX, imageSobelY, imageSobelXY);
	//计算高斯模糊XX YY XY
	MyGaussianBlur(imageSobelXX, GaussianXX, 3);
	MyGaussianBlur(imageSobelYY, GaussianYY, 3);
	MyGaussianBlur(imageSobelXY, GaussianXY, 3);
	harrisResponse(GaussianXX, GaussianYY, GaussianXY, HarrisRespond, 0.02);
	LocalMaxValue(HarrisRespond, srcGray, resultImage, 3);
	imshow("imageSobelX", imageSobelX);
	imshow("imageSobelY", imageSobelY);
	imshow("resultImage", resultImage);
	waitKey(0);
	return 0;
}
void ConvertRGB2GRAY(const Mat &image, Mat &imageGray)
{
	if (!image.data || image.channels() != 3)
	{
		return;
	}
	//创建一张单通道的灰度图像
	imageGray = Mat::zeros(image.size(), CV_8UC1);
	//取出存储图像像素的数组的指针
	uchar *pointImage = image.data;
	uchar *pointImageGray = imageGray.data;
	//取出图像每行所占的字节数
	size_t stepImage = image.step;
	size_t stepImageGray = imageGray.step;
	for (int i = 0; i < imageGray.rows; i++)
	{
		for (int j = 0; j < imageGray.cols; j++)
		{
			pointImageGray[i*stepImageGray + j] = (uchar)(0.114*pointImage[i*stepImage + 3 * j] + 0.587*pointImage[i*stepImage + 3 * j + 1] + 0.299*pointImage[i*stepImage + 3 * j + 2]);
		}
	}
}


//存储梯度膜长
void SobelGradDirction(Mat &imageSource, Mat &imageSobelX, Mat &imageSobelY)
{
	imageSobelX = Mat::zeros(imageSource.size(), CV_32SC1);
	imageSobelY = Mat::zeros(imageSource.size(), CV_32SC1);
	//取出原图和X和Y梯度图的数组的首地址
	uchar *P = imageSource.data;
	uchar *PX = imageSobelX.data;
	uchar *PY = imageSobelY.data;

	//取出每行所占据的字节数
	int step = imageSource.step;
	int stepXY = imageSobelX.step;

	int index = 0;//梯度方向角的索引
	for (int i = 1; i < imageSource.rows - 1; ++i)
	{
		for (int j = 1; j < imageSource.cols - 1; ++j)
		{
			//通过指针遍历图像上每一个像素   
			double gradY = P[(i + 1)*step + j - 1] + P[(i + 1)*step + j] * 2 + P[(i + 1)*step + j + 1] - P[(i - 1)*step + j - 1] - P[(i - 1)*step + j] * 2 - P[(i - 1)*step + j + 1];
			PY[i*stepXY + j * (stepXY / step)] = abs(gradY);

			double gradX = P[(i - 1)*step + j + 1] + P[i*step + j + 1] * 2 + P[(i + 1)*step + j + 1] - P[(i - 1)*step + j - 1] - P[i*step + j - 1] * 2 - P[(i + 1)*step + j - 1];
			PX[i*stepXY + j * (stepXY / step)] = abs(gradX);
		}
	}
	//将梯度数组转换成8位无符号整型
	convertScaleAbs(imageSobelX, imageSobelX);
	convertScaleAbs(imageSobelY, imageSobelY);
}


void SobelXX(const Mat imageGradX, Mat_<float> &SobelAmpXX)
{
	SobelAmpXX = Mat_<float>(imageGradX.size(), CV_32FC1);
	for (int i = 0; i < SobelAmpXX.rows; i++)
	{
		for (int j = 0; j < SobelAmpXX.cols; j++)
		{
			SobelAmpXX.at<float>(i, j) = imageGradX.at<uchar>(i, j)*imageGradX.at<uchar>(i, j);
		}
	}
	//convertScaleAbs(SobelAmpXX, SobelAmpXX);
}

void SobelYY(const Mat imageGradY, Mat_<float> &SobelAmpYY)
{
	SobelAmpYY = Mat_<float>(imageGradY.size(), CV_32FC1);
	for (int i = 0; i < SobelAmpYY.rows; i++)
	{
		for (int j = 0; j < SobelAmpYY.cols; j++)
		{
			SobelAmpYY.at<float>(i, j) = imageGradY.at<uchar>(i, j)*imageGradY.at<uchar>(i, j);
		}
	}
	//convertScaleAbs(SobelAmpYY, SobelAmpYY);
}

void SobelXY(const Mat imageGradX, const Mat imageGradY, Mat_<float> &SobelAmpXY)
{
	SobelAmpXY = Mat_<float>(imageGradX.size(), CV_32FC1);
	for (int i = 0; i < SobelAmpXY.rows; i++)
	{
		for (int j = 0; j < SobelAmpXY.cols; j++)
		{
			SobelAmpXY.at<float>(i, j) = imageGradX.at<uchar>(i, j)*imageGradY.at<uchar>(i, j);
		}
	}
	//convertScaleAbs(SobelAmpXY, SobelAmpXY);
}



//计算一维高斯的权值数组
double *getOneGuassionArray(int size, double sigma)
{
	double sum = 0.0;
	//定义高斯核半径
	int kerR = size / 2;

	//建立一个size大小的动态一维数组
	double *arr = new double[size];
	for (int i = 0; i < size; i++)
	{

		// 高斯函数前的常数可以不用计算,会在归一化的过程中给消去
		arr[i] = exp(-((i - kerR)*(i - kerR)) / (2 * sigma*sigma));
		sum += arr[i];//将所有的值进行相加

	}
	//进行归一化	
	for (int i = 0; i < size; i++)
	{
		arr[i] /= sum;
		cout << arr[i] << endl;
	}
	return arr;
}

void MyGaussianBlur(Mat_<float> &srcImage, Mat_<float> &dst, int size)
{
	CV_Assert(srcImage.channels() == 1 || srcImage.channels() == 3); // 只处理单通道或者三通道图像
	int kerR = size / 2;
	dst = srcImage.clone();
	int channels = dst.channels();
	double* arr;
	arr = getOneGuassionArray(size, 1);//先求出高斯数组

									   //遍历图像 水平方向的卷积
	for (int i = kerR; i < dst.rows - kerR; i++)
	{
		for (int j = kerR; j < dst.cols - kerR; j++)
		{
			float GuassionSum[3] = { 0 };
			//滑窗搜索完成高斯核平滑
			for (int k = -kerR; k <= kerR; k++)
			{

				if (channels == 1)//如果只是单通道
				{
					GuassionSum[0] += arr[kerR + k] * dst.at<float>(i, j + k);//行不变,列变换,先做水平方向的卷积
				}
				else if (channels == 3)//如果是三通道的情况
				{
					Vec3f bgr = dst.at<Vec3f>(i, j + k);
					auto a = arr[kerR + k];
					GuassionSum[0] += a * bgr[0];
					GuassionSum[1] += a * bgr[1];
					GuassionSum[2] += a * bgr[2];
				}
			}
			for (int k = 0; k < channels; k++)
			{
				if (GuassionSum[k] < 0)
					GuassionSum[k] = 0;
				else if (GuassionSum[k] > 255)
					GuassionSum[k] = 255;
			}
			if (channels == 1)
				dst.at<float>(i, j) = static_cast<float>(GuassionSum[0]);
			else if (channels == 3)
			{
				Vec3f bgr = { static_cast<float>(GuassionSum[0]), static_cast<float>(GuassionSum[1]), static_cast<float>(GuassionSum[2]) };
				dst.at<Vec3f>(i, j) = bgr;
			}

		}
	}

	//竖直方向
	for (int i = kerR; i < dst.rows - kerR; i++)
	{
		for (int j = kerR; j < dst.cols - kerR; j++)
		{
			float GuassionSum[3] = { 0 };
			//滑窗搜索完成高斯核平滑
			for (int k = -kerR; k <= kerR; k++)
			{

				if (channels == 1)//如果只是单通道
				{
					GuassionSum[0] += arr[kerR + k] * dst.at<float>(i + k, j);//行变,列不换,再做竖直方向的卷积
				}
				else if (channels == 3)//如果是三通道的情况
				{
					Vec3f bgr = dst.at<Vec3f>(i + k, j);
					auto a = arr[kerR + k];
					GuassionSum[0] += a * bgr[0];
					GuassionSum[1] += a * bgr[1];
					GuassionSum[2] += a * bgr[2];
				}
			}
			for (int k = 0; k < channels; k++)
			{
				if (GuassionSum[k] < 0)
					GuassionSum[k] = 0;
				else if (GuassionSum[k] > 255)
					GuassionSum[k] = 255;
			}
			if (channels == 1)
				dst.at<float>(i, j) = static_cast<float>(GuassionSum[0]);
			else if (channels == 3)
			{
				Vec3f bgr = { static_cast<float>(GuassionSum[0]), static_cast<float>(GuassionSum[1]), static_cast<float>(GuassionSum[2]) };
				dst.at<Vec3f>(i, j) = bgr;
			}

		}
	}
	delete[] arr;
}

void harrisResponse(Mat_<float> &GaussXX, Mat_<float> &GaussYY, Mat_<float> &GaussXY, Mat_<float> &resultData, float k)
{
	//创建一张响应函数输出的矩阵
	resultData = Mat_<float>(GaussXX.size(), CV_32FC1);
	for (int i = 0; i < resultData.rows; i++)
	{
		for (int j = 0; j < resultData.cols; j++)
		{
			float a = GaussXX.at<float>(i, j);
			float b = GaussYY.at<float>(i, j);
			float c = GaussXY.at<float>(i, j);
			resultData.at<float>(i, j) = a * b - c * c - k * (a + b)*(a + b);
		}
	}
}


//非极大值抑制
void LocalMaxValue(Mat_<float> &resultData, Mat &srcGray, Mat &ResultImage, int kSize)
{
	int r = kSize / 2;
	ResultImage = srcGray.clone();
	for (int i = r; i < ResultImage.rows - r; i++)
	{
		for (int j = r; j < ResultImage.cols - r; j++)
		{
			if (resultData.at<float>(i, j) > resultData.at<float>(i - 1, j - 1) &&
				resultData.at<float>(i, j) > resultData.at<float>(i - 1, j) &&
				resultData.at<float>(i, j) > resultData.at<float>(i - 1, j - 1) &&
				resultData.at<float>(i, j) > resultData.at<float>(i - 1, j + 1) &&
				resultData.at<float>(i, j) > resultData.at<float>(i, j - 1) &&
				resultData.at<float>(i, j) > resultData.at<float>(i, j + 1) &&
				resultData.at<float>(i, j) > resultData.at<float>(i + 1, j - 1) &&
				resultData.at<float>(i, j) > resultData.at<float>(i + 1, j) &&
				resultData.at<float>(i, j) > resultData.at<float>(i + 1, j + 1))
			{
				if ((int)resultData.at<float>(i, j) > 18000)
				{
					circle(ResultImage, Point(i, j), 5, Scalar(0, 0, 255), 2, 4, 0);
				}
			}

		}
	}
}

#include 
#include 
#include 

using namespace cv;
using namespace std;

// 定义全局变量
const string harris_winName = "自定义角点检测";
Mat src_img, gray_img;                       // src_img表示原图, gray_img表示灰度图
Mat harris_dst_img, harris_response_img;     // harris_dst_img存储自相关矩阵M的特征值和特征向量,harris_response_img存储响应函数的结果

double min_respense_value;			  // 响应函数的结果矩阵中的最小值
double max_respense_value;			  // 响应函数的结果矩阵中的最大值

int qualityValue = 30;
int max_qualityValue = 100;              // 通过qualityValue/max_qualityValue的结果作为qualitylevel来计算阈值
RNG  random_number_generator;             // 定义一个随机数发生器
void self_defining_Harris_Demo(int, void*);      //TrackBar回调函数声明

// 主函数
int main()
{
	src_img = imread("E:/C++/CVE8/img.jpg");
	if (src_img.empty())
	{
		printf("could not load the image...\n");
		return -1;
	}
	namedWindow("原图", CV_WINDOW_AUTOSIZE);
	imshow("原图", src_img);
	cvtColor(src_img, gray_img, COLOR_BGR2GRAY);      //将彩色图转化为灰度图

	// 计算特征值
	int blockSize = 3;
	int ksize = 3;
	double k = 0.04;
	harris_dst_img = Mat::zeros(src_img.size(), CV_32FC(6));
	// 目标图像harris_dst_img存储自相关矩阵M的特征值和特征向量,
	// 并将它们以(λ1, λ2, x1, y1, x2, y2)的形式存储。其中λ1, λ2是M未经过排序的特征值;
	// x1, y1是对应于λ1的特征向量;x2, y2是对应于λ2的特征向量。
	// 因此目标矩阵为6通道,即 CV_32FC(6)的矩阵。

	harris_response_img = Mat::zeros(src_img.size(), CV_32FC1);
	// harris_response_img用来存储通过每个像素值所对应的自相关矩阵所计算得到的响应值

	cornerEigenValsAndVecs(gray_img, harris_dst_img, blockSize, ksize, 4);
	// 该函数用来计算每个像素值对应的自相关矩阵的特征值和特征向量

	// 计算响应函数值
	for (int row = 0; row < harris_dst_img.rows; ++row)
	{
		for (int col = 0; col < harris_dst_img.cols; ++col)
		{
			double eigenvalue1 = harris_dst_img.at(row, col)[0];     // 获取特征值1
			double eigenvalue2 = harris_dst_img.at(row, col)[1];		// 获取特征值2
			harris_response_img.at(row, col) = eigenvalue1 * eigenvalue2 - k * pow((eigenvalue1 + eigenvalue2), 2);
			// 通过响应公式R=λ1*λ2 - k*(λ1+λ2)*(λ1+λ2)来计算每个像素对应的响应值
		}
	}
	minMaxLoc(harris_response_img, &min_respense_value, &max_respense_value, 0, 0, Mat());   // 寻找响应矩阵中的最小值和最大值
	namedWindow(harris_winName, CV_WINDOW_AUTOSIZE);
	createTrackbar("Quality Value:", harris_winName, &qualityValue, max_qualityValue, self_defining_Harris_Demo);    //创建TrackBar
	self_defining_Harris_Demo(0, 0);

	waitKey(0);
	return 0;
}


//  回调函数实现
void self_defining_Harris_Demo(int, void*)
{
	if (qualityValue < 10)
	{
		qualityValue = 10;       // 控制qualitylevel的下限值
	}
	Mat result_img = src_img.clone();    // 输出图像
	float threshold_value = min_respense_value + (((double)qualityValue) / max_qualityValue)*(max_respense_value - min_respense_value);
	for (int row = 0; row < result_img.rows; row++)
	{
		for (int col = 0; col < result_img.cols; col++)
		{
			float resp_value = harris_response_img.at(row, col);
			if (resp_value > threshold_value)
			{
				circle(result_img, Point(col, row), 2, Scalar(random_number_generator.uniform(0, 255),
					random_number_generator.uniform(0, 255), random_number_generator.uniform(0, 255)), 2, 8, 0);
			}
		}
	}
	imshow(harris_winName, result_img);
}
// CVE8.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include 
#include 
#include 
#include 

using namespace cv;
using namespace std;

Mat image;
Mat imageGray;
int thresh = 175;
int MaxThresh = 255;

void Trackbar(int, void*);  //阈值控制

int main()
{
	image = imread("E:/C++/CVE8/img.jpg");
	cvtColor(image, imageGray, CV_RGB2GRAY);
	GaussianBlur(imageGray, imageGray, Size(5, 5), 1); // 滤波
	namedWindow("Corner Detected");
	createTrackbar("threshold:", "Corner Detected", &thresh, MaxThresh, Trackbar);
	imshow("Corner Detected", image);
	Trackbar(0, 0);
	waitKey();
	return 0;
}

void Trackbar(int, void*)
{
	Mat dst, dst8u, dstshow, imageSource;
	dst = Mat::zeros(image.size(), CV_32FC1);
	imageSource = image.clone();
	cornerHarris(imageGray, dst, 3, 3, 0.04, BORDER_DEFAULT);
	normalize(dst, dst8u, 0, 255, CV_MINMAX);  //归一化
	convertScaleAbs(dst8u, dstshow);
	imshow("dst", dstshow);  //dst显示
	for (int i = 0; i < image.rows; i++)
	{
		for (int j = 0; j < image.cols; j++)
		{
			if (dstshow.at(i, j) > thresh)  //阈值判断
			{
				circle(imageSource, Point(j, i), 2, Scalar(0, 0, 255), 2); //标注角点
			}
		}
	}
	imshow("Corner Detected", imageSource);
}
#include "pch.h"
#include 
#include 
#include 
#include 
#include 

using namespace cv;
using namespace std;

int thresh = 130;
int max_count = 255;
Mat img, img_gray;
const char* output_title = "Harris Corner Dectction Result";
void Harris_Demo(int, void *);

int main(int argv, char** argc) {
	img = imread("E:/C++/CVE8/图片1.png");
	if (img.empty()) {
		printf("colud not load image...");
		return -1;
	}
	namedWindow("input image", CV_WINDOW_AUTOSIZE);
	imshow("input image", img);
	//以上是图像处理的标准开头
	namedWindow(output_title, CV_WINDOW_AUTOSIZE);
	cvtColor(img, img_gray, CV_BGR2GRAY);

	createTrackbar("Threshold", output_title, &thresh, max_count, Harris_Demo);
	Harris_Demo(0, 0);

	waitKey(0);
	return 0;
}

void Harris_Demo(int, void *) {

	Mat dst, norm_dst, normScaleDst;
	dst = Mat::zeros(img_gray.size(), CV_32FC1);
	//harris角点核心函数
	int blockSize = 3;
	int ksize = 3;
	int k = 0.04;

	cornerHarris(img_gray, dst, blockSize, ksize, k, BORDER_DEFAULT);
	//上述输出的取值范围并不是0-255 需要按照最大最小值进行归一化
	normalize(dst, norm_dst, 0, 255, NORM_MINMAX, CV_32FC1, Mat());
	convertScaleAbs(norm_dst, normScaleDst);

	Mat resultImg = img.clone();
	//用彩色来显示
	for (int row = 0; row < resultImg.rows; row++) {
		//定义每一行的指针
		uchar* currentRow = normScaleDst.ptr(row);
		for (int col = 0; col < resultImg.cols; col++) {
			int value = (int)*currentRow;
			if (value > thresh) {
				circle(resultImg, Point(col, row), 2, Scalar(0, 0, 255), 2, 8, 0);
			}
			currentRow++;
		}
	}

	imshow(output_title, resultImg);
}

你可能感兴趣的:(图像处理,南木的下午茶)