轉載請說明出處:ios
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.hspa
#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.net
#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); }