【问题标题】:How to get length of each side of a shape in an image?如何获取图像中形状每一边的长度?
【发布时间】:2021-06-09 17:54:11
【问题描述】:

假设我们有如下图片:

我想确定边的数量,以及每边的长度。

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

find number of sides 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)

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

【问题讨论】:

  • 我建议发布一个minimal reproducible example 来说明你到目前为止所做的事情。
  • 你的图片链接不见了。对于未来的访客,您能解决这个问题吗?

标签: python opencv image-processing computer-vision


【解决方案1】:

我的想法是获取形状的轮廓,尝试检测“角”,例如使用Harris corner detection,从轮廓中找到匹配点,使用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()

这是输出:

示例 #2:

输出:

示例 #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
----------------------------------------

【讨论】:

  • 感谢详细解释,我们可以得到顶点/角的数量和它们的角度吗..因为我们使用了cornerHarris ..我们可以修改相同的代码来得到那个。谢谢
  • 例如,找到个顶点/角的数量只是len(cand)len(final)。关于角度,我认为您无法从任何现有代码中获取该信息。恐怕你需要付出(更多)更多的努力,尤其是如果你想在直线和曲线边缘之间推导出一个角度。
  • 是的,这是一项艰巨的任务。我们得到的这些边缘值是像素值,我们可以在同一段代码中添加另一个 sn-p 进行校准,其中给出了一个已知边的方形对象单位大小...给定计算每个单位大小的像素数
  • 每个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(文本位置)仅用于调试文本输出。
  • @HansHirse 感谢您对此代码的回复任何帮助将不胜感激,请提供帮助。在此先感谢
猜你喜欢
  • 2013-03-02
  • 1970-01-01
  • 2017-04-21
  • 1970-01-01
  • 2021-10-07
  • 1970-01-01
  • 2020-02-04
  • 2011-10-01
  • 1970-01-01
相关资源
最近更新 更多