You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 11:09:23