这不是一项简单的任务。您需要识别图像中边界框的角,然后将它们匹配在一起,然后再绘制其余的矩形。第一部分通常使用卷积完成。有几个库可以处理这个问题:来自 scikit-image 的
pattern_match
是一个很好的开始。
然而,在这种特殊情况下,角点是单值的,并且宽和高均为一像素。您可以通过在图像阵列上扫描窗口并匹配角点的外观来强力查找角点。这将找到角点的位置。
然后你需要匹配属于在一起的角。左上角和右上角的top-value应该非常接近;左上角和左下角的左值应该非常接近;等等
这就是我上面分两步描述的内容。首先我们收集图像数据并将其转换为 numpy 数组。
import io
import numpy as np
from PIL import Image
import requests
from collections import namedtuple
# first collect our data and turn it into a numpy array
res = requests.get('https://i.stack.imgur.com/cLDfS.png')
img = Image.open(io.BytesIO(res.content))
img_arr = np.array(img)
# create objects to organize the data
CornerCollection = namedtuple('CornerCollection', ['tls', 'trs', 'brs', 'bls'])
BoundingBox = namedtuple('BoundingBox', ['tl', 'tr', 'br', 'bl'])
添加逐步遍历图像数组并查找与角点完全匹配的函数。
def find_corners(img: np.array) -> CornerCollection:
"""Finds the corners outlines in a mono-tone image."""
top_left = np.array([[1,1],[1,0]])
top_right = np.array([[1,1],[0,1]])
bot_right = np.array([[0,1],[1,1]])
bot_left = np.array([[1,0],[1,1]])
tl_corners = []
tr_corners = []
br_corners = []
bl_corners = []
window_iter = np.lib.stride_tricks.sliding_window_view(np.array(img), (2,2))
for i, row_view in enumerate(window_iter):
for j, view in enumerate(row_view):
if (view == top_left).all():
tl_corners.append((i, j))
if (view == top_right).all():
tr_corners.append((i, j))
if (view == bot_right).all():
br_corners.append((i, j))
if (view == bot_left).all():
bl_corners.append((i, j))
return CornerCollection(tl_corners, tr_corners, br_corners, bl_corners)
crnr_col = find_corners(img_arr)
在这里,我们迭代每个左上角并找到其他角的最佳匹配。这将返回
BoundingBox
对象的列表。
def merge_corners(cc: CornerCollection) -> list[BoundingBox]:
boxes = []
trs: list = cc.trs.copy()
bls: list = cc.bls.copy()
brs: list = cc.brs.copy()
for (tl_i, tl_j) in cc.tls:
best_tr = min(trs, key=lambda ij: abs(ij[0] - tl_i))
best_bl = min(bls, key=lambda ij: abs(ij[1] - tl_j))
best_br = min(brs, key=lambda ij: abs(ij[0] - tl_i) + abs(ij[1] - tl_j))
trs.remove(best_tr)
bls.remove(best_bl)
brs.remove(best_br)
boxes.append(BoundingBox((tl_i, tl_j), best_tr, best_br, best_bl))
return boxes
bboxes = merge_corners(crnr_col)
bboxes
# returns:
[BoundingBox(tl=(9, 534), tr=(9, 545), br=(41, 545), bl=(41, 534)),
BoundingBox(tl=(57, 672), tr=(57, 689), br=(80, 689), bl=(80, 672)),
BoundingBox(tl=(77, 322), tr=(77, 340), br=(127, 340), bl=(127, 322)),
BoundingBox(tl=(107, 849), tr=(107, 872), br=(143, 872), bl=(143, 849)),
BoundingBox(tl=(132, 758), tr=(132, 794), br=(268, 794), bl=(268, 758)),
BoundingBox(tl=(160, 916), tr=(160, 971), br=(298, 971), bl=(298, 916)),
BoundingBox(tl=(169, 524), tr=(169, 539), br=(186, 539), bl=(186, 524)),
BoundingBox(tl=(199, 581), tr=(199, 640), br=(325, 640), bl=(325, 581)),
BoundingBox(tl=(244, 1155), tr=(244, 1191), br=(343, 1191), bl=(343, 1155))]
使用
bboxes
,您可以添加从 tl
到 tr
到 br
到 bl
再回到 tl
的线条来制作矩形。