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.mzph.cn/news/255260.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

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

IDEA注册jar包使用 点击获取下载地址或生成注册码 一、安装完成后&#xff0c;先不启动&#xff0c;首先如下图修改相关的地方。 二、启动IDEA&#xff0c;并且激活IDEA IDEA插件仓库 IntelliJ IDEA Plugins 一、Maven Helper 我一般用这款插件来查看maven的依赖树。在不使用此…

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…

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

本工具从最初版的跳转分布图只为了更直观地分析反汇编代码的分支结构&#xff0c;第三版开始对直观图进行逆向分支代码的输出&#xff0c;第四版对分支输出策略的一些探索&#xff0c;第五版结合之前的探索进行改进。第六版在现在功能的基础上进行增强&#xff0c;利用第六版&a…

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;修改前先备…

教你如何剖析源码

一、源码阅读需求 在学习中&#xff0c;我们会需要了解&#xff0c;学习&#xff0c;使用一个框架&#xff0c;一个新的函数库。在工作中&#xff0c;因为业务需求&#xff0c;因为性能问题&#xff0c;可能通过一个更高性能的工具&#xff0c;架构去优化我们的程序。 那么&…

php调用API支付接口(转自刘68)

首先访问 https://charging.teegon.com/ 注册账号&#xff0c; 找到开发配置 记下client_id和client_secret。 点击 天工开放平台 点击天工收银 点击 SDK下载与使用 选择php版下载 解压后获得 打开form.php这个是样板文件可以用它来测试是否成功&#xff0c;测试前需要先修…

单目相机标定原理

一&#xff0e;相机标定主要涉及三个坐标系&#xff1a;图像坐标系、摄像机坐标系和世界坐标系 【图像坐标系】 摄像机采集的图像变换为数字图像后&#xff0c;每副数字图像在计算机内为M x N数组&#xff0c;M行N列的图像中每一个元素&#xff08;pixel&#xff09;数值就是图…

Android开始之 activity_lifecycle和现场保护

生命周期&#xff1a; oncreate—onstart—onresume&#xff0c;--onpause--onstop&#xff0c;——onrestart——onstart——onresume&#xff0c;。。onpause。。。onstop。。。ondestroy 现在保护---保存系统状态 通过这个方法保存到数据库中或者xml中&#xff0c;&#xff…

Apache启动错误:could not bind to address[::]:443

Q&#xff1a;Windows环境下启动apache报错如下&#xff1a; 可是在httpd.conf文件中apache listen的明明是http 80端口&#xff0c;为什么会报443的错误&#xff1f; A&#xff1a;因为你的计算机安装了VM&#xff0c;所有有个vm的进程一直占用着443端口&#xff0c;在命令行下…

针孔相机拍摄的图像坐标和空间点的对应关系

一&#xff1a;摄像机成像原理 摄像机成像模型一般有三种&#xff1a;透镜投影模型&#xff08;小孔摄像机模型&#xff09;、正交投影模型和透视投影模型 光学中最基本的高斯成像公式&#xff1a;1/u 1/v 1/f (式一) 原理图如下&#xff1a; 二&#xff1a;认识三个…

粥做得好不好,全凭一个良心!

粥做得好不好&#xff0c;全凭一个良心&#xff01; 工作不也是凭良心么&#xff01; 文章中的红色字体表达了一切&#xff01; 霜降节气的到来&#xff0c;意味着冬季的临近&#xff0c;而霜降后气温的降低&#xff0c;利于蔬菜的淀粉沉淀&#xff0c;收浆后的莲藕也变得更为…

黑盒测试

引言&#xff1a; 什么是黑盒测试呢&#xff01;&#xff1f; 像我一样的测试小白就会望文生义了&#xff1a;对黑盒子测试 那么黑盒子是什么&#xff01;? 思考5秒后&#xff0c;得出了这样的答案&#xff1a;黑盒子就是黑色的盒子&#xff0c;嘿嘿&#xff01; 正文&#xf…

Expo大作战(十二)--expo中的自定义样式Custom font,以及expo中的路由RouteNavigation

简要&#xff1a;本系列文章讲会对expo进行全面的介绍&#xff0c;本人从2017年6月份接触expo以来&#xff0c;对expo的研究断断续续&#xff0c;一路走来将近10个月&#xff0c;废话不多说&#xff0c;接下来你看到内容&#xff0c;讲全部来与官网 我猜去全部机翻个人修改补充…

悬浮按钮

效果图 //将dp转换为px public static int dip2px(Context context, float dpValue) {final float scale context.getResources().getDisplayMetrics().density;return (int) (dpValue * scale 0.5f); }//浮动图片监听 private void initListener() {//弹出对话框jianwen.set…

图像坐标:我想和世界坐标谈谈(A) 【计算机视觉学习笔记--双目视觉几何框架系列】

玉米竭力用轻松具体的描述来讲述双目三维重建中的一些数学问题。希望这样的方式让大家以一个轻松的心态阅读玉米的《计算机视觉学习笔记》双目视觉数学架构系列博客。这个系列博客旨在捋顺一下已标定的双目视觉中的数学主线。数学推导是有着几分枯燥的&#xff0c;但奇妙的计算…

img

转载于:https://www.cnblogs.com/SoulCode/p/6508720.html

图像坐标:我想和世界坐标谈谈(B)

二、图像坐标&#xff1a;我想和世界坐标谈谈(B) 玉米将在这篇博文中&#xff0c;对图像坐标与世界坐标的这场对话中涉及的第二个问题&#xff1a;谈话方式&#xff0c;进行总结。世界坐标是怎样变换进摄像机&#xff0c;投影成图像坐标的呢&#xff1f; 玉米做了一个简单的图示…

【Android】Fragment的简单笔记

被虐了&#xff0c;做某公司笔试时&#xff0c;发现自己连个Fragment的生命周期都写不详细。平时敲代码&#xff0c;有开发工具的便利&#xff0c;有网上各大神的文章&#xff0c;就算忘了也很容易的可以查到&#xff0c;但当要自己不借助外界&#xff0c;却发现自己似乎对该知…

三、致敬“张正友标定”

三、致敬“张正友标定” 此处“张正友标定”又称“张氏标定”&#xff0c;是指张正友教授于1998年提出的单平面棋盘格的摄像机标定方法。张氏标定法已经作为工具箱或封装好的函数被广泛应用。张氏标定的原文为“A Flexible New Technique forCamera Calibration”。此文中所提到…