赞
踩
sobel算子是图像边缘检测的最重要的算子之一。
Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。
Sobel算子输入的图像是灰度图,所以正常图片进行sobel算子提取边缘前,先要把图片转成灰度图。
边缘是指在图像上像素灰度变化最显著的地 方,边缘检测算子则利用图像边缘灰度的突变来检 测边缘。Sobel算子包含两组3×3的滤波器,分别对水平及垂直方向上的边缘敏感。
水平方向和垂直方向的Sobel算子分别如下:
具体与图像的运算如下:
然后分别用这两个算子对输入图像进行卷积操作,得到x,y方向上各点的卷积结果,即垂直梯度Gx和水平梯度Gy。
然后作一个平方和运算:
或者:
如果某点梯度 G 大于某一阈值,则认为该点(x,y)为边缘点。
代码:
python:
- # coding=utf-8
- import cv2
-
- img = cv2.imread("D:/fangzi.jpg", 0)
- cv2.imshow('origin',img)
- #Sobel函数求完导数后会有负值,还有会大于255的值。而原图像是uint8,即8位无符号数,
- #所以Sobel建立的图像位数不够,会有截断。因此要使用16位有符号的数据类型,即cv2.CV_16S。
- x = cv2.Sobel(img, cv2.CV_16S, 1, 0)#垂直方向梯度
- y = cv2.Sobel(img, cv2.CV_16S, 0, 1)#水平方向梯度
-
- absX = cv2.convertScaleAbs(x) #转回原来的uint8格式,否则图像无法显示。
- absY = cv2.convertScaleAbs(y) #转回原来的uint8格式,否则图像无法显示。
- #两个方向的梯度加起来形成最终的梯度
- dst = cv2.addWeighted(absX, 1, absY, 1, 0)
-
- cv2.imshow("absX", absX)
- cv2.imshow("absY", absY)
- cv2.imshow("Result", dst)
- cv2.waitKey(0)
效果:
输入:
垂直方向:
水平方向:
最终结果:
这张图可以太简单了,我们来看看复杂的:
垂直方向:
水平方向:
最终结果:
- #include<opencv2/opencv.hpp>
- #include<iostream>
- using namespace std;
- using namespace cv;
- int main()
- {
- Mat m_img = imread("D:\\fangzi.jpg");
- Mat src(m_img.rows, m_img.cols, CV_8UC1, Scalar(0));
- cvtColor(m_img, src, CV_RGB2GRAY); //转成灰度图
-
- Mat dstImage(src.rows, src.cols, CV_8UC1, Scalar(0));
- for (int i = 1; i < src.rows - 1; i++)
- {
- for (int j = 1; j < src.cols - 1; j++)
- {
- dstImage.data[i*dstImage.step + j] = sqrt((src.data[(i - 1)*src.step + j + 1] //step 为图象像素行的实际宽度
- + 2 * src.data[i*src.step + j + 1]
- + src.data[(i + 1)*src.step + j + 1]
- - src.data[(i - 1)*src.step + j - 1] - 2 * src.data[i*src.step + j - 1]
- - src.data[(i + 1)*src.step + j - 1])*(src.data[(i - 1)*src.step + j + 1]
- + 2 * src.data[i*src.step + j + 1] + src.data[(i + 1)*src.step + j + 1]
- - src.data[(i - 1)*src.step + j - 1] - 2 * src.data[i*src.step + j - 1]
- - src.data[(i + 1)*src.step + j - 1]) + (src.data[(i - 1)*src.step + j - 1] + 2 * src.data[(i - 1)*src.step + j]
- + src.data[(i - 1)*src.step + j + 1] - src.data[(i + 1)*src.step + j - 1]
- - 2 * src.data[(i + 1)*src.step + j]
- - src.data[(i + 1)*src.step + j + 1])* (src.data[(i - 1)*src.step + j - 1] + 2 * src.data[(i - 1)*src.step + j]
- + src.data[(i - 1)*src.step + j + 1] - src.data[(i + 1)*src.step + j - 1]
- - 2 * src.data[(i + 1)*src.step + j]
- - src.data[(i + 1)*src.step + j + 1]));
-
- }
-
- }
- Mat grad_y(src.rows, src.cols, CV_8UC1, Scalar(0));
- {
- for (int i = 1; i < src.rows - 1; i++)
- {
- for (int j = 1; j < src.cols - 1; j++)
- {
- grad_y.data[i*grad_y.step + j] = abs((src.data[(i - 1)*src.step + j + 1]
- + 2 * src.data[i*src.step + j + 1]
- + src.data[(i + 1)*src.step + j + 1]
- - src.data[(i - 1)*src.step + j - 1] - 2 * src.data[i*src.step + j - 1]
- - src.data[(i + 1)*src.step + j - 1]));
- }
- }
- }
- Mat grad_x(src.rows, src.cols, CV_8UC1, Scalar(0));
- {
- for (int i = 1; i < src.rows - 1; i++)
- {
- for (int j = 1; j < src.cols - 1; j++)
- {
- grad_x.data[i*grad_x.step + j] = sqrt((src.data[(i - 1)*src.step + j - 1] + 2 * src.data[(i - 1)*src.step + j]
- + src.data[(i - 1)*src.step + j + 1] - src.data[(i + 1)*src.step + j - 1]
- - 2 * src.data[(i + 1)*src.step + j]
- - src.data[(i + 1)*src.step + j + 1])* (src.data[(i - 1)*src.step + j - 1] + 2 * src.data[(i - 1)*src.step + j]
- + src.data[(i - 1)*src.step + j + 1] - src.data[(i + 1)*src.step + j - 1]
- - 2 * src.data[(i + 1)*src.step + j]
- - src.data[(i + 1)*src.step + j + 1]));
- }
- }
- }
- imshow("origin", src);
- imshow("gradient", dstImage);
- imshow("Vertical gradient", grad_y);
- imshow("Horizontal gradient", grad_x);
-
- waitKey(0);
- return 0;
- }
输入:
水平梯度:
垂直梯度:
最终结果:
优点:计算简单,速度很快;
缺点:计算方向单一,对复杂纹理的情况显得乏力;
直接用阈值来判断边缘点欠合理解释,会造成较多的噪声点误判。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。