如何使用OpenCV计算盒子相对桌面中点的像素距离?
计算桌面与棕色盒子中点的像素距离
需求
编写OpenCV程序,计算桌面中点(红点)与棕色盒子中点(蓝点)之间的像素距离。最初计划通过cv2.findContours识别两者边界,获取中点后用dist.euclidean计算距离,但遇到干扰问题。
现有问题
当前代码会识别出电线、反光等无关轮廓,即使添加面积过滤条件也无法有效排除这些干扰,导致无法准确获取桌面和盒子的轮廓。
现有代码
初始轮廓检测代码
cv2.namedWindow("Object detector", cv2.WINDOW_NORMAL) image = cv2.imread(PATH_TO_IMAGE) cv2.resizeWindow('Object detector', 800, 600) im_bw = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,2)) morphology_img = cv2.morphologyEx(im_bw, cv2.MORPH_OPEN, kernel,iterations=1) edged = cv2.Canny(morphology_img, 50, 100) edged = cv2.dilate(edged, None, iterations=1) cnts= cv2.findContours(edged.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) (cnts, _) = contours.sort_contours(cnts) cv2.drawContours(image, cnts, -1, (0,255,0), 3) orig = image.copy() cv2.imshow('Object detector', image) cv2.waitKey(0) cv2.destroyAllWindows()
轮廓过滤代码
for (i, c) in enumerate(cnts): # if the contour is not sufficiently large, ignore it if cv2.contourArea(c) < 50000: continue else: box = cv2.minAreaRect(c) box = cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box) box = np.array(box, dtype="int") cv2.drawContours(image, [box.astype("int")], -1, (0, 255, 0), 1) (tl, tr, br, bl) = box (tltrX, tltrY) = midpoint(tl, tr) (blbrX, blbrY) = midpoint(bl, br) (tlblX, tlblY) = midpoint(tl, bl) (trbrX, trbrY) = midpoint(tr, br) # compute the Euclidean distance between the midpoints dA = dist.euclidean((tltrX, tltrY), (blbrX, blbrY)) dB = dist.euclidean((tlblX, tlblY), (trbrX, trbrY)) cv2.putText(image, "{:.1f}".format(dA), (int(tltrX - 15), int(tltrY - 10)), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (255, 255, 255), 2) cv2.putText(image, "{:.1f}".format(dB), (int(trbrX + 10), int(trbrY)), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (255, 255, 255), 2)
解决方案
核心思路是用颜色分割替代灰度边缘检测,结合形状过滤精准提取目标轮廓,避免无关干扰:
关键步骤
- 颜色分割提取盒子:利用棕色在HSV空间的特征范围,生成掩码精准定位盒子区域,排除非棕色的干扰。
- 形态学去噪:通过闭运算填充盒子内部空洞,消除小噪声点。
- 形状过滤:除了面积,还通过矩形度(轮廓面积与最小外接矩形面积的比值)过滤非矩形的干扰轮廓(如电线、反光)。
- 桌面中点简化计算:若桌面占满图像,直接取图像中心;若桌面未完全占屏,可通过浅色区域提取最大轮廓计算中心。
完整代码示例
import cv2 import numpy as np from scipy.spatial import distance as dist def midpoint(ptA, ptB): return ((ptA[0] + ptB[0]) * 0.5, (ptA[1] + ptB[1]) * 0.5) # 替换为你的图像路径 PATH_TO_IMAGE = "your_image_path.jpg" image = cv2.imread(PATH_TO_IMAGE) cv2.namedWindow("Result", cv2.WINDOW_NORMAL) cv2.resizeWindow("Result", 800, 600) orig = image.copy() # 1. 提取棕色盒子区域 hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 棕色的HSV范围(可根据实际图像微调) lower_brown = np.array([10, 100, 20]) upper_brown = np.array([20, 255, 200]) brown_mask = cv2.inRange(hsv, lower_brown, upper_brown) # 形态学闭运算,填充盒子内部空洞,去除小噪声 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) brown_mask = cv2.morphologyEx(brown_mask, cv2.MORPH_CLOSE, kernel, iterations=2) # 查找盒子轮廓并过滤 box_cnts = cv2.findContours(brown_mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0] box_center = None for c in box_cnts: area = cv2.contourArea(c) # 过滤小轮廓,同时检查矩形度 if area > 1000: rect = cv2.minAreaRect(c) box = cv2.boxPoints(rect) box = np.int0(box) # 计算矩形度:轮廓面积 / 最小外接矩形面积,接近1说明是规整矩形 rect_area = rect[1][0] * rect[1][1] solidity = area / rect_area if solidity > 0.8: # 获取盒子中点 box_center = (int(rect[0][0]), int(rect[0][1])) cv2.drawContours(orig, [box], 0, (255, 0, 0), 2) cv2.circle(orig, box_center, 5, (255, 0, 0), -1) break # 2. 获取桌面中点 # 方法1:若桌面占满图像,直接取图像中心 desktop_center = (image.shape[1] // 2, image.shape[0] // 2) # 方法2:若桌面未完全占屏,提取浅色区域的最大轮廓计算中心 # hsv_lower = np.array([0, 0, 200]) # hsv_upper = np.array([180, 50, 255]) # desktop_mask = cv2.inRange(hsv, hsv_lower, hsv_upper) # desktop_cnts = cv2.findContours(desktop_mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0] # desktop_cnt = max(desktop_cnts, key=cv2.contourArea) # desktop_moments = cv2.moments(desktop_cnt) # desktop_center = (int(desktop_moments["m10"] / desktop_moments["m00"]), int(desktop_moments["m01"] / desktop_moments["m00"])) cv2.circle(orig, desktop_center, 5, (0, 0, 255), -1) # 3. 计算并显示距离 if box_center is not None: pixel_distance = dist.euclidean(desktop_center, box_center) cv2.line(orig, desktop_center, box_center, (255, 0, 255), 2) cv2.putText(orig, f"像素距离: {pixel_distance:.1f} px", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.imshow("Result", orig) cv2.waitKey(0) cv2.destroyAllWindows()
注意事项
- 若棕色盒子的颜色与示例不同,需调整
lower_brown和upper_brown的HSV数值(可通过OpenCV的颜色拾取工具获取)。 - 矩形度阈值
0.8可根据实际情况微调,确保只保留盒子轮廓。
内容的提问来源于stack exchange,提问作者a_sid
相关产品推荐
相关产品推荐

