Opencv图像识别从零到精通(19)----Robert,prewitt,Sobel边缘检测

图像的边缘检测,是根据灰度的突变或者说不连续来检测,对于其中的算子有一阶导数和二价导数,这里先说基础的三种方法---Robert,prewitt,Sobel边缘检测。

 一、梯度

首先介绍下梯度,梯度并非是一个数值,梯度严格意义上是一个向量,这个向量指向当前位置变化最快的方向,可以这么理解,当你站在一个山上,你有360°的方向可以选择,哪个方向下降速度最快(最陡峭),便是梯度方向,梯度的长度,表示为向量的长度,表示最大的变化速率。

梯度的数学表达:

在二维中只取前两项,也就是由x方向的偏微分和y方向的偏微分组成。对于图像f中点(x,y)处的梯度,定义为:

与上面所述保持一致,图像梯度方向给出图像变化最快方向,当前点的梯度长度为:

次长度计算中有平方和开平方,所以将不再是现行操作。

为了简单计算,将上面求距离简化成:

二、RObert

Roberts边缘算子是一个2x2的模板,采用的是对角方向相邻的两个像素之差。从图像处理的实际效果来看,边缘定位较准,对噪声敏感。适用于边缘明显且噪声较少的图像分割。Roberts边缘检测算子是一种利用局部差分算子寻找边缘的算子,Robert算子图像处理后结果边缘不是很平滑。经分析,由于Robert算子通常会在图像边缘附近的区域内产生较宽的响应,故采用上述算子检测的边缘图像常需做细化处理,边缘定位的精度不是很高。标准一阶差分不同,Robert采用对角线差分,边缘定位准,但是对噪声敏感。适用于边缘明显且噪声较少的图像分割。Roberts边缘检测算子是一种利用局部差分算子寻找边缘的算子,Robert算子图像处理后结果边缘不是很平滑。经分析,由于Robert算子通常会在图像边缘附近的区域内产生较宽的响应,故采用上述算子检测的边缘图像常需做细化处理,边缘定位的精度不是很高。

<span style="font-size:18px;">// roberts算子实现
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;
}</span>

三、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算子。

// 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】情况对应的内核:

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 );  
  • 第一个参数,InputArray 类型的src,为输入图像,填Mat类型即可。
  • 第二个参数,OutputArray类型的dst,即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。
  • 第三个参数,int类型的ddepth,输出图像的深度,支持如下src.depth()和ddepth的组合:
  • 若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F
  • 若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F
  • 若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F
  • 若src.depth() = CV_64F, 取ddepth = -1/CV_64F
  • 第四个参数,int类型dx,x 方向上的差分阶数。
  • 第五个参数,int类型dy,y方向上的差分阶数。
  • 第六个参数,int类型ksize,有默认值3,表示Sobel核的大小;必须取1,3,5或7。
  • 第七个参数,double类型的scale,计算导数值时可选的缩放因子,默认值是1,表示默认情况下是没有应用缩放的。我们可以在文档中查阅getDerivKernels的相关介绍,来得到这个参数的更多信息。
  • 第八个参数,double类型的delta,表示在结果存入目标图(第二个参数dst)之前可选的delta值,有默认值0。
  • 第九个参数, int类型的borderType,我们的老朋友了(万年是最后一个参数),边界模式,默认值为BORDER_DEFAULT。这个参数可以在官方文档中borderInterpolate处得到更详细的信息。
#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;
}  

五、Matlab

 clear all;
I=imread('d:\lena.jpg');
I=rgb2gray(I);%灰度图像
subplot(1,4,1);imshow(I);
xlabel('(a)原始图像');
%sobel算子
BW2=edge(I,'sobel');
subplot(1,4,2);imshow(BW2);
xlabel('(c)sobel算子')
%prewitt算子
BW3=edge(I,'prewitt');
subplot(1,4,3);imshow(BW3);
xlabel('(d)prewitt算子')
 %roberts算子
BW4=edge(I,'roberts');
subplot(1,4,4);imshow(BW4);
xlabel('(e)roberts算子')

时间: 2024-12-27 07:52:36

Opencv图像识别从零到精通(19)----Robert,prewitt,Sobel边缘检测的相关文章

Opencv图像识别从零到精通(26)---分水岭

分水岭是区域分割三个方法的最后一个,对于前景背景的分割有不错的效果. 分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭.分水岭的概念和形成可以通过模拟浸入过程来说明.在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭. 分水岭

Opencv图像识别从零到精通(13)----点线圆矩形与鼠标事件

图像中不可少的元素就是点.线.圆.椭圆.矩形,多边形,同时这些也是物体的特征组成单位,在图像识别中必不可少.所以要首先去认识这个元素怎么定义和使用,同时鼠标是电脑的窗口,我们很多的处理都会用到鼠标.本文主要有下面三个部分: (1) 点.线.圆.椭圆.矩形的基础应用 (2)点.线.圆.椭圆.矩形的进阶应用 (3)鼠标事件 一.点.线.圆.椭圆.矩形的基础应用 绘制点的函数: Point a = Point (600,600); 文字函数putText()函数 void putText( CvArr

Opencv图像识别从零到精通(30)---重映射,仿射变换

一.序言 面对图像处理的时候,我们会旋转缩放图像,例如前面所提高的resize 插值改变,也是几何变换: 几何运算需要空间变换和灰度级差值两个步骤的算法,像素通过变换映射到新的坐标位置,新的位置可能是在几个像素之间,即不一定为整数坐标.这时就需要灰度级差值将映射的新坐标匹配到输出像素之间.最简单的插值方法是最近邻插值,就是令输出像素的灰度值等于映射最近的位置像素,该方法可能会产生锯齿.这种方法也叫零阶插值,相应比较复杂的还有一阶和高阶插值. 除了插值算法感觉只要了解就可以了,图像处理中比较需要理

Opencv图像识别从零到精通(29)-----图像金字塔,向上上下采样,resize插值

金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似.我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低 一.两个金字塔 高斯金字塔(Gaussianpyramid): 用来向下采样,主要的图像金字塔 拉普拉斯金字塔(Laplacianpyramid): 用来从金字塔低层图像重建上层未采样图像,在数字图像处理中也即是预测残差,可以对图像进行最大程度的还原,配合高斯金字塔一起使用. 高斯金字塔不同(DoG)又称为拉普拉斯金字塔,给出计算方式前,先加强一下定义 记得在上面我

Opencv图像识别从零到精通(33)----moravec角点、harris角点

一.角点 图像处理和与计算机视觉领域,兴趣点(interest points),或称作关键点(keypoints).特征点(feature points) 被大量用于解决物体识别,图像识别.图像匹配.视觉跟踪.三维重建等一系列的问题.我们不再观察整幅图,而是选择某些特殊的点,然后对他们进行局部有的放矢的分析.如果能检测到足够多的这种点,同时他们的区分度很高,并且可以精确定位稳定的特征,那么这个方法就有使用价值. 图像特征类型可以被分为如下三种: <1>边缘                   

Opencv图像识别从零到精通(7)----图像平移、旋转、镜像

根据vc6.0c++的学习经验,如果可以很好的自己编程,让图像进行平移旋转这些操作,那么就好像能够清楚的看见图像的内部结构当然这里你怎么访问像素,这个可以自己选一种适合的,最多的是ptr指针,at也是挺多的.看着很简单的变换,可以对图像处理上手的更快,当然对于旋转可能就稍微i难了一点,不过opencv提供了resize(0,remap()等这样的函数,可以方便的让我们进行学习-特别是旋转的时候,有很多的变换,你可以任意旋转一个角度,也可能一直旋转,当然还可以保持图像大小不变的旋转和大小变换的旋转

Opencv图像识别从零到精通(24)------漫水填充,种子填充,区域生长、孔洞填充

可以说从这篇文章开始,就结束了图像识别的入门基础,来到了第二阶段的学习.在平时处理二值图像的时候,除了要进行形态学的一些操作,还有有上一节讲到的轮廓连通区域的面积周长标记等,还有一个最常见的就是孔洞的填充,opencv这里成为漫水填充,其实也可以叫种子填充,或者区域生长,基本的原理是一样的,但是应用的时候需要注意一下,种子填充用递归的办法,回溯算法,漫水填充使用堆栈,提高效率,同时还提供了一种方式是扫描行.经常用来填充孔洞,现在来具体看看. 漫水填充:也就是用一定颜色填充联通区域,通过设置可连通

Opencv图像识别从零到精通(28)----Kmeans

K-means算法算是个著名的聚类算法了,不仅容易实现,并且效果也不错,训练过程不需人工干预,实乃模式识别等领域的居家必备良品啊,今天就拿这个算法练练手.属于无监督学习中间接聚类方法中的动态聚类 流程: 1.随机选取样本中的K个点作为聚类中心 2.计算所有样本到各个聚类中心的距离,将每个样本规划在最近的聚类中 3.计算每个聚类中所有样本的中心,并将新的中心代替原来的中心 4.检查新老聚类中心的距离,如果距离超过规定的阈值,则重复2-4,直到小于阈值 聚类属于无监督学习,以往的回归.朴素贝叶斯.S

Opencv图像识别从零到精通(27)---grabcut

这是基于图论的分割方法,所以开始就先介绍了 Graph cuts,然后再到Grab cut   一. Graph cuts Graph cuts是一种十分有用和流行的能量优化算法,在计算机视觉领域普遍应用于前背景分割(Image segmentation).立体视觉(stereo vision).抠图(Image matting)等. 此类方法把图像分割问题与图的最小割(min cut)问题相关联.首先用一个无向图G=<V,E>表示要分割的图像,V和E分别是顶点(vertex)和边(edge)