From 1691a2355c8eff94b43ba8473ef49477a1d4e6c2 Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Mon, 3 Nov 2025 15:18:43 +0100 Subject: [PATCH] Allow protobuffer message to be compiled with LITE_RUNTIME When adding "option optimize_for = LITE_RUNTIME;" to proto messages, they are compiled as lighter MessageLite (the base class of Message). Those lighter messages do not allow for reflection though: https://developers.google.com/protocol-buffers/docs/reference/cpp-generated This fixes https://github.com/opencv/opencv/issues/20275 --- modules/dnn/src/caffe/caffe_importer.cpp | 26 +++++++++++- modules/dnn/src/caffe/caffe_io.cpp | 46 ++++++++++++++++++---- modules/dnn/src/caffe/caffe_io.hpp | 6 ++- modules/dnn/src/tensorflow/tf_importer.cpp | 16 +++++++- 4 files changed, 82 insertions(+), 12 deletions(-) diff --git a/modules/dnn/src/caffe/caffe_importer.cpp b/modules/dnn/src/caffe/caffe_importer.cpp index a4afec136a..c92ebea619 100644 --- a/modules/dnn/src/caffe/caffe_importer.cpp +++ b/modules/dnn/src/caffe/caffe_importer.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -203,7 +204,9 @@ public: return (str.size() >= _param.size()) && str.compare(str.size() - _param.size(), _param.size(), _param) == 0; } - void extractLayerParams(const Message &msg, cv::dnn::LayerParams ¶ms, bool isInternal = false) + template + typename std::enable_if::value, void>::type + extractLayerParams(const MSG &msg, cv::dnn::LayerParams ¶ms, bool isInternal = false) { const Descriptor *msgDesc = msg.GetDescriptor(); const Reflection *msgRefl = msg.GetReflection(); @@ -238,6 +241,13 @@ public: } } + template + typename std::enable_if::value, void>::type + extractLayerParams(const MSG &msg, cv::dnn::LayerParams ¶ms, bool isInternal = false) + { + CV_Error(Error::StsError, "DNN/CAFFE: do not have your message be a MessageLite"); + } + void blobShapeFromProto(const caffe::BlobProto &pbBlob, MatShape& shape) { shape.clear(); @@ -256,6 +266,18 @@ public: shape.resize(1, 1); // Is a scalar. } + template + typename std::enable_if::value, void>::type + AssertBlobProtoIsFloat(const BLOB_PROTO &pbBlob) { + CV_DbgAssert(pbBlob.GetDescriptor()->FindFieldByLowercaseName("data")->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT); + } + + template + typename std::enable_if::value, void>::type + AssertBlobProtoIsFloat(const BLOB_PROTO &pbBlob) { + CV_Error(Error::StsError, "DNN/CAFFE: do not have your message be a MessageLite"); + } + void blobFromProto(const caffe::BlobProto &pbBlob, cv::Mat &dstBlob) { MatShape shape; @@ -267,7 +289,7 @@ public: // Single precision floats. CV_Assert(pbBlob.data_size() == (int)dstBlob.total()); - CV_DbgAssert(pbBlob.GetDescriptor()->FindFieldByLowercaseName("data")->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT); + AssertBlobProtoIsFloat(pbBlob); Mat(dstBlob.dims, &dstBlob.size[0], CV_32F, (void*)pbBlob.data().data()).copyTo(dstBlob); } else diff --git a/modules/dnn/src/caffe/caffe_io.cpp b/modules/dnn/src/caffe/caffe_io.cpp index ebecf95eea..8897e70d67 100644 --- a/modules/dnn/src/caffe/caffe_io.cpp +++ b/modules/dnn/src/caffe/caffe_io.cpp @@ -101,6 +101,7 @@ #include #include #include +#include #include "caffe_io.hpp" #include "glog_emulator.hpp" @@ -1110,7 +1111,7 @@ const char* UpgradeV1LayerType(const V1LayerParameter_LayerType type) { static const int kProtoReadBytesLimit = INT_MAX; // Max size of 2 GB minus 1 byte. -bool ReadProtoFromBinary(ZeroCopyInputStream* input, Message *proto) { +bool ReadProtoFromBinary(ZeroCopyInputStream* input, MessageLite *proto) { CodedInputStream coded_input(input); #if GOOGLE_PROTOBUF_VERSION >= 3006000 coded_input.SetTotalBytesLimit(kProtoReadBytesLimit); @@ -1133,7 +1134,12 @@ bool ReadProtoFromTextFile(const char* filename, Message* proto) { return parser.Parse(&input, proto); } -bool ReadProtoFromBinaryFile(const char* filename, Message* proto) { +bool ReadProtoFromTextFile(const char* filename, MessageLite* proto) { + CV_Error(Error::StsError, "DNN/CAFFE: do not have your message be a MessageLite"); + return false; +} + +bool ReadProtoFromBinaryFile(const char* filename, MessageLite* proto) { std::ifstream fs(filename, std::ifstream::in | std::ifstream::binary); CHECK(fs.is_open()) << "Can't open \"" << filename << "\""; IstreamInputStream raw_input(&fs); @@ -1151,26 +1157,52 @@ bool ReadProtoFromTextBuffer(const char* data, size_t len, Message* proto) { return parser.Parse(&input, proto); } +bool ReadProtoFromTextBuffer(const char* data, size_t len, MessageLite* proto) { + CV_Error(Error::StsError, "DNN/CAFFE: do not have your message be a MessageLite"); + return false; +} -bool ReadProtoFromBinaryBuffer(const char* data, size_t len, Message* proto) { +bool ReadProtoFromBinaryBuffer(const char* data, size_t len, MessageLite* proto) { ArrayInputStream raw_input(data, len); return ReadProtoFromBinary(&raw_input, proto); } -void ReadNetParamsFromTextFileOrDie(const char* param_file, - NetParameter* param) { +template +typename std::enable_if::value, void>::type +ReadNetParamsFromTextFileOrDieImpl(const char* param_file, MESSAGE* param) { CHECK(ReadProtoFromTextFile(param_file, param)) << "Failed to parse NetParameter file: " << param_file; UpgradeNetAsNeeded(param_file, param); } -void ReadNetParamsFromTextBufferOrDie(const char* data, size_t len, - NetParameter* param) { +template +typename std::enable_if::value, void>::type +ReadNetParamsFromTextFileOrDieImpl(const char* param_file, MESSAGE* param) { + CV_Error(Error::StsError, "DNN/CAFFE: do not have your message be a MessageLite"); +} + +void ReadNetParamsFromTextFileOrDie(const char* param_file, NetParameter* param) { + ReadNetParamsFromTextFileOrDieImpl(param_file, param); +} + +template +typename std::enable_if::value, void>::type +ReadNetParamsFromTextBufferOrDieImpl(const char* data, size_t len, MESSAGE* param) { CHECK(ReadProtoFromTextBuffer(data, len, param)) << "Failed to parse NetParameter buffer"; UpgradeNetAsNeeded("memory buffer", param); } +template +typename std::enable_if::value, void>::type +ReadNetParamsFromTextBufferOrDieImpl(const char* data, size_t len, MESSAGE* param) { + CV_Error(Error::StsError, "DNN/CAFFE: do not have your message be a MessageLite"); +} + +void ReadNetParamsFromTextBufferOrDie(const char* data, size_t len, NetParameter* param) { + ReadNetParamsFromTextBufferOrDieImpl(data, len, param); +} + void ReadNetParamsFromBinaryFileOrDie(const char* param_file, NetParameter* param) { CHECK(ReadProtoFromBinaryFile(param_file, param)) diff --git a/modules/dnn/src/caffe/caffe_io.hpp b/modules/dnn/src/caffe/caffe_io.hpp index 544db1c3ed..71897d8af4 100644 --- a/modules/dnn/src/caffe/caffe_io.hpp +++ b/modules/dnn/src/caffe/caffe_io.hpp @@ -119,9 +119,11 @@ void ReadNetParamsFromTextBufferOrDie(const char* data, size_t len, // Utility functions used internally by Caffe and TensorFlow loaders bool ReadProtoFromTextFile(const char* filename, ::google::protobuf::Message* proto); -bool ReadProtoFromBinaryFile(const char* filename, ::google::protobuf::Message* proto); +bool ReadProtoFromTextFile(const char* filename, ::google::protobuf::MessageLite* proto); +bool ReadProtoFromBinaryFile(const char* filename, ::google::protobuf::MessageLite* proto); bool ReadProtoFromTextBuffer(const char* data, size_t len, ::google::protobuf::Message* proto); -bool ReadProtoFromBinaryBuffer(const char* data, size_t len, ::google::protobuf::Message* proto); +bool ReadProtoFromTextBuffer(const char* data, size_t len, ::google::protobuf::MessageLite* proto); +bool ReadProtoFromBinaryBuffer(const char* data, size_t len, ::google::protobuf::MessageLite* proto); } } diff --git a/modules/dnn/src/tensorflow/tf_importer.cpp b/modules/dnn/src/tensorflow/tf_importer.cpp index b000b2a71b..3bd7e63b11 100644 --- a/modules/dnn/src/tensorflow/tf_importer.cpp +++ b/modules/dnn/src/tensorflow/tf_importer.cpp @@ -27,6 +27,7 @@ Implementation of Tensorflow models parser #include #include #include +#include #include "tf_graph_simplifier.hpp" #endif @@ -3290,6 +3291,19 @@ Net readNetFromTensorflow(const std::vector& bufferModel, const std::vect bufferConfigPtr, bufferConfig.size()); } + +template +typename std::enable_if::value, void>::type +PrintToStringImpl(const GRAPH_DEF& net, std::string* content) { + google::protobuf::TextFormat::PrintToString(net, content); +} + +template +typename std::enable_if::value, void>::type +PrintToStringImpl(const GRAPH_DEF& net, std::string* content) { + CV_Error(Error::StsError, "DNN/TF: do not have your message be a MessageLite"); +} + void writeTextGraph(const String& _model, const String& output) { String model = _model; @@ -3312,7 +3326,7 @@ void writeTextGraph(const String& _model, const String& output) } std::string content; - google::protobuf::TextFormat::PrintToString(net, &content); + PrintToStringImpl(net, &content); std::ofstream ofs(output.c_str()); ofs << content;