计算机视觉讨论群162501053
转载请注明:http://blog.csdn.net/abcd1992719g
收入囊中
- 利用OpenCV Canny函数进行边缘检測
- 掌握Canny算法基本理论
- 分享Java的实现
葵花宝典
在此之前,我们先阐述一下canny检測的算法.总共分为4部分.
(1)处理噪声
一般用高斯滤波.OpenCV使用例如以下核
(2)计算梯度幅值
先用例如以下Sobel算子计算出水平和竖直梯度
我在OpenCV2马拉松第14圈——边缘检測(Sobel,prewitt,roberts)具体介绍了Sobel算子
然后计算每一个点的梯度幅值和方向
方向最后选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四个方向的原因
- ////////////////////第一种情况///////////////////////
- ///////// g1 g2 /////////////
- ///////// C /////////////
- ///////// g3 g4 /////////////
- /////////////////////////////////////////////////////
- ////////////////////另外一种情况///////////////////////
- ///////// g1 /////////////
- ///////// g2 C g3 /////////////
- ///////// g4 /////////////
- /////////////////////////////////////////////////////
- ////////////////////第三种情况///////////////////////
- ///////// g1 g2 /////////////
- ///////// C /////////////
- ///////// g4 g3 /////////////
- /////////////////////////////////////////////////////
- ////////////////////第四种情况///////////////////////
- ///////// g1 /////////////
- ///////// g4 C g2 /////////////
- ///////// g3 /////////////
- /////////////////////////////////////////////////////
(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 – 一个标识位,一旦设置就启用更准确的 norm 去计算图像梯度,默认使用 norm
荷枪实弹
我们就来看看非常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); //////////////////?¨—??…“‘”√≤a?¨μ?o?≤???///////////////////////// ///// 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?ú?′μo ? float[][] Q = new float[height][width]; //y?ú?′μo ? int[][] M = new int[height][width]; //????∑?÷μ float[][] Theta = new float[height][width]; //????∑Ω?ú //o?à?x,y∑Ω?úμ??′μo ? 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; } } //o?à?????∑?÷μ∫?????μ?∑Ω?ú 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; //Ω′’?∏?Ω?????aaμΩ0~360∑??? } } int[][] N = new int[height][width]; //∑?o′¥?÷μ“÷÷?Ω·π? int g1=0, g2=0, g3=0, g4=0; //”√”?Ωˉ––≤?÷죨μ√μΩ—??òà?μ??ˉ±í÷μ double dTmp1=0.0, dTmp2=0.0; //±£¥ê?Ω∏?—??òà?μ?≤?÷μμ√μΩμ?a“?? ??? double dWeight=0.0; //≤?÷μμ???÷? //±?Ωá≥? oa? 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£¨‘ú≤a ??÷≤??ó¥??‘∏√μ?∏≥??0 else { //////// ???≈–?? ù”???÷÷?è??£¨?a∫?∏????è??≤?÷μ/////// ////////////////////μ?“a÷÷?è??/////////////////////// ///////// g1 g2 ///////////// ///////// C ///////////// ///////// g3 g4 ///////////// ///////////////////////////////////////////////////// if( ((Theta[j][i]>=90)&&(Theta[j][i]<135)) || ((Theta[j][i]>=270)&&(Theta[j][i]<315))) { //////∏???–±? ∫?à?∏?÷–o‰÷μΩˉ––≤?÷μ??Ω? 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); } } //////////Ωˉ––?÷≤??ó¥?÷μ≈–??£¨≤¢–¥??o?≤?Ω·π?//////////////// if((M[j][i]>=dTmp1) && (M[j][i]>=dTmp2)) N[j][i] = 128; else N[j][i] = 0; //System.out.println(N[j][i]); } } //à′∑?÷μo?≤? μ?÷ int []nHist = new int[1024]; int nEdgeNum; //?…??±?Ωá ? int nMaxMag = 0; //?ó¥????? ? int nHighCount; //?≥o?÷±∑Ω?o 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]]++; } } //aò?°?ó¥?????∑?÷μo∞?±‘?±?‘μμ?∏? ? nEdgeNum = nHist[0]; nMaxMag = 0; //aò?°?ó¥?μ?????÷μ for(int i=1; i<1024; i++) //?≥o??≠π?°∞∑??ó¥?÷μ“÷÷?°±∫?”–??…??òà? { if(nHist[i] != 0) //??????0μ?μ? ?≤a?…????±?Ωáμ?μ? { nMaxMag = i; } nEdgeNum += nHist[i]; //?≠π?non-maximum suppression∫?”–??…??òà? } //o?à??Ω∏?∑?÷μ 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; //∏??–÷μ dThrLow = (int)((dThrHigh) * dRatLow + 0.5); //μ??–÷μ System.out.println("high = " + dThrHigh + "low = " + dThrLow); //dThrHigh = 10; //dThrLow = 5; //Ωˉ––±?‘μo?≤? 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); } } } //Ω′aπ√a”–…?÷√??±?Ωáμ?μ?…?÷√??∑?±?Ωáμ? for(int i=0; i<width; i++) { for(int j=0; j<height; j++) { if(N[j][i] != 255) { N[j][i] = 0 ; // …?÷√??∑?±?Ωáμ? } 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 ) { //∏√μ?…???±?Ωáμ? pResult[yy][xx] = 255; //“‘∏√μ???÷––?‘?Ωˉ––∏˙?? TraceEdge(yy,xx,nThrLow,pResult,pMag); } } }
初识API