Canny边缘检测原理与C++实现(2)实现部分

转载请说明出处:

  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); 
}


原文地址:https://www.cnblogs.com/zhubaohua-bupt/p/7182786.html