标签:
一、原理:
首先介绍背景知识:
1)边缘:灰度或结构等信息的突变处,边缘是一个区域的结束,也是另一个区域的开始,利用该特征可以分割图像。
2)边缘点:图像中具有坐标[x,y],且处在强度显著变化的位置上的点。
3)边缘段:对应于边缘点坐标[x,y]及其方位 ,边缘的方位可能是梯度角。
索贝尔算子(Sobeloperator)主要用作边缘检测,在技术上,它是一离散性差分算子,用来运算图像亮度函数的灰度之近似值。在图像的任何一点使用此算子,将会产生对应的灰度矢量或是其法矢量。
为了节省时间,我就直接截图了,原理都这样。
套用公式就如下,
Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。
如果求整体x和y方向的So被浏览梯度,只需要将Gx和Gy想家即可。
二、C++代码实现
我查过很多资料,都没有发现这个Sobel在C++上到底 是怎么实现的,以为就是没有代码,思索良久,在同学的提示下,终于写出来了,不过可能有不少瑕疵。
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main()
{
Mat m_img = imread("D://vvoo/lena.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]
+ 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("原图", src);
imshow("gradient", dstImage);
imshow("Vertical gradient", grad_y);
imshow("Horizontal gradient", grad_x);
waitKey(0);
return 0;
}截图:
为了作对比,再讲一下opencv的Sobel函数实现:
<pre name="code" class="cpp">C++: void Sobel ( InputArray src,//输入图 OutputArray dst,//输出图 int ddepth,//输出图像的深度 int dx, int dy, int ksize=3, double scale=1, double delta=0, int borderType=BORDER_DEFAULT );
有点啰嗦了
#include<opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat dstIamge, grad_x, grad_y, abs_grad_x, abs_grad_y;
Mat srcIamge = imread("D:\\vvoo\\lena.jpg");
imshow("原始图", srcIamge);
cvtColor(srcIamge, srcIamge, COLOR_RGB2GRAY);
//求 X方向梯度
Sobel(srcIamge, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);
convertScaleAbs(grad_x, abs_grad_x);//使用线性变换转换输入数组元素成8位无符号整型
imshow("X方向Sobel", abs_grad_x);
//求Y方向梯度
Sobel(srcIamge, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT);
convertScaleAbs(grad_y, abs_grad_y);
imshow("Y方向Sobel", abs_grad_y);
//合并梯度(近似)
addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0,dstIamge);
imshow("整体方向Sobel", dstIamge);
waitKey(0);
return 0;
}结果为:
参考资料:
标签:
原文地址:http://blog.csdn.net/qq_29540745/article/details/51918004