高通 QCS6490 平台上 YOLO11-obb 系列模型的性能测试

伊利丹~怒风
企业官方账号
2025-07-22 09:47:37

 前言

在当今科技高速发展的时代,边缘计算与人工智能技术的融合为众多领域带来了革命性的变革。高通 QCS6490 平台作为这一融合趋势下的杰出代表,展现出卓越的性能,为复杂的智能应用提供了坚实的硬件基础。​

高通 QCS6490 处理器专为高性能边缘计算而设计,其采用 7nm 工艺制程,集成了多达 8 核心的高通 Kryo 670 CPU,包括 4 个高性能的 Cortex - A78 核心(最高频率可达 2.7GHz)与 4 个高效能的 Cortex - A55 核心(频率约 1.9GHz) ,通过大小核的巧妙组合,可依据不同工作负载灵活调配资源,实现出色的性能功耗比。图形处理上,Adreno 643 GPU 可确保流畅的视觉体验;视频处理单元能实现 4K 分辨率视频的流畅编解码。而其搭载的第 6 代高通 AI Engine,融合 Hexagon 处理器与 AI 加速器,AI 性能最高可达 12 TOPS,可在低功耗下完成高强度 AI 推理任务。同时,该平台具备企业级 Wi - Fi 6/6E 连接能力,多千兆位速度与低延迟特性满足实时应用的数据传输需求;高性能三重 ISP 支持最多 5 路并发摄像头与 192MP 图像捕捉,为边缘 AI 视觉应用提供了有力支持。​

与此同时,在计算机视觉领域,YOLOv11 - obb 系列模型凭借其在定向目标检测方面的独特优势,正逐渐崭露头角。与传统目标检测模型不同,YOLOv11 - obb 专注于识别具有特定方向的目标物体,通过引入旋转角度参数,能够更加精准地定位和分类目标,有效减少背景干扰,提升检测精度。这种特性使其在诸多领域具有极高的应用价值,尤其是在无人机相关应用中表现突出。​

无人机作为一种灵活高效的空中平台,在测绘、巡检、农业监测等领域发挥着越来越重要的作用。在这些应用场景中,无人机需要快速、准确地识别地面上不同朝向的建筑物、设施、农作物等目标。例如在电力巡检中,无人机需精准检测不同角度的输电线路设备;农业监测里,要识别不同生长方向的农作物,以便进行针对性的分析和管理。YOLOv11 - obb 系列模型的应用,能够让无人机在复杂环境下更高效地完成任务,为各行业提供更具价值的信息。​

鉴于高通 QCS6490 平台强大的计算能力、AI 处理能力以及丰富的连接和视觉处理特性,与 YOLOv11 - obb 系列模型在无人机应用中的广阔前景,开展高通 QCS6490 平台上 YOLOv11 - obb 系列模型的性能测试具有重要意义。通过此次测试,我们旨在深入了解该平台对 YOLOv11 - obb 模型的支持程度,评估模型在实际应用中的性能表现,为进一步优化系统、推动相关技术在无人机及其他领域的广泛应用提供有力的数据支撑和实践经验。

高通6490硬件介绍

深度解析 QCS6490:硬件性能全揭秘-CSDN博客

 

YOLO11-obb模型性能指标

YOLO11-obb系列性能指标-QCS6490

模型

尺寸1024

 

CPUNPU QNN2.31
FP32INT8
YOLO11n-obb191.02 ms5.24 FPS 4.5 ms222.22  FPS
YOLO11s-obb461.77 ms2.17  FPS7.43 ms134.59  FPS
YOLO11m-obb1242.83 ms0.80 FPS 20.64 ms48.45  FPS
YOLO11l-obb1562.55 ms0.64  FPS24.41 ms40.97 FPS 
YOLO11x-obb​3373.26 ms0.30 FPS65.01 ms15.38  FPS

点击链接可以下载YOLOv11系列模型的pt格式,其他模型尺寸可以通过AIMO转换模型,并修改下面参考代码中的model_size测试即可。

 (一)将pt模型转换为onnx格式

Step1:升级pip版本为25.1.1

python3.10 -m pip install --upgrade pip
pip -V
aidlux@aidlux:~/aidcode$ pip -V
pip 25.1.1 from /home/aidlux/.local/lib/python3.10/site-packages/pip (python 3.10)

Step2:安装ultralytics和onnx

pip install ultralytics onnx

Step3:设置yolo命令的环境变量

方法 1:临时添加环境变量(立即生效)

在终端中执行以下命令,将 ~/.local/bin 添加到当前会话的环境变量中

export PATH="$PATH:$HOME/.local/bin"
  • 说明:此操作仅对当前终端会话有效,关闭终端后失效。
  • 验证:执行 yolo --version,若输出版本号(如 0.0.2),则说明命令已生效。

方法 2:永久添加环境变量(长期有效)

echo 'export PATH="$PATH:$HOME/.local/bin"' >> ~/.bashrc
source ~/.bashrc  # 使修改立即生效

验证:执行 yolo --version,若输出版本号(如 0.0.2),则说明命令已生效。

测试环境中安装yolo版本为8.3.152

 提示:如果遇到用户组权限问题,可以忽悠,因为yolo命令会另外构建临时文件,也可以执行下面命令更改用户组,执行后下面的警告会消失:

sudo chown -R aidlux:aidlux ~/.config/
sudo chown -R aidlux:aidlux ~/.config/Ultralytics

可能遇见的报错如下:

WARNING ⚠️ user config directory '/home/aidlux/.config/Ultralytics' is not writeable, defaulting to '/tmp' or CWD.Alternatively you can define a YOLO_CONFIG_DIR environment variable for this path.

Step4:将Yolov11系列模型的pt格式转换为onnx格式

新建一个python文件,命名自定义即可,用于模型转换以及导出:

from ultralytics import YOLO

# 加载同级目录下的.pt模型文件
model = YOLO('yolo11n-obb.pt')  # 替换为实际模型文件名

# 导出ONNX配置参数
export_params = {
    'format': 'onnx',
    'opset': 12,          # 推荐算子集版本
    'simplify': True,     # 启用模型简化
    'dynamic': False,     # 固定输入尺寸
    'imgsz': 1024,         # 标准输入尺寸
    'half': False         # 保持FP32精度
}

# 执行转换并保存到同级目录
model.export(**export_params)

执行该程序完成将pt模型导出为onnx模型。

提示:Yolo11s-obb,Yolo11m-obb,Yolo11l-obb,Yolo11x-obb替换代码中Yolo11n即可;

 

 (二)使用AIMO将onnx模型转换高通NPU可以运行的模型格式

 Step1:选择模型优化,模型格式选择onnx格式上传模型

 

Step2:选择芯片型号以及目标框架,这里我们选择QCS8550+Qnn2.31

 

Step3:点击查看模型,使用Netron查看模型结构,进行输入输出的填写

使用Netron工具查看onnx模型结构,选择剪枝位置。

 

 

参考上图中红色框部分填写,其他不变,注意开启自动量化功能,AIMO更多操作查看使用说明或参考AIMO平台

 

/model.23/Mul_5_output_0
/model.23/Sigmoid_1_output_0
/model.23/Mul_output_0

 

 

Step4:接下来进行提交即可,转换完成后将目标模型文件下载,解压缩后其中的.bin.aidem文件即为模型文件

 

 

测试代码

import os
import cv2
import numpy as np
import onnxruntime as ort
import logging
import aidlite
import time
import argparse
"""
YOLO11 旋转目标检测(OBB)实现
功能说明:
1. 支持ONNX模型推理及检测结果可视化
2. 模型输出格式:[x_center, y_center, width, height, class1_confidence, ..., classN_confidence, angle]
3. 核心特性:
   - 自适应不同尺寸图片输入
   - 旋转NMS(非极大值抑制)过滤重复检测框
   - ProbIoU算法计算旋转边界框交并比
"""
 
def letterbox(img, new_shape=(640, 640), color=(0, 0, 0), auto=False, scale_fill=False, scale_up=False, stride=32):
    """
    图像预处理:调整图像尺寸并保持长宽比,添加填充以适应模型输入要求
    
    参数说明:
    :param img: 输入原始图像(OpenCV格式,BGR通道)
    :param new_shape: 目标尺寸,默认(640, 640)
    :param color: 填充区域的颜色,默认黑色(0,0,0)
    :param auto: 是否自动调整填充为步幅(stride)的整数倍,确保下采样兼容性
    :param scale_fill: 是否强制拉伸图像至目标尺寸(可能导致畸变)
    :param scale_up: 是否允许放大图像(禁用时仅缩小不放大,保持精度)
    :param stride: 模型下采样步幅,用于自动计算填充量
    
    返回值:
    :return: 处理后的图像、缩放比例(宽高方向)、填充尺寸(dw:宽方向填充, dh:高方向填充)
    """
    # 获取原始图像尺寸(高,宽)
    shape = img.shape[:2]
    # 统一目标尺寸为元组格式
    if isinstance(new_shape, int):
        new_shape = (new_shape, new_shape)
    
    # 计算缩放比例:取宽高方向缩放比例的最小值(保证图像完全在目标尺寸内)
    r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
    # 若禁用放大,则缩放比例不超过1.0
    if not scale_up:
        r = min(r, 1.0)
    
    # 计算缩放后的图像尺寸
    ratio = r, r  # 宽高方向使用相同缩放比例
    new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))  # (新宽度, 新高度)
    # 计算需要填充的宽度和高度
    dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]
    
    # 自动调整填充为步幅的整数倍(便于模型下采样计算)
    if auto:
        dw, dh = np.mod(dw, stride), np.mod(dh, stride)
    # 若强制填充,则直接拉伸图像至目标尺寸
    elif scale_fill:
        dw, dh = 0.0, 0.0
        new_unpad = (new_shape[1], new_shape[0])
        ratio = new_shape[1] / shape[1], new_shape[0] / shape[0]
    
    # 将填充均匀分配到图像两侧
    dw /= 2
    dh /= 2
    
    # 调整图像尺寸至新尺寸
    if shape[::-1] != new_unpad:  # shape[::-1]为(宽,高),与new_unpad格式一致
        img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)
    # 计算上下左右填充量(四舍五入处理)
    top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
    left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
    # 添加边界填充
    img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)
    return img, ratio, (dw, dh)
 
def _get_covariance_matrix(obb):
    """
    计算旋转边界框的协方差矩阵参数,用于ProbIoU计算
    
    参数说明:
    :param obb: 旋转边界框数组,格式为[x_center, y_center, width, height, angle]
    
    返回值:
    :return: 协方差矩阵的三个元素a, b, c
    """
    # 计算半宽和半高
    widths = obb[..., 2] / 2
    heights = obb[..., 3] / 2
    # 获取旋转角度
    angles = obb[..., 4]
    
    # 计算角度的三角函数值
    cos_angle = np.cos(angles)
    sin_angle = np.sin(angles)
    
    # 计算协方差矩阵元素
    a = (widths * cos_angle)**2 + (heights * sin_angle)** 2
    b = (widths * sin_angle)**2 + (heights * cos_angle)** 2
    c = widths * cos_angle * heights * sin_angle
    
    return a, b, c
 
def batch_probiou(obb1, obb2, eps=1e-7):
    """
    批量计算两个旋转边界框集合之间的ProbIoU(概率交并比)
    
    参数说明:
    :param obb1: 第一个旋转边界框集合,形状为[N, 5]
    :param obb2: 第二个旋转边界框集合,形状为[M, 5]
    :param eps: 防止除零错误的极小值
    
    返回值:
    :return: 两个集合间的ProbIoU矩阵,形状为[N, M]
    """
    # 提取中心坐标
    x1, y1 = obb1[..., 0], obb1[..., 1]
    x2, y2 = obb2[..., 0], obb2[..., 1]
    # 获取协方差矩阵参数
    a1, b1, c1 = _get_covariance_matrix(obb1)
    a2, b2, c2 = _get_covariance_matrix(obb2)
    
    # 计算ProbIoU的中间变量
    # t1: 中心距离相关项
    t1 = ((a1[:, None] + a2) * (y1[:, None] - y2)**2 + (b1[:, None] + b2) * (x1[:, None] - x2)** 2) / (
            (a1[:, None] + a2) * (b1[:, None] + b2) - (c1[:, None] + c2)**2 + eps) * 0.25
    # t2: 旋转角度相关项
    t2 = ((c1[:, None] + c2) * (x2 - x1[:, None]) * (y1[:, None] - y2)) / (
            (a1[:, None] + a2) * (b1[:, None] + b2) - (c1[:, None] + c2)** 2 + eps) * 0.5
    # t3: 面积相关项
    t3 = np.log(((a1[:, None] + a2) * (b1[:, None] + b2) - (c1[:, None] + c2)**2) /
                (4 * np.sqrt((a1 * b1 - c1**2)[:, None] * (a2 * b2 - c2**2)) + eps) + eps) * 0.5
    
    # 计算Bhattacharyya距离和Hellinger距离
    bd = np.clip(t1 + t2 + t3, eps, 100.0)  # 限制范围防止数值溢出
    hd = np.sqrt(1.0 - np.exp(-bd) + eps)   # Hellinger距离
    return 1 - hd  # ProbIoU = 1 - Hellinger距离
 
def rotated_nms_with_probiou(boxes, scores, iou_threshold=0.5):
    """
    使用ProbIoU实现旋转边界框的非极大值抑制(NMS),过滤重复检测框
    
    参数说明:
    :param boxes: 旋转边界框集合,形状为[N, 5]
    :param scores: 每个边界框的置信度得分,形状为[N,]
    :param iou_threshold: IoU阈值,超过该值的框会被抑制
    
    返回值:
    :return: 保留的边界框索引列表
    """
    # 按置信度降序排序
    order = scores.argsort()[::-1]
    keep = []  # 保存保留的框索引
    
    while len(order) > 0:
        # 保留置信度最高的框
        i = order[0]
        keep.append(i)
        
        # 若只剩一个框,则退出循环
        if len(order) == 1:
            break
        
        # 计算当前框与剩余框的ProbIoU
        remaining_boxes = boxes[order[1:]]
        iou_values = batch_probiou(boxes[i:i+1], remaining_boxes).squeeze(0)
        
        # 保留IoU小于阈值的框
        mask = iou_values < iou_threshold
        order = order[1:][mask]
    
    return keep
 
def run_inference(session, image_bytes, imgsz=(640, 640), invoke_nums=100):
    """
    执行模型推理:图像预处理→模型预测→性能统计
    
    参数说明:
    :param session: 已初始化的推理会话对象
    :param image_bytes: 图像字节数据(用于解码原始图像)
    :param imgsz: 模型输入尺寸
    :param invoke_nums: 推理次数,用于性能统计
    
    返回值:
    :return: 模型原始输出、缩放比例、填充尺寸
    """
    # 解码图像字节数据为OpenCV格式
    im0 = cv2.imdecode(np.frombuffer(image_bytes, np.uint8), cv2.IMREAD_COLOR)
    if im0 is None:
        raise ValueError("无法从image_bytes解码图像")
    
    # 图像预处理:调整尺寸并填充
    img, ratio, (dw, dh) = letterbox(im0, new_shape=imgsz)
    print(f">> 预处理后图像尺寸 : {img.shape}")
    print(f">> 填充尺寸(dw, dh) : {dw} {dh}")
    
    # 图像归一化(转换为float32并缩放到[0,1])
    img = (img / 255.).astype(np.float32)
    
    # 设置模型输入
    session.set_input_tensor(0, img)
    
    # 多次推理并统计性能
    invoke_time = []
    for i in range(invoke_nums):
        t1 = time.time()
        # 执行推理
        if session.invoke() != 0:
            raise RuntimeError("推理失败!")
        # 计算单次推理耗时(毫秒)
        cost_time = (time.time() - t1) * 1000
        invoke_time.append(cost_time)
    
    # 计算性能指标
    max_invoke_time = max(invoke_time)
    min_invoke_time = min(invoke_time)
    mean_invoke_time = sum(invoke_time) / invoke_nums
    var_invoketime = np.var(invoke_time)  # 方差(反映耗时稳定性)
    print("====================================")
    print(f"模型推理{invoke_nums}次性能统计:")
    print(f" --平均耗时: {mean_invoke_time:.2f}ms")
    print(f" --最大耗时: {max_invoke_time:.2f}ms")
    print(f" --最小耗时: {min_invoke_time:.2f}ms")
    print(f" --耗时方差: {var_invoketime:.2f}")
    print("====================================")
    
    # 获取并整理模型输出
    # 输出形状定义:[类别置信度, 目标置信度, 位置信息]
    output_shapes = [[1, 1, 8400], [1, 15, 8400], [1, 4, 8400]]
    pred_cls = session.get_output_tensor(0).astype(np.float32).reshape(output_shapes[0])  # 类别置信度
    pred_obj = session.get_output_tensor(1).astype(np.float32).reshape(output_shapes[1])  # 目标置信度
    pred_loc = session.get_output_tensor(2).astype(np.float32).reshape(output_shapes[2])  # 位置信息(x,y,w,h)
    
    # 合并输出:[位置信息 + 目标置信度 + 类别置信度]
    result = np.concatenate([pred_loc, pred_obj, pred_cls], axis=1)
    
    return result, ratio, (dw, dh)
 
def parse_onnx_output(output, ratio, dwdh, conf_threshold=0.5, iou_threshold=0.5):
    """
    解析模型输出:提取有效检测框→坐标还原→旋转NMS→生成最终结果
    
    参数说明:
    :param output: 模型原始输出
    :param ratio: 图像缩放比例
    :param dwdh: 图像填充尺寸
    :param conf_threshold: 置信度阈值,过滤低置信度检测
    :param iou_threshold: NMS的IoU阈值
    
    返回值:
    :return: 格式化的检测结果列表,每个元素包含边界框角点、置信度、类别ID和角度
    """
    boxes, scores, classes, detections = [], [], [], []
    num_detections = output.shape[2]  # 检测框总数
    num_classes = output.shape[1] - 6  # 类别数量(总输出维度 - 5个位置参数 - 1个角度)
    
    # 逐个解析检测框
    for i in range(num_detections):
        detection = output[0, :, i]  # 第i个检测框的所有信息
        # 提取位置和角度信息
        x_center, y_center, width, height = detection[0], detection[1], detection[2], detection[3]
        angle = detection[-1]  # 旋转角度
        
        # 提取类别置信度并筛选
        if num_classes > 0:
            class_confidences = detection[4:4 + num_classes]  # 类别置信度区间
            if class_confidences.size == 0:
                continue
            class_id = np.argmax(class_confidences)  # 置信度最高的类别
            confidence = class_confidences[class_id]  # 对应的置信度值
        else:
            confidence = detection[4]  # 无类别时直接使用目标置信度
            class_id = 0  # 默认类别
        
        # 过滤低置信度检测框
        if confidence > conf_threshold:
            # 将坐标还原到原始图像尺寸(去除填充和缩放影响)
            x_center = (x_center - dwdh[0]) / ratio[0]
            y_center = (y_center - dwdh[1]) / ratio[1]
            width /= ratio[0]
            height /= ratio[1]
            
            # 保存处理后的信息
            boxes.append([x_center, y_center, width, height, angle])
            scores.append(confidence)
            classes.append(class_id)
    
    # 若没有有效检测框,直接返回空列表
    if not boxes:
        return []
    
    # 转换为NumPy数组便于处理
    boxes = np.array(boxes)
    scores = np.array(scores)
    classes = np.array(classes)
    
    # 执行旋转NMS过滤重复框
    keep_indices = rotated_nms_with_probiou(boxes, scores, iou_threshold=iou_threshold)
    
    # 生成最终检测结果
    for idx in keep_indices:
        x_center, y_center, width, height, angle = boxes[idx]
        confidence = scores[idx]
        class_id = classes[idx]
        # 计算旋转边界框的四个角点坐标
        obb_corners = calculate_obb_corners(x_center, y_center, width, height, angle)
        
        detections.append({
            "position": obb_corners,    # 角点坐标列表
            "confidence": float(confidence),  # 置信度
            "class_id": int(class_id),  # 类别ID
            "angle": float(angle)       # 旋转角度
        })
    
    return detections
 
def calculate_obb_corners(x_center, y_center, width, height, angle):
    """
    计算旋转边界框的四个角点坐标
    
    参数说明:
    :param x_center: 边界框中心x坐标
    :param y_center: 边界框中心y坐标
    :param width: 边界框宽度
    :param height: 边界框高度
    :param angle: 旋转角度(弧度)
    
    返回值:
    :return: 四个角点坐标的列表,格式为[(x1,y1), (x2,y2), (x3,y3), (x4,y4)]
    """
    # 计算旋转角度的三角函数值
    cos_angle = np.cos(angle)
    sin_angle = np.sin(angle)
    # 计算半宽和半高
    dx = width / 2
    dy = height / 2
    
    # 计算四个角点坐标(基于旋转矩阵)
    # 公式:x' = x*cosθ - y*sinθ; y' = x*sinθ + y*cosθ
    corners = [
        (int(x_center + cos_angle * dx - sin_angle * dy), 
         int(y_center + sin_angle * dx + cos_angle * dy)),  # 右上角
        (int(x_center - cos_angle * dx - sin_angle * dy), 
         int(y_center - sin_angle * dx + cos_angle * dy)),  # 左上角
        (int(x_center - cos_angle * dx + sin_angle * dy), 
         int(y_center - sin_angle * dx - cos_angle * dy)),  # 左下角
        (int(x_center + cos_angle * dx + sin_angle * dy), 
         int(y_center + sin_angle * dx - cos_angle * dy))   # 右下角
    ]
    return corners
 
def save_detections(image, detections, output_path): 
    """
    可视化检测结果:在图像上绘制旋转边界框和类别信息,并保存结果
    
    参数说明:
    :param image: 原始图像(OpenCV格式,BGR通道)
    :param detections: 检测结果列表,由parse_onnx_output生成
    :param output_path: 结果图像保存路径
    """
    print("正在保存检测结果...")
    # 类别ID到类别名称的映射(适用于DOTA等遥感数据集)
    class_names = {
        0: 'plane', 1: 'ship', 2: 'storage tank', 3: 'baseball diamond',
        4: 'tennis court', 5: 'basketball court', 6: 'ground track field',
        7: 'harbor', 8: 'bridge', 9: 'large vehicle', 10: 'small vehicle',
        11: 'helicopter', 12: 'roundabout', 13: 'soccer ball field', 14: 'swimming pool'
    }

    for det in detections:
        corners = det['position']  # 四个角点坐标
        confidence = det['confidence']
        class_id = det['class_id']

        # 绘制旋转边界框(连接四个角点)
        for j in range(4):
            pt1 = tuple(map(int, corners[j]))
            pt2 = tuple(map(int, corners[(j + 1) % 4]))  # 最后一个点连接回第一个点
            cv2.line(image, pt1, pt2, (0, 0, 255), 2)  # 红色边框,线宽2

        # 获取类别名称
        class_name = class_names.get(class_id, f'class_{class_id}')

        # 绘制类别和置信度文本
        text = f'{class_name}: {confidence:.2f}'  # 格式如"ship: 0.92"
        text_pos = (int(corners[0][0]), int(corners[0][1]) - 10)  # 文本位置在第一个角点上方
        cv2.putText(
            image, text, text_pos, 
            cv2.FONT_HERSHEY_SIMPLEX, 0.6,  # 字体及大小
            (255, 255, 255), 2  # 白色文本,线宽2
        )

    # 保存绘制后的图像
    cv2.imwrite(output_path, image)

 
def process_images_in_folder(folder_path,  output_folder, conf_threshold, iou_threshold, imgsz, args):
    """
    批量处理文件夹中的图像:加载模型→推理→解析→可视化→保存结果
    
    参数说明:
    :param folder_path: 输入图像文件夹路径
    :param output_folder: 输出结果文件夹路径
    :param conf_threshold: 置信度阈值
    :param iou_threshold: NMS的IoU阈值
    :param imgsz: 模型输入尺寸
    :param args: 命令行参数,包含模型路径和类型
    """
    # 初始化推理配置
    config = aidlite.Config.create_instance()
    if config is None:
        raise RuntimeError("创建配置实例失败!")

    # 设置推理配置参数
    config.implement_type = aidlite.ImplementType.TYPE_LOCAL  # 本地推理模式
    # 根据模型类型设置框架
    if args.model_type.lower() == "qnn":
        config.framework_type = aidlite.FrameworkType.TYPE_QNN231
    elif args.model_type.lower() in ("snpe2", "snpe"):
        config.framework_type = aidlite.FrameworkType.TYPE_SNPE2
    config.accelerate_type = aidlite.AccelerateType.TYPE_DSP  # 使用DSP加速
    config.is_quantify_model = 1  # 量化模型标识

    # 加载模型并创建推理会话
    session = aidlite.Model.create_instance(args.target_model)
    if session is None:
        raise RuntimeError("创建模型实例失败!")
    session = aidlite.InterpreterBuilder.build_interpretper_from_model_and_config(session, config)
    
    # 初始化推理会话
    if session is None \
            or session.init() != 0 \
            or session.load_model() != 0:
        raise RuntimeError("推理会话初始化/模型加载失败!")
    print("模型加载成功!")

    # 创建输出文件夹(若不存在)
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)
 
    # 遍历文件夹中的所有图像文件
    for filename in os.listdir(folder_path):
        # 只处理常见图像格式
        if filename.endswith(('.jpg', '.png', '.jpeg')):
            image_path = os.path.join(folder_path, filename)
            # 读取图像字节数据
            with open(image_path, 'rb') as f:
                image_bytes = f.read()
            print(f"正在处理图像: {image_path}")
 
            # 执行推理
            raw_output, ratio, dwdh = run_inference(
                session=session, 
                image_bytes=image_bytes, 
                imgsz=imgsz
            )
            # 解析推理结果
            detections = parse_onnx_output(
                raw_output, 
                ratio, 
                dwdh, 
                conf_threshold=conf_threshold, 
                iou_threshold=iou_threshold
            )
 
            # 解码原始图像用于绘制结果
            im0 = cv2.imdecode(np.frombuffer(image_bytes, np.uint8), cv2.IMREAD_COLOR)
            output_path = os.path.join(output_folder, filename)
            # 保存检测结果图像
            save_detections(im0, detections, output_path)


def parser_args():
    """解析命令行参数"""
    parser = argparse.ArgumentParser("YOLOv11n‑OBB 图像推理工具")
    parser.add_argument(
        "--target_model", 
        type=str, 
        default='yolov11s_obb/cutoff_yolo11s-obb_qcs8550_fp16.qnn231.ctx.bin',
        help="量化模型文件路径(.ctx/.bin)"
    )
    parser.add_argument(
        "--model_type", 
        type=str, 
        default="QNN",
        help="推理后端类型: QNN 或 SNPE2"
    )
    return parser.parse_args()

# 主函数入口
if __name__ == "__main__":
    # 解析命令行参数
    args = parser_args()
    # 配置参数
    folder_path = r"data"  # 输入图像文件夹
    output_folder = "results"  # 输出结果文件夹
    conf_threshold = 0.5  # 置信度阈值
    iou_threshold = 0.5  # NMS的IoU阈值
    imgsz = (640, 640)  # 模型输入尺寸
 
    # 执行批量处理
    process_images_in_folder(
        folder_path, 
        output_folder, 
        conf_threshold, 
        iou_threshold, 
        imgsz, 
        args
    )

 

...全文
178 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

4,662

社区成员

发帖
与我相关
我的任务
社区描述
本论坛以AI、WoS 、XR、IoT、Auto、生成式AI等核心板块组成,为开发者提供便捷及高效的学习和交流平台。 高通开发者专区主页:https://qualcomm.csdn.net/
人工智能物联网机器学习 技术论坛(原bbs) 北京·东城区
社区管理员
  • csdnsqst0050
  • chipseeker
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧