numpy - 使用openCV找到一个盒子的7个顶点
问题描述
我不知道这个问题是否在这里重复。如果是的话我很抱歉..
我有一个盒子可以看到 H、W、L 视图。我了解获取顶点的步骤,但是网络中的大多数示例仅描述了如何从 2D 平面获取 4 个顶点。所以我的问题是,如果我们想要获得 7 个顶点(如上图)并在 numpy 中处理它怎么办?如何区分高点和低点?
我将使用 Python 来确定这一点。
解决方案
这是我尝试获得 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);
推荐阅读
- java - 我怎样才能在等待投票中获得剩余时间?
- python - 如何通过用户 URL 下载 .apk 并通过 python 从中获取元数据
- vulkan - Vulkan 设备丢失 = 缺少同步,但我不知道怎么做
- python - Shapely / Pyproj 查找从纬度和经度创建的多边形的面积(以 m^2 为单位)
- android - 在 Mixpanel 上跟踪推荐链接转换
- react-native - React Native 和 react-native-ble-plx
- android - 以编程方式设置属性格式颜色android
- google-docs - 从 Google Doc 中获取值
- plot - 绘制最小二乘法
- arduino - 在Arduino中通过串行控制伺服