`

openCV综合运用 ------- 图像细化、线段长度、平行线距离检测

 
阅读更多

      在上述博客中,我分别对平滑滤波、边缘检测、直线检测做了一定程度的了解,那么最终的目的我们是需要解决实际问题,那么今天就试着完成一个简单的综合训练,来在巩固前面所学知识的同时,学到新的东西!

      题目如下:
                                                

    1.分别检测两线段的长度;

    2.算出这两平行线之间的距离。

    题目参考:http://www.opencvchina.com/thread-854-1-2.html

 

先看第一问,要求直线的距离。很容易联想到上一回所说的霍夫变换中的PHTT方法,返回的是直线的方向以及范围,说的具体一点,实际上返回的就是检测到的直线的两端点。我们不妨用霍夫变换直接试试。具体步骤:

                          将图像转换成灰度图像->霍夫变换->将检测直线的端点(绿色圆圈)标记出来

得到下面的结果:

 可以看到,我们检测到了很多条直线,如图有很多直线的端点,尤其是直线端点处,被检测到了多个端点(可以看得出有重叠效果)。其实也不难理解,这条直线在像素上来说是有厚度的,所以难免被检测到多条直线,这样的话就带来了问题,我们无法确定到底哪个是真正直线的两端点,于是就不能够得到线段的长度!那么我们在使用霍夫变换前需要做一些处理。-----------图像细化。

 

图像细化实际上就是图像骨架化,把图像核心的骨架留下,其他的赘余点删除,对于这个图来说就是把这条直线的厚度消除,得到一条单像素厚度的直线段。实际上,图像细化在图像处理、模式识别中使用的非常广发,也是非常关键的过程,这里我们介绍一种比较经典实用的图像细化方法----Zhang并行快速算法:

             具体实现方法、步骤如下:

             首先定义如下的8领域(注意顺序不能变):
                                                                  

            其中P1是我们讨论的像素点,首先前提条件是P1是前景点!若要删除该点,需要满足一下条件:

 过程1:

             (1)2 <= N(P1) <= 6,N(x)为x的8领域中黑点(背景点)的个数;

             (2)A(P1) = 1,A(x)为P2-P8之间按序前后分别为0、1的对数;

             (3)P2*P4*P6 = 0;

             (4)P4*P6*P8 = 0.

满足上述四个条件,可将P1点去除;

过程2:

             (1)2 <= N(P1) <= 6,N(x)为x的8领域中黑点(背景点)的个数;

             (2)A(P1) = 1,A(x)为P2-P8之间按序前后分别为0、1的对数;

             (3)P2*P4*P8 = 0;

             (4)P2*P6*P8 = 0.

满足上述四个条件,可将P1点去除;

以上即为Zhang并行算法的主要步骤,需要反复运行直到无法得到可删除点为止。对于图像细化,这里只做一个简单的了解,很多理论知识以及高级的算法我后续会再进行分析。

给出关键代码函数如下:

 

//寻找按序的前后分别是0、1的对数函数
int Findn(IplImage* img, int i, int j)
{
 CvScalar s1 = cvGet2D(img, i, j);
 CvScalar s2 = cvGet2D(img, i - 1, j);
 CvScalar s3 = cvGet2D(img, i - 1, j + 1);
 CvScalar s4 = cvGet2D(img, i, j + 1);
 CvScalar s5 = cvGet2D(img, i + 1, j + 1);
 CvScalar s6 = cvGet2D(img, i + 1, j);
 CvScalar s7 = cvGet2D(img, i + 1, j - 1);
 CvScalar s8 = cvGet2D(img, i, j - 1);
 CvScalar s9 = cvGet2D(img, i - 1, j - 1);
 int a = s1.val[0];
 int b = s2.val[0];
 int c = s3.val[0];
 int d = s4.val[0];
 int e = s5.val[0];
 int f = s6.val[0];
 int g = s7.val[0];
 int h = s8.val[0];
 int l = s9.val[0];

 int find[] = { a, b, c, d, e, f, g, h, l };//按8领域顺序定义数组,方便操作
 int n = 0;
 for (int x = 2; x < 9; ++x)
 {
  if (find[x] == 0 && find[x + 1] == 255)
  {
   n = n + 1;
  }
 }
 return n;
}

//这是细化直线的功能函数
IplImage* ThinImage(IplImage* img, int k) //确保输入的是二值图像
{
 int condition = 0;//满足的条件个数
 int mark = 0;//成功的标志位
 int firstN = 0;//第一个条件黑点的个数
 CvScalar s;
 for (int n = 0; n < k; ++n)
 {
  for (int i = 1; i < img->height - 1; ++i)
  {
   for (int j = 1; j < img->width - 1;++j)
   {//开始过程1的寻找
    condition = 0;//初始化条件满足数
         //cout << "1" << endl;
    s = cvGet2D(img, i, j);
    if (s.val[0] == 255)//如果这是前景点,即边缘
    {
     //cout << "2" << endl;
     //*************************第一过程****************************
     //*************************step1****************************
     firstN = 0;
     for (int ii = -1; ii < 1; ++ii)
     {
      for (int jj = -1; jj < 1; ++jj)
      {
       s = cvGet2D(img, i + ii, j + jj);
       if (s.val[0] == 255)
       {
        firstN = firstN + 1;
       }
      }
     }
     if (firstN < 3 || firstN > 7)
     {
      continue;
     }
     else
     {
      condition = condition + 1;
     }
     //****************************************************************
     //*************************step2*********************************
     if (Findn(img, i, j) != 1)
     {
      continue;
     }
     else
     {
      condition = condition + 1;
     }
     //****************************************************************
     //*************************step3*********************************
     CvScalar s1 = cvGet2D(img, i - 1, j);
     CvScalar s2 = cvGet2D(img, i, j + 1);
     CvScalar s3 = cvGet2D(img, i + 1, j);
     CvScalar s4 = cvGet2D(img, i, j - 1);
     int a = s1.val[0];//2
     int b = s2.val[0];//4
     int c = s3.val[0];//6
     int d = s4.val[0];//8

     if (a * b * c != 0)
     {
      continue;
     }
     else
     {
      condition = condition + 1;
     }
     //*********************************************************************
     //******************************step4**********************************
     if (b * c * d != 0)
     {
      continue;
     }
     else
     {
      condition = condition + 1;
     }
     //*********************************************************************
     //第一过程的结果操作
     if (condition == 4)
     {
      mark = 1;
      //((char *)(img->imageData + img->widthStep * (i)))[j] = 0;
      CvScalar p;
      p.val[0] = 0;
      cvSet2D(img, i, j, p);
      //cout << "11111111111111111111111111111111111" << endl;
     }
    }
   }
  }

  //****************************过程2**************************
  for (int i = 1; i < img->height - 1; ++i)
  {
   for (int j = 1; j < img->width - 1;++j)
   {//开始过程1的寻找
    condition = 0;//初始化条件满足数
         //cout << "1" << endl;
    s = cvGet2D(img, i, j);
    if (s.val[0] == 255)//如果这是前景点,即边缘
    {
     //cout << "2" << endl;
     //*************************第一过程****************************
     //*************************step1****************************
     firstN = 0;
     for (int ii = -1; ii < 1; ++ii)
     {
      for (int jj = -1; jj < 1; ++jj)
      {
       s = cvGet2D(img, i + ii, j + jj);
       if (s.val[0] == 255)
       {
        firstN = firstN + 1;
       }
      }
     }
     if (firstN < 3 || firstN > 7)
     {
      continue;
     }
     else
     {
      condition = condition + 1;
     }
     //****************************************************************
     //*************************step2*********************************
     if (Findn(img, i, j) != 1)
     {
      continue;
     }
     else
     {
      condition = condition + 1;
     }
     //****************************************************************
     //*************************step3*********************************
     CvScalar s1 = cvGet2D(img, i - 1, j);
     CvScalar s2 = cvGet2D(img, i, j + 1);
     CvScalar s3 = cvGet2D(img, i + 1, j);
     CvScalar s4 = cvGet2D(img, i, j - 1);
     int a = s1.val[0];//2
     int b = s2.val[0];//4
     int c = s3.val[0];//6
     int d = s4.val[0];//8

     if (a * b * d != 0)
     {
      continue;
     }
     else
     {
      condition = condition + 1;
     }
     //*********************************************************************
     //******************************step4**********************************
     if (a * c * d != 0)
     {
      continue;
     }
     else
     {
      condition = condition + 1;
     }
     //*********************************************************************
     //第一过程的结果操作
     if (condition == 4)
     {
      mark = 1;
      //((char *)(img->imageData + img->widthStep * (i)))[j] = 0;
      CvScalar p;
      p.val[0] = 0;
      cvSet2D(img, i, j, p);
      //cout << "222222222222222222222222" << endl;
     }
    }
   }
  }
  //cout << " end " << endl;
 }

 return img;

}
 以上即为图像骨架化的编程实现,让我们看一看效果吧:

 可以看到,直线已经骨架化成功了,那么接下来,我们再使用霍夫变换检测线段。使用PPHT方法,得到线段的方向以及范围信息,并将检测的直线端点标记出来,代码如下:

CvMemStorage* storageline = cvCreateMemStorage(0);
 CvSeq* lines;
 lines = cvHoughLines2(binaryline, storageline, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 300, 100, 10, 1000);
 int n = 0;
 CvPoint* point;
 double temp[8];
 for (int i = 0; i < lines->total; ++i)
 {
  point = (CvPoint*)cvGetSeqElem(lines, i);
  temp[i * 4] = point[0].x;
  temp[i * 4 + 1] = point[0].y;
  temp[i * 4 + 2] = point[1].x;
  temp[i * 4 + 3] = point[1].y;
  cvLine(resultline, point[0], point[1], CV_RGB(255, 0, 0));
  cvCircle(resultline, point[0], 3, CV_RGB(0, 255, 0), 1, 8, 3);
  cvCircle(resultline, point[1], 3, CV_RGB(0, 255, 0), 1, 8, 3);
  double distance = sqrt((point[1].x - point[0].x) * (point[1].x - point[0].x) + (point[1].y - point[0].y) * (point[1].y - point[0].y));  
  cout << "the " << i << " line's distance :" << distance << endl;
  ++n;
 }
 cout << n << endl;
 n = 0;

结果如下:

 成功得到了直线端点坐标的信息,并且输出n = 2,即刚刚好检测到两条直线!nice,这样的话直线长度就很好求了,使用数学方法,计算两坐标之间的距离即可。(结果将在后面显示)

 

第二问实际上在第一问的基础上,就是一个数学问题。

 
如图,正是求两平行线距离x。

                                                                   x = |AC|/sin(a)

于是通过向量求得

                                                         cos = (AB·AC)/|AB||AC|
                                                          sin = sqrt(i - cos^2)

于是便可求得x。

按照如此思路,编程求得上述两问的结果:

 
 

  • 大小: 10 KB
  • 大小: 2.4 KB
  • 大小: 1.3 KB
  • 大小: 1.2 KB
  • 大小: 1.2 KB
  • 大小: 4 KB
  • 大小: 10.9 KB
分享到:
评论

相关推荐

    OpenCV-MinGW-Build-OpenCV-4.5.2-x64.zip

    在本压缩包“OpenCV-MinGW-Build-OpenCV-4.5.2-x64.zip”中,提供的是一份已经针对MinGW编译器优化的OpenCV 4.5.2版本,适用于64位Windows系统。 OpenCV 4.5.2是该库的一个稳定版本,它包含了众多新特性和性能提升...

    OpenCV-MinGW-Build-OpenCV-4.5.0-with-contrib.zip

    OpenCV-MinGW-Build-OpenCV-4.5.0-with-contrib

    opencv-4.5.5-1.5.7-API文档-中英对照版.zip

    赠送jar包:opencv-4.5.5-1.5.7.jar; 赠送原API文档:opencv-4.5.5-1.5.7-javadoc.jar; 赠送源代码:opencv-4.5.5-1.5.7-sources.jar; 赠送Maven依赖信息文件:opencv-4.5.5-1.5.7.pom; 包含翻译后的API文档:...

    opencv-4.5.5-1.5.7-API文档-中文版.zip

    赠送jar包:opencv-4.5.5-1.5.7.jar; 赠送原API文档:opencv-4.5.5-1.5.7-javadoc.jar; 赠送源代码:opencv-4.5.5-1.5.7-sources.jar; 赠送Maven依赖信息文件:opencv-4.5.5-1.5.7.pom; 包含翻译后的API文档:...

    opencv-4.5.1-1.5.5-API文档-中文版.zip

    赠送jar包:opencv-4.5.1-1.5.5.jar; 赠送原API文档:opencv-4.5.1-1.5.5-javadoc.jar; 赠送源代码:opencv-4.5.1-1.5.5-sources.jar; 赠送Maven依赖信息文件:opencv-4.5.1-1.5.5.pom; 包含翻译后的API文档:...

    opencv-4.5.1-1.5.5-API文档-中英对照版.zip

    赠送jar包:opencv-4.5.1-1.5.5.jar; 赠送原API文档:opencv-4.5.1-1.5.5-javadoc.jar; 赠送源代码:opencv-4.5.1-1.5.5-sources.jar; 赠送Maven依赖信息文件:opencv-4.5.1-1.5.5.pom; 包含翻译后的API文档:...

    OpenCV-MinGW-Build-OpenCV-4.5.0-with-contrib-32bit.zip

    这个压缩包"OpenCV-MinGW-Build-OpenCV-4.5.0-with-contrib-32bit.zip"是专为在Windows环境下使用MinGW编译器进行C++开发而准备的。MinGW(Minimalist GNU for Windows)是一个小型的GNU开发工具集,它提供了一个不...

    opencv_官网 安装包 opencv-4.1.2-vc14_vc15

    opencv 安装包 opencv-4.1.2-vc14_vc15 opencv 安装包 opencv-4.1.2-vc14_vc15 opencv 安装包 opencv-4.1.2-vc14_vc15 opencv 安装包 opencv-4.1.2-vc14_vc15 opencv 安装包 opencv-4.1.2-vc14_vc15 opencv 安装包 ...

    opencv-python-4.10.0.84.tar.gz

    Python版本的OpenCV库,也就是`opencv-python`,为Python程序员提供了方便的接口来利用这个功能丰富的库。在本案例中,我们讨论的是OpenCV的Python接口的4.10.0.84版本,它是一个经过编译的二进制包,文件名为`...

    opencv_contrib-4.5.5.zip/opencv_contrib-4.5.5.zip

    3. **贡献模块**(opencv_contrib):包含了额外的模块,如Xfeatures2d(特征检测和描述符),ximgproc(图像处理算法),xobjdetect(对象检测),xphoto(照片修复),aruco(二维码和AR标记识别),face(人脸...

    opencv-4.5.4-android-sdk .zip

    opencv-4.5.4-android-sdk .zip

    opencv-4.6.0-vc14-vc15.exe

    这个压缩包"opencv-4.6.0-vc14-vc15.exe"很可能是OpenCV的特定版本,兼容Visual Studio 2014 (VC14) 和 Visual Studio 2015 (VC15) 的编译环境。 OpenCV 4.6.0是该库的一个稳定版本,可能包含了自4.0.0版本以来的...

    opencv-4.5.5-opencv-contrib-4.5.5-以及其他OpenCV配置需要的包

    opencv-4.5.5-vc14_vc15.exe opencv_contrib-4.5.5.zip cmake-3.24.0-rc1-windows-x86_64.msi jdk-17_windows-x64_bin.msi VTK-8.2.0.zip

    opencv-4.4.0-android-sdk.zip opencv Android版官网下载

    在`OpenCV-android-sdk\samples`目录下,OpenCV提供了一些示例应用,这些应用展示了如何在Android上使用OpenCV进行实际的图像处理任务。比如,`FaceDetection`示例演示了如何检测图像中的面部,而`TessTwo`则展示了...

    opencv-4.6.0-android-sdk.zip

    总的来说,OpenCV-4.6.0-android-sdk.zip为Android开发者提供了一个全面的环境,使他们能够利用强大的计算机视觉功能开发创新的应用程序,如增强现实、图像识别、安全监控等。通过深入研究和实践,开发者可以充分...

    opencv-4.9.0-android

    这个"opencv-4.9.0-android"版本是OpenCV针对Android平台的特定优化版本,包含了最新的特性和性能改进。 1. **OpenCV库的核心功能**: - 图像处理:包括滤波、边缘检测(如Canny、Sobel、Laplacian)、色彩转换...

    opencv-4.5.5-vc14_vc15最新版本

    这个压缩包“opencv-4.5.5-vc14_vc15最新版本”包含了OpenCV库的4.5.5版,适用于Visual Studio 2017(vc14)和Visual Studio 2019(vc15)编译环境。OpenCV库不仅支持C++,还提供了Python、Java等其他编程语言的接口...

    opencv-python-4.5.5.64.tar.gz

    Python版本的OpenCV(即`opencv-python`)提供了丰富的接口,使得Python开发者能够轻松地利用这个库进行图像和视频操作。`opencv-python-4.5.5.64.tar.gz`是一个包含了OpenCV Python接口的特定版本4.5.5.64的源码或...

    opencv-python-4.10.0.82.tar.gz

    这个压缩包“opencv-python-4.10.0.82.tar.gz”包含了Python接口的OpenCV库版本4.10.0.82,是开发人员在Python环境中进行图像和视频处理的重要资源。 OpenCV-Python是OpenCV库的一个Python绑定,它使得Python程序员...

    opencv_contrib-3.3.0

    3. **LineDescriptor**:用于线段检测和描述,对图像中的直线结构进行分析和匹配。 4. **aruco**:AR(增强现实)相关的标记检测和校准,常用于实现虚拟对象与真实世界的融合。 5. **BackgroundSubtractorMOG2**:...

Global site tag (gtag.js) - Google Analytics