OpenCV图像处理学习十七,Canny边缘检测算法实现_opencv canny-程序员宅基地

技术标签: c++  图像处理  opencv  opencv 图像处理  

一.Canny 边缘检测算法的诞生

提取图片的边缘信息是底层数字图像处理的基本任务之一。边缘信息对进一步提取高层语义信息有很大的影响。Canny 边缘检测算法 是 John F. Canny 于 1986年开发出来的一个多级边缘检测算法,至今仍然是边缘检测的最优算法, 最优边缘检测的三个主要评价标准是:

低错误率: 标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。
高定位性: 标识出的边缘要与图像中的实际边缘尽可能接近。
最小响应: 图像中的边缘只能标识一次。

=================================================================

二.Canny 边缘检测算法

(1)使用高斯平滑滤波器卷积处理图像,降噪消除噪声,平滑图像

#API 函数接口
GaussianBlur(Mat src,Mat dst,Size(w,h),sigmax,sigmay)
//参数说明
src:输入源图像
dst:目标图像
Size(w,h) : 要使用的内核的大小。w 和H必须是奇数和正数,否则将使用σX和σÿ参数计算大小。
sigmax : x中的标准差。写0表示使用内核大小计算σx
sigmay : y的标准差。写0表示使用内核大小计算σy。

(2)计算图像的梯度大小和方向

计算梯度幅值和方向可选用soble算子、Prewitt算子、Roberts算子模板等等.这样就可以得图像在X和Y方向梯度大小,求出总的图像梯度G,并且有X和Y方向上的梯度分量就可以求出梯度方向θ的大小,从而的到图像的边缘方向(边缘方向总是垂直于梯度方向)。

Sorbel算子X和Y方向的图像梯度分别为:

---------------------------------------------------------------------------------------------------------------------------------

第一是通过Sorbel算子X和Y方向的图像梯度可以判断

在待处理的图像中,C是需要判断梯度方向的像素,梯度方向一般都划分为4类,其反方向由于关于中心对称,因此也属于同一方向。分别为水平(0°),45°,垂直(90°)和135°这四个方向,有图像梯度的计算公式   θ  =  arctan(Gy/Gx) 可以得出θ角度的值,再通过该θ值属于哪个区间范围把图像梯度量化到某一个方向上。

将各点的梯度方向近似/量化到0°、90°、45°、135°四个梯度方向上进行,每个像素点梯度方向按照距离这四个方向的相近程度,用这四个方向来代替。通过量化,意味着图像中各点的梯度方向只能沿着0°、90°、45°、135°四个方向中的某一个。

  •  图像水平边缘——梯度方向为垂直: \theta_{M}\in [0,22.5)\cup (-22.5,0]\cup (157.5,180]\cup (-180,157.5]
  •  图像135°边缘——梯度方向为45°:                            \theta_{M}\in [22.5,67.5)\cup [-157.5,-112.5) 
  •  图像垂直边缘——梯度方向为水平:                            \theta_{M}\in [67.5,112.5]\cup [-112.5,-67.5]
  •  图像45°边缘——梯度方向为135°:                              \theta_{M}\in (112.5,157.5]\cup [-67.5,-22.5] 

梯度方向划分如上图所示:

当计算出来的角度θ的值位于22.5°和-22.5°红色线夹角区域时,此时最接近于0°,所以图像梯度被量化为 0°,图像边缘的角度为垂直90°。边缘方向总是垂直于梯度方向,如下图所示,下图的梯度方向为45°,边缘方向为135°。

=================================================================

(3)图像非极大值抑制

非极大值抑制(Non-Maximum Suppression,NMS): 抑制不是极大值的元素,可以理解为局部最大搜索。保留局部最大值,抑制非局部最大值的所有值。

图像像素最大值在图像梯度方向上时

在每一点上,领域中心像素与沿着其对应的图像梯度方向的两个像素相比,若中心像素为最大值,则保留下来;否则中心像素置0,这样可以抑制非极大值,保留局部梯度最大的点,以得到细化的边缘。 非极大值抑制的目的在于细化边缘,将原有粗略边缘检测图中的宽边细化为真正的边缘,从而可以更好的凸显物体的轮廓。非极大抑制是一种瘦边经典算法。它抑制那些梯度不够大的像素点,只保留最大的梯度,从而达到瘦边的目的。

重点:Canny中的非极大值抑制是沿着图像梯度方向对幅值进行非极大值抑制,而非边缘方向。

图像梯度方向并非特定角度(0°,45°,90°,135°)时,采用线性插值的方法来求取

在上图中,C是中心像素点,A1、A2、A3、A4都代表在图像梯度上的像素点,红色的直线是它的梯度方向,并不在0°,45°,90°和135°的图像梯度上,如果判断C是否为局部极大值,需要比较梯度幅值C和直线与g1g2和g2g3的交点处的dtmp1和dtmp2处的梯度幅值的大小关系。但是dtmp1和dtmp2不是整像素,而是亚像素点,也就是坐标是浮点的,这时就要利用线性插值求取。

写个线性插值的公式:设1的幅值M(A1),A2的幅值M(A2),则dtmp1可以得到:
 M(dTmp1)=w*M(A2)+(1-w)*M(A1)  ;
其中w=distance(dTmp1,A2)/distance(A1,A2) ;distance(A1,A2) 表示两点之间的距离。实际上w是一个比例系数,这个比例系数可以通过梯度方向(幅角的正切和余切)得到。

=========================================================================

(4)滞后阈值处理

滞后阈值处理是为了清除由于噪声和颜色变化引起的一些边缘像素,这时必须用弱梯度值过滤边缘像素,并保留具有高梯度值的边缘像素,可以通过选择高低阈值来实现。

用双阈值算法检测和连接边缘像素,选取高阈值和低阈值系数TH和TL,比率为2:1或3:1。(一般取TH=0.3或0.2,TL=0.1);将小于低阈值的像素点抛弃,赋值为0;将大于高阈值的像素点立即标记(这些点为确定边缘点),赋1或255;将小于高阈值,大于低阈值的点使用8连通区域确定(即:只有与TH像素连接时才会被接受,成为边缘点,赋 1或255);通过滞后阈值(双阈值)能够减少伪边缘,同时对真正的边缘进行连接.如下为滞后阈值示意图,经过滞后阈值后,存在缝隙的边缘被连接起来。

=========================================================================

三.Canny边缘检测API函数接口

#Canny边缘检测API
cv::Canny( 
        InputArray src,    // 8-bit的输入图像 
        OutputArray edges, // 输出边缘图像, 一般都是二值图像,背景是黑色 
        double threshold1, // 低阈值,常取高阈值的1/2或者1/3 
        double threshold2, // 高阈值 
        int aptertureSize, // Soble算子的size,通常3x3,取值3 
        bool L2gradient    // 选择 true表示是L2来归一化,否则用L1归一化 
)

=========================================================================

代码实现

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

using namespace cv;
Mat src, gray_src, dst;
int t1_value = 10;
int max_value = 255;
const char* OUTPUT_TITLE = "Canny Result";
void Canny_Demo(int, void*);
int main(int argc, char** argv) {
	src = imread("D:/vcprojects/images/lena.png");
	if (!src.data) {
		printf("could not load image...\n");
		return -1;
	}

	char INPUT_TITLE[] = "input image";
	namedWindow(INPUT_TITLE, CV_WINDOW_AUTOSIZE);
	namedWindow(OUTPUT_TITLE, CV_WINDOW_AUTOSIZE);
	imshow(INPUT_TITLE, src);

	cvtColor(src, gray_src, CV_BGR2GRAY);
	createTrackbar("Threshold Value:", OUTPUT_TITLE, &t1_value, max_value, Canny_Demo);
	Canny_Demo(0, 0);

	waitKey(0);
	return 0;
}

void Canny_Demo(int, void*) {
	Mat edge_output;
	blur(gray_src, gray_src, Size(3, 3), Point(-1, -1), BORDER_DEFAULT);
	Canny(gray_src, edge_output, t1_value, t1_value * 2, 3, false);

	//dst.create(src.size(), src.type());
	//src.copyTo(dst, edge_output);
	// (edge_output, edge_output);
	imshow(OUTPUT_TITLE, ~edge_output);
}

---------------------------------------------------------------------------------------------------------------------------------

代码中设置高阈值和低阈值系数TH和TL比例为2:1,设置高阈值为255.低阈值为10

图像处理效果

 可以看到,最低阈值设置的越高,被排除过滤的像素就越多,不连续的像素就越少,图像就越简单。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_44651073/article/details/126419515

智能推荐

分布式事务SEATA报错 Could not initialize class io.seata.rm.datasource.sql.struct.TableMetaCache_could not initialize class io.seata.config.configu-程序员宅基地

文章浏览阅读3.9k次。Cause: java.sql.SQLException: java.lang.NoClassDefFoundError: Could not initialize class io.seata.rm.datasource.sql.struct.TableMetaCache; uncategorized SQLException; SQL state [null]; error code [0]..._could not initialize class io.seata.config.configurationfactory

C语言字符串知识点和算法总结_字符串遍历,遍历的内容是什么样的-程序员宅基地

文章浏览阅读1k次,点赞30次,收藏26次。C语言字符串知识点和算法总结字符串遍历字符串拷贝字符串比较字符串分割字符串翻转回文串_字符串遍历,遍历的内容是什么样的

c# 批量mqtt_MQTT 客户端应用及常见问题(C#)-程序员宅基地

文章浏览阅读1k次。最近因为工作需要,需要使用C# 语言编写一个通过MQTT协议 ,上传数据到云端的工具。因为之前没有用过MQTT,所以 使用的时候遇到很多问题.下面将会把我遇到的问题一一解释。1.添加全局静态变量 uPLibrary.Networking.M2Mqtt.MQTTConfig.IsSocketRun;class MQTTConfig{public static bool IsSocketRun = f..._c# mqtt客户端代码

基于CNN-LSTM-Attention组合模型的时间序列预测matlab代码-程序员宅基地

文章浏览阅读430次,点赞12次,收藏5次。直接替换Excel数据即可用,注释清晰,适合新手小白,图片excel文件标有特征预测列。[3]注意力机制:为模型提供了对关键信息的聚焦能力,从而提高预测的准确度。[2]长短期记忆网络 (LSTM):处理数据捕捉长期依赖关系。[1]卷积神经网络 (CNN):捕捉数据中的局部模式和特征。,可实现回归预测或超前预测,Matlab版本最好为新版本。平均绝对百分比误差MAPE为: 0.035666。均方误差MSE为: 2.4157。均方根误差RMSE为: 1.5543。剩余预测残差RPD为: 5.0164。

给定入栈顺序,判断出栈顺序是否合法_如何根据入栈顺序判断出栈顺序是否正确-程序员宅基地

文章浏览阅读2.7k次。给定一个入栈顺序,判断出栈顺序是否有可能发生,所遵循的方法是使用一个辅助栈记录入栈的元素,当刚开始时候辅助栈为空,入栈元素第一个压入辅助栈,接下来如果看出栈顺序,如果出栈顺序的第一个元素和辅助栈的栈顶元素不相等,则继续把 入栈元素的下一个压入辅助栈;如果出栈顺序的元素和辅助栈的栈顶元素相等则直接将辅助栈的栈顶元素弹出,同时出栈序列向后移动一位。以此类推,如果当入栈元素全部进入辅助栈了,则秩序比较出栈_如何根据入栈顺序判断出栈顺序是否正确

CUPS-centos6-docker实践_docker cups-程序员宅基地

文章浏览阅读1.3k次。针对开源网络打印机软件CUPS的容器化实践创建一个目录,并在目录内建立一个文件名为Dockerfile的文件和文件名为cupsd.conf的配置文件Dockerfile#使用原始镜像FROM centos:6#作者MAINTAINER shark1985#使用阿里云yum源RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http:..._docker cups

随便推点

Python编写中国象棋棋盘(可视化页面)_python drawchinesechess-程序员宅基地

文章浏览阅读2.9k次,点赞4次,收藏22次。1.自定义画布面板大小和顶点位置import turtle # 导入turtle库函数,重中之重# 定义画布大小和顶点位置turtle.setup(width=800, height=880, startx=600, starty=0)turtle.screensize(300, 300, "white") # 这是棋盘的宽和高,还有背景颜色设置2.自定义画笔的各种设置s = turtle.Pen() # 为了后面函数调用的各种烦杂设置_python drawchinesechess

C51 ——震动传感器的介绍及震动传感器控制灯_mh mq sensor series c51-程序员宅基地

文章浏览阅读367次。震动传感器控制灯_mh mq sensor series c51

微商成功神器,python程序员教你,一键分析微信好友近期所有信息_spy++ 分析 微信-程序员宅基地

文章浏览阅读646次。好友太多怎么保活相信做微商的微信好友都会很多,多到很多都成了“僵尸”好友,但是想要成为一个成功的微商,要有两点:1、好友多,基数决定高度2、“活的”好友多今天用python写一个一键分析微信好友用户画像的程序吧!了解好友基本情况,才能做到高效的营销方案微商成功神器,python程序员教你,一键分析微信好友近期所有信息这个程序有啥功能1、统计好友的性别;2、统计好..._spy++ 分析 微信

CMOS Image Sensor的接口硬件设计(DVP/MIPI CSI)_slvs-ec协议2.0-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏45次。常见的CMOS Image Sensor(CIS)接口有DVP和MIPI,除此之外,还有Sony定义的用于传输高帧率高分辨率图像的slvs-ec接口、sub-lvds接口等,DVP接口硬件设计DVP(Digital Video Port)是并口传输,数据位宽有8bit、10bit、12bit等,是非差分信号,最高速率要比串行传输的MIPI接口低,高像素的sensor使用DVP就会比较勉强。DVP接口的主要信号如下所示:PCLK:pixel clock ,像素时钟,每个时钟对应一个像素数据,一般为几十_slvs-ec协议2.0

java isempty用法_Java String 的 isEmpty() 与 null 与 "" 区别解析-程序员宅基地

文章浏览阅读177次。问:简单说说 String 的 isEmpty() 与 null 与 "" 的区别?答:public class Test {public static void main(String[] args) {String a = new String();String b = "";String c = null;testString(a);testString(b);testString(c);}..._java string isempty

java语言概述、java语言特性、java语言发展史、java语言作用-程序员宅基地

文章浏览阅读691次。Java介绍:Java语言概述:Java语言是由美国Sun(Stanford University Network)斯坦福网络公司的java语言之父–詹姆斯·高斯林,在1995年推出的高级的编程语言。所谓编程语言,是计算机的语言,人们可以使用编程语言对计算机下达命令,让计算机完成人们需要的功能。Java 可运行于多个平台,如 Windows, Mac OS 及其他多种 UNIX 版本的系统。Java分为三个体系:JavaSE(J2SE)(Java2 Platform Standard Editio_java语言

推荐文章

热门文章

相关标签