您的位置:首页 > 运维架构

opencv提取图像边缘特征sobel算子的运用

2015-06-06 15:01 405 查看
1)sobel算子

计算机视觉领域的一种重要处理方法。主要用于获得数字图像的一阶梯度,常见的应用和物理意义是边缘检测。在技术上,它是一个离散的一阶差分算子,用来计算图像亮度函数的一阶梯度之近似值。在图像的任何一点使用此算子,将会产生该点对应的梯度矢量或是其法矢量。

该算子是由两个卷积核与对原图像进行卷积运算而得到的。其数学表达式为:

实际上Sobel边缘算子所采用的算法是先进行加权平均,然后进行微分运算,我们可以用差分代替一阶偏导,算子的计算方法如下:

(6)
Sobel算子垂直方向和水平方向的模板如图(B)所示,前者可以检测出图像中的水平方向的边缘,后者则可以检测图像中垂直方向的边缘。实际应用中,图像中的每一个像素点都用这两个卷积核进行卷积运算,取其最大值作为输出。运算结果是一幅体现边缘幅度的图像。

图(B)Sobel算子模板
该算子包含两组3x3的矩阵,分别为横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。如果以A代表原始图像,Gx及Gy分别代表经纵向向及横向边缘检测的图像,其公式如下:

图像的每一个像素的横向及纵向梯度近似值可用以下的公式结合,来计算梯度的大小。

然后可用以下公式计算梯度方向。

在以上例子中,如果以上的角度Θ等于零,即代表图像该处拥有纵向边缘,左方较右方暗。

在边缘检测中,常用的一种模板是Sobel 算子。Sobel 算子有两个,一个是检测水平边缘的 ;另一个是检测垂直边缘的
。与Prewitt算子相比,Sobel算子对于象素的位置的影响做了加权,可以降低边缘模糊程度,因此效果更好。

Sobel算子另一种形式是各向同性Sobel(Isotropic Sobel)算子,也有两个,一个是检测水平边缘的 ,另一个是检测垂直边缘的 。各向同性Sobel算子和普通Sobel算子相比,它的位置加权系数更为准确,在检测不同方向的边沿时梯度的幅度一致。将Sobel算子矩阵中的所有2改为根号2,就能得到各向同性Sobel的矩阵。

由于Sobel算子是滤波算子的形式,用于提取边缘,可以利用快速卷积函数,
简单有效,因此应用广泛。美中不足的是,Sobel算子并没有将图像的主体与背景严格地区分开来,换言之就是Sobel算子没有基于图像灰度进行处理,由于Sobel算子没有严格地模拟人的视觉生理特征,所以提取的图像轮廓有时并不能令人满意。 在观测一幅图像的时候,我们往往首先注意的是图像与背景不同的部分,正是这个部分将主体突出显示,基于该理论,我们给出了下面阈值化轮廓提取算法,该算法已在数学上证明当像素点满足正态分布时所求解是最优的。

void cvSobel( CvArr* src,CvArr* dst, int xorder,int yorder,int aperture_size = 3);

src 输入图像.

dst 输出图像.

xorder和yorder是求导的阶数,

aperture_size 核大小.函数。

Sobel导数有一个很好的性质,可以定义任意大小的核,并且这些核可以用快速且迭代的方式构造。大核对导数有更好的逼近,小核对噪声更敏感。

函数 cvSobel 通过对图像用相应的内核进行卷积操作来计算图像差分:

由于Sobel 算子结合了 Gaussian 平滑和微分,所以,其结果或多或少对噪声有一定的鲁棒性。通常情况,函数调用采用如下参数 (xorder=1, yorder=0, aperture_size=3) 或 (xorder=0, yorder=1, aperture_size=3) 来计算一阶 x- 或 y- 方向的图像差分。Sobel导数有一个很好的性质,可以定义任意大小的核,并且这些核可以用快速且迭代的方式构造。大核对导数有更好的逼近,小核对噪声更敏感。

函数 cvSobel 通过对图像用相应的内核进行卷积操作来计算图像差分:

由于Sobel 算子结合了 Gaussian 平滑和微分,所以,其结果或多或少对噪声有一定的鲁棒性。通常情况,函数调用采用如下参数 (xorder=1, yorder=0, aperture_size=3) 或 (xorder=0, yorder=1, aperture_size=3) 来计算一阶 x- 或 y- 方向的图像差分。

核则依赖于图像原点的定义 (origin 来自 IplImage 结构的定义)。由于该函数不进行图像尺度变换,所以和输入图像(数组)相比,输出图像(数组)的元素通常具有更大的绝对数值(译者注:即象素的深度)。为防止溢出,当输入图像是 8 位的,要求输出图像是
16 位的。当然可以用函数cvConvertScale 或 cvConvertScaleAbs 转换为 8 位的。除了 8-比特 图像,函数也接受 32-位 浮点数图像。所有输入和输出图像都必须是单通道的,并且具有相同的图像尺寸或者ROI尺寸。

2)cvCmpS函数 比较图像的像素点与value

void cvCmpS(const CvArr* src1, //输入图像

double value, //给定像素值

CvArr* dst, //输出图像

int cmp_op,)//比较方法

3)cvAvg计算数组平均像素值

CvScalar cvAvg(const CvArr* arr, const CvArr* mask =NULL);

计算数组arr的平均像素值,如果mask为非空,那么平均值仅由那些mask值为非0的元素相对应的像素算出。

代码:

#include <iostream>

#include <cv.h>

#include <highgui.h>

#include <cxcore.h>

using namespace std;

const CvSize size = cvSize(200,200);

const int aperture[] = {3,5,9,11,13,17};//滤波器的核

int main()

{

IplImage *src = cvCreateImage(size,8,1);

cvZero(src);

//cvPoint(0,size.height - 1) 45度直线

//cvPoint(size.width -1 , 0)

cvLine(src,cvPoint(0,0),cvPoint(size.width - 1, size.height - 1),

CV_RGB(255,255,255),3);

cvShowImage("src",src);

cvSaveImage("src.jpg",src);

IplImage *deriv_x = cvCreateImage(size,IPL_DEPTH_32F,1);

IplImage *deriv_y = cvCreateImage(size,IPL_DEPTH_32F,1);

IplImage *magnitude = cvCreateImage(size,IPL_DEPTH_32F,1);

IplImage *angle = cvCreateImage(size,IPL_DEPTH_32F,1);

IplImage *mask = cvCreateImage(size,IPL_DEPTH_8U,1);

for(int i=0;i<sizeof(aperture)/sizeof(aperture[0]);++i)

{

cvSobel(src,deriv_x,1,0,aperture[i]);//x横坐标方向进行一阶求导

cvSobel(src,deriv_y,0,1,aperture[i]);//y纵坐标方向进行一阶求导

cvCartToPolar(deriv_x,deriv_y,magnitude,angle,1);//将坐标从普通坐标转化为极坐标

cvSave("x.xml",deriv_x);

cvSave("y.xml",deriv_y);

cvSave("magnitude.xml",magnitude);

cvSave("angle.xml",angle);

double max_mag ,min_mag ,max_angle , min_angle;

cvMinMaxLoc(magnitude,&min_mag,&max_mag);//幅值求最大最小

cvMinMaxLoc(angle,&min_angle,&max_angle);//角度求最大最小

cout<<"magnitude: max = "<<max_mag<<" min = "<<min_mag<<endl;

cout<<"angle : max = "<<max_angle<<" min = "<<min_angle<<endl;

cvCmpS(magnitude,max_mag*3/4,mask,CV_CMP_GT);//比较图像的像素点与max_mag*3/4的值

cvShowImage("mask",mask);

cvSaveImage("mask.jpg",mask);

CvScalar scalar = cvAvg(angle,mask);//计算数组平均像素值

cout<<"aperture = "<<aperture[i]<<", line angle = "<<scalar.val[0]<<endl;

cvWaitKey();

}

cvReleaseImage(&src);

cvReleaseImage(&magnitude);

cvReleaseImage(&angle);

cvReleaseImage(&mask);

cvDestroyAllWindows();

}

效果:




由于水平有限原因,暂时还不能很好的解析输出结果之间的差异和比较的结果。具体分析等理解之后再更新。





在以上例子中,如果以上的角度Θ等于零,即代表图像该处拥有纵向边缘,左方较右方暗。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: