OpenCvSharp 学习笔记17 --Sobel算子
一,什么是Sobel算子Sobel算子是像素图像边缘检测中最重要的算子之一Sobel算子是一个离散微分算子。(discrete differentiation operator)用来计算图像灰度的近似值Sobel算子功能集合了高斯平滑和微分求导又被称为一阶微分算子,求导算子,在水平和垂直两个方向上求导,得到图像X方向和Y方向的梯度图像。官方图例:在图片中 红圈处,是头发和脸交汇处...
一,什么是Sobel算子
- Sobel算子是像素图像边缘检测中最重要的算子之一
- Sobel算子是一个离散微分算子。(discrete differentiation operator)用来计算图像灰度的近似值
- Sobel算子功能集合了高斯平滑和微分求导
- 又被称为一阶微分算子,求导算子,在水平和垂直两个方向上求导,得到图像X方向和Y方向的梯度图像。
官方图例:
在图片中 红圈处,是头发和脸交汇处(边缘),是不同的颜色,他们之间的像素差别最大。是像素值发生跃迁的地方,变化曲线可以用f(t)f(t)f(t)表示,f(t)f(t)f(t)函数图中红圈表示变化最大的点,而对f(t)f(t)f(t) 求导 得到 f‘(t)f`(t)f‘(t)。差值变化曲线,像一个钟摆行。最高处代表边缘点(差值最大处)。
如果f(t)f(t)f(t) 看作函数的话,f‘(t)f`(t)f‘(t)就是他的导函数,我们通过前一个像素和当前像素的差值,边缘处得到最大体现,而非边缘处的差值很小或几乎是0,得到最小体现,然后整个图像进行积分求导,提取最大体现的边缘。
卷积应用-- 边缘提取:
- 边缘是什么,是像素值发生跃迁的地方,是图像的显著特征之一,在图像特征提取,对象检测,模式识别等方面都有很重要的作用。
- 如何获取边缘 ,对图像求他的一阶导数。
delta=f(x)−f(x−1) delta=f(x) - f(x -1) delta=f(x)−f(x−1)
deltadeltadelta 越大,说明像素在 X 的方向变换越大,边缘信号越强。
二,Sobel算子:
Sobel算子是基于权重比,来扩大像素之间的差异,从而更好的寻找边缘。下面两个矩阵都是对称的。
水平梯度:
Gx=[−10+1−20+2−10+1]∗I G_x=\left[ \begin{matrix} -1 & 0 & +1 \\ -2& 0 & +2 \\ -1 & 0 & +1 \end{matrix} \right] *I Gx=⎣⎡−1−2−1000+1+2+1⎦⎤∗I
垂直梯度:
Gy=[−1−2−1000+1+2+1]∗I G_y=\left[ \begin{matrix} -1 & -2 & -1 \\ 0& 0 & 0 \\ +1 & +2 & +1 \end{matrix} \right] *I Gy=⎣⎡−10+1−20+2−10+1⎦⎤∗I
最终图像梯度:
- G=Gx2+Gy2 G= \sqrt{G_x^2+G_y^2} G=Gx2+Gy2
- 默认的计算方式: G=∣Gx∣+∣Gy∣ G= |G_x|+|G_y| G=∣Gx∣+∣Gy∣
OpenCv中边缘加强算子 (Cv2.Scharr函数):
在求取导数的近似值,kernel=3kernel=3kernel=3 时不是很准确,OpenCv 使用了改进版本 Cv2.Scharr函数,其算子如下:
Gx=[−30+3−100+10−30+3]∗I G_x=\left[ \begin{matrix} -3 & 0 & +3 \\ -10& 0 & +10\\ -3 & 0 & +3 \end{matrix} \right] *I Gx=⎣⎡−3−10−3000+3+10+3⎦⎤∗I
Gy=[−3−10−3000+3+10+3]∗I G_y=\left[ \begin{matrix} -3 & -10 & -3 \\ 0& 0 & 0 \\ +3 & +10 & +3 \end{matrix} \right] *I Gy=⎣⎡−30+3−100+10−30+3⎦⎤∗I
三,API
Cv2.Sobel():使用扩展的Sobel算子计算第一、第二、第三或混合图像导数
参数 | 说明 |
---|---|
InputArray src | 源图像 |
OutputArray dst | 输出图像 |
MatType ddepth | 输出图像的深度 |
int xorder | X方向 ,几阶导数 |
int yorder | Y方向 ,几阶导数 |
int ksize = 3 | Sobel算子 kernel大小,必须是奇数,1,3,5,7,… … |
double scale = 1 | 计算的导数值的可选缩放因子(默认情况下,不应用缩放),就是放大或缩小计算结果 |
double delta = 0 | 可选的增量值,在将结果存储到dst之前添加到结果中,相当于一个增益值 |
BorderTypes borderType = BorderTypes.Reflect101 | 边缘处理类型,默认即可 |
MatType ddepth参数值:计算后的值可能大于 255 所以要选择对应的数据类型,否则计算的结果可能有错误。
Input depth(src.depth()) | Output depth (ddepth) |
---|---|
CV_8U | -1/CV_16S/CV_32F/CV_64F |
CV_16U/CV_16S | -1/CV_32F/CV_64F |
CV_32F | -1/CV_32F/CV_64F |
CV_64F | -1/CV_64F |
改进版 Cv2.Scharr() 参数与上面一样。
边缘提取步骤:
- 高斯平滑滤波,Sobel算子对噪声比较敏感,为了不影响计算结果,要进行降噪处理。
- 转为灰度色彩空间
- 求X方向和Y 方向的梯度图像
- 根据公式得到最终图像(振幅图像,X梯度+Y梯度)
四,代码:
/// <summary>
/// Sobel算子图像边缘处理
/// </summary>
private static void Sobel(string path)
{
using (Mat src = new Mat(path, ImreadModes.AnyColor | ImreadModes.AnyDepth))
{
//1:高斯模糊平滑
Mat dst = new Mat();
Cv2.GaussianBlur(src, dst, new Size(3, 3), 0, 0, BorderTypes.Default);
//转为灰度
Mat gray = new Mat();
Cv2.CvtColor(dst, gray, ColorConversionCodes.BGR2GRAY);
MatType m = src.Type();
//求 X 和 Y 方向的梯度 Sobel and scharr
Mat xgrad = new Mat();
Mat ygrad = new Mat();
Cv2.Sobel(gray, xgrad, MatType.CV_16S, 1, 0, 3);
Cv2.Sobel(gray, ygrad, MatType.CV_16S, 0, 1, 3);
Cv2.ConvertScaleAbs(xgrad, xgrad);//缩放、计算绝对值并将结果转换为8位。不做转换的化显示不了,显示图相只能是8U类型
Cv2.ConvertScaleAbs(ygrad, ygrad);
//加强边缘检测
//Cv2.Scharr(gray, xgrad, -1, 1, 0, 3);
//Cv2.Scharr(gray, ygrad, -1, 0, 1, 3);
Mat output = new Mat(xgrad.Size(), xgrad.Type());
//图像混合相加(基于权重 0.5)不精确
//Cv2.AddWeighted(xgrad, 0.5, ygrad, 0.5, 0, output);
//基于 算法 G=|Gx|+|Gy|
int width = xgrad.Cols;
int hight = xgrad.Rows;
//for (int x = 0; x <hight ; x++)
//{
// for (int y = 0; y < width; y++)
// {
// int xg = xgrad.At<byte>(x, y);
// int yg = ygrad.At<byte>(x, y);
// //byte xy =(byte) (xg + yg);
// int v = xg + yg;
// if(v>255)
// {
// v = 255;
// }
// if(v<0)
// {
// v = 0;
// }
// byte xy = (byte)v;
// output.Set<byte>(x, y, xy);
// }
//}
//基于 G= (Gx*Gx +Gy*Gy)的开方根
for (int x = 0; x < hight; x++)
{
for (int y = 0; y < width; y++)
{
int xg = xgrad.At<byte>(x, y);
int yg = ygrad.At<byte>(x, y);
//byte xy =(byte) (xg + yg);
double v1 = Math.Pow(xg, 2);
double v2 = Math.Pow(yg, 2);
int val = (int)Math.Sqrt(v1 + v2);
if (val > 255) //确保像素值在 0 -- 255 之间
{
val = 255;
}
if (val < 0)
{
val = 0;
}
byte xy = (byte)val;
output.Set<byte>(x, y, xy);
}
}
using (new Window("X Image", WindowMode.Normal, xgrad))
using (new Window("Y Image", WindowMode.Normal, ygrad))
using (new Window("OUTPUT Image", WindowMode.Normal, output))
using (new Window("SRC", WindowMode.Normal, src))
{
Cv2.WaitKey(0);
}
}
}
X 向量 和 Y向量
源图与最终合成输出图像,边缘非常清晰。
更多推荐
所有评论(0)