OpenCV 3.4.1 Java DNN加载TensorFlow LeNet模型失败:输入层未找到
解决OpenCV DNN加载Keras/TensorFlow冻结PB模型时的输入层找不到问题
这个问题我之前帮朋友排查过,本质是Keras导出的PB模型层命名规则和OpenCV默认自动推断的逻辑不匹配导致的——报错里的convolution2d_1_b_1根本不是输入层,而是模型里的偏置层,说明OpenCV找错了输入入口。下面给你一步步解决的方案:
先复现你的问题场景
我在Java中使用OpenCV 3.4.1的DNN模块加载一个通过Python Keras+TensorFlow训练得到的LeNet冻结PB模型(.pb格式),代码如下:
Dnn cvDnn = new org.opencv.dnn.Dnn(); Net net = cvDnn.readNetFromTensorflow("C:\\Users\\kr\\Desktop\\Plate_Recognition_frozen.pb");但运行时出现错误:
OpenCV(3.4.1) Error: Unspecified error (Input layer not found: convolution2d_1_b_1) in cv::dnn::experimental_dnn_v4...
步骤1:搞清楚PB模型的真实输入层名称
Keras在训练时会自动给层加命名后缀,导出成PB模型后,输入层名称通常是input_1、conv2d_1_input这类,而非OpenCV误判的偏置层。你可以用Python写几行代码查看模型的真实输入输出层:
import tensorflow as tf from tensorflow.python.platform import gfile with tf.Session() as sess: model_path = 'Plate_Recognition_frozen.pb' with gfile.FastGFile(model_path, 'rb') as f: graph_def = tf.GraphDef() graph_def.ParseFromString(f.read()) sess.graph.as_default() tf.import_graph_def(graph_def, name='') # 打印所有输入Placeholder(即模型的输入层) print("模型输入层名称:") for op in sess.graph.get_operations(): if op.type == 'Placeholder': print(op.name) # 顺便打印可能的输出层(后续推理会用到) print("\n模型可能的输出层名称:") for op in sess.graph.get_operations(): # 关键词可根据你的LeNet结构调整,比如dense、output、predict if 'dense' in op.name or 'output' in op.name: print(op.name)
运行后你会得到类似input_1的输入层名称,记下来备用。
步骤2:修改Java代码,手动指定输入输出层
OpenCV的readNetFromTensorflow提供了重载方法,可以直接指定输入输出层,或者加载后手动设置输入。两种方式选其一即可:
方式一:加载时直接指定
// 替换成你查到的真实输入、输出层名称 String inputLayer = "input_1"; String outputLayer = "dense_2/Softmax"; Dnn cvDnn = new org.opencv.dnn.Dnn(); // 第二个参数传空字符串,因为你没有单独的pbtxt文件 Net net = cvDnn.readNetFromTensorflow( "C:\\Users\\kr\\Desktop\\Plate_Recognition_frozen.pb", "", inputLayer, outputLayer );
方式二:加载后手动设置输入输出
Dnn cvDnn = new org.opencv.dnn.Dnn(); Net net = cvDnn.readNetFromTensorflow("C:\\Users\\kr\\Desktop\\Plate_Recognition_frozen.pb"); // 先将输入图像处理成blob(参数需和模型训练时的预处理规则一致) Mat inputImage = Imgcodecs.imread("your_input_image_path.jpg"); Mat inputBlob = cvDnn.blobFromImage( inputImage, 1.0/255.0, // 归一化系数 new Size(28,28), // LeNet的输入尺寸,按需调整 new Scalar(0,0,0), // 均值 true, // 是否交换RB通道(OpenCV默认BGR,TensorFlow若用RGB需设为true) false // 是否裁剪 ); // 手动指定输入层名称 net.setInput(inputBlob, "input_1"); // 手动指定输出层名称进行推理 Mat outputResult = net.forward("dense_2/Softmax");
额外注意事项
- OpenCV 3.4.1对TensorFlow PB模型的兼容性一般,建议升级到OpenCV 3.4.5+或者4.x版本,能减少很多奇怪的兼容性问题。
- 确认你导出PB模型的流程是正确的:先将Keras模型转换成TensorFlow计算图,再冻结所有变量,不要直接把Keras的.h5模型转成PB,容易出现层命名混乱。
内容的提问来源于stack exchange,提问作者HMaster
相关产品推荐
相关产品推荐

