OpenCV Python加载YOLOv5自定义模型报真值歧义ValueError排查
报错根因
这个ValueError是ONNX模型输出维度和代码解析逻辑不匹配导致的:
- 你现在用的后处理代码,默认YOLOv5导出的ONNX输出维度是
(1, 检测框总数, 5+类别数),也就是第二维存的是所有检测框,所以代码里写rows = outputs[0].shape[1]、row = outputs[0][0][r]逐行取单个检测框的属性。 - 你自己训练导出的
best.onnx实际输出是(1, 5+类别数, 检测框总数)的通道在前格式,第二维存的是x/y/w/h/置信度/各类别得分这些属性通道,根本不是检测框。这时候你取row[4]拿到的是一整个数组(所有检测框的置信度),不是单个数值,拿数组和阈值比大小自然会报真值歧义的错。
修复步骤
1. 调整输出维度适配
在post_process函数开头加一行维度转置,把输出转成代码适配的格式,同时修正检测行的取值逻辑:
def post_process(input_image, outputs): # 把输出从(1, 属性数, 框总数)转成(1, 框总数, 属性数),适配原有解析逻辑 outputs = np.transpose(outputs[0], (0, 2, 1)) class_ids = [] confidences = [] boxes = [] # 转置后第二维就是检测框总数 rows = outputs.shape[1] image_height, image_width = input_image.shape[:2] x_factor = image_width / INPUT_WIDTH y_factor = image_height / INPUT_HEIGHT for r in range(rows): # 转置后直接取outputs[0][r]就是单条检测结果 row = outputs[0][r] confidence = row[4] if confidence >= CONFIDENCE_THRESHOLD: classes_scores = row[5:] class_id = np.argmax(classes_scores) if (classes_scores[class_id] > SCORE_THRESHOLD): confidences.append(confidence) class_ids.append(class_id) cx, cy, w, h = row[0], row[1], row[2], row[3] left = int((cx - w/2) * x_factor) top = int((cy - h/2) * y_factor) width = int(w * x_factor) height = int(h * y_factor) box = np.array([left, top, width, height]) boxes.append(box)
2. 修复NMS索引兼容问题
不同版本OpenCV的cv2.dnn.NMSBoxes返回值格式不一致,老版本返回二维数组,高版本返回一维数组,直接循环很容易索引报错。在NMS调用后加一行压扁处理即可兼容所有版本:
indices = cv2.dnn.NMSBoxes(boxes, confidences, CONFIDENCE_THRESHOLD, NMS_THRESHOLD) # 统一把索引转成一维列表,空结果直接返回空列表 indices = indices.flatten() if len(indices) > 0 else [] for i in indices: box = boxes[i] left = box[0] top = box[1] width = box[2] height = box[3] cv2.rectangle(input_image, (left, top), (left + width, top + height), BLUE, 3*THICKNESS) label = "{}:{:.2f}".format(classes[class_ids[i]], confidences[i]) draw_label(input_image, label, left, top) return input_image
其他部署注意事项
- 坐标越界问题:当前代码计算出的框坐标可能出现负数,或者宽高超出图片边界,导致画出来的框被截断。计算坐标时加个边界裁剪即可:
left = max(0, min(int((cx - w/2) * x_factor), image_width - 1)) top = max(0, min(int((cy - h/2) * y_factor), image_height - 1)) width = max(1, min(int(w * x_factor), image_width - left)) height = max(1, min(int(h * y_factor), image_height - top)) - 异常校验缺失:当前代码没判断图片、模型是否加载成功,路径写错的话直接抛模糊错误。加载完图片和模型后最好加非空判断,提前抛出明确错误。
- ONNX导出模式匹配:如果你导出ONNX时开了end2end模式(模型内置NMS),模型输出已经是过滤后的最终框,不需要再走置信度过滤和NMS逻辑,直接解析坐标即可,否则会重复处理导致结果异常。
- CPU推理加速:如果部署在CPU端,加载模型后加两行配置调用OpenCV优化后端,推理速度能提升2-5倍:
net = cv2.dnn.readNet(modelWeights) net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV) net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)
内容的提问来源于stack exchange,提问作者Abin Jilson
相关产品推荐
相关产品推荐

