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

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

      题目如下:
                                                 openCV综合运用 ------- 图像细化、线段长度、平行线距离检测_第1张图片

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

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

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

 

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

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

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

 

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

}
 以上即为图像骨架化的编程实现,让我们看一看效果吧:
openCV综合运用 ------- 图像细化、线段长度、平行线距离检测_第3张图片
 可以看到,直线已经骨架化成功了,那么接下来,我们再使用霍夫变换检测线段。使用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;

结果如下:
openCV综合运用 ------- 图像细化、线段长度、平行线距离检测_第4张图片
 成功得到了直线端点坐标的信息,并且输出n = 2,即刚刚好检测到两条直线!nice,这样的话直线长度就很好求了,使用数学方法,计算两坐标之间的距离即可。(结果将在后面显示)

 

第二问实际上在第一问的基础上,就是一个数学问题。
openCV综合运用 ------- 图像细化、线段长度、平行线距离检测_第5张图片
 
如图,正是求两平行线距离x。

                                                                   x = |AC|/sin(a)

于是通过向量求得

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

于是便可求得x。

按照如此思路,编程求得上述两问的结果:
openCV综合运用 ------- 图像细化、线段长度、平行线距离检测_第6张图片
 
 

你可能感兴趣的:(C++,openCV)