OpenCV:以给定角度穿过轮廓质心的线

时间:2015-02-25 02:59:41

标签: python opencv image-processing line contour

我有一个名为cnt的轮廓,从下面的图像中获得:

image description

我能够找到像这样的质心:

M = cv2.moments(cnt)
centroid_x = int(M['m10']/M['m00'])
centroid_y = int(M['m01']/M['m00'])

我现在想要绘制N条线,每条线相隔360 / N度,从质心开始并在所有可能的交叉点切割轮廓。 cv2.line()函数需要起点和终点,但我没有终点。

如果我画了一条穿过质心的线,其斜率为Tan(360 / N),我会用bitwise_and找到该线与轮廓的交点,但我无法弄清楚画一条线的方法。

如何绘制这些线条的任何帮助将非常感激。

2 个答案:

答案 0 :(得分:1)

我得到了一些工作。它有点特别,但这基本上就是我写的算法。我不得不重建你的图像的轮廓,所以我做的是我手动读取图像,提取对象的最外轮廓,然后从那里开始。 cv2.line方法的优点在于,如果绘制一条超出界限的线条,则线条将被图像边界剪切。这在我写的算法中很有用。

不用多说,这些是以下步骤:

  1. 读入图像,阈值然后反转图像,使黑色轮廓线变为白点。
  2. 检测最外面的轮廓。
  3. 创建原始输入图像的副本,以便我们绘制线条。请拨打此out
  4. 创建"参考"存储步骤#2中找到的最外轮廓的图像。
  5. 检测轮廓点的质心。还可以访问图像的宽度和高度。如果您没有任何宽度或高度值,则选择一个非常大的值...可能类似于1000.您需要确保此值超出轮廓上任何点的最大值。另外,设置所需的角度总数N
  6. 对于我们拥有的每个角度,i = 0, 1最多N - 1

    一个。创建临时空白图像

    湾计算适当的角度:i*(360 / N)并转换为弧度

    ℃。在临时图像上,从轮廓的质心到图像外部的坐标绘制一条线,以确保我们沿着我们想要的角度绘制一条朝向图像边界的线。此行的水平分量为cos(360/N)(参数以度为单位),而垂直分量为-sin(360/N)(参数也以度为单位)。负面原因是y轴在我们的图像坐标空间中向下是正向的,所以负面是为了使正向相对于笛卡尔坐标向上反转。这样做的原因是,当我们计算每条线与中心形成的角度时,角度将是正确的,因为正角度逆时针扫过。从质心开始,我们将移动图像的宽度,水平和图像的高度垂直保持先前找到的水平和垂直分量。这将使我们画出超出界限的线条,但线条将被图像边界剪切。

    另一个复杂的问题是在这张足够厚的临时图像中画一条线。如果我们绘制一条只有1像素厚的线,则可能会出现这样一种情况:由于像素的采样和绘制线的方式,线与轮廓不相交。我在这里选择了5像素的厚度。

    d。使用此临时图像,查看哪些位置等于参考图像。对于任何相同的位置,我们已经找到该线与原始图像的轮廓相交的位置。因此,选择任何相交的位置,因为粗线最有可能与最外面的轮廓产生多个交叉点。

    即使用步骤(d),从out的质心到我们在步骤(d)中找到的位置画一条线。

  7. 对所有角度重复步骤#6。 {#1}}将在步骤#6完成后包含我们的结果。

  8. 不用多说,这里是我写的代码:

    out

    这是我得到的结果。我选择了20个角度:

    enter image description here

答案 1 :(得分:1)

我会以一种老式的方式投入,我怀疑这可能是STL加速或对编译器的普遍信任;)。顺便说一下,C ++。

  • 将所需的所有角度放在列表中。
  • 遍历轮廓中的所有点,并收集与上述列表中的内容匹配的点。
  • 画线。

    findContours( f, contours, heirarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE );
    
    Moments M = moments( contours[0], true );
    Point2f cntr = Point2f( (int)M.m10/M.m00, (int)M.m01/M.m00 );
    circle( frame, cntr, 5, Scalar(0,0,0) );
    
    int N = 20;
    vector<float> slopes;
    for( int i=0; i<N; i++ )
        slopes.push_back( i*360.0/N ); 
    
    
    for( auto s : slopes )
        for( auto p : contours[0] )
            if( std::abs( cv::fastAtan2( p.y-cntr.y, p.x-cntr.x ) - s ) <= 0.5 )    //error margin, of sorts..
                {   finalpoints.push_back( p ); break; }
    
    cout<<"\nfound points: "<<finalpoints.size()<<endl; 
    for( auto p : finalpoints )
        line( frame, cntr, p, Scalar(0,0,0), 1 );