OpenCV DNN中为YOLOv3指定中间层输入仅推理后续层的报错问题咨询
解决OpenCV DNN中从YOLOv3中间Permute层开始推理的问题
我来帮你分析下遇到的错误,并给出两种可行的解决方案:
错误原因分析
你遇到的两个错误本质上是同一个问题:OpenCV DNN的setInput方法只能针对网络的「输入层(Input Layer)」设置数据,而permute_82/94/106是中间计算层,并非原生输入层。
- 第一个错误
Requested blob "permute_82" not found:因为你试图给一个非输入层设置输入,OpenCV找不到对应的输入blob节点。 - 第二个错误
inputs.size() == requiredOutputs:即使手动设置了输入名称,这些中间层的结构不支持直接作为输入层使用,数据层无法匹配输入与输出的数量要求,触发断言失败。
解决方案1:手动修改YOLOv3配置文件(适合需要精确控制网络结构的场景)
步骤1:获取目标层的形状
先运行一次完整推理,拿到permute层的输入(即conv_81/93/105的输出)形状:
import cv2 # 加载完整YOLOv3网络 modelWeightPath = r"./yolov3.weights" modelPath = r"./yolov3.cfg" network = cv2.dnn.readNetFromDarknet(modelPath, modelWeightPath) # 用测试图跑一次完整推理,获取中间层形状 imgPath = r'./frame_93.png' image = cv2.imread(imgPath) blob = cv2.dnn.blobFromImage(image, 1/255.0, (416,416), (0,0,0)) network.setInput(blob) # 打印三个关键conv层的输出形状 conv81_shape = network.forward('conv_81').shape conv93_shape = network.forward('conv_93').shape conv105_shape = network.forward('conv_105').shape print(f"conv_81形状: {conv81_shape}") # 通常是(1, 255, 13, 13) print(f"conv_93形状: {conv93_shape}") # 通常是(1, 255, 26, 26) print(f"conv_105形状: {conv105_shape}") # 通常是(1, 255, 52, 52)
步骤2:修改配置文件
复制yolov3.cfg为yolov3_subgraph.cfg,然后做以下修改:
- 删除文件开头到
conv_81、conv_93、conv_105的所有层(包括这三个conv层)。 - 在文件开头添加三个
[input]层,对应上面获取的形状:[input] shape = [1, 255, 13, 13] name = input_81 [input] shape = [1, 255, 26, 26] name = input_93 [input] shape = [1, 255, 52, 52] name = input_105 - 找到原配置中的
permute_82、permute_94、permute_106层,修改它们的from参数为对应的新输入层名称:# 原permute_82层修改后 [permute] from=input_81 order=0 2 3 1
步骤3:加载修改后的网络并推理
# 加载子网络(权重用原文件即可,会自动匹配对应层的参数) sub_modelPath = r"./yolov3_subgraph.cfg" sub_network = cv2.dnn.readNetFromDarknet(sub_modelPath, modelWeightPath) # 设置输入 sub_network.setInput(cv2.UMat(inputScale1), name='input_81') sub_network.setInput(cv2.UMat(inputScale2), name='input_93') sub_network.setInput(cv2.UMat(inputScale3), name='input_105') # 获取输出层并推理 output_layers = sub_network.getUnconnectedOutLayersNames() outs = sub_network.forward(output_layers) # 后续YOLO结果解析逻辑和原代码一致
解决方案2:使用OpenCV的extractSubgraph函数(推荐,无需手动修改配置)
OpenCV DNN提供了extractSubgraph函数,可以直接从原网络中提取指定层到输出层的子网络,无需修改配置文件:
import cv2 modelWeightPath = r"./yolov3.weights" modelPath = r"./yolov3.cfg" network = cv2.dnn.readNetFromDarknet(modelPath, modelWeightPath) # 定义要保留的输出层(YOLO检测层) output_layers = network.getUnconnectedOutLayersNames() # 输出为['yolo_83', 'yolo_95', 'yolo_107'] # 提取从permute层到YOLO层的子网络 sub_network = cv2.dnn.extractSubgraph(network, output_layers) # 获取子网络的输入层名称(对应原网络中permute层的输入节点) input_names = sub_network.getInputsNames() print(f"子网络输入层名称: {input_names}") # 通常是['conv_81', 'conv_93', 'conv_105'] # 设置输入并推理 sub_network.setInput(cv2.UMat(inputScale1), name=input_names[0]) sub_network.setInput(cv2.UMat(inputScale2), name=input_names[1]) sub_network.setInput(cv2.UMat(inputScale3), name=input_names[2]) outs = sub_network.forward(output_layers)
验证小技巧
可以先打印子网络输入层的形状,确认和你保存的inputScale1/2/3形状一致:
for name in input_names: layer_id = sub_network.getLayerId(name) print(f"{name}输出形状: {sub_network.getLayer(layer_id).outputShape}")
内容的提问来源于stack exchange,提问作者Daniel Klauser




