Skip to content

Commit f2440ce

Browse files
committed
Update tutorials. A new cv::dnn::readNet function
1 parent 8e4fe30 commit f2440ce

File tree

10 files changed

+150
-156
lines changed

10 files changed

+150
-156
lines changed

doc/tutorials/dnn/dnn_googlenet/dnn_googlenet.markdown

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,50 +13,53 @@ We will demonstrate results of this example on the following picture.
1313
Source Code
1414
-----------
1515

16-
We will be using snippets from the example application, that can be downloaded [here](https://github.com/opencv/opencv/blob/master/samples/dnn/caffe_googlenet.cpp).
16+
We will be using snippets from the example application, that can be downloaded [here](https://github.com/opencv/opencv/blob/master/samples/dnn/classification.cpp).
1717

18-
@include dnn/caffe_googlenet.cpp
18+
@include dnn/classification.cpp
1919

2020
Explanation
2121
-----------
2222

2323
-# Firstly, download GoogLeNet model files:
24-
[bvlc_googlenet.prototxt ](https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/bvlc_googlenet.prototxt) and
24+
[bvlc_googlenet.prototxt ](https://github.com/opencv/opencv_extra/blob/master/testdata/dnn/bvlc_googlenet.prototxt) and
2525
[bvlc_googlenet.caffemodel](http://dl.caffe.berkeleyvision.org/bvlc_googlenet.caffemodel)
2626

2727
Also you need file with names of [ILSVRC2012](http://image-net.org/challenges/LSVRC/2012/browse-synsets) classes:
28-
[synset_words.txt](https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/synset_words.txt).
28+
[classification_classes_ILSVRC2012.txt](https://github.com/opencv/opencv/tree/master/samples/dnn/classification_classes_ILSVRC2012.txt).
2929

3030
Put these files into working dir of this program example.
3131

3232
-# Read and initialize network using path to .prototxt and .caffemodel files
33-
@snippet dnn/caffe_googlenet.cpp Read and initialize network
33+
@snippet dnn/classification.cpp Read and initialize network
3434

35-
-# Check that network was read successfully
36-
@snippet dnn/caffe_googlenet.cpp Check that network was read successfully
35+
You can skip an argument `framework` if one of the files `model` or `config` has an
36+
extension `.caffemodel` or `.prototxt`.
37+
This way function cv::dnn::readNet can automatically detects a model's format.
3738

3839
-# Read input image and convert to the blob, acceptable by GoogleNet
39-
@snippet dnn/caffe_googlenet.cpp Prepare blob
40-
We convert the image to a 4-dimensional blob (so-called batch) with 1x3x224x224 shape after applying necessary pre-processing like resizing and mean subtraction using cv::dnn::blobFromImage constructor.
40+
@snippet dnn/classification.cpp Open a video file or an image file or a camera stream
4141

42-
-# Pass the blob to the network
43-
@snippet dnn/caffe_googlenet.cpp Set input blob
44-
In bvlc_googlenet.prototxt the network input blob named as "data", therefore this blob labeled as ".data" in opencv_dnn API.
42+
cv::VideoCapture can load both images and videos.
43+
44+
@snippet dnn/classification.cpp Create a 4D blob from a frame
45+
We convert the image to a 4-dimensional blob (so-called batch) with `1x3x224x224` shape
46+
after applying necessary pre-processing like resizing and mean subtraction
47+
`(-104, -117, -123)` for each blue, green and red channels correspondingly using cv::dnn::blobFromImage function.
4548

46-
Other blobs labeled as "name_of_layer.name_of_layer_output".
49+
-# Pass the blob to the network
50+
@snippet dnn/classification.cpp Set input blob
4751

4852
-# Make forward pass
49-
@snippet dnn/caffe_googlenet.cpp Make forward pass
50-
During the forward pass output of each network layer is computed, but in this example we need output from "prob" layer only.
53+
@snippet dnn/classification.cpp Make forward pass
54+
During the forward pass output of each network layer is computed, but in this example we need output from the last layer only.
5155

5256
-# Determine the best class
53-
@snippet dnn/caffe_googlenet.cpp Gather output
54-
We put the output of "prob" layer, which contain probabilities for each of 1000 ILSVRC2012 image classes, to the `prob` blob.
55-
And find the index of element with maximal value in this one. This index correspond to the class of the image.
56-
57-
-# Print results
58-
@snippet dnn/caffe_googlenet.cpp Print results
59-
For our image we get:
60-
> Best class: #812 'space shuttle'
61-
>
62-
> Probability: 99.6378%
57+
@snippet dnn/classification.cpp Get a class with a highest score
58+
We put the output of network, which contain probabilities for each of 1000 ILSVRC2012 image classes, to the `prob` blob.
59+
And find the index of element with maximal value in this one. This index corresponds to the class of the image.
60+
61+
-# Run an example from command line
62+
@code
63+
./example_dnn_classification --model=bvlc_googlenet.caffemodel --config=bvlc_googlenet.prototxt --width=224 --height=224 --classes=classification_classes_ILSVRC2012.txt --input=space_shuttle.jpg --mean="104 117 123"
64+
@endcode
65+
For our image we get prediction of class `space shuttle` with more than 99% sureness.

doc/tutorials/dnn/dnn_halide/dnn_halide.markdown

Lines changed: 4 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -74,46 +74,7 @@ When you build OpenCV add the following configuration flags:
7474

7575
- `HALIDE_ROOT_DIR` - path to Halide build directory
7676

77-
## Sample
78-
79-
@include dnn/squeezenet_halide.cpp
80-
81-
## Explanation
82-
Download Caffe model from SqueezeNet repository: [train_val.prototxt](https://github.com/DeepScale/SqueezeNet/blob/master/SqueezeNet_v1.1/train_val.prototxt) and [squeezenet_v1.1.caffemodel](https://github.com/DeepScale/SqueezeNet/blob/master/SqueezeNet_v1.1/squeezenet_v1.1.caffemodel).
83-
84-
Also you need file with names of [ILSVRC2012](http://image-net.org/challenges/LSVRC/2012/browse-synsets) classes:
85-
[synset_words.txt](https://raw.githubusercontent.com/opencv/opencv/master/samples/data/dnn/synset_words.txt).
86-
87-
Put these files into working dir of this program example.
88-
89-
-# Read and initialize network using path to .prototxt and .caffemodel files
90-
@snippet dnn/squeezenet_halide.cpp Read and initialize network
91-
92-
-# Check that network was read successfully
93-
@snippet dnn/squeezenet_halide.cpp Check that network was read successfully
94-
95-
-# Read input image and convert to the 4-dimensional blob, acceptable by SqueezeNet v1.1
96-
@snippet dnn/squeezenet_halide.cpp Prepare blob
97-
98-
-# Pass the blob to the network
99-
@snippet dnn/squeezenet_halide.cpp Set input blob
100-
101-
-# Enable Halide backend for layers where it is implemented
102-
@snippet dnn/squeezenet_halide.cpp Enable Halide backend
103-
104-
-# Make forward pass
105-
@snippet dnn/squeezenet_halide.cpp Make forward pass
106-
Remember that the first forward pass after initialization require quite more
107-
time that the next ones. It's because of runtime compilation of Halide pipelines
108-
at the first invocation.
109-
110-
-# Determine the best class
111-
@snippet dnn/squeezenet_halide.cpp Determine the best class
112-
113-
-# Print results
114-
@snippet dnn/squeezenet_halide.cpp Print results
115-
For our image we get:
116-
117-
> Best class: #812 'space shuttle'
118-
>
119-
> Probability: 97.9812%
77+
## Set Halide as a preferable backend
78+
@code
79+
net.setPreferableBackend(DNN_BACKEND_HALIDE);
80+
@endcode

modules/dnn/include/opencv2/dnn/dnn.hpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,29 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN
683683
*/
684684
CV_EXPORTS_W Net readNetFromTorch(const String &model, bool isBinary = true);
685685

686+
/**
687+
* @brief Read deep learning network represented in one of the supported formats.
688+
* @param[in] model Binary file contains trained weights. The following file
689+
* extensions are expected for models from different frameworks:
690+
* * `*.caffemodel` (Caffe, http://caffe.berkeleyvision.org/)
691+
* * `*.pb` (TensorFlow, https://www.tensorflow.org/)
692+
* * `*.t7` | `*.net` (Torch, http://torch.ch/)
693+
* * `*.weights` (Darknet, https://pjreddie.com/darknet/)
694+
* @param[in] config Text file contains network configuration. It could be a
695+
* file with the following extensions:
696+
* * `*.prototxt` (Caffe, http://caffe.berkeleyvision.org/)
697+
* * `*.pbtxt` (TensorFlow, https://www.tensorflow.org/)
698+
* * `*.cfg` (Darknet, https://pjreddie.com/darknet/)
699+
* @param[in] framework Explicit framework name tag to determine a format.
700+
* @returns Net object.
701+
*
702+
* This function automatically detects an origin framework of trained model
703+
* and calls an appropriate function such @ref readNetFromCaffe, @ref readNetFromTensorflow,
704+
* @ref readNetFromTorch or @ref readNetFromDarknet. An order of @p model and @p config
705+
* arguments does not matter.
706+
*/
707+
CV_EXPORTS_W Net readNet(String model, String config = "", String framework = "");
708+
686709
/** @brief Loads blob which was serialized as torch.Tensor object of Torch7 framework.
687710
* @warning This function has the same limitations as readNetFromTorch().
688711
*/

modules/dnn/src/dnn.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2805,5 +2805,41 @@ BackendWrapper::BackendWrapper(const Ptr<BackendWrapper>& base, const MatShape&
28052805

28062806
BackendWrapper::~BackendWrapper() {}
28072807

2808+
Net readNet(String model, String config, String framework)
2809+
{
2810+
framework = framework.toLowerCase();
2811+
const std::string modelExt = model.substr(model.rfind('.') + 1);
2812+
const std::string configExt = config.substr(config.rfind('.') + 1);
2813+
if (framework == "caffe" || modelExt == "caffemodel" || configExt == "caffemodel" ||
2814+
modelExt == "prototxt" || configExt == "prototxt")
2815+
{
2816+
if (modelExt == "prototxt" || configExt == "caffemodel")
2817+
std::swap(model, config);
2818+
return readNetFromCaffe(config, model);
2819+
}
2820+
if (framework == "tensorflow" || modelExt == "pb" || configExt == "pb" ||
2821+
modelExt == "pbtxt" || configExt == "pbtxt")
2822+
{
2823+
if (modelExt == "pbtxt" || configExt == "pb")
2824+
std::swap(model, config);
2825+
return readNetFromTensorflow(model, config);
2826+
}
2827+
if (framework == "torch" || modelExt == "t7" || modelExt == "net" ||
2828+
configExt == "t7" || configExt == "net")
2829+
{
2830+
return readNetFromTorch(model.empty() ? config : model);
2831+
}
2832+
if (framework == "darknet" || modelExt == "weights" || configExt == "weights" ||
2833+
modelExt == "cfg" || configExt == "cfg")
2834+
{
2835+
if (modelExt == "cfg" || configExt == "weights")
2836+
std::swap(model, config);
2837+
return readNetFromDarknet(config, model);
2838+
}
2839+
CV_Error(Error::StsError, "Cannot determine an origin framework of files: " +
2840+
model + (config.empty() ? "" : ", " + config));
2841+
return Net();
2842+
}
2843+
28082844
CV__DNN_EXPERIMENTAL_NS_END
28092845
}} // namespace

modules/dnn/test/test_misc.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,22 @@ TEST(imagesFromBlob, Regression)
5757
}
5858
}
5959

60+
TEST(readNet, Regression)
61+
{
62+
Net net = readNet(findDataFile("dnn/squeezenet_v1.1.prototxt", false),
63+
findDataFile("dnn/squeezenet_v1.1.caffemodel", false));
64+
EXPECT_FALSE(net.empty());
65+
net = readNet(findDataFile("dnn/opencv_face_detector.caffemodel", false),
66+
findDataFile("dnn/opencv_face_detector.prototxt", false));
67+
EXPECT_FALSE(net.empty());
68+
net = readNet(findDataFile("dnn/openface_nn4.small2.v1.t7", false));
69+
EXPECT_FALSE(net.empty());
70+
net = readNet(findDataFile("dnn/tiny-yolo-voc.cfg", false),
71+
findDataFile("dnn/tiny-yolo-voc.weights", false));
72+
EXPECT_FALSE(net.empty());
73+
net = readNet(findDataFile("dnn/ssd_mobilenet_v1_coco.pbtxt", false),
74+
findDataFile("dnn/ssd_mobilenet_v1_coco.pb", false));
75+
EXPECT_FALSE(net.empty());
76+
}
77+
6078
}} // namespace

samples/dnn/README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,12 @@
1414
| [Faster-RCNN](https://github.com/rbgirshick/py-faster-rcnn) | `1.0` | `800x600` | `102.9801, 115.9465, 122.7717` | BGR |
1515
| [R-FCN](https://github.com/YuwenXiong/py-R-FCN) | `1.0` | `800x600` | `102.9801 115.9465 122.7717` | BGR |
1616

17-
1817
### Classification
1918
| Model | Scale | Size WxH| Mean subtraction | Channels order |
2019
|---------------|-------|-----------|--------------------|-------|
2120
| GoogLeNet | `1.0` | `224x224` | `104 117 123` | BGR |
2221
| [SqueezeNet](https://github.com/DeepScale/SqueezeNet) | `1.0` | `227x227` | `0 0 0` | BGR |
2322

24-
2523
## References
2624
* [Models downloading script](https://github.com/opencv/opencv_extra/blob/master/testdata/dnn/download_models.py)
2725
* [Configuration files adopted for OpenCV](https://github.com/opencv/opencv_extra/tree/master/testdata/dnn)

samples/dnn/classification.cpp

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
#include <iostream>
33
#include <sstream>
44

5-
#include <opencv2/opencv.hpp>
65
#include <opencv2/dnn.hpp>
6+
#include <opencv2/imgproc.hpp>
7+
#include <opencv2/highgui.hpp>
78

89
const char* keys =
910
"{ help h | | Print help message. }"
@@ -33,8 +34,6 @@ using namespace dnn;
3334

3435
std::vector<std::string> classes;
3536

36-
Net readNet(const std::string& model, const std::string& config = "", const std::string& framework = "");
37-
3837
int main(int argc, char** argv)
3938
{
4039
CommandLineParser parser(argc, argv, keys);
@@ -49,6 +48,11 @@ int main(int argc, char** argv)
4948
bool swapRB = parser.get<bool>("rgb");
5049
int inpWidth = parser.get<int>("width");
5150
int inpHeight = parser.get<int>("height");
51+
String model = parser.get<String>("model");
52+
String config = parser.get<String>("config");
53+
String framework = parser.get<String>("framework");
54+
int backendId = parser.get<int>("backend");
55+
int targetId = parser.get<int>("target");
5256

5357
// Parse mean values.
5458
Scalar mean;
@@ -77,22 +81,24 @@ int main(int argc, char** argv)
7781
}
7882
}
7983

80-
// Load a model.
8184
CV_Assert(parser.has("model"));
82-
Net net = readNet(parser.get<String>("model"), parser.get<String>("config"), parser.get<String>("framework"));
83-
net.setPreferableBackend(parser.get<int>("backend"));
84-
net.setPreferableTarget(parser.get<int>("target"));
85+
//! [Read and initialize network]
86+
Net net = readNet(model, config, framework);
87+
net.setPreferableBackend(backendId);
88+
net.setPreferableTarget(targetId);
89+
//! [Read and initialize network]
8590

8691
// Create a window
8792
static const std::string kWinName = "Deep learning image classification in OpenCV";
8893
namedWindow(kWinName, WINDOW_NORMAL);
8994

90-
// Open a video file or an image file or a camera stream.
95+
//! [Open a video file or an image file or a camera stream]
9196
VideoCapture cap;
9297
if (parser.has("input"))
9398
cap.open(parser.get<String>("input"));
9499
else
95100
cap.open(0);
101+
//! [Open a video file or an image file or a camera stream]
96102

97103
// Process frames.
98104
Mat frame, blob;
@@ -105,24 +111,29 @@ int main(int argc, char** argv)
105111
break;
106112
}
107113

108-
// Create a 4D blob from a frame.
114+
//! [Create a 4D blob from a frame]
109115
blobFromImage(frame, blob, scale, Size(inpWidth, inpHeight), mean, swapRB, false);
116+
//! [Create a 4D blob from a frame]
110117

111-
// Run a model.
118+
//! [Set input blob]
112119
net.setInput(blob);
113-
Mat out = net.forward();
114-
out = out.reshape(1, 1);
120+
//! [Set input blob]
121+
//! [Make forward pass]
122+
Mat prob = net.forward();
123+
//! [Make forward pass]
115124

116-
// Get a class with a highest score.
125+
//! [Get a class with a highest score]
117126
Point classIdPoint;
118127
double confidence;
119-
minMaxLoc(out, 0, &confidence, 0, &classIdPoint);
128+
minMaxLoc(prob.reshape(1, 1), 0, &confidence, 0, &classIdPoint);
120129
int classId = classIdPoint.x;
130+
//! [Get a class with a highest score]
121131

122132
// Put efficiency information.
123133
std::vector<double> layersTimes;
124-
double t = net.getPerfProfile(layersTimes);
125-
std::string label = format("Inference time: %.2f", t * 1000 / getTickFrequency());
134+
double freq = getTickFrequency() / 1000;
135+
double t = net.getPerfProfile(layersTimes) / freq;
136+
std::string label = format("Inference time: %.2f ms", t);
126137
putText(frame, label, Point(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0));
127138

128139
// Print predicted class.
@@ -135,19 +146,3 @@ int main(int argc, char** argv)
135146
}
136147
return 0;
137148
}
138-
139-
Net readNet(const std::string& model, const std::string& config, const std::string& framework)
140-
{
141-
std::string modelExt = model.substr(model.rfind('.'));
142-
if (framework == "caffe" || modelExt == ".caffemodel")
143-
return readNetFromCaffe(config, model);
144-
else if (framework == "tensorflow" || modelExt == ".pb")
145-
return readNetFromTensorflow(model, config);
146-
else if (framework == "torch" || modelExt == ".t7" || modelExt == ".net")
147-
return readNetFromTorch(model);
148-
else if (framework == "darknet" || modelExt == ".weights")
149-
return readNetFromDarknet(config, model);
150-
else
151-
CV_Error(Error::StsError, "Cannot determine an origin framework of model from file " + model);
152-
return Net();
153-
}

samples/dnn/classification.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,7 @@
4848
classes = f.read().rstrip('\n').split('\n')
4949

5050
# Load a network
51-
modelExt = args.model[args.model.rfind('.'):]
52-
if args.framework == 'caffe' or modelExt == '.caffemodel':
53-
net = cv.dnn.readNetFromCaffe(args.config, args.model)
54-
elif args.framework == 'tensorflow' or modelExt == '.pb':
55-
net = cv.dnn.readNetFromTensorflow(args.model, args.config)
56-
elif args.framework == 'torch' or modelExt in ['.t7', '.net']:
57-
net = cv.dnn.readNetFromTorch(args.model)
58-
elif args.framework == 'darknet' or modelExt == '.weights':
59-
net = cv.dnn.readNetFromDarknet(args.config, args.model)
60-
else:
61-
print('Cannot determine an origin framework of model from file %s' % args.model)
62-
sys.exit(0)
63-
51+
net = cv.dnn.readNet(args.model, args.config, args.framework)
6452
net.setPreferableBackend(args.backend)
6553
net.setPreferableTarget(args.target)
6654

0 commit comments

Comments
 (0)