OpenCV
使用 OpenCVfindContours https://docs.opencv.org/4.1.1/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0 and drawContours https://docs.opencv.org/4.1.1/d6/d6e/group__imgproc__draw.html#ga746c0625f1781f1ffc9056259103edbc可以首先对线条进行矢量化,然后精确地重新创建原始图像:
import numpy as np
import cv2
img = cv2.imread('loadtest.png', 0)
result_fill = np.ones(img.shape, np.uint8) * 255
result_borders = np.zeros(img.shape, np.uint8)
# the '[:-1]' is used to skip the contour at the outer border of the image
contours = cv2.findContours(img, cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)[0][:-1]
# fill spaces between contours by setting thickness to -1
cv2.drawContours(result_fill, contours, -1, 0, -1)
cv2.drawContours(result_borders, contours, -1, 255, 1)
# xor the filled result and the borders to recreate the original image
result = result_fill ^ result_borders
# prints True: the result is now exactly the same as the original
print(np.array_equal(result, img))
cv2.imwrite('contours.png', result)
Result
Scikit-图像
使用 scikit 图像find_contours https://scikit-image.org/docs/0.16.x/api/skimage.measure.html#skimage.measure.find_contours and approximate_polygon https://scikit-image.org/docs/0.16.x/api/skimage.measure.html#skimage.measure.approximate_polygon允许您通过近似多边形来减少线数(基于这个例子 https://scikit-image.org/docs/stable/auto_examples/edges/plot_polygon.html#sphx-glr-auto-examples-edges-plot-polygon-py):
import numpy as np
from skimage.measure import approximate_polygon, find_contours
import cv2
img = cv2.imread('loadtest.png', 0)
contours = find_contours(img, 0)
result_contour = np.zeros(img.shape + (3, ), np.uint8)
result_polygon1 = np.zeros(img.shape + (3, ), np.uint8)
result_polygon2 = np.zeros(img.shape + (3, ), np.uint8)
for contour in contours:
print('Contour shape:', contour.shape)
# reduce the number of lines by approximating polygons
polygon1 = approximate_polygon(contour, tolerance=2.5)
print('Polygon 1 shape:', polygon1.shape)
# increase tolerance to further reduce number of lines
polygon2 = approximate_polygon(contour, tolerance=15)
print('Polygon 2 shape:', polygon2.shape)
contour = contour.astype(np.int).tolist()
polygon1 = polygon1.astype(np.int).tolist()
polygon2 = polygon2.astype(np.int).tolist()
# draw contour lines
for idx, coords in enumerate(contour[:-1]):
y1, x1, y2, x2 = coords + contour[idx + 1]
result_contour = cv2.line(result_contour, (x1, y1), (x2, y2),
(0, 255, 0), 1)
# draw polygon 1 lines
for idx, coords in enumerate(polygon1[:-1]):
y1, x1, y2, x2 = coords + polygon1[idx + 1]
result_polygon1 = cv2.line(result_polygon1, (x1, y1), (x2, y2),
(0, 255, 0), 1)
# draw polygon 2 lines
for idx, coords in enumerate(polygon2[:-1]):
y1, x1, y2, x2 = coords + polygon2[idx + 1]
result_polygon2 = cv2.line(result_polygon2, (x1, y1), (x2, y2),
(0, 255, 0), 1)
cv2.imwrite('contour_lines.png', result_contour)
cv2.imwrite('polygon1_lines.png', result_polygon1)
cv2.imwrite('polygon2_lines.png', result_polygon2)
Results
Python输出:
Contour shape: (849, 2)
Polygon 1 shape: (28, 2)
Polygon 2 shape: (9, 2)
Contour shape: (825, 2)
Polygon 1 shape: (31, 2)
Polygon 2 shape: (9, 2)
Contour shape: (1457, 2)
Polygon 1 shape: (9, 2)
Polygon 2 shape: (8, 2)
Contour shape: (879, 2)
Polygon 1 shape: (5, 2)
Polygon 2 shape: (5, 2)
Contour shape: (973, 2)
Polygon 1 shape: (5, 2)
Polygon 2 shape: (5, 2)
Contour shape: (224, 2)
Polygon 1 shape: (4, 2)
Polygon 2 shape: (4, 2)
Contour shape: (825, 2)
Polygon 1 shape: (13, 2)
Polygon 2 shape: (13, 2)
Contour shape: (781, 2)
Polygon 1 shape: (13, 2)
Polygon 2 shape: (13, 2)
轮廓线.png:
多边形1_线.png:
多边形2_线.png:
然后可以通过将毕达哥拉斯定理应用于坐标来计算线的长度:line_length = math.sqrt(abs(x2 - x1)**2 + abs(y2 - y1)**2)
。如果您想以数值形式获取线条的宽度,请查看以下答案“如何确定线的宽度?” https://stackoverflow.com/questions/22081908/how-to-determine-the-width-of-the-lines一些建议的方法。