zoukankan      html  css  js  c++  java
  • OpenCV2马拉松第17圈——边缘检測(Canny边缘检測)

    计算机视觉讨论群162501053
    转载请注明:http://blog.csdn.net/abcd1992719g


    收入囊中

    • 利用OpenCV Canny函数进行边缘检測
    • 掌握Canny算法基本理论
    • 分享Java的实现


    葵花宝典
    在此之前,我们先阐述一下canny检測的算法.总共分为4部分.
    (1)处理噪声
    一般用高斯滤波.OpenCV使用例如以下核
    K = dfrac{1}{159}egin{bmatrix}          2 & 4 & 5 & 4 & 2 \          4 & 9 & 12 & 9 & 4 \          5 & 12 & 15 & 12 & 5 \          4 & 9 & 12 & 9 & 4 \          2 & 4 & 5 & 4 & 2                  end{bmatrix}

    (2)计算梯度幅值
    先用例如以下Sobel算子计算出水平和竖直梯度
    G_{x} = egin{bmatrix}-1 & 0 & +1  \-2 & 0 & +2  \-1 & 0 & +1end{bmatrix}G_{y} = egin{bmatrix}-1 & -2 & -1  \0 & 0 & 0  \+1 & +2 & +1end{bmatrix}


    然后计算每一个点的梯度幅值和方向   
    egin{array}{l}G = sqrt{ G_{x}^{2} + G_{y}^{2} } \	heta = arctan(dfrac{ G_{y} }{ G_{x} })end{array}
    方向最后选4个 (0, 45, 90 or 135)

    (3)非极大值抑制
    (图片来自http://blog.csdn.net/likezhaobin/article/details/6892176)
    以下的蓝色字体分析也来自http://blog.csdn.net/likezhaobin/article/details/6892176  很感谢
    图像梯度幅值矩阵中的元素值越大,说明图像中该点的梯度值越大,但这不不能说明该点就是边缘(这不过属于图像增强的过程)。在Canny算法中,非极大值抑制是进行边缘检測的重要步骤,通俗意义上是指寻找像素点局部最大值,将非极大值点所相应的灰度值置为0,这样能够剔除掉一大部分非边缘的点。

    依据上图可知。要进行非极大值抑制,就首先要确定像素点C的灰度值在其8值邻域内是否为最大。图1中蓝色的线条方向为C点的梯度方向。这样就能够确定其局部的最大值肯定分布在这条线上,也即出了C点外,梯度方向的交点dTmp1和dTmp2这两个点的值也可能会是局部最大值。因此。推断C点灰度与这两个点灰度大小就可以推断C点是否为其邻域内的局部最大灰度点。假设经过推断,C点灰度值小于这两个点中的任一个。那就说明C点不是局部极大值。那么则能够排除C点为边缘。把C的灰度值改为0,假设C是极大值。能够设为128。

    这就是非极大值抑制的工作原理。



    但实际上,我们仅仅能得到C点邻域的8个点的值,而dTmp1和dTmp2并不在当中,要得到这两个值就须要对该两个点两端的已知灰度进行线性插值,也即依据图1中的g1和g2对dTmp1进行插值。依据g3和g4对dTmp2进行插值,这要用到其梯度方向,这是上文Canny算法中要求解梯度方向矩阵theta的原因。

    我相信上面的解释很明确了。这也是为什么我们要选0。45,90,135四个方向的原因

    1.         ////////////////////第一种情况///////////////////////  
    2.         /////////       g1  g2                  /////////////  
    3.         /////////           C                   /////////////  
    4.         /////////           g3  g4              /////////////  
    5.         /////////////////////////////////////////////////////  

    6.            
    7.         ////////////////////另外一种情况///////////////////////  
    8.         /////////       g1                      /////////////  
    9.         /////////       g2  C   g3              /////////////  
    10.         /////////               g4              /////////////  
    11.         /////////////////////////////////////////////////////  

    12.              
    13.         ////////////////////第三种情况///////////////////////  
    14.         /////////           g1  g2              /////////////  
    15.         /////////           C                   /////////////  
    16.         /////////       g4  g3                  /////////////  
    17.         /////////////////////////////////////////////////////  
    18.         
    19.               
    20.         ////////////////////第四种情况///////////////////////  
    21.         /////////               g1              /////////////  
    22.         /////////       g4  C   g2              /////////////  
    23.         /////////       g3                      /////////////  
    24.         /////////////////////////////////////////////////////  

    (4)双阀值检測

    在上个步骤中,产生了梯度幅值, (upper and lower):

    假设一个像素点的梯度值大于upper,则是边界

    假设一个像素点的梯度值小于lower,则不是边界

    假设介于两者之间,仅当这个点和边界点连通才会被觉得是边界点

    依据高阈值得到一个边缘图像,这样一个图像含有非常少的假边缘。可是因为阈值较高。产生的图像边缘可能不闭合。为解决这样一个问题採用了另外一个低阈值。在高阈值图像中把边缘链接成轮廓,当到达轮廓的端点时。该算法会在断点的8邻域点中寻找满足低阈值的点。再依据此点收集新的边缘,直到整个图像边缘闭合。

    Canny推荐upper:lower 的比例为  2:1 或者 3:1.

    小提示:由于,所以最高阀值不是255而是360!还有处于[lower,upper]的点检測是否是边界肯定要放在最后一步。也就是[0,lower),(upper,360]的点都处理完再处理。这样才干判连通!




    初识API
    C++: void Canny(InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false )
     
    • image – 单通道8比特图像
    • edges – 输出图像。和src有同样的大小类型
    • threshold1 – 低阀值
    • threshold2 – 高阀值
    • apertureSize – Sobel算子的大小
    • L2gradient – 一个标识位,一旦设置就启用更准确的 L_2 norm =sqrt{(dI/dx)^2 + (dI/dy)^2} 去计算图像梯度,默认使用L_1 norm =|dI/dx|+|dI/dy|


    荷枪实弹
    我们就来看看非常easy的例子程序吧
    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include <iostream>
    
    using namespace cv;
    
    /// Global variables
    Mat src, src_gray;
    Mat dst, detected_edges;
    
    int lowThreshold;
    int const max_lowThreshold = 100;
    int ratio = 3;
    int kernel_size = 3;
    const char* window_name = "Edge Map";
    
    static void CannyThreshold(int, void*)
    {
        blur( src_gray, detected_edges, Size(3,3) );
        Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
        dst = Scalar::all(0);
        //detected_edges是mask,仅仅有detected_edges被置上(边缘)。才会从原始彩色图像copy到dot中,所以展示的是彩色边缘
        src.copyTo( dst, detected_edges);
        imshow( window_name, dst );
    }
    
    int main( int, char** argv )
    {
      src = imread( argv[1] );
      dst.create( src.size(), src.type() );
      cvtColor( src, src_gray, CV_BGR2GRAY );
    
      namedWindow( window_name, CV_WINDOW_AUTOSIZE );
      createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold );
    
      CannyThreshold(0, 0);
    
      waitKey(0);
      return 0;
    }

    效果图:



    举一反三


    C的实现http://blog.csdn.net/likezhaobin/article/details/6892629

    以下是一个Java实现,曾经是在windows上写的。如今在mac里怎么编码有问题了...

    public int[][] CannyEdgeDetect(int[][] oldmat)
    	{
    		
    		int[][] tempI = gaussFilter(oldmat); 
    		
    		//////////////////Õ¨—˘ø…“‘”√≤ªÕ¨µƒºÏ≤‚∆˜/////////////////////////  
    		/////    P[i,j]=(S[i,j+1]-S[i,j]+S[i+1,j+1]-S[i+1,j])/2     /////  
    		/////    Q[i,j]=(S[i,j]-S[i+1,j]+S[i,j+1]-S[i+1,j+1])/2     /////  
    		/////////////////////////////////////////////////////////////////  
    		float[][] P = new float[height][width];                 //xœÚ∆´µº ˝  
    		float[][] Q = new float[height][width];                 //yœÚ∆´µº ˝  
    		int[][] M = new int[height][width];                       //Û∂»∑˘÷µ  
    		float[][] Theta = new float[height][width];             //Û∂»∑ΩœÚ  
    		//º∆À„x,y∑ΩœÚµƒ∆´µº ˝  
    		for(int i=0; i<(width-1); i++)  
    		{  
    			for(int j=0; j<(height-1); j++)  
    			{  
    				P[j][i] = (float)(tempI[j][Math.min(i+1, width-1)] - tempI[j][i] + tempI[Math.min(j+1, height-1)][Math.min(i+1, width-1)] - tempI[Math.min(j+1, height-1)][i])/2;  
    				Q[j][i] = (float)(tempI[j][i] - tempI[Math.min(j+1, height-1)][i] + tempI[j][Math.min(i+1, width-1)] - tempI[Math.min(j+1, height-1)][Math.min(i+1, width-1)])/2; 
    				
    			}  
    		}  
    		//º∆À„Û∂»∑˘÷µ∫ÕÛ∂»µƒ∑ΩœÚ  
    		for(int i=0; i<width; i++)  
    		{  
    			for(int j=0; j<height; j++)  
    			{  
    				M[j][i] = (int)(Math.sqrt(P[j][i]*P[j][i] + Q[j][i]*Q[j][i])+0.5); 
    				//System.out.println(M[j][i]);
    				Theta[j][i] = (float) (Math.atan2(Q[j][i], P[j][i]) * 57.3);  
    				if(Theta[j][i] < 0)  
    					Theta[j][i] += 360;              //Ω´’‚∏ˆΩ«∂»◊™ªªµΩ0~360∑∂Œß  
    			}  
    		}  
    		
    		
    		int[][] N = new int[height][width];  			//∑«º´¥Û÷µ“÷÷∆Ω·π˚  
    		int g1=0, g2=0, g3=0, g4=0;                     //”√”⁄Ω¯––≤Â÷µ£¨µ√µΩ—«œÒÀÿµ„◊¯±Í÷µ  
    		double dTmp1=0.0, dTmp2=0.0;                    //±£¥Ê¡Ω∏ˆ—«œÒÀÿµ„≤Â÷µµ√µΩµƒª“∂» ˝æ›  
    		double dWeight=0.0;                             //≤Â÷µµƒ»®÷ÿ  
    		
    		//±flΩÁ≥ı ºªØ
    		for(int i=0; i<width; i++)  
    		{  
    		        N[0][i] = 0;  
    		        N[height-1][i] = 0;  
    		}  
    		for(int j=0; j<height; j++)  
    		{  
    		        N[j][0] = 0;  
    		        N[j][width-1] = 0;  
    		}  
    		
    		//Ω¯––æ÷≤ø◊Ó¥Û÷µ—∞’“
    		for(int i=1; i<(width-1); i++)  
    		{  
    		    for(int j=1; j<(height-1); j++)  
    		    {  
    		        if(M[j][i] == 0)  
    		            N[j][i] = 0;         //»Áπ˚µ±«∞Û∂»∑˘÷µŒ™0£¨‘Ú≤ª «æ÷≤ø◊Ó¥Û∂‘∏√µ„∏≥Œ™0  
    		        else  
    		        {  
    		        //////// ◊œ»≈–∂œ Ù”⁄ƒ«÷÷«Èøˆ£¨»ª∫Û∏˘æ›«Èøˆ≤Â÷µ///////  
    		        ////////////////////µ⁄“ª÷÷«Èøˆ///////////////////////  
    		        /////////       g1  g2                  /////////////  
    		        /////////           C                   /////////////  
    		        /////////           g3  g4              /////////////  
    		        /////////////////////////////////////////////////////  
    		        if( ((Theta[j][i]>=90)&&(Theta[j][i]<135)) ||   
    		                ((Theta[j][i]>=270)&&(Theta[j][i]<315)))  
    		            {  
    		                //////∏˘æ›–±¬ ∫ÕÀƒ∏ˆ÷–º‰÷µΩ¯––≤Â÷µ«ÛΩ‚  
    		                g1 = M[j-1][i-1];  
    		                g2 = M[j-1][i];  
    		                g3 = M[j+1][i];  
    		                g4 = M[j+1][i+1];  
    		                dWeight = Math.abs(P[j][i])/Math.abs(Q[j][i]);   //∑¥’˝«–  
    		                dTmp1 = g1*dWeight+g2*(1-dWeight);  
    		                dTmp2 = g4*dWeight+g3*(1-dWeight);  
    		            }  
    		        ////////////////////µ⁄∂˛÷÷«Èøˆ///////////////////////  
    		        /////////       g1                      /////////////  
    		        /////////       g2  C   g3              /////////////  
    		        /////////               g4              /////////////  
    		        /////////////////////////////////////////////////////  
    		            else if( ((Theta[j][i]>=135)&&(Theta[j][i]<180)) ||   
    		                ((Theta[j][i]>=315)&&(Theta[j][i]<360)))  
    		            {  
    		                g1 = M[j-1][i-1];  
    		                g2 = M[j][i-1];  
    		                g3 = M[j][i+1];  
    		                g4 = M[j+1][i+1];  
    		                dWeight = Math.abs(Q[j][i])/Math.abs(P[j][i]);   //’˝«–  
    		                dTmp1 = g2*dWeight+g1*(1-dWeight);  
    		                dTmp2 = g4*dWeight+g3*(1-dWeight);  
    		            }  
    		        ////////////////////µ⁄»˝÷÷«Èøˆ///////////////////////  
    		        /////////           g1  g2              /////////////  
    		        /////////           C                   /////////////  
    		        /////////       g4  g3                  /////////////  
    		        /////////////////////////////////////////////////////  
    		            else if( ((Theta[j][i]>=45)&&(Theta[j][i]<90)) ||   
    		                ((Theta[j][i]>=225)&&(Theta[j][i]<270)))  
    		            {  
    		                g1 = M[j-1][i];  
    		                g2 = M[j-1][i+1];  
    		                g3 = M[j+1][i];  
    		                g4 = M[j+1][i-1];  
    		                dWeight = Math.abs(P[j][i])/Math.abs(Q[j][i]);   //∑¥’˝«–  
    		                dTmp1 = g2*dWeight+g1*(1-dWeight);  
    		                dTmp2 = g3*dWeight+g4*(1-dWeight);  
    		            }  
    		            ////////////////////µ⁄Àƒ÷÷«Èøˆ///////////////////////  
    		            /////////               g1              /////////////  
    		            /////////       g4  C   g2              /////////////  
    		            /////////       g3                      /////////////  
    		            /////////////////////////////////////////////////////  
    		            else if( ((Theta[j][i]>=0)&&(Theta[j][i]<45)) ||   
    		                ((Theta[j][i]>=180)&&(Theta[j][i]<225)))  
    		            {  
    		                g1 = M[j-1][i+1];  
    		                g2 = M[j][i+1];  
    		                g3 = M[j+1][i-1];  
    		                g4 = M[j][i-1];  
    		                dWeight = Math.abs(Q[j][i])/Math.abs(P[j][i]);   //’˝«–  
    		                dTmp1 = g1*dWeight+g2*(1-dWeight);  
    		                dTmp2 = g3*dWeight+g4*(1-dWeight);  
    		            }  
    		        }         
    		        //////////Ω¯––æ÷≤ø◊Ó¥Û÷µ≈–∂œ£¨≤¢–¥»ÎºÏ≤‚Ω·π˚////////////////  
    		        if((M[j][i]>=dTmp1) && (M[j][i]>=dTmp2))  
    		            N[j][i] = 128;  
    		        else  
    		            N[j][i] = 0;  
    		        
    		        //System.out.println(N[j][i]);
    		        }  
    		}  
    		
    		
    		//À´∑ß÷µºÏ≤‚ µœ÷
    		int []nHist = new int[1024];   
    		int nEdgeNum;             //ø…ƒ‹±flΩÁ ˝  
    		int nMaxMag = 0;          //◊Ó¥ÛÛ∂» ˝  
    		int nHighCount;  
    		
    		//Õ≥º∆÷±∑ΩÕº
    		for(int i=0;i<1024;i++)  
    	        nHist[i] = 0;  
    		for(int i=0; i<width; i++)  
    		{  
    	        for(int j=0; j<height; j++)  
    	        {  
    	              if(N[j][i]==128)  
    	                   nHist[M[j][i]]++;  
    	        }  
    		}  
    		
    		//ªÒ»°◊Ó¥ÛÛ∂»∑˘÷µº∞«±‘⁄±fl‘µµ„∏ˆ ˝
    		nEdgeNum = nHist[0];  
    		nMaxMag = 0;                    //ªÒ»°◊Ó¥ÛµƒÃ›∂»÷µ        
    		for(int i=1; i<1024; i++)           //Õ≥º∆æ≠π˝°∞∑«◊Ó¥Û÷µ“÷÷∆°±∫Û”–∂‡…ŸœÒÀÿ  
    		{  
    		    if(nHist[i] != 0)       //Û∂»Œ™0µƒµ„ «≤ªø…ƒ‹Œ™±flΩÁµ„µƒ  
    		    {  
    		        nMaxMag = i;  
    		    }     
    		    nEdgeNum += nHist[i];   //æ≠π˝non-maximum suppression∫Û”–∂‡…ŸœÒÀÿ  
    		}  
    		
    		//º∆À„¡Ω∏ˆ∑ß÷µ
    		float  dRatHigh = 0.79f;  
    		float  dThrHigh;  
    		float  dThrLow;  
    		float  dRatLow = 0.5f;  
    		nHighCount = (int)(dRatHigh * nEdgeNum + 0.5);  
    		int k = 1;  
    		nEdgeNum = nHist[1];  
    		while((k<(nMaxMag-1)) && (nEdgeNum < nHighCount))  
    		{  
    		       k++;  
    		       nEdgeNum += nHist[k];  
    		}  
    		dThrHigh = k;                                   //∏fl„–÷µ  
    		dThrLow = (int)((dThrHigh) * dRatLow + 0.5);    //µÕ„–÷µ  
    		System.out.println("high = " + dThrHigh + "low = " + dThrLow);
    		//dThrHigh = 10;
    		//dThrLow = 5;
    		
    		
    		//Ω¯––±fl‘µºÏ≤‚ 
    		int cx = width;  
    		int cy = height;  
    		for(int i=0; i<width; i++)  
    		{  
    		    for(int j=0; j<height; j++)  
    		    {  
    		        if((N[j][i]==128) && (M[j][i] >= dThrHigh))  
    		        {  
    		            N[j][i] = 255;  
    		            TraceEdge(j, i, (int)(dThrLow+0.5), N, M);  
    		        }  
    		    }  
    		}  
    		
    		//Ω´ªπ√ª”–…Ë÷√Œ™±flΩÁµƒµ„…Ë÷√Œ™∑«±flΩÁµ„  
    		for(int i=0; i<width; i++)  
    		{  
    		    for(int j=0; j<height; j++)  
    		    {  
    		        if(N[j][i] != 255)  
    		        {  
    		            N[j][i]  = 0 ;   // …Ë÷√Œ™∑«±flΩÁµ„  
    		        }  
    		        N[j][i] = N[j][i] + (N[j][i] << 8) + (N[j][i] << 16);
    		    }  
    		}  
    		
    		
    		return N;
    		
    	}
    	
    	/*
    	 * cancy ∏®÷˙∫Ø ˝
    	 */
    	private void TraceEdge(int y, int x, int nThrLow, int[][] pResult, int[][] pMag)  
    	{  
    	    //∂‘8¡⁄”ÚœÒÀÿΩ¯––≤È—Ø  
    	    int[] xNum = {1,1,0,-1,-1,-1,0,1};  
    	    int[] yNum = {0,1,1,1,0,-1,-1,-1};  
    	    int yy,xx,k;  
    	    for(k=0;k<8;k++)  
    	    {  
    	        yy = y+yNum[k];  
    	        xx = x+xNum[k];  
    	        if(pResult[yy][xx]==128 && pMag[yy][xx]>=nThrLow )  
    	        {  
    	            //∏√µ„…ËŒ™±flΩÁµ„  
    	            pResult[yy][xx] = 255;  
    	            //“‘∏√µ„Œ™÷––ƒ‘ŸΩ¯––∏˙◊Ÿ  
    	            TraceEdge(yy,xx,nThrLow,pResult,pMag);  
    	        }  
    	    }  
    	}  


    初识API
  • 相关阅读:
    Python笔记_函数,模块,包的关系
    「GXOI / GZOI2019」宝牌一大堆
    「BalticOI 2020」村庄 (贪心)
    CF Round #635 Div.1 Chiori and Doll Picking (hard version)
    「BalticOI 2020」病毒
    「BalticOI 2020」小丑
    「BalticOI 2020」混合物
    k短路
    「JSOI2019」神经网络
    「NOI2020」时代的眼泪
  • 原文地址:https://www.cnblogs.com/yxysuanfa/p/7133737.html
Copyright © 2011-2022 走看看