用opencv寻找手写箭头的特征

Finding the characteristics of a hand written Arrows with opencv

我正在尝试检索手写箭头的方向: 移除阴影并应用二值化和扩大线条后,这里是图像:

现在我想获得箭头的方向,所以我尝试使用 HoughLines,

lines = cv2.HoughLines(edges, rho=1, theta=np.pi / 180, threshold=20) 

但是它生成的行似乎太多(大约 54 行),我希望它只生成 3 行,这样我就可以找到这些行的交点。我可以将线分组为相似角度(+/-20 度)的组,然后平均角度。但我不确定平均线的 rho 应该是什么,有人可以举一个简单的例子吗?

有没有其他更准确的方法?

很高兴听到,谢谢大家

我建议采用不同的方法。总而言之,方法如下(匆忙完成,可能需要一些调整):

  1. 找到包围整个箭头的最小面积矩形(旋转矩形)的中心。 (第三张图画的圆圈)
  2. 找出所有白点的重心。它将向箭头的实际头部移动一点。 (画在第4图作为特征向量的原点)
  3. 找出所有白点的特征向量。
  4. 求位移矢量(重心-旋转矩形的中心)

现在:

  1. Arrow angle(unoriented): 是第一个特征向量的角度
  2. 箭头方向:是(第一个特征向量和中心的位移向量)点积的符号

代码:

与 PCA 相关的部分受到 this 的启发并且大部分是从 this 复制而来的。我只对 "getOrientation" 方法做了一个小改动,在它之前添加了以下行 returns

angle = (angle - math.pi) * 180 / math.pi
return angle, (mean[0,0]), (mean[0,1]), p1

实现上述逻辑的代码:

#threshold
_, img = cv2.threshold(img, 128, 255, cv2.THRESH_OTSU)
imshow(img)

#close the image to make sure the contour is connected)
st_el = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
img = cv2.morphologyEx(img, cv2.MORPH_CLOSE, st_el)
imshow(img)

#get white points
pnts = cv2.findNonZero(img)

#min area rect
rect_center = cv2.minAreaRect(pnts)[0]

#draw rect center
cv2.circle(img, (int(rect_center[0]), int(rect_center[1])), 3, 128, -1)
imshow(img)

angle, pca_center, eigen_vec = getOrientation(pnts, img)

cc_vec = (rect_center[0] - pca_center[0], rect_center[1] - pca_center[1])
dot_product = cc_vec[0] * eigen_vec[0] + cc_vec[1] * eigen_vec[1]
if dot_product > 0:
    angle *= -1

print ("Angle = ", angle)
imshow(img)

编辑

我建议一个更简单的方法。这种新方法不依赖于 PCA 来寻找无方向角 [0 - 180]。相反,立即使用最小区域矩形角度。并使用轮廓动量来寻找重心。

更简单的方法代码:

#get white points
pnts = cv2.findNonZero(img)

#min area rect
rect_center, size,  angle = cv2.minAreaRect(pnts)

#simple fix for angle to make it in [0, 180]
angle = abs(angle)
if size[0] < size[1]:
    angle += 90

#find center of gravity    
M = cv2.moments(img)
gravity_center = (M["m10"] / M["m00"], M["m01"] / M["m00"])

#rot rect vec based on angle
angle_unit_vec = (math.cos(angle * 180 / math.pi), math.sin(angle * 180 / math.pi))

#cc_vec = gravity center - rect center
cc_vec = (gravity_center[0] - rect_center[0], gravity_center[1] - rect_center[1])

#if dot product is negative add 180 -> angle between [0, 360]
dot_product = cc_vec[0] * angle_unit_vec[0] + cc_vec[1] * angle_unit_vec[1]
angle += (dot_product < 0) * 180

#draw rect center
cv2.circle(img, (int(rect_center[0]), int(rect_center[1])), 3, 128, -1)
cv2.circle(img, (int(gravity_center[0]), int(gravity_center[1])), 3, 20, -1)

imshow(img)
print ("Angle = ", angle)

编辑2: 此修改包括以下更改:

  1. 使用 cv2.fitLine() 并使用拟合线角度进行定向。
  2. 将angle_unit_vec替换为以重心为原点并与拟合线平行的矢量。

代码

#get white points
pnts = cv2.findNonZero(img)
#min area rect
rect_center, size,  angle = cv2.minAreaRect(pnts)

#fit line to get angle
[vx, vy, x, y] =cv2.fitLine(pnts, cv2.DIST_L12, 0, 0.01, 0.01)
angle = (math.atan2(vy, -vx)) * 180 / math.pi

M = cv2.moments(img)
gravity_center = (M["m10"] / M["m00"], M["m01"] / M["m00"])

angle_vec = (int(gravity_center[0] + 100 * vx), int(gravity_center[1] + 100 * vy))

#cc_vec = gravity center - rect center
cc_vec = (gravity_center[0] - rect_center[0], gravity_center[1] - rect_center[1])

#if dot product is positive add 180 -> angle between [0, 360]
dot_product = cc_vec[0] * angle_vec[0] + cc_vec[1] * angle_vec[1]
angle += (dot_product > 0) * 180


angle += (angle < 0) * 360


#draw rect center
cv2.circle(img, (int(rect_center[0]), int(rect_center[1])), 3, 128, -1)
cv2.circle(img, (int(gravity_center[0]), int(gravity_center[1])), 3, 20, -1)

imshow(img)
print ("Angle = ", angle)

输出: 使用来自 edit2 的代码:

第一张图片:

第二张图片:

第三张图片: