首页 > 解决方案 > 使用openCV找到一个盒子的7个顶点

问题描述

我不知道这个问题是否在这里重复。如果是的话我很抱歉..

在此处输入图像描述

我有一个盒子可以看到 H、W、L 视图。我了解获取顶点的步骤,但是网络中的大多数示例仅描述了如何从 2D 平面获取 4 个顶点。所以我的问题是,如果我们想要获得 7 个顶点(如上图)并在 numpy 中处理它怎么办?如何区分高点和低点?

在此处输入图像描述

我将使用 Python 来确定这一点。

标签: numpyopencvopencv-contour

解决方案


这是我尝试获得 3d 矩形的 8 个角。我屏蔽了 HSV 颜色空间的饱和度通道,因为它会分离出白色。

我使用 findContours 来获取框的轮廓,然后使用 approxPolyDP 来获取六点近似值(六个可见角)。

从那里我通过平行四边形近似来近似两个“隐藏”的角落。对于每个点,我向后看两个点并创建第四个点,该点将与该边形成平行四边形。然后我取这些平行四边形点的质心来猜测角落。我希望取点的质心有助于消除平行四边形假设和透视扭曲之间的误差,但它做得很糟糕。

如果您需要更好的近似值,可能有一些方法可以估计透视扭曲以获得角落。

在此处输入图像描述

import cv2
import numpy as np
import random

def tup(point):
    return (int(point[0]), int(point[1]));

# load image
img = cv2.imread("box.jpg");

# reduce size to fit on screen
scale = 0.25;
h,w = img.shape[:2];
h = int(scale*h);
w = int(scale*w);
img = cv2.resize(img, (w,h));
copy = np.copy(img);

# convert to hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV);
h,s,v = cv2.split(hsv);

# make mask
mask = cv2.inRange(s, 30, 255);

# dilate and erode to get rid of small holes
kernel = np.ones((5,5), np.uint8);
mask = cv2.dilate(mask, kernel, iterations = 1);
mask = cv2.erode(mask, kernel, iterations = 1);

# contours # OpenCV 3.4, in OpenCV 2 or 4 it returns (contours, _)
_, contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);
contour = contours[0]; # just take the first one

# approx until 6 points
num_points = 999999;
step_size = 0.01;
percent = step_size;
while num_points >= 6:
    # get number of points
    epsilon = percent * cv2.arcLength(contour, True);
    approx = cv2.approxPolyDP(contour, epsilon, True);
    num_points = len(approx);

    # increment
    percent += step_size;

# step back and get the points
# there could be more than 6 points if our step size misses it
percent -= step_size * 2;
epsilon = percent * cv2.arcLength(contour, True);
approx = cv2.approxPolyDP(contour, epsilon, True);

# draw contour
cv2.drawContours(img, [approx], -1, (0,0,200), 2);

# draw points
for point in approx:
    point = point[0]; # drop extra layer of brackets
    center = (int(point[0]), int(point[1]));
    cv2.circle(img, center, 4, (150, 200, 0), -1);

# do parallelogram approx to get the two "hidden" corners to complete our 3d rectangle
proposals = [];
size = len(approx);
for a in range(size):
    # get points backwards
    two = approx[a - 2][0];
    one = approx[a - 1][0];
    curr = approx[a][0];

    # get vector from one -> two
    dx = two[0] - one[0];
    dy = two[1] - one[1];
    hidden = [curr[0] + dx, curr[1] + dy];
    proposals.append([hidden, curr, a, two]);

    # debug draw
    c = np.copy(copy);
    cv2.circle(c, tup(two), 4, (255, 0, 0), -1);
    cv2.circle(c, tup(one), 4, (0,255,0), -1);
    cv2.circle(c, tup(curr), 4, (0,0,255), -1);
    cv2.circle(c, tup(hidden), 4, (255,255,0), -1);
    cv2.line(c, tup(two), tup(one), (0,0,200), 1);
    cv2.line(c, tup(curr), tup(hidden), (0,0,200), 1);
    cv2.imshow("Mark", c);
    cv2.waitKey(0);

# draw proposals
for point in proposals:
    point = point[0];
    center = (point[0], point[1]);
    cv2.circle(img, center, 4, (200, 100, 0), -1);

# group points and sum up points
hidden_corners = [[0,0], [0,0]];
for point in proposals:
    # get index and update hidden corners
    index = point[2] % 2;
    pos = point[0];
    hidden_corners[index][0] += pos[0];
    hidden_corners[index][1] += pos[1];

# divide to get centroid
hidden_corners[0][0] /= 3.0;
hidden_corners[0][1] /= 3.0;
hidden_corners[1][0] /= 3.0;
hidden_corners[1][1] /= 3.0;

# draw new points
for point in proposals:
    # unpack
    pos = point[0];
    parent = point[1];
    index = point[2] % 2;
    source = point[3];

    # draw
    color = [random.randint(0, 150) for a in range(3)];
    cv2.line(img, tup(hidden_corners[index]), tup(parent), (0,0,200), 2);
    cv2.line(img, tup(pos), tup(parent), color, 1);
    cv2.line(img, tup(pos), tup(source), color, 1);
    cv2.circle(img, tup(hidden_corners[index]), 4, (200, 200, 0), -1);
    
# show
cv2.imshow("Image", img);
cv2.imshow("Mask", mask);
cv2.waitKey(0);

推荐阅读