对于一幅图像,假设图像为全白,但是中间有一条黑色的垂直的直线。那么直线所在的位置有何特征呢?
稍微想想便可以知道,在直线的左边为全白,那么从图像最左边至中间黑色的直线,其像素点的灰度变化率始终是为0的。同理,直线右边也是这种情况。但是,在黑色直线旁边的点,其会度变化率则不为0。如果把黑色直线看成是图像左右两边的分界线,那么只要知道灰度变化率不为0 的点,就可以找到这个边界的准确位置。边缘检测理论就是是基于这种灰度变化率的思想。
在数学中,这种灰度变化率是一种抽象的函数关系,那么在计算机中,是怎么实现计算变化率的呢?
考虑到对与点(x,y)其灰度变化率的表达形式为△f/△x(水平方向)或者△f/△y(垂直方向)即梯度,则考虑采用一个3X3的模板来与以此点为中心的3X3区域进行卷积。
上图给出的模板,就是Prewitt算子。
对于Prewitt算子模板,拿X方向梯度举例,其点的灰度变化率与左右两侧的了六个点有关,但是相关程度都是一样的,即权值都为1。而且Prewitt算子只能计算水平和垂直方向的边缘变化,如果我想检测45度方向,135方向的边缘情况,在Prewitt算子中是通过水平和垂直方向的梯度叠加来算的,但是这种方法与真实情况下的45度方向梯度变化是不一样的,降低了梯度值。
sobel算子在Prewitt算子的基础上,给六个点加了不同的权值,因为这个权值的存在就可以表达出对不同方向边缘的敏感度。下面给出不同方向的模板:
对比这四个模板,也能更好的理解sobel算子给点不同权值的意义。由于权值的存在,那么周围点对此点的影响程度就是可以变化的。即可以通过调节不同的权值,实现不同方向的影响力。
opencv实现程序如下:
#include <cv.hpp> #include <opencv2/highgui/highgui.hpp> using namespace cv; using namespace std; void main() { IplImage *frame,*sobel; frame=cvLoadImage("1.jpg",CV_LOAD_IMAGE_GRAYSCALE);//以灰度方式加载图像 sobel=cvCreateImage(cvGetSize(frame),IPL_DEPTH_16S,1);//图像的梯度有正负,且大小可能超过255, //故深度选为signed int cvNamedWindow("frame"); cvNamedWindow("sobel"); cvSobel(frame,sobel,1,0,3);//sobel算子运算 IplImage *sobel8u=cvCreateImage(cvGetSize(sobel),IPL_DEPTH_8U,1); cvConvertScaleAbs(sobel,sobel8u,1,0); //图像显示只能显示无符号8位 //故要将原来的有符号16转换为无符号8位 cvShowImage("frame",frame); cvShowImage("sobel",sobel8u); cvWaitKey(0);//等待 cvReleaseImage(&frame);//释放空间 cvReleaseImage(&sobel); cvDestroyWindow("frame"); cvDestroyWindow("sobel"); }
void cvSobel( const CvArr* src, CvArr* dst, int xorder, int yorder, int aperture_size=3 );
src:源图像;dst:目标图像;xorder:x 方向上的差分阶数;yorder:y 方向上的差分阶数;
aperture_size 扩展 Sobel 核的大小(既窗口阶数),必须是 1(注意这是一个3×1或1×3向量而不是一个方阵), 3, 5 或 7。
void cvConvertScaleAbs( const CvArr* src, CvArr* dst, double scale=1, double shift=0 );
src:源图像;dst:目标图像;scale:转化前乘的系数;shift:转化前加的系数。 这个函数实现有符号的图像到无符号的图像的转化
原文地址:http://blog.csdn.net/autocyz/article/details/42676627