手把手教你使用OpenCV,ONNXRuntime部署yolov5旋转目标检测

✨博主介绍

🌊 作者主页:苏州程序大白

🌊 作者简介:🏆CSDN人工智能域优质创作者🥇,苏州市凯捷智能科技有限公司创始之一,目前合作公司富士康、歌尔等几家新能源公司

💬如果文章对你有帮助,欢迎关注、点赞、收藏

💅 有任何问题欢迎私信,看到会及时回复
💅关注苏州程序大白,分享粉丝福利

学习旋转角度

在无人机/遥感目标检测领域,拍摄到的物体通常是,如下图中红色矩形框里的飞机,这是用ultralytics/yolov5检测到的目标,很明显水平矩形检测框在检测旋转目标时,检测框里包含了无关背景区域,因此ultralytics/yolov5检测旋转目标,不是一个理想的解决方案。
在这里插入图片描述
在ultralytics/yolov5的检测头里添加一个学习旋转角度angle的分支,考虑到旋转角度是分布在0到180度之内的一个整数值,因此把旋转角度angle做为一个分类问题来学习。

这时模型输出的每一个候选检测框里的信息是 x,y,w,h,box_score,class_score,angle_score 这种形式的,其中x,y,w,h表示检测框的中心点坐标,宽度和高度,box_score表示检测框的置信度,class_score表示类别置信度,假如在coco数据集上训练的,coco数据集里有80个类别,那么class_score是一个长度为80的数组,它里面第i个元素表示第i个类别的置信度。angle_score表示倾斜角度的置信度,它是一个长度为180的数组,它里面第i个元素表示检测矩形框的倾斜角等于i度的置信度。那么这时检测框里包含信息的长度是5+80+180=265,这就使得yolov5的检测头里的最后3个1x1卷积的输出通道数也别大,这时无疑会增大模型的计算量。

我在github发布了一套分别使用OpenCV,ONNXRuntime部署yolov5旋转目标检测的demo程序,分别包含C++和Python两个版本的程序。程序输出矩形框的中心点坐标(x, y),矩形框的高宽(h, w),矩形框的倾斜角,源码地址是:https://github.com/hpc203/rotate-yolov5-opencv-onnxrun

在编写后处理NMS函数时,需要注意的一个问题是用C++编程实现计算两个旋转矩形框的重叠面积,实现细节可以参考源码。

在这里插入图片描述

学习旋转角度的余弦值

在第1部分里讲到学习旋转角度会存在的一个问题是:由于旋转角度是分布在0到180度之内的一个整数值,也就是说有180种取值的可能,把旋转角度作为一个分类任务,会使得yolov5的检测头里的最后3个1x1卷积的输出通道数也别大,这时会增大模型的计算量。注意到角度的余弦值是分布在-1到1之间的小数值,那么可以考虑学习旋转角度的余弦值和正弦值。如下图所示:

在这里插入图片描述

损失函数是SmoothL1Loss,示意图如下:

在这里插入图片描述
这时,模型输出的每个候选框里包含的信息是x,y,w,h, cos,sin, box_score,class_score这种形式的,其中cos表示检测矩形框的倾斜角的余弦值,sin表示检测矩形框的倾斜角的正弦值。假如在coco数据集上训练的,那么这时候选框的长度是4+2+1+80=87,很明显这时模型的计算量会减少很多。

程序输出矩形框的中心点坐标(x, y),矩形框的高宽(h, w),矩形框的倾斜角的余弦值和正弦值的GitHub源码地址
在这里插入图片描述

不规则四边形的目标检测

最近在极市平台的打榜项目里有车牌识别项目,项目里要求检测车牌的4个角点。可以仿照第1部分里的思路,在ultralytics/yolov5的检测头里添加一个学习四个角点的分支,这时,模型输出的每个候选框里包含的信息是 x,y,w,h, box_score, x1,y1,x2,y2,x3,y3,x4,y4, class_score这种形式,其中x1,y1,x2,y2,x3,y3,x4,y4表示4个角点的坐标值x和y。这时候的模型就能满足项目的需求,但是注意到水平矩形框的位置信息x,y,w,h在项目里并没有起作用,因此水平矩形框的位置信息是一个冗余的信息。

接着对模型继续做修改,使模型输出的是x1,y1,x2,y2,x3,y3,x4,y4,box_score,class_score,也就是去掉box分支,这时可以减小模型体积,减少计算量。修改的地方在yolov5的检测头和loss函数,图解如下:

build_targets函数和检测头Detect
在这里插入图片描述
4个角点逆时针排序
在这里插入图片描述
polygon box loss
在这里插入图片描述
程序输出不规则四边形的4个角点的坐标x,y的源码地址

在编写后处理NMS函数时,一个棘手的问题是计算不规则四边形的面积,计算两个不规则四边形的重叠面积。如果是编写Python程序,可以直接调用shapely库解决,如果是编写C++程序,那就比较棘手了,C++编程实现的细节可以参考源码。

在这里插入图片描述
为了便于直观理解第2节里的旋转目标检测和第3节里的不规则四边形目标检测,它们的差异,图解如下:
在这里插入图片描述

💫点击直接资料领取💫

在这里插入图片描述

❤️关注苏州程序大白公众号❤️


👇 👇👇

  • 9
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
好的,下面是使用OpenCVONNXRuntime部署YOLOV7实时目标检测的代码: ```python import cv2 import numpy as np import onnxruntime as ort # 加载模型 model_path = "yolov7.onnx" session = ort.InferenceSession(model_path) # 输入、输出名 input_name = session.get_inputs()[0].name output_names = [session.get_outputs()[i].name for i in range(len(session.get_outputs()))] # 类别名称 class_names = ["person", "car", "truck", "bus"] # 预处理函数 def preprocess(image, target_shape): # 图像缩放 h, w = image.shape[:2] scale = min(target_shape[0] / h, target_shape[1] / w) new_h, new_w = int(h * scale), int(w * scale) image_resized = cv2.resize(image, (new_w, new_h)) # 图像填充 pad_h = target_shape[0] - new_h pad_w = target_shape[1] - new_w top, bottom = pad_h // 2, pad_h - pad_h // 2 left, right = pad_w // 2, pad_w - pad_w // 2 image_padded = cv2.copyMakeBorder(image_resized, top, bottom, left, right, cv2.BORDER_CONSTANT) # 图像归一化 image_scaled = image_padded / 255.0 image_normalized = (image_scaled - [0.485, 0.456, 0.406]) / [0.229, 0.224, 0.225] image_transposed = np.transpose(image_normalized, [2, 0, 1]) image_batched = np.expand_dims(image_transposed, axis=0) return image_batched # 后处理函数 def postprocess(outputs, conf_threshold, iou_threshold): # 输出解码 objects = [] for i, output in enumerate(outputs): grid_size = output.shape[2] anchor_size = 3 num_classes = output.shape[1] - 5 boxes = output.reshape([-1, 5 + num_classes]) boxes[:, 0:2] = (boxes[:, 0:2] + np.arange(grid_size).reshape([1, -1, 1])) / grid_size boxes[:, 2:4] = np.exp(boxes[:, 2:4]) * anchor_size / grid_size boxes[:, 4:] = np.exp(boxes[:, 4:]) / (1 + np.exp(-boxes[:, 4:])) boxes[:, 5:] = boxes[:, 4:5] * boxes[:, 5:] mask = boxes[:, 4] > conf_threshold boxes = boxes[mask] classes = np.argmax(boxes[:, 5:], axis=-1) scores = boxes[:, 4] * boxes[:, 5 + classes] mask = scores > conf_threshold boxes = boxes[mask] classes = classes[mask] scores = scores[mask] for cls, score, box in zip(classes, scores, boxes): if cls >= num_classes: continue x, y, w, h = box[:4] x1, y1, x2, y2 = x - w / 2, y - h / 2, x + w / 2, y + h / 2 objects.append([x1, y1, x2, y2, score, class_names[cls]]) # 非极大抑制 objects = sorted(objects, key=lambda x: x[4], reverse=True) for i in range(len(objects)): if objects[i][4] == 0: continue for j in range(i + 1, len(objects)): if iou(objects[i][:4], objects[j][:4]) > iou_threshold: objects[j][4] = 0 # 输出筛选 objects = [obj for obj in objects if obj[4] > conf_threshold] return objects # IOU计算函数 def iou(box1, box2): x1, y1, x2, y2 = box1 x3, y3, x4, y4 = box2 left = max(x1, x3) top = max(y1, y3) right = min(x2, x4) bottom = min(y2, y4) intersection = max(0, right - left) * max(0, bottom - top) area1 = (x2 - x1) * (y2 - y1) area2 = (x4 - x3) * (y4 - y3) union = area1 + area2 - intersection return intersection / (union + 1e-6) # 摄像头读取 cap = cv2.VideoCapture(0) while True: # 读取帧 ret, frame = cap.read() # 预处理 image = preprocess(frame, (416, 416)) # 推理 outputs = session.run(output_names, {input_name: image}) # 后处理 objects = postprocess(outputs, conf_threshold=0.5, iou_threshold=0.5) # 可视化 for obj in objects: x1, y1, x2, y2, score, class_name = obj cv2.rectangle(frame, (int(x1 * frame.shape[1]), int(y1 * frame.shape[0])), (int(x2 * frame.shape[1]), int(y2 * frame.shape[0])), (0, 255, 0), 2) cv2.putText(frame, class_name + ": " + str(round(score, 2)), (int(x1 * frame.shape[1]), int(y1 * frame.shape[0]) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) # 显示结果 cv2.imshow("YOLOV7", frame) # 退出 if cv2.waitKey(1) == ord("q"): break # 释放资源 cap.release() cv2.destroyAllWindows() ``` 这段代码通过摄像头实时读取视频流,对每一帧进行目标检测,并将检测结果可视化显示在窗口中。在代码中,我们首先加载了YOLOV7模型,并定义了输入、输出名和类别名称。接着,我们定义了预处理函数和后处理函数,用于对输入图像进行预处理和输出结果进行解码、筛选和可视化。最后,我们通过OpenCV读取摄像头视频流,对每一帧进行目标检测并实时显示在窗口中,直到按下“q”键退出程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值