TensorFlow2 C++加载SavedModel报错:Graph不含终端节点detection_boxes
解决TensorFlow C++加载SavedModel时"Graph does not contain terminal node detection_boxes"错误
你的问题核心是代码里使用的输出层名称和模型实际导出的计算图节点名不匹配,同时代码里还有几个容易忽略的字符串格式问题,我来一步步帮你解决:
问题根源解释
你用saved_model_cli看到的结果里,outputs['detection_boxes']是SavedModel SignatureDef里的逻辑键名,而真正在计算图中存在的节点名是后面的StatefulPartitionedCall:1这类标识。TensorFlow C++ API加载模型时,需要使用真实的计算图节点名,而不是SignatureDef的逻辑键。
另外你的代码里还有几个小问题:
- 路径、节点名字符串里多了转义的
\",导致TensorFlow寻找带引号的节点/路径,自然找不到 - 输入张量的通道格式处理有误(OpenCV默认BGR,多数TF模型期望RGB)
- 会话对象未正确从
SavedModelBundle中获取
修改后的完整代码示例
#include <tensorflow/core/public/session.h> #include <tensorflow/core/platform/env.h> #include <tensorflow/core/protobuf/meta_graph.pb.h> #include <opencv2/opencv.hpp> using namespace tensorflow; int main() { // 1. 修正路径和节点名:去掉多余的转义引号,使用真实节点名 string strModelDir = "/home/hxy/tf/objectrec/train/train/saved_model/"; string inputLayer = "input_tensor:0"; // 确保和saved_model_cli显示的输入tensor name一致 // 对应saved_model_cli里输出的name字段 vector<string> outputLayer = {"StatefulPartitionedCall:1", "StatefulPartitionedCall:2", "StatefulPartitionedCall:4", "StatefulPartitionedCall:5"}; tensorflow::SessionOptions session_option; tensorflow::RunOptions run_option; tensorflow::SavedModelBundle iBundle; // 2. 修正tags参数:去掉引号,直接用{"serve"} Status load_graph_status = tensorflow::LoadSavedModel(session_option, run_option, strModelDir, {"serve"}, &iBundle); if (!load_graph_status.ok()) { std::cerr << "模型加载失败: " << load_graph_status.ToString() << std::endl; return -1; } // 3. 读取图像并修正格式 cv::Mat frame = cv::imread("/home/hxy/tf/objectrec/test_images/raccoon_dt2.jpg"); if (frame.empty()) { std::cerr << "图像读取失败!" << std::endl; return -1; } // OpenCV默认BGR,转成TF模型常用的RGB格式 cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB); // 归一化到0-1范围(多数目标检测模型要求) frame.convertTo(frame, CV_32FC3, 1.0/255.0); // 4. 构造输入张量 Tensor input_tensor(tensorflow::DT_FLOAT, tensorflow::TensorShape({1, frame.rows, frame.cols, frame.channels()})); float* p = input_tensor.flat<float>().data(); cv::Mat m_input(frame.rows, frame.cols, CV_32FC3, p); frame.copyTo(m_input); // 5. 运行模型:使用SavedModelBundle中的会话对象 std::vector<tensorflow::Tensor> outputs; Status runStatus = iBundle.session()->Run({{inputLayer, input_tensor},}, outputLayer, {}, &outputs); if (!runStatus.ok()) { std::cerr << "模型推理失败: " << runStatus.ToString() << std::endl; return -1; } // 这里可以添加输出结果的解析逻辑 std::cout << "推理成功!输出张量数量: " << outputs.size() << std::endl; return 0; }
关键注意事项
- 区分逻辑键和真实节点名:以后用
saved_model_cli查看时,一定要看name:字段的值,这才是C++代码需要用的节点标识。 - 字符串格式:C++字符串本身用双引号包裹,内部不需要再加转义的
\",否则会把引号当成路径/节点名的一部分。 - 输入预处理:务必确认模型期望的输入格式(RGB/BGR、归一化范围),这是目标检测模型常见的坑。
- 会话对象:加载
SavedModelBundle后,必须通过iBundle.session()获取会话,不能直接使用未初始化的session变量。
内容的提问来源于stack exchange,提问作者Maura
相关产品推荐
相关产品推荐

