Canny邊緣檢測算法原理及其VC實現詳解(二)

3、? Canny算法的實現流程

?? ? ? 由于本文主要目的在于學習和實現算法,而對于圖像讀取、視頻獲取等內容不進行闡述。因此選用OpenCV算法庫作為其他功能的實現途徑(關于OpenCV的使用,作者將另文表述)。首先展現本文將要處理的彩色圖片。


圖2 待處理的圖像

3.1 圖像讀取和灰度化

?? ? ??編程時采用上文所描述的第二種方法來實現圖像的灰度化。其中ptr數組中保存的灰度化后的圖像數據。具體的灰度化后的效果如圖3所示。

  1. IplImage*?ColorImage?=?cvLoadImage(?"12.jpg",?-1?);???//讀入圖像,獲取彩圖指針??
  2. IplImage*?OpenCvGrayImage;????????????????????????????//定義變換后的灰度圖指針??
  3. unsigned?char*?ptr;???????????????????????????????????//指向圖像的數據首地址??
  4. if?(ColorImage?==?NULL)??
  5. ?????return;????????
  6. int?i?=?ColorImage->width?*?ColorImage->height;?????????
  7. BYTE?data1;???????//中間過程變量??
  8. BYTE?data2;??
  9. BYTE?data3;??
  10. ptr?=?new?unsigned?char[i];??
  11. for(intj=0;?j<ColorImage->height;?j++)?????????????????//對RGB加權平均,權值參考OpenCV??
  12. {??
  13. ?????for(intx=0;?x<ColorImage->width;?x++)??
  14. ?????{??
  15. ?????????data1?=?(BYTE)ColorImage->imageData[j*ColorImage->widthStep?+?i*3];?????//B分量??
  16. ?????data2?=?(BYTE)ColorImage->imageData[j*ColorImage->widthStep?+?i*3?+?1];?//G分量??
  17. ?????data3?=?(BYTE)ColorImage->imageData[j*ColorImage->widthStep?+?i*3?+?2];?//R分量??
  18. ?????????ptr[j*ColorImage->width+x]=(BYTE)(0.072169*data1?+?0.715160*data2?+?0.212671*data3);??
  19. ?????}??
  20. }??
  21. OpenCvGrayImage=cvCreateImageHeader(cvGetSize(ColorImage),?ColorImage->depth,?1);????
  22. cvSetData(GrayImage,ptr,?GrayImage->widthStep);?????????//根據數據生成灰度圖??
  23. cvNamedWindow("GrayImage",CV_WINDOW_AUTOSIZE);??
  24. cvShowImage("GrayImage",OpenCvGrayImage);???????????????//顯示灰度圖??
  25. cvWaitKey(0);??
  26. cvDestroyWindow("GrayImage");??

圖3 灰度化后的圖像

3.2 圖像的高斯濾波

?? ? ??根據上面所講的邊緣檢測過程,下一個步驟就是對圖像進行高斯濾波。可根據之前博文描述的方法獲取一維或者二維的高斯濾波核。因此進行圖像高斯濾波可有兩種實現方式,以下具體進行介紹。

?? ? ??首先定義該部分的通用變量:
  1. double?nSigma?=?0.4;????????????????????????????//定義高斯函數的標準差??
  2. int?nWidowSize?=?1+2*ceil(3*nSigma);????????????//定義濾波窗口的大小??
  3. int?nCenter?=?(nWidowSize)/2;???????????????????//定義濾波窗口中心的索引??
?? ? ? 兩種方法都需要用到的變量:
  1. int?nWidth?=?OpenCvGrayImage->width;?????????????????????????????//獲取圖像的像素寬度??
  2. int?nHeight?=?OpenCvGrayImage->height;???????????????????????????//獲取圖像的像素高度??
  3. unsigned?char*?nImageData?=?new?unsigned?char[nWidth*nHeight];???//暫時保存圖像中的數據??
  4. unsigned?char*pCanny?=?new?unsigned?char[nWidth*nHeight];????????//為平滑后的圖像數據分配內存??
  5. double*?nData?=?new?double[nWidth*nHeight];??????????????????????//兩次平滑的中間數據??
  6. for(int?j=0;?j<nHeight;?j++)?????????????????????????????????????//獲取數據??
  7. {??
  8. ????for(i=0;?i<nWidth;?i++)??
  9. ?????????????nImageData[j*nWidth+i]?=?(unsigned?char)OpenCvGrayImage->imageData[j*nWidth+i];??
  10. }??

3.2.1 根據一維高斯核進行兩次濾波

?? ? ??1)生成一維高斯濾波系數

  1. //生成一維高斯濾波系數/??
  2. double*?pdKernal_1?=?new?double[nWidowSize];????//定義一維高斯核數組??
  3. double??dSum_1?=?0.0;???????????????????????????//求和,用于進行歸一化??????????
  4. 一維高斯函數公式//???????
  5. ???????????????????x*x???????????????????????????/??
  6. ??????????-1*----------------????????????????????/??
  7. ?????????1?????2*Sigma*Sigma?????????????????????/??
  8. ???------------?e????????????????????????????????/??
  9. ?????????????????????????????????????????????????/??
  10. ???\/2*pi*Sigma??????????????????????????????????/??
  11. //??
  12. for(int?i=0;?i<nWidowSize;?i++)??
  13. {??
  14. ????????double?nDis?=?(double)(i-nCenter);??
  15. ????pdKernal_1[i]?=?exp(-(0.5)*nDis*nDis/(nSigma*nSigma))/(sqrt(2*3.14159)*nSigma);??
  16. ????dSum_1?+=?pdKernal_1[i];??
  17. }??
  18. for(i=0;?i<nWidowSize;?i++)??
  19. {??
  20. ????pdKernal_1[i]?/=?dSum_1;?????????????????//進行歸一化??
  21. }??

?? ? ? 2)分別進行x向和y向的一維加權濾波,濾波后的數據保存在矩陣pCanny中
  1. for(i=0;?i<nHeight;?i++)???????????????????????????????//進行x向的高斯濾波(加權平均)??
  2. {??
  3. ????for(j=0;?j<nWidth;?j++)??
  4. ????{??
  5. ????????double?dSum?=?0;??
  6. ????????double?dFilter=0;???????????????????????????????????????//濾波中間值??
  7. ????????for(int?nLimit=(-nCenter);?nLimit<=nCenter;?nLimit++)??
  8. ????????{??
  9. ????????????if((j+nLimit)>=0?&&?(j+nLimit)?<?nWidth?)???????//圖像不能超出邊界??
  10. ????????????{??
  11. ????????????????dFilter?+=?(double)nImageData[i*nWidth+j+nLimit]?*?pdKernal_1[nCenter+nLimit];??
  12. ????????????????dSum?+=?pdKernal_1[nCenter+nLimit];??
  13. ????????????}??
  14. ????????}??
  15. ????????nData[i*nWidth+j]?=?dFilter/dSum;??
  16. ????}??
  17. }??
  18. ??
  19. for(i=0;?i<nWidth;?i++)????????????????????????????????//進行y向的高斯濾波(加權平均)??
  20. {??
  21. ????for(j=0;?j<nHeight;?j++)??
  22. ????{??
  23. ????????double?dSum?=?0.0;??
  24. ????????double?dFilter=0;??
  25. ????????for(int?nLimit=(-nCenter);?nLimit<=nCenter;?nLimit++)??
  26. ????????{??
  27. ????????????if((j+nLimit)>=0?&&?(j+nLimit)?<?nHeight)???????//圖像不能超出邊界??
  28. ????????????{??
  29. ????????????????dFilter?+=?(double)nData[(j+nLimit)*nWidth+i]?*?pdKernal_1[nCenter+nLimit];??
  30. ????????????????dSum?+=?pdKernal_1[nCenter+nLimit];??
  31. ????????????}??
  32. ????????}??
  33. ????????pCanny[j*nWidth+i]?=?(unsigned?char)(int)dFilter/dSum;??
  34. ????}??
  35. }??

3.2.2?根據二維高斯核進行濾波

?? ? ?1)生成二維高斯濾波系數

  1. //生成一維高斯濾波系數//????
  2. double*?pdKernal_2?=?new?double[nWidowSize*nWidowSize];?//定義一維高斯核數組??
  3. double??dSum_2?=?0.0;???????????????????????????????????//求和,進行歸一化????????
  4. ///二維高斯函數公式??????
  5. ?????????????????????????x*x+y*y????????????????????????///??
  6. ???????????????????-1*--------------????????????????///??
  7. ?????????1?????????????2*Sigma*Sigma????????????????///??
  8. ???----------------?e???????????????????????????????????///??
  9. ???2*pi*Sigma*Sigma?????????????????????????????????????///??
  10. ///??
  11. for(i=0;?i<nWidowSize;?i++)??
  12. {??
  13. ????for(int?j=0;?j<nWidowSize;?j++)??
  14. ????{??
  15. ????????int?nDis_x?=?i-nCenter;??
  16. ????????int?nDis_y?=?j-nCenter;??
  17. ????????pdKernal_2[i+j*nWidowSize]=exp(-(1/2)*(nDis_x*nDis_x+nDis_y*nDis_y)??
  18. ????????????/(nSigma*nSigma))/(2*3.1415926*nSigma*nSigma);??
  19. ????????dSum_2?+=?pdKernal_2[i+j*nWidowSize];??
  20. ????}??
  21. }??
  22. for(i=0;?i<nWidowSize;?i++)??
  23. {??
  24. ????for(int?j=0;?j<nWidowSize;?j++)?????????????????//進行歸一化??
  25. ????????{??
  26. ????????pdKernal_2[i+j*nWidowSize]?/=?dSum_2;??
  27. ????}??
  28. }??

?? ? ?2)采用高斯核進行高斯濾波,濾波后的數據保存在矩陣pCanny中
  1. int?x;??
  2. int?y;??
  3. for(i=0;?i<nHeight;?i++)??
  4. {??
  5. ????for(j=0;?j<nWidth;?j++)??
  6. ????{??
  7. ????????double?dFilter=0.0;??
  8. ????????double?dSum?=?0.0;??
  9. ????????for(x=(-nCenter);?x<=nCenter;?x++)?????????????????????//行??
  10. ????????{??
  11. ????????????????????????for(y=(-nCenter);?y<=nCenter;?y++)?????????????//列??
  12. ????????????{??
  13. ????????????????if(?(j+x)>=0?&&?(j+x)<nWidth?&&?(i+y)>=0?&&?(i+y)<nHeight)?//判斷邊緣??
  14. ????????????????{??
  15. ????????????????????dFilter?+=?(double)nImageData?[(i+y)*nWidth?+?(j+x)]??
  16. ????????????????????????*?pdKernal_2[(y+nCenter)*nWidowSize+(x+nCenter)];??
  17. ????????????????????dSum?+=?pdKernal_2[(y+nCenter)*nWidowSize+(x+nCenter)];??
  18. ????????????????}??
  19. ????????????}??
  20. ????????}??
  21. ????????pCanny[i*nWidth+j]?=?(unsigned?char)dFilter/dSum;??
  22. ????}??
  23. }??

3.3 圖像增強——計算圖像梯度及其方向
?? ? ?根據上文分析可知,實現代碼如下
  1. //同樣可以用不同的檢測器/??
  2. /????P[i,j]=(S[i,j+1]-S[i,j]+S[i+1,j+1]-S[i+1,j])/2?????/??
  3. /????Q[i,j]=(S[i,j]-S[i+1,j]+S[i,j+1]-S[i+1,j+1])/2?????/??
  4. /??
  5. double*?P?=?new?double[nWidth*nHeight];?????????????????//x向偏導數??
  6. double*?Q?=?new?double[nWidth*nHeight];?????????????????//y向偏導數??
  7. int*?M?=?new?int[nWidth*nHeight];???????????????????????//梯度幅值??
  8. double*?Theta?=?new?double[nWidth*nHeight];?????????????//梯度方向??
  9. //計算x,y方向的偏導數??
  10. for(i=0;?i<(nHeight-1);?i++)??
  11. {??
  12. ????????for(j=0;?j<(nWidth-1);?j++)??
  13. ????????{??
  14. ??????????????P[i*nWidth+j]?=?(double)(pCanny[i*nWidth?+?min(j+1,?nWidth-1)]?-?pCanny[i*nWidth+j]?+?pCanny[min(i+1,?nHeight-1)*nWidth+min(j+1,?nWidth-1)]?-?pCanny[min(i+1,?nHeight-1)*nWidth+j])/2;??
  15. ??????????????Q[i*nWidth+j]?=?(double)(pCanny[i*nWidth+j]?-?pCanny[min(i+1,?nHeight-1)*nWidth+j]?+?pCanny[i*nWidth+min(j+1,?nWidth-1)]?-?pCanny[min(i+1,?nHeight-1)*nWidth+min(j+1,?nWidth-1)])/2;??
  16. ????}??
  17. }??
  18. //計算梯度幅值和梯度的方向??
  19. for(i=0;?i<nHeight;?i++)??
  20. {??
  21. ????????for(j=0;?j<nWidth;?j++)??
  22. ????????{??
  23. ??????????????M[i*nWidth+j]?=?(int)(sqrt(P[i*nWidth+j]*P[i*nWidth+j]?+?Q[i*nWidth+j]*Q[i*nWidth+j])+0.5);??
  24. ??????????????Theta[i*nWidth+j]?=?atan2(Q[i*nWidth+j],?P[i*nWidth+j])?*?57.3;??
  25. ??????????????if(Theta[i*nWidth+j]?<?0)??
  26. ????????????????????Theta[i*nWidth+j]?+=?360;??????????????//將這個角度轉換到0~360范圍??
  27. ????}??
  28. }??


3.4 非極大值抑制

?? ? ?根據上文所述的工作原理,這部分首先需要求解每個像素點在其鄰域內的梯度方向的兩個灰度值,然后判斷是否為潛在的邊緣,如果不是則將該點灰度值設置為0.

?? ? ?首先定義相關的參數如下:

  1. unsigned?char*?N?=?new?unsigned?char[nWidth*nHeight];??//非極大值抑制結果??
  2. int?g1=0,?g2=0,?g3=0,?g4=0;????????????????????????????//用于進行插值,得到亞像素點坐標值??
  3. double?dTmp1=0.0,?dTmp2=0.0;???????????????????????????//保存兩個亞像素點插值得到的灰度數據??
  4. double?dWeight=0.0;????????????????????????????????????//插值的權重??
?? ? ?其次,對邊界進行初始化:
  1. for(i=0;?i<nWidth;?i++)??
  2. {??
  3. ????????N[i]?=?0;??
  4. ????????N[(nHeight-1)*nWidth+i]?=?0;??
  5. }??
  6. for(j=0;?j<nHeight;?j++)??
  7. {??
  8. ????????N[j*nWidth]?=?0;??
  9. ????????N[j*nWidth+(nWidth-1)]?=?0;??
  10. }??
?? ? ?進行局部最大值尋找,根據上文圖1所述的方案進行插值,然后判優,實現代碼如下:
  1. for(i=1;?i<(nWidth-1);?i++)??
  2. {??
  3. ????for(j=1;?j<(nHeight-1);?j++)??
  4. ????{??
  5. ????????int?nPointIdx?=?i+j*nWidth;???????//當前點在圖像數組中的索引值??
  6. ????????if(M[nPointIdx]?==?0)??
  7. ????????????N[nPointIdx]?=?0;?????????//如果當前梯度幅值為0,則不是局部最大對該點賦為0??
  8. ????????else??
  9. ????????{??
  10. ????????首先判斷屬于那種情況,然后根據情況插值///??
  11. ????????第一種情況///??
  12. ????????/???????g1??g2??????????????????/??
  13. ????????/???????????C???????????????????/??
  14. ????????/???????????g3??g4??????????????/??
  15. ????????/??
  16. ????????if(?((Theta[nPointIdx]>=90)&&(Theta[nPointIdx]<135))?||???
  17. ????????????????((Theta[nPointIdx]>=270)&&(Theta[nPointIdx]<315)))??
  18. ????????????{??
  19. ????????????????//根據斜率和四個中間值進行插值求解??
  20. ????????????????g1?=?M[nPointIdx-nWidth-1];??
  21. ????????????????g2?=?M[nPointIdx-nWidth];??
  22. ????????????????g3?=?M[nPointIdx+nWidth];??
  23. ????????????????g4?=?M[nPointIdx+nWidth+1];??
  24. ????????????????dWeight?=?fabs(P[nPointIdx])/fabs(Q[nPointIdx]);???//反正切??
  25. ????????????????dTmp1?=?g1*dWeight+g2*(1-dWeight);??
  26. ????????????????dTmp2?=?g4*dWeight+g3*(1-dWeight);??
  27. ????????????}??
  28. ????????第二種情況///??
  29. ????????/???????g1??????????????????????/??
  30. ????????/???????g2??C???g3??????????????/??
  31. ????????/???????????????g4??????????????/??
  32. ????????/??
  33. ????????????else?if(?((Theta[nPointIdx]>=135)&&(Theta[nPointIdx]<180))?||???
  34. ????????????????((Theta[nPointIdx]>=315)&&(Theta[nPointIdx]<360)))??
  35. ????????????{??
  36. ????????????????g1?=?M[nPointIdx-nWidth-1];??
  37. ????????????????g2?=?M[nPointIdx-1];??
  38. ????????????????g3?=?M[nPointIdx+1];??
  39. ????????????????g4?=?M[nPointIdx+nWidth+1];??
  40. ????????????????dWeight?=?fabs(Q[nPointIdx])/fabs(P[nPointIdx]);???//正切??
  41. ????????????????dTmp1?=?g2*dWeight+g1*(1-dWeight);??
  42. ????????????????dTmp2?=?g4*dWeight+g3*(1-dWeight);??
  43. ????????????}??
  44. ????????第三種情況///??
  45. ????????/???????????g1??g2??????????????/??
  46. ????????/???????????C???????????????????/??
  47. ????????/???????g4??g3??????????????????/??
  48. ????????/??
  49. ????????????else?if(?((Theta[nPointIdx]>=45)&&(Theta[nPointIdx]<90))?||???
  50. ????????????????((Theta[nPointIdx]>=225)&&(Theta[nPointIdx]<270)))??
  51. ????????????{??
  52. ????????????????g1?=?M[nPointIdx-nWidth];??
  53. ????????????????g2?=?M[nPointIdx-nWidth+1];??
  54. ????????????????g3?=?M[nPointIdx+nWidth];??
  55. ????????????????g4?=?M[nPointIdx+nWidth-1];??
  56. ????????????????dWeight?=?fabs(P[nPointIdx])/fabs(Q[nPointIdx]);???//反正切??
  57. ????????????????dTmp1?=?g2*dWeight+g1*(1-dWeight);??
  58. ????????????????dTmp2?=?g3*dWeight+g4*(1-dWeight);??
  59. ????????????}??
  60. ????????????第四種情況///??
  61. ????????????/???????????????g1??????????????/??
  62. ????????????/???????g4??C???g2??????????????/??
  63. ????????????/???????g3??????????????????????/??
  64. ????????????/??
  65. ????????????else?if(?((Theta[nPointIdx]>=0)&&(Theta[nPointIdx]<45))?||???
  66. ????????????????((Theta[nPointIdx]>=180)&&(Theta[nPointIdx]<225)))??
  67. ????????????{??
  68. ????????????????g1?=?M[nPointIdx-nWidth+1];??
  69. ????????????????g2?=?M[nPointIdx+1];??
  70. ????????????????g3?=?M[nPointIdx+nWidth-1];??
  71. ????????????????g4?=?M[nPointIdx-1];??
  72. ????????????????dWeight?=?fabs(Q[nPointIdx])/fabs(P[nPointIdx]);???//正切??
  73. ????????????????dTmp1?=?g1*dWeight+g2*(1-dWeight);??
  74. ????????????????dTmp2?=?g3*dWeight+g4*(1-dWeight);??
  75. ????????????}??
  76. ????????}?????????
  77. ????????//進行局部最大值判斷,并寫入檢測結果??
  78. ????????if((M[nPointIdx]>=dTmp1)?&&?(M[nPointIdx]>=dTmp2))??
  79. ????????????N[nPointIdx]?=?128;??
  80. ????????else??
  81. ????????????N[nPointIdx]?=?0;??
  82. ????????}??
  83. }??

3.5雙閾值檢測實現

?? ? ?1)定義相應參數如下

  1. int?nHist[1024];???
  2. int?nEdgeNum;?????????????//可能邊界數??
  3. int?nMaxMag?=?0;??????????//最大梯度數??
  4. int?nHighCount;??

?? ? ?2)構造灰度圖的統計直方圖,根據上文梯度幅值的計算公式可知,最大的梯度幅值為:
?? ? ?因此設置nHist為1024足夠。以下實現統計直方圖:
  1. for(i=0;i<1024;i++)??
  2. ????????nHist[i]?=?0;??
  3. for(i=0;?i<nHeight;?i++)??
  4. {??
  5. ????????for(j=0;?j<nWidth;?j++)??
  6. ????????{??
  7. ??????????????if(N[i*nWidth+j]==128)??
  8. ???????????????????nHist[M[i*nWidth+j]]++;??
  9. ????????}??
  10. }??

?? ? ?3)獲取最大梯度幅值及潛在邊緣點個數

  1. nEdgeNum?=?nHist[0];??
  2. nMaxMag?=?0;????????????????????//獲取最大的梯度值????????
  3. for(i=1;?i<1024;?i++)???????????//統計經過“非最大值抑制”后有多少像素??
  4. {??
  5. ????if(nHist[i]?!=?0)???????//梯度為0的點是不可能為邊界點的??
  6. ????{??
  7. ????????nMaxMag?=?i;??
  8. ????}?????
  9. ????nEdgeNum?+=?nHist[i];???//經過non-maximum?suppression后有多少像素??
  10. }??

?? ? ?4)計算兩個閾值
  1. double??dRatHigh?=?0.79;??
  2. double??dThrHigh;??
  3. double??dThrLow;??
  4. double??dRatLow?=?0.5;??
  5. nHighCount?=?(int)(dRatHigh?*?nEdgeNum?+?0.5);??
  6. j=1;??
  7. nEdgeNum?=?nHist[1];??
  8. while((j<(nMaxMag-1))?&&?(nEdgeNum?<?nHighCount))??
  9. {??
  10. ???????j++;??
  11. ???????nEdgeNum?+=?nHist[j];??
  12. }??
  13. dThrHigh?=?j;???????????????????????????????????//高閾值??
  14. dThrLow?=?(int)((dThrHigh)?*?dRatLow?+?0.5);????//低閾值??

?? ? ?這段代碼的意思是,按照灰度值從低到高的順序,選取前79%個灰度值中的最大的灰度值為高閾值,低閾值大約為高閾值的一半。這是根據經驗數據的來的,至于更好地參數選取方法,作者后面會另文研究。
?? ? ?5)進行邊緣檢測
  1. SIZE?sz;??
  2. sz.cx?=?nWidth;??
  3. sz.cy?=?nHeight;??
  4. for(i=0;?i<nHeight;?i++)??
  5. {??
  6. ????for(j=0;?j<nWidth;?j++)??
  7. ????{??
  8. ????????if((N[i*nWidth+j]==128)?&&?(M[i*nWidth+j]?>=?dThrHigh))??
  9. ????????{??
  10. ????????????N[i*nWidth+j]?=?255;??
  11. ????????????TraceEdge(i,?j,?dThrLow,?N,?M,?sz);??
  12. ????????}??
  13. ????}??
  14. }??

?? ? ? ?以上代碼在非極大值抑制產生的二值灰度矩陣的潛在點中按照高閾值尋找邊緣,并以所找到的點為中心尋找鄰域內滿足低閾值的點,從而形成一個閉合的輪廓。然后對于不滿足條件的點,可用如下代碼直接刪除掉。
  1. //將還沒有設置為邊界的點設置為非邊界點??
  2. for(i=0;?i<nHeight;?i++)??
  3. {??
  4. ????for(j=0;?j<nWidth;?j++)??
  5. ????{??
  6. ????????if(N[i*nWidth+j]?!=?255)??
  7. ????????{??
  8. ????????????N[i*nWidth+j]??=?0?;???//?設置為非邊界點??
  9. ????????}??
  10. ????}??
  11. }??

?? ? ? 其中TraceEdge函數為一個嵌套函數,用于在每個像素點的鄰域內尋找滿足條件的點。其實現代碼如下:

  1. void?TraceEdge(int?y,?int?x,?int?nThrLow,?LPBYTE?pResult,?int?*pMag,?SIZE?sz)??
  2. {??
  3. ????//對8鄰域像素進行查詢??
  4. ????int?xNum[8]?=?{1,1,0,-1,-1,-1,0,1};??
  5. ????int?yNum[8]?=?{0,1,1,1,0,-1,-1,-1};??
  6. ????????LONG?yy,xx,k;??
  7. ????for(k=0;k<8;k++)??
  8. ????{??
  9. ????????yy?=?y+yNum[k];??
  10. ????????xx?=?x+xNum[k];??
  11. ????????if(pResult[yy*sz.cx+xx]==128?&&?pMag[yy*sz.cx+xx]>=nThrLow?)??
  12. ????????{??
  13. ????????????//該點設為邊界點??
  14. ????????????pResult[yy*sz.cx+xx]?=?255;??
  15. ????????????//以該點為中心再進行跟蹤??
  16. ????????????TraceEdge(yy,xx,nThrLow,pResult,pMag,sz);??
  17. ????????}??
  18. ????}??
  19. }??

以上就從原理上實現了整個Canny算法。其檢測效果如圖4所示。注意:以上代碼僅為作者理解所為,目的是驗證本人對算法的理解,暫時沒有考慮到代碼的執行效率的問題。

圖4 邊緣檢測結果

4、擴展

首先看一下OpenCV中cvCanny函數對該圖像的處理結果,如圖5所示。

圖5 OpenCV中的Canny邊緣檢測結果

?? ? 對比圖4和圖5可以發現,作者自己實現的邊緣檢測效果沒有OpenCV的好,具體體現在:1)丟失了一些真的邊緣;2)增加了一些假的邊緣。

?? ? ?經過對整個算法的來回檢查,初步推斷主要的問題可能在于在進行灰度矩陣梯度幅值計算式所采用的模板算子性能不是太好,還有就是關于兩個閾值的選取方法。關于這兩個方面的改進研究,后文闡述。

5、總結

?? ? ? ? 本文是過去一段時間,對圖像邊緣檢測方法學習的總結。主要闡述了Canny算法的工作原理,實現過程,在此基礎上基于VC6.0實現了該算法,并給出了效果圖。最后,通過對比發現本文的實現方法雖然能夠實現邊緣檢測,但效果還不是很理想,今后將在閾值選取原則和梯度幅值算子兩個方面進行改進。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/255260.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/255260.shtml
英文地址,請注明出處:http://en.pswp.cn/news/255260.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

IDEA注冊jar包使用和常用插件

IDEA注冊jar包使用 點擊獲取下載地址或生成注冊碼 一、安裝完成后&#xff0c;先不啟動&#xff0c;首先如下圖修改相關的地方。 二、啟動IDEA&#xff0c;并且激活IDEA IDEA插件倉庫 IntelliJ IDEA Plugins 一、Maven Helper 我一般用這款插件來查看maven的依賴樹。在不使用此…

HALCON示例程序class_2dim_unsup.hdev基于二維直方圖的聚類分類

HALCON示例程序class_2dim_unsup.hdev基于二維直方圖聚類分類的顏色分類 示例程序源碼&#xff08;加注釋&#xff09; 讀入圖片 read_image (Image, ‘patras’)關閉窗口 dev_close_window ()得到圖片尺寸大小 get_image_size (Image, Width, Height)打開窗口 dev_open_wind…

Android Monkey壓力測試

一. JAVA環境的搭建 安裝jdk-8u151-windows-x64,可以到官網或者應用中心下載.JAVA環境變量的搭建: 在"我的電腦"-"屬性"-"高級"-"環境變量"中,點擊新建,填寫變量名為JAVA_HOME,變量值為JAVA安裝的路徑.在系統變量中找到Path,點擊編輯,…

bzoj 4517: [Sdoi2016]排列計數

4517: [Sdoi2016]排列計數 Time Limit: 60 Sec Memory Limit: 128 MBSubmit: 637 Solved: 396[Submit][Status][Discuss]Description 求有多少種長度為 n 的序列 A&#xff0c;滿足以下條件&#xff1a;1 ~ n 這 n 個數在序列中各出現了一次若第 i 個數 A[i] 的值為 i&#x…

idea compare功能 之一次bug修復

一次bug修復 最近開發完了一套單點系統&#xff0c;jenkins打包上傳到服務器就出問題&#xff0c; 可以啟動但是不能正常工作。 首先想到的是環境不一樣&#xff0c; 于是把jenkins的jdk和maven都調整和本機大版本相同。 當然肯定是沒卵用的&#xff0c; 于是查看日志 Class pa…

黑白CCD成像不理想,如何補色

黑白CCD成像不理想&#xff0c;如何補色

HALCON示例程序class_ndim_box.hdev基于多通道圖像的分類

HALCON示例程序class_ndim_box.hdev基于多通道圖像的分類 示例程序源碼&#xff08;加注釋&#xff09; 讀入圖片 read_image (Image, ‘ic’)創建一個新的分類器 create_class_box (ClassifHandle)獲取圖片大小 get_image_size (Image, Width, Height)關閉窗口 dev_close_wi…

gcc編譯系統

一、 C語言編譯過程 C語言的編譯過程可分為四個階段&#xff1a; 1、預處理&#xff08;Preprocessing&#xff09; 對源程序中的偽指令&#xff08;即以#開頭的指令&#xff09;和特殊符號進行處理的過程。 偽指令包括&#xff1a;1&#xff09;宏定義指令&#xff1b; 2&…

自制反匯編逆向分析工具 迭代第六版本 (五)

本工具從最初版的跳轉分布圖只為了更直觀地分析反匯編代碼的分支結構&#xff0c;第三版開始對直觀圖進行逆向分支代碼的輸出&#xff0c;第四版對分支輸出策略的一些探索&#xff0c;第五版結合之前的探索進行改進。第六版在現在功能的基礎上進行增強&#xff0c;利用第六版&a…

模型搜索 及 輪廓搜索 的原理

模型搜索 及 輪廓搜索 的原理

Linux中設置vim自動在運算符號兩邊加上空格

vim中設置自動在-之類的運算符號左右兩邊加上空格。原版的vim不帶這個功能&#xff0c;寫出的代碼例如zxy&#xff0c;不美觀&#xff0c;很多編譯器&#xff08;如VS&#xff09;能夠自動在符號兩邊加上空格&#xff0c;如z x y&#xff0c;看起來比較美觀。 只要在.vimrc里…

HALCON示例程序class_ndim_norm.hdev基于多通道圖像的分類

HALCON示例程序class_ndim_norm.hdev基于多通道圖像的分類 示例程序源碼&#xff08;加注釋&#xff09; 讀入圖片 read_image (Image, ‘ic’)得到圖片大小 get_image_size (Image, Width, Height)關閉窗口 dev_close_window ()根據圖片大小打開窗口 dev_open_window (0, 0,…

centos7搭建FTP服務器

1.   使用 yum 安裝 vsftpd&#xff1a;yum install -y vsftpd 2.  啟動 VSFTPD&#xff1a;systemctl start vsftpd.service 3.  啟動后可監聽到21端口&#xff1a;netstat -nltp | grep 21 4.  配置FTP權限&#xff1a;&#xff08;vsftpd.conf&#xff0c;修改前先備…

[HDOJ2586]How far away?(最近公共祖先, 離線tarjan, 并查集)

題目鏈接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid2586 這題以前做過…現在用tarjan搞一發…竟然比以前暴力過的慢………… 由于是離線算法&#xff0c;需要Query來保存查詢數據&#xff0c;Ans來保存結果。最后輸出的時候按照idx的順序輸出&#xff0c;所以胡搞…

愛普生SCARA機器人參考文檔列表

愛普生SCARA機器人參考文檔列表軟件EPSON RC 5.0 用戶指南記載了機器人系統與程序開發軟件。 - 安全 - 機器人系統的操作與設定 - 程序開發軟件 EPSON RC GUI的操作 - SPEL語言、應用程序 - 機器人…

HALCON示例程序classify_citrus_fruits.hdev應用常規gmm分類器進行水果分類

HALCON示例程序classify_citrus_fruits.hdev應用常規gmm分類器進行水果分類 示例程序源碼&#xff08;加注釋&#xff09; 讀入圖片、顯示與顯示相關設置 read_image (Image, ‘color/citrus_fruits_01’) get_image_pointer1 (Image, Pointer, Type, Width, Height) dev_clos…

python進階10 MySQL補充 編碼、別名、視圖、數據庫修改

python進階10 MySQL補充 編碼、別名、視圖、數據庫修改 一、編碼問題 #MySQL級別編碼 #修改位置&#xff1a; /etc/mysql/mysql.conf.d/mysqld.cnf default-character-set utf8character-set-server utf8 collation-server utf8_general_ci#最佳實踐 #1、無論mysql是否設…

教你如何剖析源碼

一、源碼閱讀需求 在學習中&#xff0c;我們會需要了解&#xff0c;學習&#xff0c;使用一個框架&#xff0c;一個新的函數庫。在工作中&#xff0c;因為業務需求&#xff0c;因為性能問題&#xff0c;可能通過一個更高性能的工具&#xff0c;架構去優化我們的程序。 那么&…

SPEL + 中的錯誤處理

SPEL 中的錯誤處理 如果在SPEL程序中發生錯誤&#xff0c;可以將執行轉到錯誤處理例程中進行錯誤處理。該例程必須在函數定義內。下一頁的表格顯示了用于錯誤處理的程序指令。 項目 目的 OnErr 使用OnErr語句定義錯誤處理…

HALCON示例程序classify_halogen_bulbs.hdev使用SVM分類器檢測鹵素燈泡的好壞

HALCON示例程序classify_halogen_bulbs.hdev使用SVM分類器檢測鹵素燈泡的好壞 示例程序源碼&#xff08;加注釋&#xff09; 得到halcon默認圖片存儲路徑 get_system (‘image_dir’, HalconImages) 獲得操作系統類型 get_system (‘operating_system’, OS) OS{0:2}的意思是…