转载请说明出处:
http://blog.csdn.net/zhubaohua_bupt/article/details/73844187
本代码实现完全脱离opencv,如果需要显示,可以调用,以便观察检测效果。
首先,由于多次用到图像,所以定义图像数据结构,
DATA.h
#ifndef DATA_
#define DATA_
#include <vector>
#include<deque>
#include"memory.h"
using namespace std;
typedef unsigned char PIXUC1;
typedef float PIXFC1;
//单通道 类型图像
template<class PIXVALUETYPE>
class IMGCH1{
public:
IMGCH1(unsigned int HEIGHT_,unsigned int WIDTH_,unsigned char INITVALUE);
IMGCH1(unsigned int HEIGHT_,unsigned int WIDTH_,PIXVALUETYPE* dataPtr_);
~IMGCH1();
PIXVALUETYPE* dataPtr;
unsigned int rows;
unsigned int cols;
};
template<class PIXVALUETYPE>
IMGCH1<PIXVALUETYPE>::IMGCH1(unsigned int HEIGHT_,unsigned int WIDTH_,unsigned char INITVALUE):rows(HEIGHT_),cols(WIDTH_)
{
dataPtr=new PIXVALUETYPE[rows*cols];
memset(dataPtr,INITVALUE,rows*cols);
}
template<class PIXVALUETYPE>
IMGCH1<PIXVALUETYPE>::IMGCH1(unsigned int HEIGHT_,unsigned int WIDTH_,PIXVALUETYPE* dataPtr_):rows(HEIGHT_),cols(WIDTH_)
{
dataPtr=new PIXVALUETYPE[rows*cols];
long int datalength =rows*cols;
//拷贝数据
PIXVALUETYPE*pt=dataPtr;
PIXVALUETYPE*pt_=dataPtr_;
for(int i=0;i<datalength;i++,pt++,pt_++)
*pt=*pt_;
}
template<class PIXVALUETYPE>
IMGCH1<PIXVALUETYPE>:: ~IMGCH1()
{
delete [] dataPtr;
}
#endif
MyCanny.h
#include "iostream"
#include "math.h"
#include"DATA.h"
using namespace std;
class MyCanny
{
public:
void operator()(const IMGCH1<PIXUC1>& srcimg,IMGCH1<PIXUC1>& CannyImg,int lowthread,int highthread,int gaussSize);
//******************灰度转换函数*************************
//第一个参数image输入的彩色RGB图像;
//第二个参数imageGray是转换后输出的灰度图像;
//*************************************************************
void ToUchar(const IMGCH1<PIXFC1> &floatImage,IMGCH1<PIXUC1> &imageUchar);
//******************高斯卷积核生成函数*************************
// gaus是一个指向含有N个double类型数组的二维指针;
// size是高斯卷积核的尺寸大小;
// gausArray 是一个指向含有N个double类型数组的一维指针;
// sigma是卷积核的标准差
//*************************************************************
void GetGaussianKernel(float **gaus, float* gausArray,const int size,const float sigma);
//******************高斯滤波*************************
//imageSource是待滤波原始图像;
//imageGaussian是滤波后输出图像;
//gausArray是一个指向含有N个double类型数组的指针;
//size是滤波核的尺寸
//*************************************************************
void GaussianFilter(const IMGCH1<PIXUC1>& srcimg,IMGCH1<PIXUC1>&imageGaussian,float gausArray[],int size);
//******************Sobel算子计算梯度和方向********************
//imageSourc原始灰度图像;
//imageSobelXY是梯度图像;
//pointDrection是梯度方向数组指针
//*************************************************************
void SobelGradDirection(const IMGCH1<PIXUC1> &imageSource,IMGCH1<PIXUC1> &imageSobelXY,char *pointDrection);
//******************局部极大值抑制*************************
//imageInput输入的Sobel梯度图像;
//imageOutPut是输出的局部极大值抑制图像;
//pointDrection是图像上每个点的梯度方向数组指针 梯度方向角,简化为 0(水平) 45,-45,90(垂直)
//*************************************************************
void LocalMaxValue(const IMGCH1<PIXUC1> &imageInput, IMGCH1<PIXUC1> &imageOutput, char *pointDrection);
//******************双阈值处理*************************
//imageInput经过局部极大值抑制的梯度图
//lowThreshold是低阈值
//highThreshold是高阈值
//******************************************************
void DoubleThreshold( IMGCH1<PIXUC1> &imageIput,int lowThreshold,int highThreshold);
//******************双阈值中间像素连接处理*********************
//imageInput 经过处理的局部极大值抑制的梯度图 <lowThreshold ->0 >highThreshold ->255
//函数执行完后是Canny边缘检测图, 边缘(255)其他(0)
//lowThreshold是低阈值
//highThreshold是高阈值
//*************************************************************
void DoubleThresholdLink(IMGCH1<PIXUC1> &imageInput,IMGCH1<PIXUC1> &CannyImg,int lowThreshold) ;
//******************递归连接边缘*********************
//imageInput 梯度幅值图像;
// x,y 为要检测点的坐标
//lowThreshold是低阈值
//*************************************************************
void LinkEdge(IMGCH1<PIXUC1> &imageInput,int x,int y,int lowThreshold );
void SHOW(const IMGCH1<PIXUC1> &imageInput);
int width;
int height;
};
MyCanny.cpp
#include"MyCanny.h"
//为了显示,可以把类里的show函数注释掉,就可以在实现部分不依赖于opencv
#include"opencv.hpp"
using namespace cv;
void MyCanny::operator()(const IMGCH1<PIXUC1>& srcimg_,IMGCH1<PIXUC1>& CannyImg_ ,int lowthread,int highthread,int size)
{
if(srcimg_.cols==0&&srcimg_.rows==0)
{
cerr<<" imageSource is empty"<<endl;
return ;
}
if(lowthread>highthread)
{
cerr<<"Fourth parameter must large than third parameter. "<<endl;
return;
}
if(size<1)
{
cerr<<"size must be a Positive. "<<endl;
return;
}
if(size%2!=1)
{
cout<<" size is expected a Odd. "<<endl;
size+=1;
}
if(lowthread<1) lowthread=1;
width=srcimg_.cols;
height=srcimg_.rows;
//高斯核
float **gaus=new float *[size]; //卷积核数组
for(int i=0;i<size;i++)
{
gaus[i]=new float[size]; //二维矩阵
}
float *gausArray=new float[size*size];
GetGaussianKernel(gaus,gausArray,size,1); //生成size*size 大小高斯卷积核,Sigma=1;
//滤波
IMGCH1<PIXUC1> imageGaussian_(height,width,PIXUC1(0));
GaussianFilter(srcimg_,imageGaussian_,gausArray,size);
//梯度
IMGCH1<PIXUC1> SobelGradAmpl_(height,width,PIXUC1(0));
char *pointDirection=new char[srcimg_.cols*srcimg_.rows]; //定义梯度方向角数组
SobelGradDirection(imageGaussian_,SobelGradAmpl_,pointDirection); //计算X、Y方向梯度和方向角
//局部非极大值抑制
IMGCH1<PIXUC1> imageLocalMax_(height,width,PIXUC1(0));
LocalMaxValue(SobelGradAmpl_,imageLocalMax_,pointDirection);
//双阈值处理
DoubleThreshold(imageLocalMax_,lowthread,highthread);
//双阈值中间阈值滤除及连接
DoubleThresholdLink(imageLocalMax_,CannyImg_,lowthread);
delete []pointDirection;
for(int i=0;i<size;i++) //删除高斯核数组
delete [] gaus[i];
delete []gausArray;
}
void MyCanny::GetGaussianKernel(float **gaus, float *gausArray, const int size,const float sigma)
{
const double PI=4.0*atan(1.0); //圆周率π赋值
int center=size/2;
double sum=0;
for(int i=0;i<size;i++)
{
for(int j=0;j<size;j++)
{
gaus[i][j]=(1/(2*PI*sigma*sigma))*exp(-((i-center)*(i-center)+(j-center)*(j-center))/(2*sigma*sigma));
sum+=gaus[i][j];
}
}
for(int i=0;i<size;i++)
for(int j=0;j<size;j++)
gaus[i][j]/=sum;
for(int i=0;i<size*size;i++)
{
gausArray[i]=0; //赋初值,空间分配
}
int array=0;
for(int i=0;i<size;i++)
for(int j=0;j<size;j++)
{
gausArray[array]=gaus[i][j];//二维数组到一维 方便计算
array++;
}
return ;
}
//******************高斯滤波*************************
void MyCanny::GaussianFilter(const IMGCH1<PIXUC1>& imageSource, IMGCH1<PIXUC1> &imageGaussian,float *gausArray,int size)
{
//滤波
for(int _row=0;_row<imageSource.rows;_row++)
{
for(int _col=0;_col<imageSource.cols;_col++)
{
int k=0;
for(int l=-size/2;l<=size/2;l++)
{
for(int g=-size/2;g<=size/2;g++)
{
//以下处理针对滤波后图像边界处理,为超出边界的值赋值为边界值
int row=_row+l;
int col=_col+g;
row=row<0?0:row;
row=row>=imageSource.rows?imageSource.rows-1:row;
col=col<0?0:col;
col=col>=imageSource.cols?imageSource.cols-1:col;
//卷积和
imageGaussian.dataPtr[_row*width+_col]+=gausArray[k]*imageSource.dataPtr[row*width+col];
k++;
}
}
}
}
}
//******************Sobel算子计算X、Y方向梯度和梯度方向角********************
void MyCanny::SobelGradDirection(const IMGCH1<PIXUC1>& imageSource, IMGCH1<PIXUC1>&SobelAmpXY, char *pointDrection)
{
for(int i=0;i<(imageSource.rows-1)*(imageSource.cols-1);i++)
{
pointDrection[i]=0;
}
IMGCH1<PIXFC1> imageSobelX(height,width,PIXFC1(0));
IMGCH1<PIXFC1> imageSobelY(height,width,PIXFC1(0));
PIXUC1 *P=imageSource.dataPtr;
PIXFC1 *PX=imageSobelX.dataPtr;
PIXFC1 *PY=imageSobelY.dataPtr;
int k=0;
for(int row=1;row<(imageSource.rows-1);row++)
{
for(int col=1;col<(imageSource.cols-1);col++)
{
pointDrection[k]=-1;
//通过指针遍历图像上每一个像素
int gradY=P[(row-1)*width+col+1]+P[row*width+col+1]*2+P[(row+1)*width+col+1]-P[(row-1)*width+col-1]-P[row*width+col-1]*2-P[(row+1)*width+col-1];
PY[row*width+col]=abs(gradY);
int gradX=P[(row+1)*width+col-1]+P[(row+1)*width+col]*2+P[(row+1)*width+col+1]-P[(row-1)*width+col-1]-P[(row-1)*width+col]*2-P[(row-1)*width+col+1];
PX[row*width+col]=abs(gradX);
if(gradX==0)
{
gradX=0.01; //防止除数为0异常
}
float gradDrection=atan2(gradY,gradX)*57.3;//弧度转换为度
if(gradDrection<=-67.5&&gradDrection<=-112.5||gradDrection>=67.5&&gradDrection<=-112.5)
pointDrection[k]=90;
else if(gradDrection>=22.5&&gradDrection<67.5||gradDrection>=-157.5&&gradDrection<-112.5)
pointDrection[k]=45;
else if(gradDrection>=-67.5&&gradDrection<22.5||gradDrection>=112.5&&gradDrection<157.5)
pointDrection[k]=-45;
else
pointDrection[k]=0;
k++;
}
}
IMGCH1<PIXFC1> imageSobelXY(height,width,PIXFC1(0));
for(int row=0;row<imageSobelXY.rows;row++)
for(int col=0;col<imageSobelXY.cols;col++)
imageSobelXY.dataPtr[row*width+col]=sqrt(imageSobelX.dataPtr[row*width+col]*imageSobelX.dataPtr[row*width+col]+imageSobelY.dataPtr[row*width+col]*imageSobelY.dataPtr[row*width+col]);
ToUchar(imageSobelXY,SobelAmpXY);
}
//******************局部极大值抑制*************************
void MyCanny::LocalMaxValue(const IMGCH1<PIXUC1> &imageInput, IMGCH1<PIXUC1> &imageOutput, char *pointDrection)
{
for(int row=0;row<height;row++)
for(int col=0;col<width;col++)
imageOutput.dataPtr[row*width+col]= imageInput.dataPtr[row*width+col];
int k=0;
for(int row=1;row<imageInput.rows-1;row++)
{
for(int col=1;col<imageInput.cols-1;col++)
{
int U=row-1,D=row+1,L=col-1,R=col+1;
int value00=imageInput.dataPtr[U*width+L];
int value01=imageInput.dataPtr[U*width+col];
int value02=imageInput.dataPtr[U*width+R];
int value10=imageInput.dataPtr[row*width+L];
int value11=imageInput.dataPtr[row*width+col];
int value12=imageInput.dataPtr[row*width+R];
int value20=imageInput.dataPtr[D*width+L];
int value21=imageInput.dataPtr[D*width+col];
int value22=imageInput.dataPtr[D*width+R];
if(pointDrection[k]==90)
{
if(value11<=value01||value11<=value21)
imageOutput.dataPtr[row*width+col]=0;
}
else if(pointDrection[k]=45)
{
if(value11<=value20||value11<value02)
imageOutput.dataPtr[row*width+col]=0;
}
else if(pointDrection[k]=-45)
{
if(value11<=value00||value11<=value22)
imageOutput.dataPtr[row*width+col]=0;
}
else
{
if(value11<=value10||value11<=value12)
imageOutput.dataPtr[row*width+col]=0;
}
k++;
}
}
}
//******************双阈值处理*************************
void MyCanny::DoubleThreshold( IMGCH1<PIXUC1> &imageIput,int lowThreshold,int highThreshold)
{
for(int row=0;row<imageIput.rows;row++)
for(int col=0;col<imageIput.cols;col++)
{
if(imageIput.dataPtr[row*width+col]>highThreshold)
imageIput.dataPtr[row*width+col]=255;
if(imageIput.dataPtr[row*width+col]<lowThreshold)
imageIput.dataPtr[row*width+col]=0;
}
}
//******************双阈值中间像素连接处理*********************
void MyCanny::DoubleThresholdLink(IMGCH1<PIXUC1> &imageInput,IMGCH1<PIXUC1> &CannyImg,int lowThreshold)
{
for(int row=1;row<imageInput.rows-1;row++)
for(int col=1;col<imageInput.cols-1;col++)
{
if(imageInput.dataPtr[row*width+col]==255)
LinkEdge(imageInput,col, row, lowThreshold );
}
for(int row=1;row<imageInput.rows;row++)
for(int col=1;col<imageInput.cols;col++)
{
if(imageInput.dataPtr[row*width+col]==255)
CannyImg.dataPtr[row*width+col]=255;
}
}
//******************递归连接边缘*********************
void MyCanny::LinkEdge(IMGCH1<PIXUC1> &imageInput,int x,int y,int lowThreshold )
{
int nextpoint_x=-1;
int nextpoint_y=-1;
if(x<1||x>width-1||y<1||y>height-1) return;
if( imageInput.dataPtr[(y-1)*width+x-1]>=lowThreshold&& imageInput.dataPtr[(y-1)*width+x-1]!=255)
{
nextpoint_x=x-1;
nextpoint_y=y-1;
imageInput.dataPtr[nextpoint_y*width+nextpoint_x]=255;
LinkEdge(imageInput,nextpoint_x,nextpoint_y,lowThreshold);
}
else if( imageInput.dataPtr[(y-1)*width+x]>=lowThreshold&&imageInput.dataPtr[(y-1)*width+x]!=255)
{
nextpoint_x=x;
nextpoint_y=y-1;
imageInput.dataPtr[nextpoint_y*width+nextpoint_x]=255;
LinkEdge(imageInput,nextpoint_x,nextpoint_y,lowThreshold);
}
else if(imageInput.dataPtr[(y-1)*width+x+1]>=lowThreshold&&imageInput.dataPtr[(y-1)*width+x+1]!=255)
{
nextpoint_x=x+1;
nextpoint_y=y-1;
imageInput.dataPtr[nextpoint_y*width+nextpoint_x]=255;
LinkEdge(imageInput,nextpoint_x,nextpoint_y,lowThreshold);
}
else if( imageInput.dataPtr[y*width+x-1]>=lowThreshold&&imageInput.dataPtr[y*width+x-1]!=255 )
{
nextpoint_x=x-1;
nextpoint_y=y;
imageInput.dataPtr[nextpoint_y*width+nextpoint_x]=255;
LinkEdge(imageInput,nextpoint_x,nextpoint_y,lowThreshold);
}
else if( imageInput.dataPtr[y*width+x+1]>=lowThreshold&&imageInput.dataPtr[y*width+x+1]!=255 )
{
nextpoint_x=x+1;
nextpoint_y=y;
imageInput.dataPtr[nextpoint_y*width+nextpoint_x]=255;
LinkEdge(imageInput,nextpoint_x,nextpoint_y,lowThreshold);
}
else if( imageInput.dataPtr[(y+1)*width+x-1]>=lowThreshold&&imageInput.dataPtr[(y+1)*width+x-1]!=255 )
{
nextpoint_x=x-1;
nextpoint_y=y+1;
imageInput.dataPtr[nextpoint_y*width+nextpoint_x]=255;
LinkEdge(imageInput,nextpoint_x,nextpoint_y,lowThreshold);
}
else if( imageInput.dataPtr[(y+1)*width+x]>=lowThreshold&& imageInput.dataPtr[(y+1)*width+x]!=255 )
{
nextpoint_x=x;
nextpoint_y=y+1;
imageInput.dataPtr[nextpoint_y*width+nextpoint_x]=255;
LinkEdge(imageInput,nextpoint_x,nextpoint_y,lowThreshold);
}
else if(imageInput.dataPtr[(y+1)*width+x+1]>=lowThreshold&&imageInput.dataPtr[(y+1)*width+x+1]!=255)
{
nextpoint_x=x+1;
nextpoint_y=y+1;
imageInput.dataPtr[nextpoint_y*width+nextpoint_x]=255;
LinkEdge(imageInput,nextpoint_x,nextpoint_y,lowThreshold);
}
}
void MyCanny::ToUchar(const IMGCH1<float> &floatImage,IMGCH1<PIXUC1> &imageUchar)
{
for(int row=0;row<floatImage.rows;row++)
for(int col=0;col<floatImage.cols;col++)
imageUchar.dataPtr[row*width+col]= floatImage.dataPtr[row*width+col]>255?255: floatImage.dataPtr[row*width+col];
}
void MyCanny::SHOW(const IMGCH1<PIXUC1> &imageInput)
{
Mat show=Mat::zeros(imageInput.rows,imageInput.cols,CV_8UC1);
for(int row=0;row<imageInput.rows;row++)
for(int col=0;col<imageInput.cols;col++)
{
show.at<uchar>(row,col)=imageInput.dataPtr[row*imageInput.cols+col];
}
imshow(" ",show);
waitKey(0);
}
测试
#include"opencv.hpp"
using namespace cv;
#include"MyCanny.h"
using namespace std;
int main()
{
Mat src=imread("E:\\matchpic\\right_16488.jpg",0);
//转换数据结构
IMGCH1<PIXUC1> src_(src.rows,src.cols,PIXUC1(0));
for(int row=0;row<src.rows;row++)
for(int col=0;col<src.cols;col++)
src_.dataPtr[row*src.cols+col]=src.at<uchar>(row,col);
Mat Cannyedge=Mat::zeros(src.rows,src.cols,CV_8UC1);
IMGCH1<PIXUC1> Cannyedge_(src.rows,src.cols,PIXUC1(0));
MyCanny MyCanny_;
MyCanny_(src_,Cannyedge_,10,100,3);//检测
//转换回Mat显示
for(int row=0;row<src_.rows;row++)
for(int col=0;col<src_.cols;col++)
Cannyedge.at<uchar>(row,col)=Cannyedge_.dataPtr[row*src.cols+col];
imshow("Cannyedge ",Cannyedge);
waitKey(0);
}