如何在OpenCV Python中对巡线轮廓进行水平分割?
解决OpenCV黑线巡线机器人急弯时质心偏离问题(水平分割轮廓方案)
当前基于OpenCV的黑线巡线逻辑通过识别黑线最大轮廓并计算质心,对比图像中心控制转向,但遇到黑线急弯时,整体轮廓的质心会偏离实际黑线区域。以下是通过水平分割图像为多个小区域,分别提取每个区域内的黑线轮廓并计算质心的解决方案,无需拉近相机。
核心思路
- 将捕获的图像按高度平均分割为多个水平条带(比如3个,可根据实际需求调整数量)
- 对每个水平条带单独处理:提取黑线掩码、寻找轮廓、计算该区域内的黑线质心
- 优先参考靠近机器人的下方条带质心(更贴近当前需要转向的方向),或综合多个条带的偏移量来调整机器人转向
修改后的代码
import numpy as np import cv2 import math # HSV格式的颜色阈值(黑线) lb = np.array([0,32,0]) ub = np.array([179, 255, 93]) # 忽略此函数(用于巡线的其他部分) def percentage_calculator(boundaries, colour, image): for(lower, upper) in boundaries: lower = np.array(lower) upper = np.array(upper) mask = cv2.inRange(image, lower, upper) output = cv2.bitwise_and(image, image, mask = mask) tot_pixel = image.size pixel = np.count_nonzero(output) percentage = round(pixel * 100 / tot_pixel, 2) print(colour + " pixels: " + str(pixel)) print("Total pixels: " + str(tot_pixel)) print("Percentage of " + colour + " pixels: " + str(percentage) + "%") green_boundaries = [([75, 52, 60], [106, 255, 255])] cap = cv2.VideoCapture(1) # 图像尺寸参数 frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) Xmid = frame_width // 2 Ymid = frame_height // 2 # 水平分割的区域数量,可根据实际调整 split_count = 3 strip_height = frame_height // split_count while True: _, img = cap.read() hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 获取黑线掩码 mask1 = cv2.inRange(hsv, lb, ub) black_result = cv2.bitwise_and(hsv, hsv, mask=mask1) # 存储每个分割区域的质心 centroids = [] # 遍历每个水平分割区域 for i in range(split_count): # 计算当前条带的上下边界 y_start = i * strip_height y_end = (i + 1) * strip_height if i != split_count-1 else frame_height # 提取当前条带的掩码 strip_mask = mask1[y_start:y_end, :] # 在当前条带中寻找轮廓 contours, _ = cv2.findContours(strip_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) if not contours: continue # 找到当前条带中最大的黑线轮廓 max_contour = max(contours, key=cv2.contourArea) # 简化轮廓 approx_contour = cv2.approxPolyDP(max_contour, 0.05 * cv2.arcLength(max_contour, True), True) # 计算质心(注意坐标要转换回原图坐标系) M = cv2.moments(approx_contour) if M["m00"] == 0: continue cX = int(M["m10"] / M["m00"]) # 质心的y坐标需要加上条带的起始y值 cY = int(M["m01"] / M["m00"]) + y_start centroids.append((cX, cY)) # 在原图上标记当前条带的质心和分割线 cv2.circle(img, (cX, cY), 5, (0, 0, 255) if i == split_count-1 else (0, 255, 0), -1) cv2.line(img, (0, y_start), (frame_width, y_start), (255, 255, 255), 1) # 图像中心标记 cv2.circle(img, (Xmid, Ymid), 5, (255, 255, 0), -1) # 如果有质心,优先使用最下方区域的质心计算偏移(更贴近机器人当前位置) if centroids: # 取最后一个(最下方)的质心 target_cX, target_cY = centroids[-1] cv2.line(img, (target_cX, target_cY), (Xmid, Ymid), (255, 0, 0), 2) # 计算偏移距离 distance = math.sqrt((Xmid - target_cX) ** 2 + (Ymid - target_cY) ** 2) # 这里可以加入机器人转向控制逻辑,比如根据target_cX与Xmid的差值调整左右轮速度 cv2.imshow('black', black_result) cv2.imshow('img', img) k = cv2.waitKey(30) & 0xff if k == 27: break cap.release() cv2.destroyAllWindows()
代码说明
split_count:控制水平分割的区域数量,建议从3开始测试,根据实际巡线效果调整- 每个水平条带单独处理轮廓,避免急弯时整体轮廓的质心偏离
- 优先使用最下方条带的质心作为转向参考,因为该区域更接近机器人的当前位置,能更准确反映即时转向需求
- 可扩展逻辑:如果多个条带都有质心,可通过加权平均(比如下方条带权重更高)计算综合偏移量,提升稳定性
内容的提问来源于stack exchange,提问作者Noah Robb
相关产品推荐
相关产品推荐

