diff --git a/modules/dnn/include/opencv2/dnn/dnn.hpp b/modules/dnn/include/opencv2/dnn/dnn.hpp index 78662544d8..4f109ed82c 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.hpp @@ -1074,14 +1074,17 @@ CV__DNN_INLINE_NS_BEGIN * Model creates net from file with trained weights and config, * sets preprocessing input and runs forward pass. */ - class CV_EXPORTS_W_SIMPLE Model : public Net + class CV_EXPORTS_W_SIMPLE Model { public: - /** - * @brief Default constructor. - */ + CV_DEPRECATED_EXTERNAL // avoid using in C++ code, will be moved to "protected" (need to fix bindings first) Model(); + Model(const Model&) = default; + Model(Model&&) = default; + Model& operator=(const Model&) = default; + Model& operator=(Model&&) = default; + /** * @brief Create model from deep learning network represented in one of the supported formats. * An order of @p model and @p config arguments does not matter. @@ -1102,13 +1105,12 @@ CV__DNN_INLINE_NS_BEGIN */ CV_WRAP Model& setInputSize(const Size& size); - /** @brief Set input size for frame. + /** @overload * @param[in] width New input width. * @param[in] height New input height. - * @note If shape of the new blob less than 0, - * then frame size not change. */ - CV_WRAP Model& setInputSize(int width, int height); + CV_WRAP inline + Model& setInputSize(int width, int height) { return setInputSize(Size(width, height)); } /** @brief Set mean value for frame. * @param[in] mean Scalar with mean values which are subtracted from channels. @@ -1145,10 +1147,31 @@ CV__DNN_INLINE_NS_BEGIN * @param[in] frame The input image. * @param[out] outs Allocated output blobs, which will store results of the computation. */ - CV_WRAP void predict(InputArray frame, OutputArrayOfArrays outs); + CV_WRAP void predict(InputArray frame, OutputArrayOfArrays outs) const; + + + // ============================== Net proxy methods ============================== + // Never expose methods with network implementation details, like: + // - addLayer, addLayerToPrev, connect, setInputsNames, setInputShape, setParam, getParam + // - getLayer*, getUnconnectedOutLayers, getUnconnectedOutLayersNames, getLayersShapes + // - forward* methods, setInput + + /// @sa Net::setPreferableBackend + CV_WRAP Model& setPreferableBackend(dnn::Backend backendId); + /// @sa Net::setPreferableTarget + CV_WRAP Model& setPreferableTarget(dnn::Target targetId); + + CV_DEPRECATED_EXTERNAL + operator Net&() const { return getNetwork_(); } + + //protected: - internal/tests usage only + Net& getNetwork_() const; + inline Net& getNetwork_() { return const_cast(this)->getNetwork_(); } - protected: struct Impl; + inline Impl* getImpl() const { return impl.get(); } + inline Impl& getImplRef() const { CV_DbgAssert(impl); return *impl.get(); } + protected: Ptr impl; }; diff --git a/modules/dnn/include/opencv2/dnn/version.hpp b/modules/dnn/include/opencv2/dnn/version.hpp index f91b44d142..62ecadb6f7 100644 --- a/modules/dnn/include/opencv2/dnn/version.hpp +++ b/modules/dnn/include/opencv2/dnn/version.hpp @@ -6,7 +6,7 @@ #define OPENCV_DNN_VERSION_HPP /// Use with major OpenCV version only. -#define OPENCV_DNN_API_VERSION 20200908 +#define OPENCV_DNN_API_VERSION 20201117 #if !defined CV_DOXYGEN && !defined CV_STATIC_ANALYSIS && !defined CV_DNN_DONT_ADD_INLINE_NS #define CV__DNN_INLINE_NS __CV_CAT(dnn4_v, OPENCV_DNN_API_VERSION) diff --git a/modules/dnn/src/model.cpp b/modules/dnn/src/model.cpp index 677228bcf2..acee29e680 100644 --- a/modules/dnn/src/model.cpp +++ b/modules/dnn/src/model.cpp @@ -15,6 +15,9 @@ namespace dnn { struct Model::Impl { +//protected: + Net net; + Size size; Scalar mean; double scale = 1.0; @@ -23,7 +26,70 @@ struct Model::Impl Mat blob; std::vector outNames; - void predict(Net& net, const Mat& frame, OutputArrayOfArrays outs) +public: + virtual ~Impl() {} + Impl() {} + Impl(const Impl&) = delete; + Impl(Impl&&) = delete; + + virtual Net& getNetwork() const { return const_cast(net); } + + virtual void setPreferableBackend(Backend backendId) { net.setPreferableBackend(backendId); } + virtual void setPreferableTarget(Target targetId) { net.setPreferableTarget(targetId); } + + /*virtual*/ + void initNet(const Net& network) + { + net = network; + + outNames = net.getUnconnectedOutLayersNames(); + std::vector inLayerShapes; + std::vector outLayerShapes; + net.getLayerShapes(MatShape(), 0, inLayerShapes, outLayerShapes); + if (!inLayerShapes.empty() && inLayerShapes[0].size() == 4) + size = Size(inLayerShapes[0][3], inLayerShapes[0][2]); + else + size = Size(); + } + + /*virtual*/ + void setInputParams(double scale_, const Size& size_, const Scalar& mean_, + bool swapRB_, bool crop_) + { + size = size_; + mean = mean_; + scale = scale_; + crop = crop_; + swapRB = swapRB_; + } + /*virtual*/ + void setInputSize(const Size& size_) + { + size = size_; + } + /*virtual*/ + void setInputMean(const Scalar& mean_) + { + mean = mean_; + } + /*virtual*/ + void setInputScale(double scale_) + { + scale = scale_; + } + /*virtual*/ + void setInputCrop(bool crop_) + { + crop = crop_; + } + /*virtual*/ + void setInputSwapRB(bool swapRB_) + { + swapRB = swapRB_; + } + + /*virtual*/ + void processFrame(InputArray frame, OutputArrayOfArrays outs) { if (size.empty()) CV_Error(Error::StsBadSize, "Input size not specified"); @@ -34,96 +100,115 @@ struct Model::Impl // Faster-RCNN or R-FCN if (net.getLayer(0)->outputNameToIndex("im_info") != -1) { - Mat imInfo = (Mat_(1, 3) << size.height, size.width, 1.6f); + Mat imInfo(Matx31f(size.height, size.width, 1.6f)); net.setInput(imInfo, "im_info"); } net.forward(outs, outNames); } }; -Model::Model() : impl(new Impl) {} +Model::Model() + : impl(makePtr()) +{ + // nothing +} Model::Model(const String& model, const String& config) - : Net(readNet(model, config)), impl(new Impl) + : Model() { - impl->outNames = getUnconnectedOutLayersNames(); - std::vector inLayerShapes; - std::vector outLayerShapes; - getLayerShapes(MatShape(), 0, inLayerShapes, outLayerShapes); - if (!inLayerShapes.empty() && inLayerShapes[0].size() == 4) - impl->size = Size(inLayerShapes[0][3], inLayerShapes[0][2]); -}; + impl->initNet(readNet(model, config)); +} -Model::Model(const Net& network) : Net(network), impl(new Impl) +Model::Model(const Net& network) + : Model() { - impl->outNames = getUnconnectedOutLayersNames(); - std::vector inLayerShapes; - std::vector outLayerShapes; - getLayerShapes(MatShape(), 0, inLayerShapes, outLayerShapes); - if (!inLayerShapes.empty() && inLayerShapes[0].size() == 4) - impl->size = Size(inLayerShapes[0][3], inLayerShapes[0][2]); -}; + impl->initNet(network); +} -Model& Model::setInputSize(const Size& size) +Net& Model::getNetwork_() const +{ + CV_DbgAssert(impl); + return impl->getNetwork(); +} + +Model& Model::setPreferableBackend(Backend backendId) +{ + CV_DbgAssert(impl); + impl->setPreferableBackend(backendId); + return *this; +} +Model& Model::setPreferableTarget(Target targetId) { - impl->size = size; + CV_DbgAssert(impl); + impl->setPreferableTarget(targetId); return *this; } -Model& Model::setInputSize(int width, int height) +Model& Model::setInputSize(const Size& size) { - impl->size = Size(width, height); + CV_DbgAssert(impl); + impl->setInputSize(size); return *this; } Model& Model::setInputMean(const Scalar& mean) { - impl->mean = mean; + CV_DbgAssert(impl); + impl->setInputMean(mean); return *this; } Model& Model::setInputScale(double scale) { - impl->scale = scale; + CV_DbgAssert(impl); + impl->setInputScale(scale); return *this; } Model& Model::setInputCrop(bool crop) { - impl->crop = crop; + CV_DbgAssert(impl); + impl->setInputCrop(crop); return *this; } Model& Model::setInputSwapRB(bool swapRB) { - impl->swapRB = swapRB; + CV_DbgAssert(impl); + impl->setInputSwapRB(swapRB); return *this; } void Model::setInputParams(double scale, const Size& size, const Scalar& mean, bool swapRB, bool crop) { - impl->size = size; - impl->mean = mean; - impl->scale = scale; - impl->crop = crop; - impl->swapRB = swapRB; + CV_DbgAssert(impl); + impl->setInputParams(scale, size, mean, swapRB, crop); } -void Model::predict(InputArray frame, OutputArrayOfArrays outs) +void Model::predict(InputArray frame, OutputArrayOfArrays outs) const { - impl->predict(*this, frame.getMat(), outs); + CV_DbgAssert(impl); + impl->processFrame(frame, outs); } + ClassificationModel::ClassificationModel(const String& model, const String& config) - : Model(model, config) {}; + : Model(model, config) +{ + // nothing +} -ClassificationModel::ClassificationModel(const Net& network) : Model(network) {}; +ClassificationModel::ClassificationModel(const Net& network) + : Model(network) +{ + // nothing +} std::pair ClassificationModel::classify(InputArray frame) { std::vector outs; - impl->predict(*this, frame.getMat(), outs); + impl->processFrame(frame, outs); CV_Assert(outs.size() == 1); double conf; @@ -145,11 +230,11 @@ KeypointsModel::KeypointsModel(const Net& network) : Model(network) {}; std::vector KeypointsModel::estimate(InputArray frame, float thresh) { - int frameHeight = frame.getMat().size[0]; - int frameWidth = frame.getMat().size[1]; + int frameHeight = frame.rows(); + int frameWidth = frame.cols(); std::vector outs; - impl->predict(*this, frame.getMat(), outs); + impl->processFrame(frame, outs); CV_Assert(outs.size() == 1); Mat output = outs[0]; @@ -202,9 +287,8 @@ SegmentationModel::SegmentationModel(const Net& network) : Model(network) {}; void SegmentationModel::segment(InputArray frame, OutputArray mask) { - std::vector outs; - impl->predict(*this, frame.getMat(), outs); + impl->processFrame(frame, outs); CV_Assert(outs.size() == 1); Mat score = outs[0]; @@ -250,12 +334,14 @@ void disableRegionNMS(Net& net) } DetectionModel::DetectionModel(const String& model, const String& config) - : Model(model, config) { - disableRegionNMS(*this); + : Model(model, config) +{ + disableRegionNMS(getNetwork_()); // FIXIT Move to DetectionModel::Impl::initNet() } -DetectionModel::DetectionModel(const Net& network) : Model(network) { - disableRegionNMS(*this); +DetectionModel::DetectionModel(const Net& network) : Model(network) +{ + disableRegionNMS(getNetwork_()); // FIXIT Move to DetectionModel::Impl::initNet() } void DetectionModel::detect(InputArray frame, CV_OUT std::vector& classIds, @@ -263,7 +349,7 @@ void DetectionModel::detect(InputArray frame, CV_OUT std::vector& classIds, float confThreshold, float nmsThreshold) { std::vector detections; - impl->predict(*this, frame.getMat(), detections); + impl->processFrame(frame, detections); boxes.clear(); confidences.clear(); @@ -271,15 +357,15 @@ void DetectionModel::detect(InputArray frame, CV_OUT std::vector& classIds, int frameWidth = frame.cols(); int frameHeight = frame.rows(); - if (getLayer(0)->outputNameToIndex("im_info") != -1) + if (getNetwork_().getLayer(0)->outputNameToIndex("im_info") != -1) { frameWidth = impl->size.width; frameHeight = impl->size.height; } - std::vector layerNames = getLayerNames(); - int lastLayerId = getLayerId(layerNames.back()); - Ptr lastLayer = getLayer(lastLayerId); + std::vector layerNames = getNetwork_().getLayerNames(); + int lastLayerId = getNetwork_().getLayerId(layerNames.back()); + Ptr lastLayer = getNetwork_().getLayer(lastLayerId); if (lastLayer->type == "DetectionOutput") { diff --git a/modules/dnn/test/test_caffe_importer.cpp b/modules/dnn/test/test_caffe_importer.cpp index e1ffa762de..5440f4734f 100644 --- a/modules/dnn/test/test_caffe_importer.cpp +++ b/modules/dnn/test/test_caffe_importer.cpp @@ -563,7 +563,7 @@ TEST_P(Test_Caffe_nets, DenseNet_121) } normAssert(outs[0], ref, "", l1, lInf); if (target != DNN_TARGET_MYRIAD || getInferenceEngineVPUType() != CV_DNN_INFERENCE_ENGINE_VPU_TYPE_MYRIAD_X) - expectNoFallbacksFromIE(model); + expectNoFallbacksFromIE(model.getNetwork_()); } TEST(Test_Caffe, multiple_inputs)