繁体   English   中英

如何使用 OpenCV python 绘制平行于最小面积矩形宽度的线连接相对的轮廓点

[英]How to draw lines parallel to the width of minimum area rectangle joing opposite contour points using OpenCV python

我正在尝试绘制与轮廓上绘制的最小面积矩形的宽度平行的线,这些矩形连接轮廓的相对点。 我打算用这个做的是找到轮廓的真实宽度。 为了找到对象的实际宽度,我目前在图像中使用参考 object 并找到每厘米像素值,该值用于将像素中获得的宽度值转换为厘米。要找到宽度,我使用的是最小面积矩形方法出现在 OpenCV 中,我还尝试找到最左边和最右边的轮廓点,然后将它们连接起来以找到长度,但这些值总是被过度预测。 以下是我用来查找值的代码。

commodity_mask = copy.deepcopy(r['masks'][i]).astype(np.uint8)
im, contours_m, hierarchy  = cv2.findContours(commodity_mask, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
contours = max(contours_m, key=cv2.contourArea)leftmost = tuple(contours[contours[:,:,0].argmin()][0])
rightmost = tuple(contours[contours[:,:,0].argmax()][0])
topmost = tuple(contours[contours[:,:,1].argmin()][0])
bottommost = tuple(contours[contours[:,:,1].argmax()][0])
cv2.circle(minrect, leftmost, 8, (0, 50, 255), -1)
cv2.circle(minrect, rightmost, 8, (0, 50, 255), -1)
cv2.line(minrect, leftmost, rightmost, (0,0,255),2)
dist = np.sqrt((leftmost[0]-rightmost[0])**2 + (leftmost[1]-rightmost[1])**2)
dist = dist*ratio     #finding distance b/w leftmost and rightmost contour point and converting them to pixels.

commodity_rect = cv2.minAreaRect(commodity_contour)  # (top-left corner(x,y), (width, height), angle of rotation)
cv2.drawContours(minrect, [np.int0(cv2.boxPoints(commodity_rect))], 0, (0, 255, 0), 2)
(xrect, yrect), (wrect, hrect), alpha = commodity_rect
print('Width: ', wrect * ratio, 'Height: ', hrect * ratio)

r['masks'][i]值是从图像中获得的第 i 个轮廓。

到目前为止,我已经实现了以下图像。

轮廓

绿色框是在轮廓上绘制的最小面积矩形,红线连接轮廓的最左边和最右边的点。

我想做的是以下。

在此处输入图像描述

绘制平行于矩形宽度的线(黄色线)连接轮廓的相对点,然后我可以找到它们的长度并使用它们来找到轮廓的宽度。

这是原始图像,没有绘制矩形。

在此处输入图像描述

这将帮助您找到所需的线坐标。 我在轮廓点上使用了cv2.pointPolygonTest来找到您想要的点。 更多关于cv2.pointPolygonTest 在这里

import numpy as np
import cv2

image = cv2.imread('image.png')

image = cv2.resize(image, (800, 800))
output_image = image.copy()

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

HSV = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
low = np.array([33, 91, 21])
high = np.array([253, 255, 255])

mask = cv2.inRange(HSV, low, high)
contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
sorted_contour = sorted(contours, key=lambda x: cv2.contourArea(x), reverse=True)

# As I know there is 6 max contour we want I'm selecting 6 contour only to creating perfact mask
selected_contour = sorted_contour[:6]
blank_mask = np.zeros_like(mask)
mask = cv2.drawContours(blank_mask, selected_contour, -1, 255, -1)
cv2.imshow("mask", mask)

for i, cnt in enumerate(selected_contour):
    # find min enclosing rect
    commodity_rect = cv2.minAreaRect(cnt)
    cv2.drawContours(output_image, [np.int0(cv2.boxPoints(commodity_rect))], 0, (0, 255, 0), 2)

    # find center point of rect
    M = cv2.moments(cnt)
    cX = int(M["m10"] / M["m00"])
    cY = int(M["m01"] / M["m00"])
    cv2.circle(output_image, (cX, cY), 5, (0, 255, 255), -1)

    selceted_points = [[px, cY] for [[px, py]] in cnt if cv2.pointPolygonTest(cnt, (px, cY), measureDist=False) == 0]

    left_point = min(selceted_points, key=lambda x: x[0])
    right_point = max(selceted_points, key=lambda x: x[0])

    print(f"\nfor {i}th contour : ")
    print("left_point : ", left_point)
    print("right_point : ", right_point)

    cv2.circle(output_image, tuple(left_point), 3, (255, 0, 0), -1)
    cv2.circle(output_image, tuple(right_point), 3, (255, 0, 0), -1)
    cv2.line(output_image, tuple(left_point), tuple(right_point), (0, 0, 255), 1)

cv2.imshow("output_image", output_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Output:

for 0th contour : 
left_point :  [606, 613]
right_point :  [786, 613]

for 1th contour : 
left_point :  [73, 233]
right_point :  [211, 233]

for 2th contour : 
left_point :  [329, 248]
right_point :  [501, 248]

for 3th contour : 
left_point :  [58, 637]
right_point :  [246, 637]

for 4th contour : 
left_point :  [364, 600]
right_point :  [508, 600]

for 5th contour : 
left_point :  [605, 237]
right_point :  [753, 237]

蒙版图片: 在此处输入图像描述

Output 图片: 在此处输入图像描述

在解决方案一中,我误解了一些东西并在边界框上找到了一条水平线,这是您想要的线坐标。

import numpy as np
import cv2


def get_order_points(pts):
    # first - top-left,
    # second - top-right
    # third - bottom-right
    # fourth - bottom-left

    rect = np.zeros((4, 2), dtype="int")

    # top-left point will have the smallest sum
    # bottom-right point will have the largest sum
    s = pts.sum(axis=1)
    rect[0] = pts[np.argmin(s)]
    rect[2] = pts[np.argmax(s)]

    # top-right point will have the smallest difference
    # bottom-left will have the largest difference
    diff = np.diff(pts, axis=1)
    rect[1] = pts[np.argmin(diff)]
    rect[3] = pts[np.argmax(diff)]

    return rect


image = cv2.imread('image.png')

image = cv2.resize(image, (800, 800))
output_image = image.copy()

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

HSV = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
low = np.array([33, 91, 21])
high = np.array([253, 255, 255])

mask = cv2.inRange(HSV, low, high)
contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
sorted_contour = sorted(contours, key=lambda x: cv2.contourArea(x), reverse=True)

# As I know there is 6 max contour we want I'm selecting 6 contour only to creating perfact mask
selected_contour = sorted_contour[:6]
blank_mask = np.zeros_like(mask)
mask = cv2.drawContours(blank_mask, selected_contour, -1, 255, -1)
cv2.imshow("mask", mask)
cv2.imwrite("mask.png", mask)

for i, cnt in enumerate(selected_contour):
    # find min enclosing rect
    commodity_rect = cv2.minAreaRect(cnt)
    bounding_rect_points = np.array(cv2.boxPoints(commodity_rect), dtype=np.int)
    cv2.drawContours(output_image, [bounding_rect_points], 0, (0, 255, 0), 2)

    tl, tr, br, bl = get_order_points(bounding_rect_points)

    left_point = (tl + bl) // 2
    right_point = (tr + br) // 2

    print(f"\nfor {i}th contour : ")
    print("left_point : ", left_point)
    print("right_point : ", right_point)

    cv2.circle(output_image, tuple(left_point), 3, (255, 0, 0), -1)
    cv2.circle(output_image, tuple(right_point), 3, (255, 0, 0), -1)
    cv2.line(output_image, tuple(left_point), tuple(right_point), (0, 0, 255), 1)

cv2.imshow("output_image", output_image)
cv2.imwrite("output_image.png", output_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Output:

for 0th contour : 
left_point :  [606 597]
right_point :  [785 622]

for 1th contour : 
left_point :  [ 64 236]
right_point :  [214 236]

for 2th contour : 
left_point :  [325 201]
right_point :  [507 295]

for 3th contour : 
left_point :  [ 56 638]
right_point :  [244 619]

for 4th contour : 
left_point :  [359 574]
right_point :  [504 625]

for 5th contour : 
left_point :  [605 241]
right_point :  [753 241]

输出图像: 在此处输入图像描述

你有没有尝试过这样的事情:

commodity_rect = cv2.minAreaRect(commodity_contour) #Find the minAreaRect for each contour
minAxis = round(min(commodity_rect[1])) #Find minor axis size
maxAxis = round(max(commodity_rect[1])) #Find major axis size

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM