文章目录
- 1、功能描述
- 2、代码实现
- 3、效果展示
- 4、更多例子
- 5、参考
1、功能描述
给出一张图片,里面含有各种图形,取各种图形的中心点,从左到右从上到下排序
例如
2、代码实现
import cv2
import numpy as np
def process_img(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_canny = cv2.Canny(img_gray, 100, 100)
kernel = np.ones((2, 3))
img_dilate = cv2.dilate(img_canny, kernel, iterations=1)
img_erode = cv2.erode(img_dilate, kernel, iterations=1)
return img_erode
def get_centeroid(cnt):
length = len(cnt)
sum_x = np.sum(cnt[..., 0])
sum_y = np.sum(cnt[..., 1])
return int(sum_x / length), int(sum_y / length)
def get_centers(img):
contours, hierarchies = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for cnt in contours:
if cv2.contourArea(cnt) > 100:
yield get_centeroid(cnt)
def get_rows(img, centers, row_amt, row_h):
centers = np.array(centers)
d = row_h / row_amt # 每行的间距
for i in range(row_amt): # 遍历行数
f = centers[:, 1] - d * i # 行首纵坐标
a = centers[(f < d) & (f > 0)] # 一行内的 x
yield a[a.argsort(0)[:, 0]]
img = cv2.imread("shape.png")
img_processed = process_img(img)
cv2.imwrite("shape_processed.png", img_processed)
centers = list(get_centers(img_processed))
print(centers)
"""
[(478, 466), (38, 454), (478, 432), (159, 442), (646, 436), (157, 403), (317, 430), (161, 369), (139, 368), (523, 385),
(64, 381), (690, 355), (636, 366), (341, 325), (506, 329), (212, 319), (86, 283), (180, 275), (674, 283), (379, 322),
(525, 256), (299, 311), (434, 254), (634, 212), (316, 203), (233, 204), (567, 172), (148, 228), (59, 199), (418, 158),
(478, 171), (363, 109), (549, 88), (281, 89), (211, 58), (441, 50), (21, 75), (104, 62), (677, 86), (621, 39),
(485, 32), (323, 24)]
"""
print(len(centers)) # 42
h, w, c = img.shape
count = 0
for row in get_rows(img, centers, 4, h):
cv2.polylines(img, [row], False, (255, 0, 255), 2) # 绘制每一行的点连成线
for x, y in row:
count += 1
cv2.circle(img, (x, y), 10, (0, 0, 255), -1) # 绘制每个点
cv2.putText(img, str(count), (x - 10, y + 5), 1, cv2.FONT_HERSHEY_PLAIN, (0, 255, 255), 2)
# 每个点上写上序号
cv2.imshow("Ordered", img)
cv2.waitKey(0)
process_img
后图像为
get_centers
函数找轮廓,面积较大的轮廓经过 get_centeroid
函数找轮廓中心
找轮廓中心的方法是遍历轮廓点,求横坐标和纵坐标的平均值
轮廓中心 centers
的长度为 42,可以看到我们找出来了图片中 42 个中心 ,也即找出来了 42 个轮廓
[(478, 466), (38, 454), (478, 432), (159, 442), (646, 436), (157, 403), (317, 430), (161, 369), (139, 368), (523, 385),
(64, 381), (690, 355), (636, 366), (341, 325), (506, 329), (212, 319), (86, 283), (180, 275), (674, 283), (379, 322),
(525, 256), (299, 311), (434, 254), (634, 212), (316, 203), (233, 204), (567, 172), (148, 228), (59, 199), (418, 158),
(478, 171), (363, 109), (549, 88), (281, 89), (211, 58), (441, 50), (21, 75), (104, 62), (677, 86), (621, 39),
(485, 32), (323, 24)]
遍历每行,排序纵坐标落在行内区间的轮廓中心
get_rows
中 f
array([466., 454., 432., 442., 436., 403., 430., 369., 368., 385., 381.,
355., 366., 325., 329., 319., 283., 275., 283., 322., 256., 311.,
254., 212., 203., 204., 172., 228., 199., 158., 171., 109., 88.,
89., 58., 50., 75., 62., 86., 39., 32., 24.])
get_rows
中 a
array([[363, 109],
[549, 88],
[281, 89],
[211, 58],
[441, 50],
[ 21, 75],
[104, 62],
[677, 86],
[621, 39],
[485, 32],
[323, 24]])
a.argsort(0)
按列排序
array([[ 5, 10],
[ 6, 9],
[ 3, 8],
[ 2, 4],
[10, 3],
[ 0, 6],
[ 4, 5],
[ 9, 7],
[ 1, 1],
[ 8, 2],
[ 7, 0]])
取出 x 的排序索引 a.argsort(0)[:, 0]
array([ 5, 6, 3, 2, 10, 0, 4, 9, 1, 8, 7])
得到排序后的结果 a[a.argsort(0)[:, 0]]
array([[ 21, 75],
[104, 62],
[211, 58],
[281, 89],
[323, 24],
[363, 109],
[441, 50],
[485, 32],
[549, 88],
[621, 39],
[677, 86]])
可视化结果,同一行的点连成线,绘制点,标上序号
最终输出
我们设置的 4 行,可以看到有 4 条直线
3、效果展示
get_rows(img, centers, 4, h)
配置不同的行数看看效果
1 行
2 行
3 行
4 行
5 行
6 行
7 行
4、更多例子
输入图片
前处理后的图片
2 行输出结果
输入图片
前处理后的图片
2 行输出结果
可以看到没有闭合的轮廓被舍弃掉了
输入图片
前处理后的图片
10 行输出结果
输入图片
前处理后的图片
4 行输出结果
输入图片
前处理后的图片
6 行输出结果
输入图片
前处理后的图片
2 行输出结果
5、参考
- 使用OpenCV对点集从左上到右下排序