7

我正在开发一个程序,该程序将跟踪类似于弯曲五彩纸屑的二进制斑点中心的点。稍后,我将使用跟踪曲线的三次样条拟合这些点。

作为该计划的一部分,我需要:

-创建一个 2D 矢量,对二值图像上的斜线进行采样

-计算在沿五彩纸屑斑点的每个位置使用的角度

以下是一些图像示例,以及所追踪的点可能看起来的草图:

在此处输入图像描述 在此处输入图像描述 在此处输入图像描述 在此处输入图像描述 在此处输入图像描述 在此处输入图像描述

找到黑色五彩纸屑垂直部分的中心很简单。假设你在一个黑色像素中,找到左右白色边缘,中间是它们之间距离的一半。这样做很容易,因为用于进行上述计算的二维向量只是图像的一行。

这是其中一个五彩纸屑的 10 像素高度段

在此处输入图像描述

但五彩纸屑并不总是笔直垂直排列!有时它们是弯曲的或水平对齐的。这里需要的是一个 2d 向量,它以一定角度穿过五彩纸屑。 从图像中采样这个有角度的矢量的最有效方法是什么?实际上,在 Python PIL 或 OpenCV 等图像处理库中,是否存在可以通过图像以角度获取线向量的操作?如果我做一个,我怎么能注意确保它是有效的?

计算所需矢量角度的最有效方法是什么? 获得适当角度的一种方法是在返回的 2d 向量中找到导致最小宽度黑色段的角度。我不需要详尽地做到这一点,只需以 30 度的增量循环 360 度获得适当角度的另一种方法可能是找到五彩纸屑曲线的切线,并使用垂直于该切线的线 - 但是可能更复杂。

任何关于如何更好地解决问题的想法都会非常有帮助。关于如何在图像上获取二维线的任何具体建议,以及获得垂直角度的有效方法也很棒。

在此处输入图像描述

4

3 回答 3

4

您似乎对“中轴拟合”感兴趣 - 以及方向的估计(如果您有轴,那么通常轴在任何点的切线就足够了)。

从技术上讲,对于 OpenCV,您可能会考虑使用距离变换 ( cv.PointPolygonTest),使用 Voronoi 单元 ( cv.cvCalcSubdivVoronoi2D),或者 - 正如 @remi形态细化所建议的那样......

但是,如果您不想使用该scikits-image软件包,而只需要使用 OpenCV - 这是使用一些骨架化代码(基于快速简便的技术)的入门尝试。

简单的第一个图像骨架在此处输入图像描述在此处输入图像描述

然后,您可以通过沿着发现的点进行一些样条拟合来计算您的样本和切线(但这需要更多的工作来发现末端并消除任何杂散点/间隙......)

import cv

# get images 
orig = cv.LoadImage('o1Mlp.png')

# create storage images
grey = cv.CreateImage(cv.GetSize(orig), 8, 1) 
skel = cv.CreateImage(cv.GetSize(orig),8, 1) 
temp = cv.CreateImage(cv.GetSize(orig),8,1)

# convert image to pure binary B&W based on threshold
cv.CvtColor(orig, grey, cv.CV_RGB2GRAY)
cv.Threshold(grey,grey,200,255,cv.CV_THRESH_BINARY_INV)

# Create cross operator - good for skeletonization 
elem = cv.CreateStructuringElementEx(3,3,1,1,cv.CV_SHAPE_CROSS)

# Loop until we have consumed all the values in the original image
while True:
  cv.MorphologyEx(grey,temp,None,elem,cv.CV_MOP_OPEN) # Shrink..
  cv.Not(temp,temp) # ...invert...
  cv.And(grey,temp,temp) # ...intersect with original...
  cv.Or(skel,temp,skel) # ... add to current skeleton...
  cv.Erode(grey,grey,elem) # and reduce original ready for next.

  (minVal,maxVal,minLoc,maxLoc)= cv.MinMaxLoc(grey)
  if (maxVal==0): # Any pixels left?
    break

# show result 
cv.ShowImage("orig", orig)
cv.ShowImage("skel", skel)
cv.WaitKey(-1)
于 2012-10-10T15:41:48.603 回答
2

关于问题的最后一部分,找到法线:我使用自己的算法。它似乎工作。如果您找到标准解决方案或改进我的解决方案,请告诉我们!

    /// <summary>
    /// Returns absolute angle between points at offset length, or double.MinValue when not found.
    /// </summary>
    /// <param name="sequence">Ordered array of points (e.g., from OpenCV Contour)</param>
    /// <param name="length">Number of points used to calculate angle</param>
    /// /// <param name="increment">number of points between each angle calculation (e.g., 1 to attempt to determine angles for all points)</param>
    /// <returns></returns>
    public static double[] FindAbsoluteAngles(Point[] sequence, int length, int increment)
    {
        double[] angles = new double[sequence.Length];
        for (int i = 0; i < sequence.Length; i++)
            angles[i] = double.MinValue;

        double last = double.MinValue;
        for (int i = length; i < sequence.Length; i += increment)
        {
            int i1 = i - length;
            int i2 = i - ((int)length / 2);
            int i3 = i;

            Point p1 = sequence[i1];
            Point p2 = sequence[i2];
            Point p3 = sequence[i3];

            if (p1.X != p3.X & p1.Y != p3.Y)//Is a diagonal
            {
                angles[i2] = 180 - Math.Atan(1.0 * (p1.X - p3.X) / (p1.Y - p3.Y)) * 180 / Math.PI;
            }
            else if (last != double.MinValue)
            {
                //USe previous angle to determine non-diagonals (which can be: 0 or 180; 90 or 270) 
                double error;
                if (p1.X == p3.X)//Is a vertical
                {
                    error = Math.Abs(last - 180);
                    if (Math.Min(error, 180 - error) == error)
                        angles[i2] = 180;
                    else
                        angles[i2] = 0;
                }
                else if (p1.Y == p3.Y)//Is a horizontal
                {
                    error = Math.Abs(last - 270);
                    if (Math.Min(error, 180 - error) == error)
                        angles[i2] = 270;
                    else
                        angles[i2] = 90;
                }
            }

            last = angles[i2];
        }

        return angles;
    }
于 2013-01-09T18:49:06.590 回答
1

This looks like contour thinning or skeleton. Have a look at that answer. There is even a link to a Python package.

于 2012-10-09T15:30:32.017 回答