1//
2// this sample demonstrates the use of pretrained openpose networks with opencv's dnn module.
3//
4// it can be used for body pose detection, using either the COCO model(18 parts):
5// http://posefs1.perception.cs.cmu.edu/OpenPose/models/pose/coco/pose_iter_440000.caffemodel
6// https://raw.githubusercontent.com/opencv/opencv_extra/5.x/testdata/dnn/openpose_pose_coco.prototxt
7//
8// or the MPI model(16 parts):
9// http://posefs1.perception.cs.cmu.edu/OpenPose/models/pose/mpi/pose_iter_160000.caffemodel
10// https://raw.githubusercontent.com/opencv/opencv_extra/5.x/testdata/dnn/openpose_pose_mpi_faster_4_stages.prototxt
11//
12// (to simplify this sample, the body models are restricted to a single person.)
13//
14//
15// you can also try the hand pose model:
16// http://posefs1.perception.cs.cmu.edu/OpenPose/models/hand/pose_iter_102000.caffemodel
17// https://raw.githubusercontent.com/CMU-Perceptual-Computing-Lab/openpose/master/models/hand/pose_deploy.prototxt
18//
19
20#include <opencv2/dnn.hpp>
21#include <opencv2/imgproc.hpp>
22#include <opencv2/highgui.hpp>
23using namespace cv;
24using namespace cv::dnn;
25
26#include <iostream>
27using namespace std;
28
29
30// connection table, in the format [model_id][pair_id][from/to]
31// please look at the nice explanation at the bottom of:
32// https://github.com/CMU-Perceptual-Computing-Lab/openpose/blob/master/doc/output.md
33//
34const int POSE_PAIRS[3][20][2] = {
35{ // COCO body
36 {1,2}, {1,5}, {2,3},
37 {3,4}, {5,6}, {6,7},
38 {1,8}, {8,9}, {9,10},
39 {1,11}, {11,12}, {12,13},
40 {1,0}, {0,14},
41 {14,16}, {0,15}, {15,17}
42},
43{ // MPI body
44 {0,1}, {1,2}, {2,3},
45 {3,4}, {1,5}, {5,6},
46 {6,7}, {1,14}, {14,8}, {8,9},
47 {9,10}, {14,11}, {11,12}, {12,13}
48},
49{ // hand
50 {0,1}, {1,2}, {2,3}, {3,4}, // thumb
51 {0,5}, {5,6}, {6,7}, {7,8}, // pinkie
52 {0,9}, {9,10}, {10,11}, {11,12}, // middle
53 {0,13}, {13,14}, {14,15}, {15,16}, // ring
54 {0,17}, {17,18}, {18,19}, {19,20} // small
55}};
56
57int main(int argc, char **argv)
58{
59 CommandLineParser parser(argc, argv,
60 "{ h help | false | print this help message }"
61 "{ p proto | | (required) model configuration, e.g. hand/pose.prototxt }"
62 "{ m model | | (required) model weights, e.g. hand/pose_iter_102000.caffemodel }"
63 "{ i image | | (required) path to image file (containing a single person, or hand) }"
64 "{ d dataset | | specify what kind of model was trained. It could be (COCO, MPI, HAND) depends on dataset. }"
65 "{ width | 368 | Preprocess input image by resizing to a specific width. }"
66 "{ height | 368 | Preprocess input image by resizing to a specific height. }"
67 "{ t threshold | 0.1 | threshold or confidence value for the heatmap }"
68 "{ s scale | 0.003922 | scale for blob }"
69 );
70
71 String modelTxt = samples::findFile(parser.get<string>("proto"));
72 String modelBin = samples::findFile(parser.get<string>("model"));
73 String imageFile = samples::findFile(parser.get<String>("image"));
74 String dataset = parser.get<String>("dataset");
75 int W_in = parser.get<int>("width");
76 int H_in = parser.get<int>("height");
77 float thresh = parser.get<float>("threshold");
78 float scale = parser.get<float>("scale");
79
80 if (parser.get<bool>("help") || modelTxt.empty() || modelBin.empty() || imageFile.empty())
81 {
82 cout << "A sample app to demonstrate human or hand pose detection with a pretrained OpenPose dnn." << endl;
83 parser.printMessage();
84 return 0;
85 }
86
87 int midx, npairs, nparts;
88 if (!dataset.compare("COCO")) { midx = 0; npairs = 17; nparts = 18; }
89 else if (!dataset.compare("MPI")) { midx = 1; npairs = 14; nparts = 16; }
90 else if (!dataset.compare("HAND")) { midx = 2; npairs = 20; nparts = 22; }
91 else
92 {
93 std::cerr << "Can't interpret dataset parameter: " << dataset << std::endl;
94 exit(-1);
95 }
96
97 // read the network model
98 Net net = readNet(modelBin, modelTxt);
99 // and the image
100 Mat img = imread(imageFile);
101 if (img.empty())
102 {
103 std::cerr << "Can't read image from the file: " << imageFile << std::endl;
104 exit(-1);
105 }
106
107 // send it through the network
108 Mat inputBlob = blobFromImage(img, scale, Size(W_in, H_in), Scalar(0, 0, 0), false, false);
109 net.setInput(inputBlob);
110 Mat result = net.forward();
111 // the result is an array of "heatmaps", the probability of a body part being in location x,y
112
113 int H = result.size[2];
114 int W = result.size[3];
115
116 // find the position of the body parts
117 vector<Point> points(22);
118 for (int n=0; n<nparts; n++)
119 {
120 // Slice heatmap of corresponding body's part.
121 Mat heatMap(H, W, CV_32F, result.ptr(0,n));
122 // 1 maximum per heatmap
123 Point p(-1,-1),pm;
124 double conf;
125 minMaxLoc(heatMap, 0, &conf, 0, &pm);
126 if (conf > thresh)
127 p = pm;
128 points[n] = p;
129 }
130
131 // connect body parts and draw it !
132 float SX = float(img.cols) / W;
133 float SY = float(img.rows) / H;
134 for (int n=0; n<npairs; n++)
135 {
136 // lookup 2 connected body/hand parts
137 Point2f a = points[POSE_PAIRS[midx][n][0]];
138 Point2f b = points[POSE_PAIRS[midx][n][1]];
139
140 // we did not find enough confidence before
141 if (a.x<=0 || a.y<=0 || b.x<=0 || b.y<=0)
142 continue;
143
144 // scale to image size
145 a.x*=SX; a.y*=SY;
146 b.x*=SX; b.y*=SY;
147
148 line(img, a, b, Scalar(0,200,0), 2);
149 circle(img, a, 3, Scalar(0,0,200), -1);
150 circle(img, b, 3, Scalar(0,0,200), -1);
151 }
152
153 imshow("OpenPose", img);
154 waitKey();
155
156 return 0;
157}