OpenCV加载TensorFlow2.4训练的MNIST-Fashion模型.pb文件报错
解决TensorFlow 2.4训练的Fashion-MNIST模型无法被OpenCV读取的问题
这个问题我之前帮人排查过,核心原因是TensorFlow 2.x原生的SavedModel格式和OpenCV DNN模块的兼容性不足。你直接读取的saved_model.pb是TF2的专用格式,里面包含了TF2特有的FunctionDef结构,而OpenCV的protobuf解析器对这些新结构的处理不够完善,才会抛出UTF-8无效数据的错误。
下面给你三个可行的解决方案,按推荐程度排序:
方案一:将TF2模型转换为TF1兼容的冻结图(最稳妥)
OpenCV的DNN模块对TF1的冻结图格式支持得非常好,我们可以把TF2训练的模型转换成这种格式:
步骤1:用Python导出冻结图
import tensorflow as tf from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2 # 加载你训练好的SavedModel model_path = "path/to/your/saved_model_directory" loaded_model = tf.saved_model.load(model_path) # 获取模型的推理函数(默认是serving_default,如果你改过名字要对应) infer_func = loaded_model.signatures['serving_default'] # 将函数转换为可序列化的ConcreteFunction input_spec = tf.TensorSpec(infer_func.inputs[0].shape, infer_func.inputs[0].dtype) concrete_func = infer_func.get_concrete_function(input_spec) # 把模型中的变量全部转换为常量,生成冻结图 frozen_func = convert_variables_to_constants_v2(concrete_func) frozen_graph_def = frozen_func.graph.as_graph_def() # 保存冻结图到本地 with open("fashion_mnist_frozen.pb", "wb") as f: f.write(frozen_graph_def.SerializeToString())
步骤2:用OpenCV读取冻结图
读取前建议先确认模型的输入输出节点名称(可以用Netron工具打开冻结图查看),然后在代码中指定:
// 读取冻结图 cv::dnn::Net net = cv::dnn::readNetFromTensorflow("fashion_mnist_frozen.pb"); // 指定输入输出节点名(替换成你自己的节点名称) net.setInputNames({"input_1"}); // 示例名称,以实际为准 net.setOutputNames({"dense_1/Softmax"}); // 示例名称,以实际为准
方案二:转换为TensorFlow Lite格式(更轻量)
TF Lite是专门为部署优化的格式,OpenCV 4.5及以上版本对它的支持很友好:
步骤1:导出TFLite模型
import tensorflow as tf converter = tf.lite.TFLiteConverter.from_saved_model("path/to/your/saved_model_directory") # 如果需要量化优化可以加这行,不需要就注释掉 # converter.optimizations = [tf.lite.Optimize.DEFAULT] tflite_model = converter.convert() # 保存TFLite模型 with open("fashion_mnist.tflite", "wb") as f: f.write(tflite_model)
步骤2:用OpenCV读取TFLite模型
// 注意:需要OpenCV 4.5及以上版本 cv::dnn::Net net = cv::dnn::readNetFromTensorflow("fashion_mnist.tflite");
方案三:升级OpenCV到最新版本
如果你不想转换模型,可以尝试升级OpenCV到4.5以上的版本——新版本的DNN模块对TF2的SavedModel格式做了兼容优化,有可能直接读取成功。不过这种方式稳定性不如前两种,还是推荐用冻结图或TFLite。
最后提醒一下:不管用哪种方式,OpenCV读取图片后要做和训练时一致的预处理(比如归一化到[0,1]或者[-1,1]、调整尺寸为28x28等),否则模型推理结果会出错。
内容的提问来源于stack exchange,提问作者Sobel Li




