如何获取图像中形状每边的长度?

k_p*_*k_p 2 python opencv image-processing computer-vision

假设我们有如下图像:

[![输入][1]][1]

我想确定边数和每边的长度。

在图像中,我们将三个边缘作为直线,上面的一个是弯曲的边缘。我能够使用 Canny 边缘检测找到三个直边的长度。我们可以有四个顶点坐标,我们可以计算出三个直边/线的长度,但无法求出曲线边的长度。

在图像python中找到边数和每边的长度以及顶点数这是获取图像中边数的一个很好的答案,我们通过上面链接中的代码获得顶点坐标。进一步使用坐标获取边的长度,如果边是直线,我们可以使用下面的代码来获取长度:

for pt in zip(points,np.roll(points,-1,0)):
#     print( pt )
    x = pt[0][0]
    y = pt[1][0]
    d = math.sqrt((x[1]-y[1])*(x[1]-y[1]) + (x[0]-y[0])*(x[0]-y[0]))
    print('length between point:',x,'and', y,'is', d)
Run Code Online (Sandbox Code Playgroud)

为了简化我的问题:如果图像形状有任何弯曲的边,我想找到图像中的形状有多少条边/边,以及它们各自的长度。

Han*_*rse 7

我的想法是获取形状的轮廓,尝试检测“角点”,例如使用Harris 角点检测,从轮廓中找到匹配点,并使用cv2.arcLength.

以下extract_and_measure_edges方法的输入需要一些二值化轮廓图像,例如从您的实际输入图像派生的图像:

输入

因此,预处理必须适应输入图像,这超出了我的回答范围!在下面的代码中,预处理是针对给定的输入图像,而不是针对其他两个示例。

import cv2
import matplotlib.pyplot as plt
import numpy as np


def extract_and_measure_edges(img_bin):

    # Detect possible corners, and extract candidates
    dst = cv2.cornerHarris(img_bin, 2, 3, 0.04)
    cand = []
    for i, c in enumerate(np.argwhere(dst > 0.1 * np.max(dst)).tolist()):
        c = np.flip(np.array(c))
        if len(cand) == 0:
            cand.append(c)
        else:
            add = True
            for j, d in enumerate(cand):
                d = np.array(d)
                if np.linalg.norm(c - d) < 5:
                    add = False
                    break
            if add:
                cand.append(c)

    # Get indices of actual, nearest matching contour points
    corners = sorted([np.argmin(np.linalg.norm(c - cnt.squeeze(), axis=1))
                      for c in cand])

    # Extract edges from contour, and measure their lengths
    output = cv2.cvtColor(np.zeros_like(img_bin), cv2.COLOR_GRAY2BGR)
    for i_c, c in enumerate(corners):
        if i_c == len(corners) - 1:
            edge = np.vstack([cnt[c:, ...], cnt[0:corners[0], ...]])
        else:
            edge = cnt[c:corners[i_c + 1], ...]

        loc = tuple(np.mean(edge.squeeze(), axis=0, dtype=int).tolist())
        color = tuple(np.random.randint(0, 255, 3).tolist())
        length = cv2.arcLength(edge, False)
        cv2.polylines(output, [edge], False, color, 2)
        cv2.putText(output, '{:.2f}'.format(length), loc, cv2.FONT_HERSHEY_COMPLEX, 0.5, color, 1)
    return output


# Read and pre-process image, extract contour of shape
# TODO: MODIFY TO FIT YOUR INPUT IMAGES
img = cv2.imread('2B2m4.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thr = cv2.threshold(gray, 16, 255, cv2.THRESH_BINARY_INV)[1]
cnts = cv2.findContours(thr, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnt = max(cnts, key=cv2.contourArea)
thr = cv2.drawContours(np.zeros_like(thr), [cnt], -1, 255, 1)

# Extract and measure edges, and visualize output
out = extract_and_measure_edges(thr)
plt.figure(figsize=(18, 6))
plt.subplot(1, 3, 1), plt.imshow(img), plt.title('Original input image')
plt.subplot(1, 3, 2), plt.imshow(thr, cmap='gray'), plt.title('Contour needed')
plt.subplot(1, 3, 3), plt.imshow(out), plt.title('Results')
plt.tight_layout(), plt.show()
Run Code Online (Sandbox Code Playgroud)

这是输出:

输出 #1

示例#2:

示例#2

输出:

输出#2

示例#3:

示例 #3

输出:

输出 #3

(我没有注意正确的颜色排序......)

----------------------------------------
System information
----------------------------------------
Platform:      Windows-10-10.0.19041-SP0
Python:        3.9.1
PyCharm:       2021.1.1
Matplotlib:    3.4.2
NumPy:         1.19.5
OpenCV:        4.5.2
----------------------------------------
Run Code Online (Sandbox Code Playgroud)

  • 每个“c”(角点)都是实际轮廓元素的索引,如下所示:“... - (x, y) - (c2x, c2y) - (x, y) - ... - ( x, y) - (c3x, c3y) - (x, y) - ...`。使用“cnt[c:corners[i_c + 1]”,您只需从两个角之间的实际轮廓中选择所有元素,例如“(c2x, c2y)”和“(c3x, c3y)”之间的每个点。最后一个角有一个特殊情况,因为轮廓元素不一定从角开始。所以,‘edge’只是轮廓‘cnt’的一部分,对应于当前的边缘。`loc`(文本位置)仅用于调试文本输出。 (2认同)