Add readNet* functions which parse models from byte arrays
authorDmitry Kurtaev <dmitry.kurtaev+github@gmail.com>
Wed, 4 Jul 2018 15:15:31 +0000 (18:15 +0300)
committerDmitry Kurtaev <dmitry.kurtaev+github@gmail.com>
Tue, 10 Jul 2018 08:12:01 +0000 (11:12 +0300)
modules/dnn/include/opencv2/dnn/dnn.hpp
modules/dnn/src/caffe/caffe_importer.cpp
modules/dnn/src/darknet/darknet_importer.cpp
modules/dnn/src/dnn.cpp
modules/dnn/src/tensorflow/tf_importer.cpp
modules/dnn/test/test_darknet_importer.cpp

index 68e1994..a32b33d 100644 (file)
@@ -644,13 +644,23 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN
     */
     CV_EXPORTS_W Net readNetFromDarknet(const String &cfgFile, const String &darknetModel = String());
 
-   /** @brief Reads a network model stored in <a href="https://pjreddie.com/darknet/">Darknet</a> model files.
-    *  @param cfgFile      file node to the .cfg file with text description of the network architecture.
-    *  @param darknetModel file node to the .weights file with learned network.
-    *  @returns Network object that ready to do forward, throw an exception in failure cases.
-    *  @returns Net object.
-    */
-    CV_EXPORTS_W Net readNetFromDarknet(const FileNode &cfgFile, const FileNode &darknetModel = FileNode());
+    /** @brief Reads a network model stored in <a href="https://pjreddie.com/darknet/">Darknet</a> model files.
+     *  @param bufferCfg   A buffer contains a content of .cfg file with text description of the network architecture.
+     *  @param bufferModel A buffer contains a content of .weights file with learned network.
+     *  @returns Net object.
+     */
+    CV_EXPORTS_W Net readNetFromDarknet(const std::vector<char>& bufferCfg,
+                                        const std::vector<char>& bufferModel = std::vector<char>());
+
+    /** @brief Reads a network model stored in <a href="https://pjreddie.com/darknet/">Darknet</a> model files.
+     *  @param bufferCfg   A buffer contains a content of .cfg file with text description of the network architecture.
+     *  @param lenCfg      Number of bytes to read from bufferCfg
+     *  @param bufferModel A buffer contains a content of .weights file with learned network.
+     *  @param lenModel    Number of bytes to read from bufferModel
+     *  @returns Net object.
+     */
+    CV_EXPORTS Net readNetFromDarknet(const char *bufferCfg, size_t lenCfg,
+                                      const char *bufferModel = NULL, size_t lenModel = 0);
 
     /** @brief Reads a network model stored in <a href="http://caffe.berkeleyvision.org">Caffe</a> framework's format.
       * @param prototxt   path to the .prototxt file with text description of the network architecture.
@@ -660,6 +670,14 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN
     CV_EXPORTS_W Net readNetFromCaffe(const String &prototxt, const String &caffeModel = String());
 
     /** @brief Reads a network model stored in Caffe model in memory.
+      * @param bufferProto buffer containing the content of the .prototxt file
+      * @param bufferModel buffer containing the content of the .caffemodel file
+      * @returns Net object.
+      */
+    CV_EXPORTS_W Net readNetFromCaffe(const std::vector<char>& bufferProto,
+                                      const std::vector<char>& bufferModel = std::vector<char>());
+
+    /** @brief Reads a network model stored in Caffe model in memory.
       * @details This is an overloaded member function, provided for convenience.
       * It differs from the above function only in what argument(s) it accepts.
       * @param bufferProto buffer containing the content of the .prototxt file
@@ -681,6 +699,14 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN
     CV_EXPORTS_W Net readNetFromTensorflow(const String &model, const String &config = String());
 
     /** @brief Reads a network model stored in <a href="https://www.tensorflow.org/">TensorFlow</a> framework's format.
+      * @param bufferModel buffer containing the content of the pb file
+      * @param bufferConfig buffer containing the content of the pbtxt file
+      * @returns Net object.
+      */
+    CV_EXPORTS_W Net readNetFromTensorflow(const std::vector<char>& bufferModel,
+                                           const std::vector<char>& bufferConfig = std::vector<char>());
+
+    /** @brief Reads a network model stored in <a href="https://www.tensorflow.org/">TensorFlow</a> framework's format.
       * @details This is an overloaded member function, provided for convenience.
       * It differs from the above function only in what argument(s) it accepts.
       * @param bufferModel buffer containing the content of the pb file
@@ -743,6 +769,18 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN
       */
      CV_EXPORTS_W Net readNet(const String& model, const String& config = "", const String& framework = "");
 
+     /**
+      * @brief Read deep learning network represented in one of the supported formats.
+      * @details This is an overloaded member function, provided for convenience.
+      *          It differs from the above function only in what argument(s) it accepts.
+      * @param[in] framework    Name of origin framework.
+      * @param[in] bufferModel  A buffer with a content of binary file with weights
+      * @param[in] bufferConfig A buffer with a content of text file contains network configuration.
+      * @returns Net object.
+      */
+     CV_EXPORTS_W Net readNet(const String& framework, const std::vector<char>& bufferModel,
+                              const std::vector<char>& bufferConfig = std::vector<char>());
+
     /** @brief Loads blob which was serialized as torch.Tensor object of Torch7 framework.
      *  @warning This function has the same limitations as readNetFromTorch().
      */
index 37db7f0..b47c586 100644 (file)
@@ -453,6 +453,12 @@ Net readNetFromCaffe(const char *bufferProto, size_t lenProto,
     return net;
 }
 
+Net readNetFromCaffe(const std::vector<char>& bufferProto, const std::vector<char>& bufferModel)
+{
+    return readNetFromCaffe(&bufferProto[0], bufferProto.size(),
+                            bufferModel.empty() ? NULL : &bufferModel[0], bufferModel.size());
+}
+
 #endif //HAVE_PROTOBUF
 
 CV__DNN_EXPERIMENTAL_NS_END
index 17506c2..08083a3 100644 (file)
@@ -181,45 +181,71 @@ public:
     }
 };
 
+static Net readNetFromDarknet(std::istream &cfgFile, std::istream &darknetModel)
+{
+    Net net;
+    DarknetImporter darknetImporter(cfgFile, darknetModel);
+    darknetImporter.populateNet(net);
+    return net;
 }
 
-Net readNetFromDarknet(const String &cfgFile, const String &darknetModel /*= String()*/)
+static Net readNetFromDarknet(std::istream &cfgFile)
 {
     Net net;
+    DarknetImporter darknetImporter(cfgFile);
+    darknetImporter.populateNet(net);
+    return net;
+}
+
+}
+
+Net readNetFromDarknet(const String &cfgFile, const String &darknetModel /*= String()*/)
+{
     std::ifstream cfgStream(cfgFile.c_str());
-    if(!cfgStream.is_open()) {
+    if (!cfgStream.is_open())
+    {
         CV_Error(cv::Error::StsParseError, "Failed to parse NetParameter file: " + std::string(cfgFile));
-        return net;
     }
-    DarknetImporter darknetImporter;
-    if (darknetModel != String()) {
-        std::ifstream darknetModelStream(darknetModel.c_str());
-        if(!darknetModelStream.is_open()){
+    if (darknetModel != String())
+    {
+        std::ifstream darknetModelStream(darknetModel.c_str(), std::ios::binary);
+        if (!darknetModelStream.is_open())
+        {
             CV_Error(cv::Error::StsParseError, "Failed to parse NetParameter file: " + std::string(darknetModel));
-            return net;
         }
-        darknetImporter = DarknetImporter(cfgStream, darknetModelStream);
-    } else {
-        darknetImporter = DarknetImporter(cfgStream);
+        return readNetFromDarknet(cfgStream, darknetModelStream);
     }
-    darknetImporter.populateNet(net);
-    return net;
+    else
+        return readNetFromDarknet(cfgStream);
 }
 
-Net readNetFromDarknet(const FileNode &cfgFile, const FileNode &darknetModel /*= FileNode()*/)
+struct BufferStream : public std::streambuf
 {
-    DarknetImporter darknetImporter;
-    if(darknetModel.empty()){
-        std::istringstream cfgStream((std::string)cfgFile);
-        darknetImporter = DarknetImporter(cfgStream);
-    }else{
-        std::istringstream cfgStream((std::string)cfgFile);
-        std::istringstream darknetModelStream((std::string)darknetModel);
-        darknetImporter = DarknetImporter(cfgStream, darknetModelStream);
+    BufferStream(const char* s, std::size_t n)
+    {
+        char* ptr = const_cast<char*>(s);
+        setg(ptr, ptr, ptr + n);
     }
-    Net net;
-    darknetImporter.populateNet(net);
-    return net;
+};
+
+Net readNetFromDarknet(const char *bufferCfg, size_t lenCfg, const char *bufferModel, size_t lenModel)
+{
+    BufferStream cfgBufferStream(bufferCfg, lenCfg);
+    std::istream cfgStream(&cfgBufferStream);
+    if (lenModel)
+    {
+        BufferStream weightsBufferStream(bufferModel, lenModel);
+        std::istream weightsStream(&weightsBufferStream);
+        return readNetFromDarknet(cfgStream, weightsStream);
+    }
+    else
+        return readNetFromDarknet(cfgStream);
+}
+
+Net readNetFromDarknet(const std::vector<char>& bufferCfg, const std::vector<char>& bufferModel)
+{
+    return readNetFromDarknet(&bufferCfg[0], bufferCfg.size(),
+                              bufferModel.empty() ? NULL : &bufferModel[0], bufferModel.size());
 }
 
 CV__DNN_EXPERIMENTAL_NS_END
index 080de30..3803743 100644 (file)
@@ -3047,6 +3047,23 @@ Net readNet(const String& _model, const String& _config, const String& _framewor
                                       model + (config.empty() ? "" : ", " + config));
 }
 
+Net readNet(const String& _framework, const std::vector<char>& bufferModel,
+            const std::vector<char>& bufferConfig)
+{
+    String framework = _framework.toLowerCase();
+    if (framework == "caffe")
+        return readNetFromCaffe(bufferConfig, bufferModel);
+    else if (framework == "tensorflow")
+        return readNetFromTensorflow(bufferModel, bufferConfig);
+    else if (framework == "darknet")
+        return readNetFromDarknet(bufferConfig, bufferModel);
+    else if (framework == "torch")
+        CV_Error(Error::StsNotImplemented, "Reading Torch models from buffers");
+    else if (framework == "dldt")
+        CV_Error(Error::StsNotImplemented, "Reading Intel's Model Optimizer models from buffers");
+    CV_Error(Error::StsError, "Cannot determine an origin framework with a name " + framework);
+}
+
 Net readNetFromModelOptimizer(const String &xml, const String &bin)
 {
     return Net::readFromModelOptimizer(xml, bin);
index 7d7d300..987d63d 100644 (file)
@@ -1856,5 +1856,11 @@ Net readNetFromTensorflow(const char* bufferModel, size_t lenModel,
     return net;
 }
 
+Net readNetFromTensorflow(const std::vector<char>& bufferModel, const std::vector<char>& bufferConfig)
+{
+    return readNetFromCaffe(&bufferModel[0], bufferModel.size(),
+                            bufferConfig.empty() ? NULL : &bufferConfig[0], bufferConfig.size());
+}
+
 CV__DNN_EXPERIMENTAL_NS_END
 }} // namespace
index c585d40..077498d 100644 (file)
@@ -65,16 +65,32 @@ TEST(Test_Darknet, read_yolo_voc)
     ASSERT_FALSE(net.empty());
 }
 
-TEST(Test_Darknet, read_filestorage_yolo_voc)
+TEST(Test_Darknet, read_yolo_voc_stream)
 {
-    std::ifstream ifile(_tf("yolo-voc.cfg").c_str());
-    std::stringstream buffer;
-    buffer << " " << ifile.rdbuf(); // FIXME: FileStorage drops first character.
-    FileStorage ofs(".xml", FileStorage::WRITE | FileStorage::MEMORY);
-    ofs.write("cfgFile", buffer.str());
-    FileStorage ifs(ofs.releaseAndGetString(), FileStorage::READ | FileStorage::MEMORY | FileStorage::FORMAT_XML);
-    Net net = readNetFromDarknet(ifs["cfgFile"]);
-    ASSERT_FALSE(net.empty());
+    Mat ref;
+    Mat sample = imread(_tf("dog416.png"));
+    Mat inp = blobFromImage(sample, 1.0/255, Size(416, 416), Scalar(), true, false);
+    const std::string cfgFile = findDataFile("dnn/yolo-voc.cfg", false);
+    const std::string weightsFile = findDataFile("dnn/yolo-voc.weights", false);
+    // Import by paths.
+    {
+        Net net = readNetFromDarknet(cfgFile, weightsFile);
+        net.setInput(inp);
+        net.setPreferableBackend(DNN_BACKEND_OPENCV);
+        ref = net.forward();
+    }
+    // Import from bytes array.
+    {
+        std::string cfg, weights;
+        readFileInMemory(cfgFile, cfg);
+        readFileInMemory(weightsFile, weights);
+
+        Net net = readNetFromDarknet(&cfg[0], cfg.size(), &weights[0], weights.size());
+        net.setInput(inp);
+        net.setPreferableBackend(DNN_BACKEND_OPENCV);
+        Mat out = net.forward();
+        normAssert(ref, out);
+    }
 }
 
 class Test_Darknet_layers : public DNNTestLayer