当前位置:   article > 正文

Canny算子-检测边界_canny算子的三个准则

canny算子的三个准则

OpenCV官方教程

原理

  • Canny Edge detector 在1986年提出,此算法主要满足以下三个准则
  1. Low error rate: 尽可能多地检测真实的边缘
  2. Good localization:检测出的边缘像素点和实际边缘像素点的距离尽可能的小
  3. Minimal response:图像中的任意边缘应该只被标记一次,同时图像噪声不应产生伪边缘

  • 算法步骤
  1. 使用高斯滤波器进行滤波,如:
  1. 计算图像强度的梯度和方向,和Sobel算子类似:

    a. 分别使用 x , y x,y x,y两个方向的卷积核进行处理:

    b. 计算梯度 G G G 和方向 θ \theta θ

     θ \theta θ 近似到四个方向,分别代表水平,垂直和两个对角线方向(0°,45°,90°,135°)


  1. 非极大值抑制

   非极大值抑制是一种边缘稀疏技术,通常得出来的梯度边缘不止一个像素宽,而是多个像素宽。非最大值抑制能帮助保留局部最大梯度而抑制所有其他梯度值。这意味着只保留了梯度变化中最大的位置,其他都为0。算法如下:

  a. 将当前的梯度与正负梯度方向的两个像素的梯度G1、G2进行比较
  b. 如果当前像素的梯度强度与另外两个像素相比最大,则该像素点保留为边缘点,否则该像素点将被抑制;比如当前点的梯度方向指向正上方90°方向,那它需要和垂直方向的正上方和正下方的像素的梯度进行比较。

伪代码:

if G>G1 && G>G2
	G may be edge
else
	G should be suppressed
  • 1
  • 2
  • 3
  • 4

  1. 双阈值检测
    在施加非极大值抑制之后,剩余的像素可以更准确地表示图像中的实际边缘。然而,仍然存在由于噪声和颜色变化引起的一些边缘像素。为了解决这些杂散响应,Canny算法应用双阀值,即一个高阀值和一个低阀值来区分边缘像素。如果边缘像素点梯度值大于高阀值,则被认为是强边缘点。如果边缘梯度值小于高阀值,大于低阀值,则标记为弱边缘点。小于低阀值的点则被抑制掉。
    官方建议:upper:lower ratio between 2:1 and 3:1.
    伪代码:
if G>HighThreshold
    G is a strong edge
else if G>= LowThreshold
	G is a weak edge
else 
	G should be suppressed
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

  1. 抑制孤立 Weak edge
    至此,我们已经将强边缘视为真实的边缘,但是对于弱边缘,可以从真实边缘提取也可以是因噪声或颜色变化引起的。所以对于噪声引起的弱边缘,我们应该抑制。通常,由真实边缘引起的弱边缘像素将连接到强边缘像素,而噪声响应未连接。为了跟踪边缘连接,通过查看弱边缘像素及其8个邻域像素,只要其中一个为强边缘像素,则该弱边缘点就可以保留为真实的边缘。

伪代码:

if G is weak edge && connected to strong edge
	G is a real edge
else
	G should be suppressed
  • 1
  • 2
  • 3
  • 4

官方例子

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

using namespace cv;
using namespace std;


Mat src,src_gray;
Mat dst,detected_edges;

int lowThreshold = 0;
const int kernel_size = 3;
const int max_lowThreshold = 100;
const char* window_name = "Canny detectes edges";

static void CannyThreshold(int, void*)
{
	blur(src_gray,detected_edges,Size(3,3));

	Canny(detected_edges,detected_edges,lowThreshold,lowThreshold*3,kernel_size); //高阈值是低阈值的3倍

	dst = Scalar::all(0);
	src.copyTo(dst,detected_edges);  //detected_edges是Canny算子检测的结果,作为mask,将src复制给dst
	imshow(window_name,dst);
}


int main(void)
{

	src = imread("../res/lena.jpeg",cv::IMREAD_COLOR);
	if(src.empty())
	{
		cout << "can't load image" << endl;
	}

	dst.create(src.size(),src.type());
	cv::cvtColor(src,src_gray,cv::COLOR_BGR2GRAY);

	namedWindow(window_name, cv::WINDOW_AUTOSIZE);
	createTrackbar("Min Threshold",window_name,&lowThreshold,max_lowThreshold,CannyThreshold);

	CannyThreshold(0,0);
	waitKey(0);

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

结果:

在这里插入图片描述


OpenCV API

void cv::Canny
(
InputArray image,     // 8-bit input image
OutputArray edges,    // 输出edge图,和输入一样,单通道 8bits 图
double threshold1,    // lowThreshold
double threshold2,    // highThreshold
int apertureSize = 3,    // 定义调用Sobel核大小
bool L2gradient = false     // 计算梯度方式, L 2 L_2 L2是精确的, L 1 L_1 L1是近似
)

在这里插入图片描述
在这里插入图片描述



输入的是:分别对图像x ,y 的导数的结果
void cv::Canny
(
InputArray dx,    // 16-bit x derivative of input image (CV_16SC1 or CV_16SC3).
InputArray dy,    // 16-bit y derivative of input image (same type as dx).
OutputArray edges,     // single channels 8-bit image, which has the same size as input .
double threshold1,     // lowThreshold
double threshold2,     // highThreshold
bool L2gradient = false     // 和上面一样
)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/206615
推荐阅读
相关标签
  

闽ICP备14008679号