You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

OpenCV DNN中为YOLOv3指定中间层输入仅推理后续层的报错问题咨询

解决OpenCV DNN中从YOLOv3中间Permute层开始推理的问题

我来帮你分析下遇到的错误,并给出两种可行的解决方案:

错误原因分析

你遇到的两个错误本质上是同一个问题:OpenCV DNN的setInput方法只能针对网络的「输入层(Input Layer)」设置数据,而permute_82/94/106是中间计算层,并非原生输入层

  1. 第一个错误Requested blob "permute_82" not found:因为你试图给一个非输入层设置输入,OpenCV找不到对应的输入blob节点。
  2. 第二个错误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.cfgyolov3_subgraph.cfg,然后做以下修改:

  1. 删除文件开头到conv_81conv_93conv_105的所有层(包括这三个conv层)。
  2. 在文件开头添加三个[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
    
  3. 找到原配置中的permute_82permute_94permute_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

火山引擎 最新活动