OpenCV+YOLO实现无人机影像着陆区状态检测
着陆区可用性判定实现方案
核心实现逻辑
- 先对非极大值抑制(NMS)输出的最终检测结果做分类归集:把属于UAP、UAİ的着陆区检测框、属于人员/其他障碍物的检测框分别存到两个独立列表里,不要在遍历画框的过程中零散判断。
- 对每一个识别到的着陆区框,逐一和所有障碍物检测框做矩形重叠校验:只要有任意一个障碍物有一定比例的面积落在当前着陆区范围内,就判定该着陆区被占用。
- 可视化和结果输出环节,对着陆区做差异化标记:可用着陆区画绿色边框配可用提示,被占用的着陆区画红色边框配不可用提示,同时在控制台打印对应判定结果。
判定逻辑原理
俯拍视角下YOLO输出的检测框都是和图像边缘平行的轴对齐矩形,直接计算两个矩形的重叠面积占比就能判断障碍物是否进入着陆区,计算量极小,完全适配OpenCV部署的实时性要求。
注意避坑:不要用「障碍物中心点是否落在着陆区内」做判断规则——如果障碍物大半个身子进入着陆区、中心点刚好在着陆区边框外时,会出现漏判;用重叠面积占比做判定的容错率更高,一般把重叠阈值设为5%~10%即可,既能识别到障碍物入侵,又能避免两个框边缘刚好挨在一起产生的误判。
修改完成的可运行代码
import numpy as np import cv2 # 计算两个xywh格式矩形的重叠面积占着陆区面积的比例 def calc_overlap_ratio(landing_box, obstacle_box): # 把xywh(左上角x,左上角y,宽,高)转成x1y1x2y2(左上角+右下角坐标) lx1, ly1, lw, lh = landing_box lx2, ly2 = lx1 + lw, ly1 + lh ox1, oy1, ow, oh = obstacle_box ox2, oy2 = ox1 + ow, oy1 + oh # 计算交集区域坐标 inter_x1 = max(lx1, ox1) inter_y1 = max(ly1, oy1) inter_x2 = min(lx2, ox2) inter_y2 = min(ly2, oy2) # 无重叠直接返回0 if inter_x1 >= inter_x2 or inter_y1 >= inter_y2: return 0.0 inter_area = (inter_x2 - inter_x1) * (inter_y2 - inter_y1) landing_area = lw * lh return inter_area / landing_area # step 1 - 加载模型 net = cv2.dnn.readNet('best.onnx') # step 2 - 预处理图像获取推理结果 def format_yolov5(frame): row, col, _ = frame.shape _max = max(col, row) result = np.zeros((_max, _max, 3), np.uint8) result[0:row, 0:col] = frame return result image = cv2.imread('datasets/Test/frame_014348.jpg') input_image = format_yolov5(image) # 补边成正方形输入 blob = cv2.dnn.blobFromImage(input_image , 1/255.0, (640, 640), swapRB=True) net.setInput(blob) predictions = net.forward() # step 3 - 解析推理结果 class_ids = [] confidences = [] boxes = [] output_data = predictions[0] image_width, image_height, _ = input_image.shape x_factor = image_width / 640 y_factor = image_height / 640 for r in range(25200): row = output_data[r] confidence = row[4] if confidence >= 0.4: classes_scores = row[5:] _, _, _, max_indx = cv2.minMaxLoc(classes_scores) class_id = max_indx[1] if (classes_scores[class_id] > .25): confidences.append(confidence) class_ids.append(class_id) x, y, w, h = row[0].item(), row[1].item(), row[2].item(), row[3].item() left = int((x - 0.5 * w) * x_factor) top = int((y - 0.5 * h) * y_factor) width = int(w * x_factor) height = int(h * y_factor) box = np.array([left, top, width, height]) boxes.append(box) # 加载类别列表 class_list = [] with open("config.txt", "r") as f: class_list = [cname.strip() for cname in f.readlines()] # NMS去重 indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.25, 0.45) # 归集最终检测结果,拆分着陆区和障碍物 landing_boxes = [] landing_confs = [] obstacle_boxes = [] obstacle_confs = [] obstacle_classes = [] for i in indexes: cls_id = class_ids[i] cls_name = class_list[cls_id] # 两类着陆区归到着陆区列表 if cls_name in ["UAP", "UAİ"]: landing_boxes.append(boxes[i]) landing_confs.append(confidences[i]) # 其余类别全部算障碍物 else: obstacle_boxes.append(boxes[i]) obstacle_confs.append(confidences[i]) obstacle_classes.append(cls_id) OVERLAP_THRESHOLD = 0.05 # 重叠面积超过着陆区5%就算占用 # 先画障碍物(避免遮挡着陆区标记) for i in range(len(obstacle_boxes)): box = obstacle_boxes[i] cls_id = obstacle_classes[i] cv2.rectangle(image, box, (0,0,255), 2) cv2.rectangle(image, (box[0], box[1] - 20), (box[0] + box[2], box[1]), (0,0,255), -1) cv2.putText(image, class_list[cls_id], (box[0], box[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, .5, (255,255,255)) # 逐个判定着陆区状态并标记 for idx, l_box in enumerate(landing_boxes): is_available = True # 遍历所有障碍物检查重叠 for o_box in obstacle_boxes: overlap_ratio = calc_overlap_ratio(l_box, o_box) if overlap_ratio >= OVERLAP_THRESHOLD: is_available = False break # 根据状态选标记颜色和文字 if is_available: box_color = (0,255,0) tip_text = "Landing area is available" print(f"检测到第{idx+1}个着陆区:{tip_text}") else: box_color = (0,0,255) tip_text = "Landing area is not suitable" print(f"检测到第{idx+1}个着陆区:{tip_text}") # 画着陆区框和提示 cv2.rectangle(image, l_box, box_color, 2) cv2.rectangle(image, (l_box[0], l_box[1] - 20), (l_box[0] + l_box[2], l_box[1]), box_color, -1) cv2.putText(image, tip_text, (l_box[0], l_box[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, .5, (0,0,0)) # 没检测到任何着陆区的提示 if len(landing_boxes) == 0: print("未检测到有效着陆区") cv2.imwrite("Saving/image.png", image) cv2.imshow("output", image) cv2.waitKey()
可调参数说明
OVERLAP_THRESHOLD:重叠判定阈值,实际使用时可根据场景调整:需要障碍物刚接触着陆区就判定不可用,就把值调小到0.02;需要障碍物进入区域较深才判定占用,就把值调大到0.15。- 代码默认将UAP、UAİ之外的所有检测类别归为障碍物,后续如果需要新增豁免类别,直接修改类别归集环节的判断逻辑即可,核心重叠判定代码不需要改动。
内容的提问来源于stack exchange,提问作者Tony Stark
相关产品推荐
相关产品推荐

