转自:http://blog.csdn.net/likezhaobin/article/details/6892629

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. for(i=0; i<nWidth; i++)                                //进行y向的高斯滤波(加权平均)
  19. {
  20. for(j=0; j<nHeight; j++)
  21. {
  22. double dSum = 0.0;
  23. double dFilter=0;
  24. for(int nLimit=(-nCenter); nLimit<=nCenter; nLimit++)
  25. {
  26. if((j+nLimit)>=0 && (j+nLimit) < nHeight)       //图像不能超出边界
  27. {
  28. dFilter += (double)nData[(j+nLimit)*nWidth+i] * pdKernal_1[nCenter+nLimit];
  29. dSum += pdKernal_1[nCenter+nLimit];
  30. }
  31. }
  32. pCanny[j*nWidth+i] = (unsigned char)(int)dFilter/dSum;
  33. }
  34. }

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实现了该算法,并给出了效果图。最后,通过对比发现本文的实现方法虽然能够实现边缘检测,但效果还不是很理想,今后将在阈值选取原则和梯度幅值算子两个方面进行改进。

Canny边缘检测算法原理及其VC实现详解(二)的更多相关文章

  1. OpenCV: Canny边缘检测算法原理及其VC实现详解(转载)

    原文地址:http://blog.csdn.net/likezhaobin/article/details/6892176 原文地址:http://blog.csdn.net/likezhaobin/ ...

  2. Canny边缘检测算法原理及其VC实现详解(一)

    转自:http://blog.csdn.net/likezhaobin/article/details/6892176 图象的边缘是指图象局部区域亮度变化显著的部分,该区域的灰度剖面一般可以看作是一个 ...

  3. Canny边缘检测算法原理及C语言实现详解

    Canny算子是John Canny在1986年提出的,那年老大爷才28岁,该文章发表在PAMI顶级期刊上的(1986. A computational approach to edge detect ...

  4. 十五 Canny边缘检测算法

    一.Canny算法介绍 Canny 的目标是找到一个最优的边缘检测算法,最优边缘检测的含义是: 好的检测- 算法能够尽可能多地标识出图像中的实际边缘. 好的定位- 标识出的边缘要尽可能与实际图像中的实 ...

  5. 【算法随记】Canny边缘检测算法实现和优化分析。

    以前的博文大部分都写的非常详细,有很多分析过程,不过写起来确实很累人,一般一篇好的文章要整理个三四天,但是,时间越来越紧张,后续的一些算法可能就以随记的方式,把实现过程的一些比较容易出错和有价值的细节 ...

  6. 一些关于Canny边缘检测算法的改进

    传统的Canny边缘检测算法是一种有效而又相对简单的算法,可以得到很好的结果(可以参考上一篇Canny边缘检测算法的实现).但是Canny算法本身也有一些缺陷,可以有改进的地方. 1. Canny边缘 ...

  7. Canny边缘检测算法的一些改进

    传统的Canny边缘检测算法是一种有效而又相对简单的算法,可以得到很好的结果(可以参考上一篇Canny边缘检测算法的实现).但是Canny算法本身也有一些缺陷,可以有改进的地方. 1. Canny边缘 ...

  8. 【数字图像分析】基于Python实现 Canny Edge Detection(Canny 边缘检测算法)

    Canny 边缘检测算法 Steps: 高斯滤波平滑 计算梯度大小和方向 非极大值抑制 双阈值检测和连接 代码结构: Canny Edge Detection | Gaussian_Smoothing ...

  9. Canny边缘检测算法(基于OpenCV的Java实现)

    目录 Canny边缘检测算法(基于OpenCV的Java实现) 绪论 Canny边缘检测算法的发展历史 Canny边缘检测算法的处理流程 用高斯滤波器平滑图像 彩色RGB图像转换为灰度图像 一维,二维 ...

随机推荐

  1. oracle数据库之组函数

    组函数也叫聚合函数,用来对一组值进行运算,并且可以返回单个值 常见的组函数: (1)count(*),count(列名)  统计行数:找到所有不为 null 的数据来统计行数 (2)avg(列名)  ...

  2. 【python 3.6】调用另一个文件的类的方法

    文件1:test12.py 文件2:test13.py 文件1 如下: #!/usr/bin/python # -*- coding: utf-8 -*- ''' ''' class abcd(obj ...

  3. cinder的组件

    跟nova相似,cinder也有很多组件,每个组件负责各自的业务,然后共同协作完成volume的管理.组件之间的通信方式与nova个组件之间的通信方式相同,都是通过消息队列进行通信. cinder-a ...

  4. H2O Driverless AI

    H2O Driverless AI(H2O无驱动人工智能平台)是一个自动化的机器学习平台,它给你一个有着丰富经验的“数据科学家之盒”来完成你的算法. 使AI技术得到大规模应用 各地的企业都意识到人工智 ...

  5. 阿里IPO法律咨询费达1580万美元 为Facebook六倍

    据路透社报道,阿里巴巴集团周五在 IPO (首次公开招股)更新文件中披露,将向美国盛信律师事务所(Simpson Thacher)以及其他为 IPO 提供咨询服务的律师事务所支付 1580 万美元的法 ...

  6. 阿里与ShopRunner达成协议 联手在国内推出服务

    阿里巴巴集团与美国在线零售商 ShopRunner 达成协议,将帮助后者在中国大陆销售商品和履行订单交付产品. ShopRunner 首席战略官菲奥娜·迪亚斯(Fiona Dias)周三接受媒体采访时 ...

  7. git blame 查看某行代码提交记录

    1. 在当前git项目目录下执行 git blame -L 38,38 <filename> 例子:  git blame -L 38,38 src/component/BarCode/i ...

  8. Live Archive 训练题

    7091 Height Ordering Mrs. Chambers always has her class line up in height order (shortest at the fro ...

  9. Walking Between Houses(贪心+思维)

    Walking Between Houses There are nn houses in a row. They are numbered from 11 to nn in order from l ...

  10. 王者荣耀交流协会final发布版本控制报告

    二次开发软件说明文档 Dec 6 纠正饼状图点击选择PSP文件无效. 添加饼状图丢失的代码. submit the files that last night I forgot. add shurum ...