由于项目里要用到边缘检测,所以今天研究了一下最简单的梯度的方法。
X点以Sobel方式求导数ΔX=1×50+2×30+1×50-(1×50+2×30+1×50)=0。这样可以看出这个点不是边界。
#include <cv.h>
#include <highgui.h>
void main()
{
IplImage *frame,*gray,*sobel;
frame=cvLoadImage("lena.jpg");//加载图像
gray=cvCreateImage(cvGetSize(frame),frame->depth,1);//分配图像空间
sobel=cvCreateImage(cvGetSize(frame),frame->depth,1);
cvNamedWindow("frame");
cvNamedWindow("gray");
cvNamedWindow("sobel");
cvCvtColor(frame,gray,CV_BGR2GRAY);//转为灰度
cvSobel(gray,sobel,1,0,3);
cvShowImage("frame",frame);//显示图像
cvShowImage("gray",gray);
cvShowImage("sobel",sobel);
cvWaitKey(0);//等待
cvReleaseImage(&frame);//释放空间(对视频处理很重要,不释放会造成内存泄露)
cvReleaseImage(&gray);
cvReleaseImage(&sobel);
cvDestroyWindow("frame");
cvDestroyWindow("gray");
cvDestroyWindow("sobel");
}
运行,你会发现出错,仔细看看没有问题啊。其实,这里是问题的,因为以Sobel方式求完导数后会有负值,还有会大于255的值而你建的Sobel的图像是 IPL_DEPTH_8U,也就是8位无符号数,所以Sobel建立的图像位数不够,要16位有符号的,也就是 IPL_DEPTH_16S。把建立图像这句改为
sobel=cvCreateImage(cvGetSize(frame),IPL_DEPTH_16S,1);运行,发现不报错了,但是Sobel图像显示不出来,这是什么原因呢?原来图像显示是以8位无符号显示的,现在是16位有符号,当然显示会出问题了。所以还要将Sobel转为8位无符号。OpenCv里提供了一个函数,就是cvConvertScaleAbs( const CvArr* src, CvArr* dst, double scale=1, double shift=0 );src:源图像;dst:目标图像;scale:转化前乘的系数;shift转化前加的系数。这样新建一个无符号图像再转换就可以实现了。
IplImage *sobel8u=cvCreateImage(cvGetSize(sobel),IPL_DEPTH_8U,1);
再在显示图像前加上cvConvertScaleAbs(sobel,sobel8u,1,0);这样就可以看到cvSobel的效果了。可以看X方向或Y方向求导是什么效果。
为了方便大家,我把改好后的程序也放上来了。
#include <cv.h>
#include <highgui.h>
void main()
{
IplImage *frame,*gray,*sobel;
frame=cvLoadImage("d:\\picture\\01.bmp");//加载图像
gray=cvCreateImage(cvGetSize(frame),frame->depth,1);//分配图像空间
sobel=cvCreateImage(cvGetSize(frame),IPL_DEPTH_16S,1);
cvNamedWindow("frame");
cvNamedWindow("gray");
cvNamedWindow("sobel");
cvCvtColor(frame,gray,CV_BGR2GRAY);//转为灰度
cvSobel(gray,sobel,1,0,3);
IplImage *sobel8u=cvCreateImage(cvGetSize(sobel),IPL_DEPTH_8U,1);
cvConvertScaleAbs(sobel,sobel8u,1,0);
cvShowImage("frame",frame);//显示图像
cvShowImage("gray",gray);
cvShowImage("sobel",sobel8u);
cvWaitKey(0);//等待
cvReleaseImage(&frame);//释放空间(对视频处理很重要,不释放会造成内存泄露)
cvReleaseImage(&gray);
cvReleaseImage(&sobel);
cvDestroyWindow("frame");
cvDestroyWindow("gray");
cvDestroyWindow("sobel");
}
下面一段代码是对 sobel 的优化,忘记从哪拷贝的
#define SOBEL_HORZ 1
#define SOBEL_VERT 2
#define SOBEL_BOTH 4
bool dzSobel(const Mat& image, Mat& result, int TYPE)
{
if(image.channels()!=1) return false;
int kx(0), ky(0); // 系数设置
if( TYPE==SOBEL_HORZ )
{
kx=0;ky=1;
}
else if( TYPE==SOBEL_VERT )
{
kx=1;ky=0;
}
else if( TYPE==SOBEL_BOTH )
{
kx=1;ky=1;
}
else return false;
// 设置mask
float mask[3][3]={{1,2,1},{0,0,0},{-1,-2,-1}};
Mat y_mask=Mat(3,3,CV_32F,mask)/8;
Mat x_mask=y_mask.t(); // 转置
// 计算x方向和y方向上的滤波
Mat sobelX,sobelY;
filter2D(image,sobelX,CV_32F,x_mask);
filter2D(image,sobelY,CV_32F,y_mask);
sobelX=abs(sobelX);
sobelY=abs(sobelY);
// 梯度图
Mat gradient=kx*sobelX.mul(sobelX)+ky*sobelY.mul(sobelY);
// 计算阈值
int scale=1;
double cutoff=scale*mean(gradient)[0];
result.create(image.size(),image.type());
result.setTo(0);
for(int i=1;i<image.rows-1;i++)
{
float* sbxPtr=sobelX.ptr<float>(i);
float* sbyPtr=sobelY.ptr<float>(i);
float* prePtr=gradient.ptr<float>(i-1);
float* curPtr=gradient.ptr<float>(i);
float* lstPtr=gradient.ptr<float>(i+1);
uchar* rstPtr=result.ptr<uchar>(i);
for(int j=1;j<image.cols-1;j++) // 阈值化和极大值抑制
{
if( curPtr[j]>cutoff && (
(sbxPtr[j]>kx*sbyPtr[j] && curPtr[j]>curPtr[j-1] && curPtr[j]>curPtr[j+1]) ||
(sbyPtr[j]>ky*sbxPtr[j] && curPtr[j]>prePtr[j] && curPtr[j]>lstPtr[j]) ))
rstPtr[j]=255;
}
}
return true;
}