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


  • 相关阅读:
    [翻译] AAPullToRefresh
    [翻译] DKTagCloudView
    【转】Data URL和图片,及Data URI的利弊
    【转】C#获取当前日期时间(转)
    【转】Android的setTag
    【转】Android之Adapter用法总结
    【转】jpg png区别和使用
    【转】Android UI开发第三十一篇——Android的Holo Theme
    【转】android中的Style与Theme
    【转】关于Eclipse创建Android项目时,会多出一个appcompat_v7的问题
  • 原文地址:https://www.cnblogs.com/zhubaohua-bupt/p/7182786.html
Copyright © 2020-2023  润新知