参考网址:
https://blog.csdn.net/qq_20823641/article/details/52079628
Roberts算子
Roberts边缘算子是一个2x2的模板,采用的是对角方向相邻的两个像素之差。从图像处理的实际效果来看,边缘定位较准,对噪声敏感。适用于边缘明显且噪声较少的图像分割。Roberts边缘检测算子是一种利用局部差分算子寻找边缘的算子,Robert算子图像处理后结果边缘不是很平滑。经分析,由于Robert算子通常会在图像边缘附近的区域内产生较宽的响应,故采用上述算子检测的边缘图像常需做细化处理,边缘定位的精度不是很高。标准一阶差分不同,Robert采用对角线差分,边缘定位准,但是对噪声敏感。适用于边缘明显且噪声较少的图像分割。Roberts边缘检测算子是一种利用局部差分算子寻找边缘的算子,Robert算子图像处理后结果边缘不是很平滑。经分析,由于Robert算子通常会在图像边缘附近的区域内产生较宽的响应,故采用上述算子检测的边缘图像常需做细化处理,边缘定位的精度不是很高。
cv::Mat roberts(cv::Mat srcImage)
{
cv::Mat dstImage = srcImage.clone();
int nRows = dstImage.rows;
int nCols = dstImage.cols;
for (int i = 0; i < nRows-1; i++)
{
for (int j = 0; j < nCols-1; j++)
{
int t1 = (srcImage.at<uchar>(i, j) -
srcImage.at<uchar>(i+1, j+1)) *
(srcImage.at<uchar>(i, j) -
srcImage.at<uchar>(i+1, j+1));
int t2 = (srcImage.at<uchar>(i+1, j) -
srcImage.at<uchar>(i, j+1)) *
(srcImage.at<uchar>(i+1, j) -
srcImage.at<uchar>(i, j+1));
dstImage.at<uchar>(i, j) = (uchar)sqrt(t1 + t2);
}
}
return dstImage;
}
prewitt算子
Prewitt算子是一种一阶微分算子的边缘检测,利用像素点上下、左右邻点的灰度差,在边缘处达到极值检测边缘,去掉部分伪边缘,对噪声具有平滑作用 。其原理是在图像空间利用两个方向模板与图像进行邻域卷积来完成的,这两个方向模板一个检测水平边缘,一个检测垂直边缘。
对数字图像f(x,y),Prewitt算子的定义如下:
G(i)=|[f(i-1,j-1)+f(i-1,j)+f(i-1,j+1)]-[f(i+1,j-1)+f(i+1,j)+f(i+1,j+1)]|
G(j)=|[f(i-1,j+1)+f(i,j+1)+f(i+1,j+1)]-[f(i-1,j-1)+f(i,j-1)+f(i+1,j-1)]|
则 P(i,j)=max[G(i),G(j)]或 P(i,j)=G(i)+G(j)
经典Prewitt算子认为:凡灰度新值大于或等于阈值的像素点都是边缘点。即选择适当的阈值T,若P(i,j)≥T,则(i,j)为边缘点,P(i,j)为边缘图像。这种判定是欠合理的,会造成边缘点的误判,因为许多噪声点的灰度值也很大,而且对于幅值较小的边缘点,其边缘反而丢失了。
Prewitt算子对噪声有抑制作用,抑制噪声的原理是通过像素平均,但是像素平均相当于对图像的低通滤波,所以Prewitt算子对边缘的定位不如Roberts算子。
因为平均能减少或消除噪声,Prewitt梯度算子法就是先求平均,再求差分来求梯度。水平和垂直梯度模板分别为:
该算子与Sobel算子类似,只是权值有所变化,但两者实现起来功能还是有差距的,据经验得知Sobel要比Prewitt更能准确检测图像边缘。
对噪声有抑制作用,抑制噪声的原理是通过像素平均,但是像素平均相当于对图像的低通滤波,所以Prewitt算子对边缘的定位不如Roberts算子。
Mat prewitt(Mat imageP)
{
cvtColor(imageP,imageP,CV_RGB2GRAY);
float prewittx[9] =
{
-1,0,1,
-1,0,1,
-1,0,1
};
float prewitty[9] =
{
1,1,1,
0,0,0,
-1,-1,-1
};
Mat px=Mat(3,3,CV_32F,prewittx);
cout<<px<<endl;
Mat py=Mat(3,3,CV_32F,prewitty);
cout<<py<<endl;
Mat dstx=Mat(imageP.size(),imageP.type(),imageP.channels());
Mat dsty=Mat(imageP.size(),imageP.type(),imageP.channels());
Mat dst=Mat(imageP.size(),imageP.type(),imageP.channels());
filter2D(imageP,dstx,imageP.depth(),px);
filter2D(imageP,dsty,imageP.depth(),py);
float tempx,tempy,temp;
for(int i=0;i<imageP.rows;i++)
{
for(int j=0;j<imageP.cols;j++)
{
tempx=dstx.at<uchar>(i,j);
tempy=dsty.at<uchar>(i,j);
temp=sqrt(tempx*tempx+tempy*tempy);
dst.at<uchar>(i,j)=temp;
}
}
return dst;
}
Sobel算子
其主要用于边缘检测,在技术上它是以离散型的差分算子,用来运算图像亮度函数的梯度的近似值, Sobel算子是典型的基于一阶导数的边缘检测算子,由于该算子中引入了类似局部平均的运算,因此对噪声具有平滑作用,能很好的消除噪声的影响。Sobel算子对于象素的位置的影响做了加权,与Prewitt算子、Roberts算子相比因此效果更好。
Sobel算子包含两组3x3的矩阵,分别为横向及纵向模板,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。实际使用中,常用如下两个模板来检测图像边缘。
图像的每一个像素的横向及纵向梯度近似值可用以下的公式结合,来计算梯度的大小。
然后可用以下公式计算梯度方向。
在以上例子中,如果以上的角度Θ等于零,即代表图像该处拥有纵向边缘,左方较右方暗。
缺点是Sobel算子并没有将图像的主题与背景严格地区分开来,换言之就是Sobel算子并没有基于图像灰度进行处理,由于Sobel算子并没有严格地模拟人的视觉生理特征,所以提取的图像轮廓有时并不能令人满意。
Sobel 算子是一个主要用作边缘检测的离散微分算子 (discrete differentiation operator)。 它Sobel算子结合了高斯平滑和微分求导,用来计算图像灰度函数的近似梯度。在图像的任何一点使用此算子,将会产生对应的梯度矢量或是其法矢量。
因为Sobel算子结合了高斯平滑和分化(differentiation),因此结果会具有更多的抗噪性。大多数情况下,我们使用sobel函数时,取【xorder = 1,yorder = 0,ksize = 3】来计算图像X方向的导数,【xorder = 0,yorder = 1,ksize = 3】来计算图像y方向的导数。
计算图像X方向的导数,取【xorder= 1,yorder = 0,ksize = 3】情况对应的内核:
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
using namespace cv;
int main( int argc, char** argv )
{
Mat src, src_gray;
Mat grad;
char* window_name = "Sobel Demo - Simple Edge Detector";
int scale = 1;//默认值
int delta = 0;//默认值
int ddepth = CV_16S;//防止输出图像深度溢出
int c;
src = imread( "lena.jpg" );
if( !src.data )
{ return -1; }
//高斯模糊
GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );
//变换为灰度图
cvtColor( src, src_gray, CV_RGB2GRAY );
//创建窗口
namedWindow( window_name, CV_WINDOW_AUTOSIZE );
//生成 grad_x and grad_y
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y;
// Gradient X x方向梯度 1,0:x方向计算微分即导数
//Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT );
Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );
convertScaleAbs( grad_x, abs_grad_x );
// Gradient Y y方向梯度 0,1:y方向计算微分即导数
//Scharr( src_gray, grad_y, ddepth, 0, 1, scale, delta, BORDER_DEFAULT );
Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );
convertScaleAbs( grad_y, abs_grad_y );
//近似总的梯度
addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );
imshow( window_name, grad );
waitKey(0);
return 0;
}