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

标签 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 找到线与轮廓的交点。但我无法想出画出那条线的方法。

任何有关如何绘制此类线条的帮助将不胜感激。

最佳答案

我得到了一些工作。这有点特别,但这基本上是我写的算法。我必须重建图像的轮廓,所以我手动读取图像,提取对象的最外层轮廓,然后从那里开始。 cv2.line 有什么好处?方法是,如果您绘制的线超出范围,则该线将被图像边界剪切。这在我写的算法中很有用。

事不宜迟,以下是步骤:

  • 读入图像,阈值然后反转图像,使黑色轮廓线变成白点。
  • 检测最外面的轮廓。
  • 创建原始输入图像的副本,以便我们绘制线条。调用 out .
  • 创建一个“引用”图像,存储在步骤 #2 中找到的最外层轮廓。
  • 检测轮廓点的质心。还可以访问图像的宽度和高度。如果您没有任何宽度或高度值,请选择一个非常大的值......也许像 1000 这样的值。您需要确保该值超出轮廓上任何点的最大值。另外,设置你想要的角度总数,N .
  • 对于我们拥有的每个角度,所以对于 i = 0, 1高达 N - 1 :

    一种。创建临时空白图像

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

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

    另一个复杂之处是在这个临时图像中画一条线,即 。足够厚的。如果我们画了一条只有 1 个像素粗的线,由于像素的采样和线的绘制方式,您可能会遇到线不与轮廓相交的情况。为了确定,我在这里选择了 5 个像素的厚度。

    d。使用这个临时图像,查看哪些位置是 等于 引用图像。对于任何相等的位置,我们已经找到了这条线与原始图像的轮廓相交的位置。因此,选择任何相交的位置,因为粗线很可能会与最外层轮廓产生多个相交点。

    e.使用步骤 (d),从 ​​out 的质心画一条线到我们在步骤 (d) 中找到的位置。
  • 对所有角度重复步骤#6。 out将包含我们在第 6 步完成后的结果。

  • 废话不多说,下面是我写的代码:
    # Step #1
    img = cv2.imread('contour.png', 0)
    img_bw = img <= 128
    img_bw = 255*img_bw.astype('uint8')
    
    # Step #2
    contours, _ = cv2.findContours(img_bw,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
    
    # Step #3
    out = img.copy()
    
    # Step #4
    ref = np.zeros_like(img_bw)
    cv2.drawContours(ref, contours, 0, 255, 1)
    
    # Step #5
    M = cv2.moments(contours[0])
    centroid_x = int(M['m10']/M['m00'])
    centroid_y = int(M['m01']/M['m00'])
    
    # Get dimensions of the image
    width = img.shape[1]
    height = img.shape[0]
    
    # Define total number of angles we want
    N = 20
    
    # Step #6
    for i in range(N):
      # Step #6a
      tmp = np.zeros_like(img_bw)
    
      # Step #6b
      theta = i*(360/N)
      theta *= np.pi/180.0
    
      # Step #6c
      cv2.line(tmp, (centroid_x, centroid_y),
               (int(centroid_x+np.cos(theta)*width),
                int(centroid_y-np.sin(theta)*height)), 255, 5)
    
      # Step #6d
      (row,col) = np.nonzero(np.logical_and(tmp, ref))
    
      # Step #6e
      cv2.line(out, (centroid_x, centroid_y), (col[0],row[0]), 0, 1)
    
    # Show the image
    # Step #7
    cv2.imshow('Output', out)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

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

    enter image description here

    关于python - OpenCV:以给定角度穿过轮廓质心的线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28710337/

    相关文章:

    java - 我应该将 BufferedImage.TYPE_4BYTE_ABGR 转换为 BufferedImage.TYPE_3BYTE_BGR 吗?

    algorithm - 平均矢量图像以获得中间图像

    python - OpenCV 中使用图像矩进行字体匹配

    python - 我的 Python 第一步

    python - 在 ubuntu 16.04 上安装 wxPython 需要很长时间

    python - 如何使用kivy相机与opencv 4.0

    python - 如何将cv2/Pillow修改的图像写出到磁盘?

    python - python 中的文件复制在 50000 行后停止

    python - SQLAlchemy 中的跨服务器选择

    c++ - Boost thread_group 返回空矩阵(openCV)