C++ Opencv 实现Canny算法

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

 

文章目录

  • 前言
  • 一、pandas是什么?
  • 二、使用步骤
    • 1.引入库
    • 2.读入数据
  • 总结

 


前言

复习Canny边缘检测算法原理,并借助C++, Opencv简单实现


 

一、Canny 实现步骤描述

本文Canny边缘检测算法实现步骤如下:

1. 应用高斯模糊滤除图像噪声,算法借助梯度检测边缘,但是梯度检测对噪声敏感,应用高斯滤波器可以滤除图像噪声,提高边缘检测准确率;

2. 应用Sobel算法计算整幅图像的梯度幅值和角度,这里借助opencv cartToPolar()方法实现梯度幅值和角度的计算;

3. 对角度图像中每一角度值从0~360角度值归一化到[0, 45, 90, 135]这四个值;

4. 应用梯度幅值的非极大值抑制算法剔除不满足条件的边缘像素:遍历梯度幅值图像中每一像素点,根据该像素点角度值找到该像素点正、反方向邻近像素点梯度幅值并作比较,如果该像素点的梯度幅值大于邻近正、反方向邻近像素点,则在梯度幅值图像中保留,否则该像素点梯度幅值置为0;

5. 应用高、低阈值判断是否为边缘像素点:(1)如果梯度幅值图像中该点值大于高阈值,判定为边缘像素点;(2)如果梯度幅值图像中该点值小于低阈值,判定为非边缘像素点;(3)如果梯度幅值图像中该点值介于高、低阈值间,则要进行进一步判断:如果该点8邻域像素点有被判定为边缘像素点,则判定该点为边缘像素点, 否则判定为非边缘像素点。

二、代码实现

{
	
		// 1. Gaussian blurring
		Mat gauImg;
		GaussianBlur(*pSrcImg, gauImg, Size(5, 5), 0);

		if (debug)
		{
			imshow("gaussian", gauImg);
			cv::waitKey(0);
		}

		Mat grayImg;
		if (gauImg.channels() == 3)
			cvtColor(gauImg, grayImg, COLOR_BGR2GRAY);
		else
			grayImg = gauImg.clone();

		if (debug)
		{
			imshow("gray", grayImg);
			cv::waitKey(0);
		}

		// 2. calc gradient angle and magnitude
		Mat sobxImg, sobyImg, angleImg_orig, angleImg, magImg;
		Sobel(grayImg, sobxImg, CV_32FC1, 1, 0);
		Sobel(grayImg, sobyImg, CV_32FC1, 0, 1);

		if (debug)
		{
			imshow("sobel x", sobxImg);
			imshow("sobel y", sobyImg);
			cv::waitKey(0);
		}

		cartToPolar(sobxImg, sobyImg, magImg, angleImg_orig, true);
		
		// normal angleImg_orig(angle value: 0~360) to angleImg( angle value: [0, 45, 90, 135])
		angleImg = Mat(angleImg_orig.size(), CV_8UC1);
		for (int row = 0; row < angleImg_orig.rows; row++)
		{
			for (int col = 0; col < angleImg_orig.cols; col++)
			{
				if ((angleImg_orig.at(row, col) >= 0 && angleImg_orig.at(row, col) < 22.5) ||
					(angleImg_orig.at(row, col) >= 337.5 && angleImg_orig.at(row, col) < 360) ||
					angleImg_orig.at(row, col) >= 157.5 && angleImg_orig.at(row, col) < 202.5)
				{
					angleImg.at(row, col) = 0;
				}
				else if ((angleImg_orig.at(row, col) >= 22.5 && angleImg_orig.at(row, col) < 67.5) ||
					(angleImg_orig.at(row, col) >= 202.5 && angleImg_orig.at(row, col) < 247.5))
				{
					angleImg.at(row, col) = 45;
				}
				else if ((angleImg_orig.at(row, col) >= 67.5 && angleImg_orig.at(row, col) < 112.5) ||
					(angleImg_orig.at(row, col) >= 247.5 && angleImg_orig.at(row, col) < 292.5))
				{
					angleImg.at(row, col) = 90;
				}
				else
				{
					angleImg.at(row, col) = 135;
				}

			}
		}

		// 3. non-maximum magnitude edge point suppression
		Mat nmsImg = Mat::zeros(magImg.size(), CV_8UC1);
		Mat nmsMagImg = Mat::zeros(magImg.size(), CV_32FC1);
		for (int row = 1; row < nmsImg.rows - 1; row++)
		{
			for (int col = 1; col < nmsImg.cols - 1; col++)
			{

				if (angleImg.at(row, col) == 0 &&
					(magImg.at(row, col) > magImg.at(row, col - 1) &&
						magImg.at(row, col) > magImg.at(row, col + 1)))
				{
					nmsMagImg.at(row, col) = magImg.at(row, col);
					nmsImg.at(row, col) = 255;
					continue;
				}

				if (angleImg.at(row, col) == 45 &&
					(magImg.at(row, col) > magImg.at(row - 1, col + 1) &&
						magImg.at(row, col) > magImg.at(row + 1, col - 1)))
				{
					nmsMagImg.at(row, col) = magImg.at(row, col);
					nmsImg.at(row, col) = 255;
					continue;
				}

				if (angleImg.at(row, col) == 90 &&
					(magImg.at(row, col) > magImg.at(row - 1, col) &&
						magImg.at(row, col) > magImg.at(row + 1, col)))
				{
					nmsMagImg.at(row, col) = magImg.at(row, col);
					nmsImg.at(row, col) = 255;
					continue;
				}

				if (angleImg.at(row, col) == 135 &&
					(magImg.at(row, col) > magImg.at(row - 1, col - 1) &&
						magImg.at(row, col) > magImg.at(row + 1, col + 1)))
				{
					nmsMagImg.at(row, col) = magImg.at(row, col);
					nmsImg.at(row, col) = 255;
					continue;
				}

			}
		}

		if (debug)
		{
			imshow("nms", nmsImg);
			imshow("nms mag", nmsMagImg);
			cv::waitKey(0);
		}

		// 4. filter out edge points by low theshold and high threshold
		
		// 4.1 judge strong edge point: nmxMagImg pixel value > hightThres
		// judge weakge edge point: nmxMagImg pixel value < lowThres
		// note: if use magImg to judge, result image will like threshold() output image - rough edge
		Mat result = nmsImg.clone();
		for (int row = 1; row < result.rows - 1; row++)
		{
			for (int col = 1; col < result.cols - 1; col++)
			{
				if (nmsMagImg.at(row, col) >= highThres)// note: must be nmsMagImg, not be magImg
				{
					result.at(row, col) = 255;
				}
				if (nmsMagImg.at(row, col) < lowThres)
				{
					result.at(row, col) = 0;
				}
			}
		}

		if (debug)
		{
			imshow("strong edge", result);
			cv::waitKey(0);
		}

		// .4.2 judge medium edge points : lowThres< nmsMagImg pixel value (row, col) >= lowThres && nmsMagImg.at(row, col) < highThres)
				{
					if (result.at(row - 1, col - 1) == 255 || result.at(row - 1, col) == 255 ||
						result.at(row - 1, col + 1) == 255 || result.at(row, col + 1) == 255 ||
						result.at(row + 1, col + 1) == 255 || result.at(row + 1, col) == 255 ||
						result.at(row + 1, col - 1) == 255 || result.at(row, col - 1) == 255)
					{
						result.at(row, col) = 255;
					}
					else
					{
						result.at(row, col) = 0;
					}
				}
			}
		}

		if (debug)
		{
			imshow("my canny", result);
			cv::waitKey(0);
		}

	}

三、测试结果

1. 原图:

 

2. 本文实现Canny输出:lowThres=50, hightThres=100

C++ Opencv 实现Canny算法_第1张图片


总结

本文只是简单实现了Canny算法,和opencv Canny函数的输出还是有差距的,特别是当高、低阈值比较大时,Opencv Canny函数输出图像保留更多图像边缘信息(个人认为是梯度幅值介于高、低阈值时只是简单依据该像素点8邻域范围内是否有强边缘像素点判定是否为强边缘像素点,导致很多边缘像素点丢失,造成边缘不连续,读者可以在这一方向改进)。

你可能感兴趣的:(opencv,图像处理,边缘检测)