我得到了一些工作。这有点特别,但这基本上是我写的算法。我必须重建图像的轮廓,所以我所做的是手动读取图像,提取对象的最外层轮廓,然后从那里开始。 cv2.line 方法的好处在于,如果您绘制的线超出边界,则该线会被图像边界剪裁。这对我写的算法很有用。
事不宜迟,以下是步骤:
- 读入图像,阈值然后反转图像,使黑色轮廓线变成白点。
- 检测最外面的轮廓。
- 创建原始输入图像的副本,以便我们绘制线条。打电话给
out。
- 创建一个“参考”图像,用于存储在第 2 步中找到的最外层轮廓。
- 检测轮廓点的质心。还可以访问图像的宽度和高度。如果您没有任何宽度或高度值,请选择一个非常大的值......也许像 1000 这样的值。您需要确保该值超出轮廓上任何点的最大值。另外,设置您想要的角度总数,
N。
-
对于我们拥有的每个角度,所以对于i = 0, 1 到N - 1:
一个。创建临时空白图片
b.计算合适的角度: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个角度: