C++ 实现canny算子

非极大值抑制
在这里插入图片描述

//

#include <iostream>
#include <opencv2/opencv.hpp>

cv::Mat getConvolutionKernel(cv::Size ksize, double sigam);
void GaussinaBlur(cv::Mat& input, cv::Mat& output, cv::Size ksize, double sigam);
void imagePading(cv::Mat& input, cv::Mat& output, int hight, int low, int left, int right);
void CalculatingGradient(cv::Mat& input, cv::Mat& Gradent, cv::Mat& theat, cv::Mat& dX, cv::Mat& dY);
void nmsFunction(cv::Mat grandent, cv::Mat dX, cv::Mat dY, cv::Mat& output);
void doubleThreshold(cv::Mat& input, cv::Mat& output, double lowTh, double highTh);

int main() {

	cv::Mat image = cv::imread("D:\\code\\c++Code\\TestCanny\\111.png", 0);

	image.convertTo(image, CV_64F);
	GaussinaBlur(image, image, cv::Size(3,3), 1.5);

	// 计算图像梯度
	cv::Mat Gradent;
	cv::Mat theat, dX, dY;
	CalculatingGradient(image, Gradent, theat, dX, dY);

	cv::Mat nmsImage;
	nmsFunction(Gradent, dX, dY, nmsImage);

	cv::Mat fImage;
	doubleThreshold(nmsImage, fImage, 50, 100);



    cv::normalize(nmsImage, nmsImage, 0, 255, cv::NORM_MINMAX);
	nmsImage.convertTo(nmsImage, CV_8U);
	//cv::convertScaleAbs(Gradent, Gradent);
	cv::imshow("gaussian", nmsImage);
	cv::waitKey(0);
}

// 高斯滤波
void GaussinaBlur(cv::Mat &input, cv::Mat &output, cv::Size ksize, double sigam) {
	// step1 : 生成卷积核
	cv::Mat convolutionKernel = getConvolutionKernel( ksize,  sigam);
	
	int width = ksize.width;
	int hight = ksize.height;

	int halfWidth = (width - 1) / 2;
	int halfHight = (hight - 1) / 2;
	

	int addHight = halfHight, addLow = halfHight, addLeft = halfWidth, addLight = halfWidth;
	
	cv::Mat pimage;
	imagePading(input, pimage, addHight, addLow, addLeft, addLight);

	// 卷积运算
	output = cv::Mat::zeros(input.size(), input.type());

	if (input.channels() == 1) {
		for (int i = halfHight; i < pimage.rows - halfHight; i++) {
			for (int j = halfWidth; j < pimage.cols - halfWidth; j++) {
				double sumA = 0;
				for (int z = i - halfHight; z <= i + halfHight / 2; z++) {
					for (int h = j - halfWidth; h <= j + halfWidth; h++) {
						sumA += pimage.at<double>(z, h) * convolutionKernel.at<double>(z - (i - halfHight), h - (j - halfWidth));
					}
				}
				pimage.at<double>(i, j) = sumA;
			}
		}
	}
	else if (input.channels() == 3)
	{
		for (int i = halfHight; i < pimage.rows - halfHight; i++) {
			for (int j = halfWidth; j < pimage.cols - halfWidth; j++) {
				cv::Vec3d sumA = 0;
				for (int z = i - halfHight; z <= i + halfHight / 2; z++) {
					for (int h = j - halfWidth; h <= j + halfWidth; h++) {
						sumA[0] += pimage.at<cv::Vec3d>(z, h)[0] * convolutionKernel.at<double>(z - (i - halfHight), h - (j - halfWidth));
						sumA[1] += pimage.at<cv::Vec3d>(z, h)[1] * convolutionKernel.at<double>(z - (i - halfHight), h - (j - halfWidth));
						sumA[2] += pimage.at<cv::Vec3d>(z, h)[2] * convolutionKernel.at<double>(z - (i - halfHight), h - (j - halfWidth));
					}
				}

				pimage.at<cv::Vec3d>(i, j) = sumA;
			}
		}
	}

	output = pimage(cv::Rect(halfHight, halfWidth,input.cols, input.rows));

}

void imagePading(cv::Mat& input, cv::Mat &output, int hight, int low, int left, int right) {

	// 填充0
	int rows = input.rows + hight + low;
	int cols = input.cols + left + right;

	if (input.channels() == 1) {
		output = cv::Mat::zeros(rows, cols, CV_64F);

		for (int i = 0; i < rows; i++) {
			for (int j = 0; j < cols; j++) {
				if (i >= hight && i < rows - low && j >= left && j < cols - right) {

					output.at<double>(i, j) = input.at<double>(i - hight, j - left);
				}
			}
		}
	}
	else if(input.channels() == 3)
	{
		output = cv::Mat::zeros(rows, cols, CV_64FC3);

		for (int c = 0; c < input.channels(); c++) {
			for (int i = 0; i < rows; i++) {
				for (int j = 0; j < cols; j++) {
					if (i >= hight && i < rows - low && j >= left && j < cols - right) {

						output.at<cv::Vec3d>(i, j)[0] = input.at<cv::Vec3d>(i - hight, j - left)[0];
						output.at<cv::Vec3d>(i, j)[1] = input.at<cv::Vec3d>(i - hight, j - left)[1];
						output.at<cv::Vec3d>(i, j)[2] = input.at<cv::Vec3d>(i - hight, j - left)[2];
					}
				}
			}
		}

	}


}

cv::Mat getConvolutionKernel(cv::Size ksize, double sigam) {   // size 

	// 生成卷积核
	cv::Mat convolutionKernel(ksize , CV_64FC1);
	int width = ksize.width;
	int hight = ksize.height;

	int centerW = (width - 1) / 2;
	int centerH = (hight - 1) / 2;

	double x, y;
	double sum = 0;

	for (int i = 0; i < hight; i++) {
		x = std::pow(i - centerH, 2);

		for (int j = 0; j < width; j++) {
			y = std::pow(j - centerW, 2);

			double temp = std::exp(-(x + y) / (2 * std::pow(sigam, 2)));
			convolutionKernel.at<double>(i, j) = temp;
			sum += temp;
		}
	}
	// 归一化
	convolutionKernel = convolutionKernel / sum;


	return convolutionKernel;
}


// 计算图像梯度
void CalculatingGradient(cv::Mat& input, cv::Mat& Gradent, cv::Mat& theat, cv::Mat& dX, cv::Mat& dY ){
	Gradent = cv::Mat::zeros(input.size(), input.type());
	theat = cv::Mat::zeros(input.size(), input.type());
	dX = cv::Mat::zeros(input.size(), input.type());
	dY = cv::Mat::zeros(input.size(), input.type());

	// 
	if (input.channels() == 1) {
		for (int i = 1; i < input.rows - 1; i++) {
			for (int j = 1; j < input.cols - 1; j++) {

				double dx2 = input.at<double>(i - 1, j + 1) + 2 * input.at<double>(i, j + 1) + input.at<double>(i + 1, j + 1) - input.at<double>(i - 1, j - 1) - 2 * input.at<double>(i, j - 1) - input.at<double>(i + 1, j - 1);
				double dy2 = input.at<double>(i + 1, j - 1) + 2 * input.at<double>(i + 1, j) + input.at<double>(i + 1, j + 1) - input.at<double>(i - 1, j - 1) - 2 * input.at<double>(i - 1, j) - input.at<double>(i - 1, j + 1);

				Gradent.at<double>(i, j) = std::sqrt(std::pow(dx2, 2) + std::pow(dy2, 2));

				theat.at<double>(i, j) = atan(dy2 / dx2);
				dX.at<double>(i, j) = dx2;
				dY.at<double>(i, j) = dy2;

			}
		}
	}
	else if (input.channels() == 3) {
		for (int i = 1; i < input.rows - 1; i++) {
			for (int j = 1; j < input.cols - 1; j++) {

				cv::Vec3d dx2 = input.at<cv::Vec3d>(i - 1, j + 1) + 2 * input.at<cv::Vec3d>(i, j + 1) + input.at<cv::Vec3d>(i + 1, j + 1) - input.at<cv::Vec3d>(i - 1, j - 1) - 2 * input.at<cv::Vec3d>(i, j - 1) - input.at<cv::Vec3d>(i + 1, j - 1);
				cv::Vec3d dy2 = input.at<cv::Vec3d>(i + 1, j - 1) + 2 * input.at<cv::Vec3d>(i + 1, j) + input.at<cv::Vec3d>(i + 1, j + 1) - input.at<cv::Vec3d>(i - 1, j - 1) - 2 * input.at<cv::Vec3d>(i - 1, j) - input.at<cv::Vec3d>(i - 1, j + 1);

				Gradent.at<cv::Vec3d>(i, j)[0] = std::sqrt(std::pow(dx2[0], 2) + std::pow(dy2[0], 2));
				Gradent.at<cv::Vec3d>(i, j)[1] = std::sqrt(std::pow(dx2[1], 2) + std::pow(dy2[1], 2));
				Gradent.at<cv::Vec3d>(i, j)[2] = std::sqrt(std::pow(dx2[2], 2) + std::pow(dy2[2], 2));

				theat.at<cv::Vec3d>(i, j)[0] = atan(dy2[0] / dx2[0]);
				theat.at<cv::Vec3d>(i, j)[1] = atan(dy2[1] / dx2[1]);
				theat.at<cv::Vec3d>(i, j)[2] = atan(dy2[2] / dx2[2]);

			}
		}
	}


}


// 非极大值抑制
void nmsFunction(cv::Mat grandent, cv::Mat dX, cv::Mat dY, cv::Mat &output) {

	double g1 = 0, g2 = 0 ,g3 = 0, g4 = 0 ;
	double wight = 0;
	output = cv::Mat::zeros(grandent.size(), grandent.type());

	for (int i = 1; i < grandent.rows - 1; i++) {
		for (int j = 1; j < grandent.cols - 1; j++) {
			
			if (std::abs(dY.at<double>(i, j)) > std::abs(dX.at<double>(i, j))) 
			{
				g2 = grandent.at<double>(i - 1, j);
				g3 = grandent.at<double>(i + 1, j);
				wight = std::abs(dX.at<double>(i, j) /dY.at<double>(i, j)) ;

				if (dY.at<double>(i, j) * dX.at<double>(i, j) > 0) {  // 同号
					
					g1 = grandent.at<double>(i - 1, j - 1);
					g4 = grandent.at<double>(i + 1, j + 1);
				}
				else
				{
					g1 = grandent.at<double>(i - 1, j + 1);
					g4 = grandent.at<double>(i + 1, j - 1);
				}
			}
			else if (std::abs(dY.at<double>(i, j)) < std::abs(dX.at<double>(i, j)))
			{
				g2 = grandent.at<double>(i, j - 1);
				g3 = grandent.at<double>(i, j + 1);
				wight = std::abs(dY.at<double>(i, j) /dX.at<double>(i, j));

				if (dY.at<double>(i, j) * dX.at<double>(i, j) > 0) {
					g1 = grandent.at<double>(i - 1, j - 1);
					g4 = grandent.at<double>(i + 1, j + 1);
				}
				else
				{
					g1 = grandent.at<double>(i + 1, j - 1);
					g4 = grandent.at<double>(i - 1, j + 1);
				}

			}
			double gA = wight * g1 + (1 - wight) * g2;
			double gB = wight * g4 + (1 - wight) * g3;

			if (grandent.at<double>(i, j) >= gA && grandent.at<double>(i, j) >= gB) {
				output.at<double>(i, j) = grandent.at<double>(i, j);
			}

		}

	}
}


// 双阈值
/*
	大于搞阈值置为255  在阈值中间先不动
*/

void doubleThreshold(cv::Mat& input, cv::Mat& output, double lowTh, double highTh) {

	output = cv::Mat::zeros(input.size(), CV_8U);

	// 强边缘
	for (int i = 0; i < input.rows; i++) {
		for (int j = 0; j < input.cols; j++) {
			if (input.at<double>(i, j) >= highTh) {
				output.at<uchar>(i, j) = 255;
			}
		}
	}

	// 弱边缘
	for (int i = 1; i < input.rows - 1; i++) {
		for (int j = 1; j < input.cols - 1; j++) {
			if (input.at<double>(i, j) < highTh && input.at<double>(i, j) >= lowTh) {
				
				if (output.at<uchar>(i - 1, j - 1) + output.at<uchar>(i - 1, j) + output.at<uchar>(i - 1, j + 1) +
					output.at<uchar>(i, j - 1) + output.at<uchar>(i, j + 1) +
					output.at<uchar>(i + 1, j - 1) + output.at<uchar>(i + 1, j) + output.at<uchar>(i + 1, j + 1)
					!= 0
					) {
					output.at<uchar>(i, j) = 255;
				}
				
			}
		}
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值