OpenCvSharp 学习笔记19 --Canny边缘检测
一 ,Canny算法介绍:Canny : 边缘检测是由 J.CannyJ.CannyJ.Canny 在1986年 对简单的 LaplaceLaplaceLaplace 滤波器改进而成的边缘检测算法。在CannyCannyCanny 算法中,先在 X 和 Y 方向求得一阶导数,然后将它们组合成4个方向 的导数。其中方向导数是局部最大值的点是组成边缘的候选项。CannyCannyCanny算法最明显.
一 ,Canny算法介绍:
Canny : 边缘检测是由 J . C a n n y J.Canny J.Canny 在1986年 对简单的 L a p l a c e Laplace Laplace 滤波器改进而成的边缘检测算法。在 C a n n y Canny Canny 算法中,先在 X 和 Y 方向求得一阶导数,然后将它们组合成4个方向 的导数。其中方向导数是局部最大值的点是组成边缘的候选项。 C a n n y Canny Canny算法最明显的创新,就是将单个的边缘候选像素加入轮廓。
Canny边缘检测满足三个主要标准:
- 低错误率:意味着只检测存在的边缘。
- 良好的定位:必须最小化检测到的边缘像素和真实边缘像素之间的距离。
- 最小响应:每个边缘只有一个检测器响应。
Canny算法步骤:
- 高斯模糊 — GaussianBlur
- 灰度转化 —CvtColor
- 计算梯度 — Sobel / Scharr
- 非最大信号抑制:边缘信号很强,但只能有一个信号(边缘只能是一条线,而不能是一个有宽度的面),对其他的非边缘信号进行压制(在梯度方向上,如果不是最大信号就剔除)。
- 高低阈值输出二值图像。
非最大信号抑制公式原理:
梯度计算 Sobel 卷积核:
X方向:
G
x
=
[
+
1
0
+
1
−
2
0
+
2
−
1
0
+
1
]
G_x=\left[ \begin{matrix} +1 & 0 & +1 \\ -2 & 0 & +2 \\ -1 & 0 & +1 \end{matrix} \right]
Gx=⎣⎡+1−2−1000+1+2+1⎦⎤
Y方向:
G
y
=
[
−
1
−
2
−
1
0
0
0
+
1
+
2
+
1
]
G_y=\left[ \begin{matrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ +1 & +2 & +1 \end{matrix} \right]
Gy=⎣⎡−10+1−20+2−10+1⎦⎤
找到梯度强度和方向:
像 素 值 : G = G x 2 + G y 2 像素值: G=\sqrt{G_x^2+G_y^2} 像素值:G=Gx2+Gy2
梯
度
方
向
:
θ
=
a
r
c
t
a
n
(
G
y
G
x
)
(
a
r
c
t
a
n
为
反
正
切
函
数
)
梯度方向:\theta=arctan( \dfrac{G_y}{G_x}) (\tt arctan为反正切函数)
梯度方向:θ=arctan(GxGy)(arctan为反正切函数)
θ
\theta
θ:在 0 ~ 180 之间,表示梯度方向 ,值表现了梯度在那个方向的变化率最大。
45 ,90 ,135 对应区域
- 黄色区域其值范围: 0 ~ 22.5 与 157.5 ~ 180
- 绿色区域取值范围:22.5 ~ 67.5
- 蓝色区域取值范围:67.5 ~ 112.5
- 棕色区域取值范围:112.5 ~ 157.5
有梯度得到变化趋势方向,在与变化趋势垂直的方向寻找相邻的左右两个像素,与中间值作比较。
计算出来的像素值 G G G 在其阈值范围中,寻找值对应的梯度 上下两个像素值,如果上下像素值小于当前像素值,则上下像素值将被舍弃(不是最大信号),当前的像素值将被保留。如果当前的像素值比上下像素值小,则舍弃当前像素值(不是最大信号),保留上下像素值。
高低阈值链接
- 假设 T1 是低阈值 T2是高阈值。凡是高于T2的都保留,凡是低于T1的都舍弃。如果在两个阈值之间 则仅当它连接到T2才接受,反之则舍弃。
- Canny建议 高阈值 T2 与 低阈值 T1 之比 为 : ( 2 :1 ) 或 ( 3 : 1 )
二 API:
Cv2.Canny():
参数 | 描述 |
---|---|
InputArray src | 8 bit 输入图像 |
OutputArray edges | 输出边缘图像,一般是二值图像,背景是黑色 |
double threshold1 | 低阈值 |
double threshold2 | 高阈值 在程序中设置为下限阈值的三倍或二倍(遵循Canny的建议) |
int apertureSize = 3 | Soble算子的 size 一般 3X3 大小 取3 |
bool L2gradient = false | 选择 true 用 L2 归一化 ,否则 L1归一化 默认 fase |
L1 与 L2 计算公式:
L
2
:
n
o
r
m
=
(
d
I
/
d
x
)
2
+
(
d
I
/
d
y
)
2
L2 : norm=\sqrt{(dI/dx)^2+(dI/dy)^2}
L2:norm=(dI/dx)2+(dI/dy)2
L
1
:
n
o
r
m
=
∣
d
I
/
d
x
∣
+
∣
d
I
/
d
y
∣
L1 : norm=|dI/dx|+|dI/dy|
L1:norm=∣dI/dx∣+∣dI/dy∣
三 代码:
#region Canny算子 边缘提取
static Mat src = new Mat();
static Mat dst = new Mat();
static Mat gray = new Mat();
static Mat output = new Mat();
static int minVal = 50;
static int maxVal = 255;
const string outputName = "Canny Result";
private static void Canny(string path)
{
src = new Mat(path, ImreadModes.AnyColor | ImreadModes.AnyDepth);
dst = new Mat(src.Size(), src.Type());
new Window("SRC", WindowMode.AutoSize, src);
src.CopyTo(dst);
Cv2.CvtColor(dst, gray, ColorConversionCodes.RGB2GRAY); //转为灰度图(必须转)
new Window(outputName, WindowMode.AutoSize);
Cv2.Blur(gray, gray, new Size(3, 3), new Point(-1, -1), BorderTypes.Default);//模糊处理(降低噪点)
CvTrackbarCallback2 CannyDome = new CvTrackbarCallback2(CallBarck_CannyDome);
CvTrackbar cvt = new CvTrackbar("Bar :", outputName, minVal, maxVal, CallBarck_CannyDome, gray);
//Cv2.Canny(gray, dst, 100, 255, 3,true);
//using (new Window("DST", WindowMode.AutoSize, dst))
//using (new Window("SRC", WindowMode.AutoSize, src))
//{
Cv2.WaitKey(0);
}
/// <summary>
/// 委托函数
/// </summary>
/// <param name="pos">变化的量</param>
/// <param name="userdata">传入的数据对象</param>
private static void CallBarck_CannyDome(int pos, object userdata)
{
Mat m = (Mat)userdata;
Cv2.Canny(m, output, pos, pos * 2, 3, true);
dst = new Mat(src.Size(), src.Type());
/*
* 拷贝数据
* 参数 Mat m :拷贝到
* Mat mask : 要拷贝的对象 (mask 的像素是非0的才拷贝,是0不拷贝)
*
*
*/
src.CopyTo(dst, output); //拷贝:
Cv2.ImShow(outputName, dst);
}
#endregion
拖动滑块,实现不同效果:
更多推荐
所有评论(0)