为了简单起见,我正在使用一个轮廓,它非常接近平行四边形。我需要在轮廓上画一条线,使该线完美地贴合轮廓。
这是一个最小的可重现示例:
import numpy as np
import cv2
from math import dist
# setup stuff
filler_img = np.zeros((1600, 1600), dtype=np.uint8)
cnt = np.array([[490, 885], [648, 1065], [636, 1201], [486, 1026]]) # [[733, 712], [897, 886], [688, 1006], [528, 828]]
c1, c2, c3 = cnt[:-1]
moments = cv2.moments(cnt)
center = (int(moments["m10"] / moments["m00"]), int(moments["m01"] / moments["m00"])) # (x, y)
# calculating pass-through line
y_to_x_ratio = (c2[1] - c3[1]) / (c3[0] - c2[0])
point1 = (0, int(y_to_x_ratio*center[0] + center[1]))
point2 = (1600, int(-y_to_x_ratio*(1600-center[0]) + center[1]))
cv2.line(filler_img, point1, point2, 100, thickness=int(dist(c1, c2))) # problems with thcikness argument
# filling circles at c1, c2, and c3
disp_img = cv2.drawContours(cv2.cvtColor(filler_img, cv2.COLOR_GRAY2BGR), [cnt], -1, (255, 255, 255), 5)
cv2.circle(disp_img, c1, 20, (0, 0, 255), -1) # red
cv2.circle(disp_img, c2, 20, (0, 255, 0), -1) # green
cv2.circle(disp_img, c3, 20, (255, 0, 0), -1) # blue
imshow(disp_img)
注意:
imshow()
是一个在Jupyter Notebook上显示图像的函数 - 它的实现如下:
def imshow(*imgs: cv2.Mat) -> None:
def helper(img: cv2.Mat) -> None:
plt.axis('off')
plt.grid(False)
plt.imshow(cv2.cvtColor(img, cv2.COLOR_RGB2BGR))
plt.show()
for img in imgs:
helper(img)
这就是代码产生的结果:
正如您在代码中看到的,我已经正确计算了线的角度。它基于轮廓中的角点,我还在输出图像上标记了这些角点:
c1: red, c2: green, c3: blue
。使用c2
和c3
(绘制的线平行于c2和c3之间的线)即可简单地找到角度,并且我发现厚度是c1
和c2
之间的距离(垂直于绘制的线)线)。然而,画出的线明显比应有的粗。
更有趣的是,这个问题以不同的轮廓消失。这是相同代码的结果,但轮廓位于
cnt = ...
行注释中:
如您所见,这更适合轮廓。如何一致地绘制尽可能准确地填充该区域的线条?
我傻了。我使用平行四边形边的长度作为厚度的长度,当我真的必须做一些三角函数来找到平行四边形其他边之间的距离时:
_____
/ | /
/__|_/
直线标记的距离就是我需要找到的。 这是有效的厚度计算:
thickness = int(dist(c1, c2) * abs(sin(atan2(c2[1] - c3[1], c3[0] - c2[0]) - atan2(c2[1] - c1[1], c1[0] - c2[0]))))