用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 应该是什么,有人可以举一个简单的例子吗?
有没有其他更准确的方法?
很高兴听到,谢谢大家
我建议采用不同的方法。总而言之,方法如下(匆忙完成,可能需要一些调整):
- 找到包围整个箭头的最小面积矩形(旋转矩形)的中心。 (第三张图画的圆圈)
- 找出所有白点的重心。它将向箭头的实际头部移动一点。 (画在第4图作为特征向量的原点)
- 找出所有白点的特征向量。
- 求位移矢量(重心-旋转矩形的中心)
现在:
- Arrow angle(unoriented): 是第一个特征向量的角度
- 箭头方向:是(第一个特征向量和中心的位移向量)点积的符号
代码:
与 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:
此修改包括以下更改:
- 使用 cv2.fitLine() 并使用拟合线角度进行定向。
- 将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 的代码:
第一张图片:
第二张图片:
第三张图片:
我正在尝试检索手写箭头的方向: 移除阴影并应用二值化和扩大线条后,这里是图像:
现在我想获得箭头的方向,所以我尝试使用 HoughLines,
lines = cv2.HoughLines(edges, rho=1, theta=np.pi / 180, threshold=20)
但是它生成的行似乎太多(大约 54 行),我希望它只生成 3 行,这样我就可以找到这些行的交点。我可以将线分组为相似角度(+/-20 度)的组,然后平均角度。但我不确定平均线的 rho 应该是什么,有人可以举一个简单的例子吗?
有没有其他更准确的方法?
很高兴听到,谢谢大家
我建议采用不同的方法。总而言之,方法如下(匆忙完成,可能需要一些调整):
- 找到包围整个箭头的最小面积矩形(旋转矩形)的中心。 (第三张图画的圆圈)
- 找出所有白点的重心。它将向箭头的实际头部移动一点。 (画在第4图作为特征向量的原点)
- 找出所有白点的特征向量。
- 求位移矢量(重心-旋转矩形的中心)
现在:
- Arrow angle(unoriented): 是第一个特征向量的角度
- 箭头方向:是(第一个特征向量和中心的位移向量)点积的符号
代码:
与 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: 此修改包括以下更改:
- 使用 cv2.fitLine() 并使用拟合线角度进行定向。
- 将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 的代码:
第一张图片:
第二张图片:
第三张图片: