一个问题是你过于复杂了。子图同构比您尝试做的要困难得多。
假设您能够分析图像并确定每个字母的大致坐标(在图像上)。您应该能够获取字母的点集,每个点唯一地映射到一个字母,并进行线性搜索以找到最接近图像中心的三个点。
下一步是三角形查找。首先,重要的是要知道,由于网格中的每个三角形都是唯一的,您可以简单地遍历网格中的所有三角形,标准化(通过规范化)它们,然后将它们添加到字典中以提供快速查找。因此,构建查找字典的代码如下所示:
def canonize_triangle_letters(letter_triple):
# Used in larger algorithm below
return tuple(sorted(list(letter_triple)))
def triangle_lookup_from_grid(triangle_grid)
# This is a preprocessing step
# Only needs to be done once if the grid doesn't change.
# If grid does change, a more complex approach will be needed.
triangle_lookup = {} # Used in larger algorithm below
for points_triple in get_points_triples(triangle_grid):
letter_to_point = dict((point_to_letter[p],p) for p in points_triple)
triangle = canonize_triangle_letters(letter_to_point.keys())
triangle_lookup[triangle] = letter_to_point
return triangle_lookup
下一步是确定返回的是顶点、边还是三角形。一种简单但相当主观的方法,例如,如果您想让算法偏向于返回边而不是顶点或三角形,则非常有用。
- 如果中心明显更接近一个点,则返回该点的字母。
- 如果中心靠近两个点,则返回这两个点的字母。
- 否则,返回所有三个点。
更平衡、更精确的方法需要一些额外的数学运算。下图大致显示了如何去做。为了避免返回顶点 (A) 的区域之间的混淆,返回边 (AB) 或返回三角形 (ABC) 以及数学。顶点 A 标记为 5,顶点 B 标记为 6,顶点 C 标记为 7。请注意,L/3 表示那里的半径,其中 L 是边的长度。该图像假设离中心最近的点是 A,然后是 B,然后是 C。因此,点永远不会位于顶点 5 和 8 的直线的右侧,因为它打破了该点更靠近 A 的假设,并且B 比 C。
其评估方式如下:
- 如果最近点 (A) 在中心的 L/3 范围内。然后返回 A。
- 创建一个点 p1(图中的顶点 8),即沿着从 A 到 B 和 A 到 C 的角度之间的中间角。然后放置第二个点 p2(图中的顶点 9) ,与 A 到 B 的角度相同,距离为 L。从那里您可以使用叉积来确定中心在直线的哪一侧。
- 如果 cross(p1,screen_center,p2) 小于 0,则返回 AB,否则返回 ABC。
代码如下所示。代码中有一些神奇的数学函数,但它们的算法应该不难在网上查找。
def find_nearest_triangle(points, screen_center):
# Returns the nearest triangle, sorted by distance to center
dist_to_center = lambda p: distance(p, screen_center)
# Use the first three points in the list to create the inital triangle
nearest_triangle = set(points[:3])
farthest_point = max(nearest_triangle, key=dist_to_center)
farthest_dist = dist_to_center(farthest_point)
for point in points[3:]:
dist = dist_to_center(point)
if dist < farthest_dist: # Check for a closer point
farthest_dist = dist
nearest_triangle.remove(farthest_point)
nearest_triangle.add(point)
# Find the new farthest point
farthest_point = max(nearest_triangle, key=dist_to_center)
return sorted(list(nearest_triangle), key=dist_to_center)
def get_location(nearest_triangle, screen_center):
# nearest_triangle should be the same as returned by find_nearest_triangle.
# This algorithm only returns the 1-3 points that make up the triangle.
A, B = nearest_triangle[:2]
side_length = distance(A, B)
vertex_radius = side_length / 3.0
if distance(A, screen_center) < vertex_radius:
return [A], nearest_triangle
def cross(o, a, b): # Cross product
return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0])
angle_AB = angle(A, B)
angle_AC = angle(A, C)
middle_angle = ((angle_AB + angle_AC) % 360) / 2.0 # For angle in degrees
p1 = offset_point_by_angle_dist(A, middle_angle, distance)
p2 = offset_point_by_angle_dist(p1, angle_AB, side_length)
if cross(p1,screen_center,p2) < 0:
return [A,B]
return [A,B,C]
def lookup_location(triangle_lookup, image_point_to_letter, points, screen_center):
nearest_triangle = find_nearest_triangle(points, screen_center)
triangle = canonize_triangle_letters([image_point_to_letter[p] for p in nearest_triangle])
letter_to_position = triangle_lookup[triangle]
location = get_location(nearest_triangle, screen_center)
letters = [image_point_to_letter[p] for p in location]
return [letter_to_position[L] for L in letters]
请注意,上述算法的运行时间为 O(N),其中 N 是点集中的点数。但是,必须为每个图像运行它。因此,如果需要检查大量图像,最好尽量限制字母的数量。虽然,从图像中提取字母可能会更加耗时。虽然,由于算法只需要最接近的三个字母,应该是最好的