??邊緣檢測(cè)目的是在保留原有圖像屬性的情況下,顯著減少圖像的數(shù)據(jù)規(guī)模。
??有多種算法可以進(jìn)行邊緣檢測(cè),雖然Canny算法年代久遠(yuǎn),但可以說它是邊緣檢測(cè)的一種標(biāo)準(zhǔn)算法。
一、Canny邊緣檢測(cè)
- Canny邊緣檢測(cè)的歷史比較久遠(yuǎn),最早是在1986年的時(shí)候提出的,通常稱為Canny邊緣檢測(cè)算法。
- Canny邊緣檢測(cè)算法是一種對(duì)噪聲比較敏感的邊緣檢測(cè)方法,
所以通常在使用Canny邊緣檢測(cè)之前,首先對(duì)圖像進(jìn)行降噪。 - 圖像噪聲抑制方法主要有均值濾波、高斯濾波、中值濾波,
一般情況下,會(huì)優(yōu)先考慮使用高斯濾波來完成噪聲抑制,
因?yàn)槎鄶?shù)噪聲都是自然界中的隨機(jī)噪聲,高斯模糊對(duì)它們有不同程度的抑制作用。 - Canny邊緣檢測(cè)一個(gè)最大的創(chuàng)新在于其使用兩個(gè)閾值嘗試把所有的邊緣像素連接起來,形成邊緣曲線或者線段。

程序運(yùn)行結(jié)果
二、檢測(cè)步驟
??完整的Canny邊緣檢測(cè)由如下步驟組成。
- 1)高斯模糊:完成噪聲抑制。
- 2)灰度轉(zhuǎn)換:在灰度圖像上計(jì)算梯度值。
- 3)計(jì)算梯度:使用Sobel/Scharr。
- 4)非最大信號(hào)抑制:在梯度圖像上尋找局部最大值。
- 5)高低閾值連接:把邊緣像素連接為線段,形成完整邊緣輪廓。
上面的第5步是使用高低閾值連接,Canny推薦的高低閾值比在2:1到3:1之間,
首先使用低閾值,把低于低閾值邊緣的像素點(diǎn)都去掉,
然后保留所有高于高閾值的像素點(diǎn),
對(duì)于處于高閾值與低閾值之間的像素點(diǎn),如果從高閾值像素點(diǎn)出發(fā),經(jīng)過的所有像素點(diǎn)都高于低閾值,則保留這些像素,否則丟棄。
OpenCV中Canny邊緣檢測(cè)函數(shù)已經(jīng)包含了上述5個(gè)步驟。
三、函數(shù)Cv2.Canny
Cv2.Canny(src_img, dst, tkBarCannyMin.Value, kBarCannyMax.Value, hole, rbBtnTrue.Checked);
參數(shù):
- 1,8 bit 輸入圖像;
- 2,輸出邊緣圖像,一般是二值圖像,背景是黑色;
- 3,低閾值。值越大,找到的邊緣越少;
- 4,高閾值;
- 5,表示應(yīng)用Sobel算子的孔徑大小,其有默認(rèn)值3;
- 6,計(jì)算圖像梯度幅值的標(biāo)識(shí),有默認(rèn)值false。
低于閾值1的像素點(diǎn)會(huì)被認(rèn)為不是邊緣;
高于閾值2的像素點(diǎn)會(huì)被認(rèn)為是邊緣;
在閾值1和閾值2之間的像素點(diǎn),若與一階偏導(dǎo)算子計(jì)算梯度得到的邊緣像素點(diǎn)相鄰,則被認(rèn)為是邊緣,否則被認(rèn)為不是邊緣。
四、程序
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using Sunny.UI;
namespace Ky_Cv2Canny
{
public partial class Form1 : UIForm
{
private Image image = null;
private Mat dst = new Mat();
private Mat src_img;
string filePath = "";
//private static int Num = 20;
private List<Mat> reList = new List<Mat>();
private int step = 1;
public Form1()
{
InitializeComponent();
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.AllPaintingInWmPaint, true);
}
private void openImage_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Title = "選擇操作的圖片";
openFileDialog.Filter = "圖片 *.jpg|*.jpg|圖像*.png|*.png";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
filePath = openFileDialog.FileName;
image = Image.FromFile(filePath);
src_img = Cv2.ImRead(filePath);
Mat tem1 = new Mat();
src_img.CopyTo(tem1);
if (reList.Count > 0)
{
reList[0] = tem1;
}
else
{
reList.Add(tem1);
}
}
if (filePath != "")
{
picBoxShowDel.Image = image;
picShowOri.Image = image;
}
}
/// <summary>
/// 邊緣檢測(cè)Canny
/// </summary>
private void Canny()
{
int hole = 0;
if (!int.TryParse(txtBoxCannyHole.Text, out hole))
{
return;
}
try
{
Cv2.Canny(src_img, dst, tkBarCannyMin.Value, tkBarCannyMax.Value, hole, rbBtnTrue.Checked);
picBoxShowDel.Image = dst.ToBitmap();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private void tkBarCannyMin_Scroll(object sender, EventArgs e)
{
if (tkBarCannyMin.Value < 0)
{
tkBarCannyMin.Value = 0;
}
uiLabel_Cs3.Text = tkBarCannyMin.Value.ToString();
}
private void tkBarCannyMax_Scroll(object sender, EventArgs e)
{
if (tkBarCannyMax.Value < 0)
{
tkBarCannyMax.Value = 0;
}
uiLabel_Cs4.Text = tkBarCannyMax.Value.ToString();
}
private void uiButton1_Click(object sender, EventArgs e)
{
Canny();
}
}
}
五、資料
xaiqpl的博客 https://blog.csdn.net/xaiqpl/article/details/118597812?spm=1001.2014.3001.5502