multi-person-openpose.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. #include<opencv2/dnn.hpp>
  2. #include<opencv2/imgproc.hpp>
  3. #include<opencv2/highgui.hpp>
  4. #include<iostream>
  5. #include<chrono>
  6. #include<random>
  7. #include<set>
  8. #include<cmath>
  9. ////////////////////////////////
  10. struct KeyPoint{
  11. KeyPoint(cv::Point point,float probability){
  12. this->id = -1;
  13. this->point = point;
  14. this->probability = probability;
  15. }
  16. int id;
  17. cv::Point point;
  18. float probability;
  19. };
  20. std::ostream& operator << (std::ostream& os, const KeyPoint& kp)
  21. {
  22. os << "Id:" << kp.id << ", Point:" << kp.point << ", Prob:" << kp.probability << std::endl;
  23. return os;
  24. }
  25. ////////////////////////////////
  26. struct ValidPair{
  27. ValidPair(int aId,int bId,float score){
  28. this->aId = aId;
  29. this->bId = bId;
  30. this->score = score;
  31. }
  32. int aId;
  33. int bId;
  34. float score;
  35. };
  36. std::ostream& operator << (std::ostream& os, const ValidPair& vp)
  37. {
  38. os << "A:" << vp.aId << ", B:" << vp.bId << ", score:" << vp.score << std::endl;
  39. return os;
  40. }
  41. ////////////////////////////////
  42. template < class T > std::ostream& operator << (std::ostream& os, const std::vector<T>& v)
  43. {
  44. os << "[";
  45. bool first = true;
  46. for (typename std::vector<T>::const_iterator ii = v.begin(); ii != v.end(); ++ii, first = false)
  47. {
  48. if(!first) os << ",";
  49. os << " " << *ii;
  50. }
  51. os << "]";
  52. return os;
  53. }
  54. template < class T > std::ostream& operator << (std::ostream& os, const std::set<T>& v)
  55. {
  56. os << "[";
  57. bool first = true;
  58. for (typename std::set<T>::const_iterator ii = v.begin(); ii != v.end(); ++ii, first = false)
  59. {
  60. if(!first) os << ",";
  61. os << " " << *ii;
  62. }
  63. os << "]";
  64. return os;
  65. }
  66. ////////////////////////////////
  67. const int nPoints = 18;
  68. const std::string keypointsMapping[] = {
  69. "Nose", "Neck",
  70. "R-Sho", "R-Elb", "R-Wr",
  71. "L-Sho", "L-Elb", "L-Wr",
  72. "R-Hip", "R-Knee", "R-Ank",
  73. "L-Hip", "L-Knee", "L-Ank",
  74. "R-Eye", "L-Eye", "R-Ear", "L-Ear"
  75. };
  76. const std::vector<std::pair<int,int>> mapIdx = {
  77. {31,32}, {39,40}, {33,34}, {35,36}, {41,42}, {43,44},
  78. {19,20}, {21,22}, {23,24}, {25,26}, {27,28}, {29,30},
  79. {47,48}, {49,50}, {53,54}, {51,52}, {55,56}, {37,38},
  80. {45,46}
  81. };
  82. const std::vector<std::pair<int,int>> posePairs = {
  83. {1,2}, {1,5}, {2,3}, {3,4}, {5,6}, {6,7},
  84. {1,8}, {8,9}, {9,10}, {1,11}, {11,12}, {12,13},
  85. {1,0}, {0,14}, {14,16}, {0,15}, {15,17}, {2,17},
  86. {5,16}
  87. };
  88. void getKeyPoints(cv::Mat& probMap,double threshold,std::vector<KeyPoint>& keyPoints){
  89. cv::Mat smoothProbMap;
  90. cv::GaussianBlur( probMap, smoothProbMap, cv::Size( 3, 3 ), 0, 0 );
  91. cv::Mat maskedProbMap;
  92. cv::threshold(smoothProbMap,maskedProbMap,threshold,255,cv::THRESH_BINARY);
  93. maskedProbMap.convertTo(maskedProbMap,CV_8U,1);
  94. std::vector<std::vector<cv::Point> > contours;
  95. cv::findContours(maskedProbMap,contours,cv::RETR_TREE,cv::CHAIN_APPROX_SIMPLE);
  96. for(int i = 0; i < contours.size();++i){
  97. cv::Mat blobMask = cv::Mat::zeros(smoothProbMap.rows,smoothProbMap.cols,smoothProbMap.type());
  98. cv::fillConvexPoly(blobMask,contours[i],cv::Scalar(1));
  99. double maxVal;
  100. cv::Point maxLoc;
  101. cv::minMaxLoc(smoothProbMap.mul(blobMask),0,&maxVal,0,&maxLoc);
  102. keyPoints.push_back(KeyPoint(maxLoc, probMap.at<float>(maxLoc.y,maxLoc.x)));
  103. }
  104. }
  105. void populateColorPalette(std::vector<cv::Scalar>& colors,int nColors){
  106. std::random_device rd;
  107. std::mt19937 gen(rd());
  108. std::uniform_int_distribution<> dis1(64, 200);
  109. std::uniform_int_distribution<> dis2(100, 255);
  110. std::uniform_int_distribution<> dis3(100, 255);
  111. for(int i = 0; i < nColors;++i){
  112. colors.push_back(cv::Scalar(dis1(gen),dis2(gen),dis3(gen)));
  113. }
  114. }
  115. void splitNetOutputBlobToParts(cv::Mat& netOutputBlob,const cv::Size& targetSize,std::vector<cv::Mat>& netOutputParts){
  116. int nParts = netOutputBlob.size[1];
  117. int h = netOutputBlob.size[2];
  118. int w = netOutputBlob.size[3];
  119. for(int i = 0; i< nParts;++i){
  120. cv::Mat part(h, w, CV_32F, netOutputBlob.ptr(0,i));
  121. cv::Mat resizedPart;
  122. cv::resize(part,resizedPart,targetSize);
  123. netOutputParts.push_back(resizedPart);
  124. }
  125. }
  126. void populateInterpPoints(const cv::Point& a,const cv::Point& b,int numPoints,std::vector<cv::Point>& interpCoords){
  127. float xStep = ((float)(b.x - a.x))/(float)(numPoints-1);
  128. float yStep = ((float)(b.y - a.y))/(float)(numPoints-1);
  129. interpCoords.push_back(a);
  130. for(int i = 1; i< numPoints-1;++i){
  131. interpCoords.push_back(cv::Point(a.x + xStep*i,a.y + yStep*i));
  132. }
  133. interpCoords.push_back(b);
  134. }
  135. void getValidPairs(const std::vector<cv::Mat>& netOutputParts,
  136. const std::vector<std::vector<KeyPoint>>& detectedKeypoints,
  137. std::vector<std::vector<ValidPair>>& validPairs,
  138. std::set<int>& invalidPairs) {
  139. int nInterpSamples = 10;
  140. float pafScoreTh = 0.1;
  141. float confTh = 0.7;
  142. for(int k = 0; k < mapIdx.size();++k ){
  143. //A->B constitute a limb
  144. cv::Mat pafA = netOutputParts[mapIdx[k].first];
  145. cv::Mat pafB = netOutputParts[mapIdx[k].second];
  146. //Find the keypoints for the first and second limb
  147. const std::vector<KeyPoint>& candA = detectedKeypoints[posePairs[k].first];
  148. const std::vector<KeyPoint>& candB = detectedKeypoints[posePairs[k].second];
  149. int nA = candA.size();
  150. int nB = candB.size();
  151. /*
  152. # If keypoints for the joint-pair is detected
  153. # check every joint in candA with every joint in candB
  154. # Calculate the distance vector between the two joints
  155. # Find the PAF values at a set of interpolated points between the joints
  156. # Use the above formula to compute a score to mark the connection valid
  157. */
  158. if(nA != 0 && nB != 0){
  159. std::vector<ValidPair> localValidPairs;
  160. for(int i = 0; i< nA;++i){
  161. int maxJ = -1;
  162. float maxScore = -1;
  163. bool found = false;
  164. for(int j = 0; j < nB;++j){
  165. std::pair<float,float> distance(candB[j].point.x - candA[i].point.x,candB[j].point.y - candA[i].point.y);
  166. float norm = std::sqrt(distance.first*distance.first + distance.second*distance.second);
  167. if(!norm){
  168. continue;
  169. }
  170. distance.first /= norm;
  171. distance.second /= norm;
  172. //Find p(u)
  173. std::vector<cv::Point> interpCoords;
  174. populateInterpPoints(candA[i].point,candB[j].point,nInterpSamples,interpCoords);
  175. //Find L(p(u))
  176. std::vector<std::pair<float,float>> pafInterp;
  177. for(int l = 0; l < interpCoords.size();++l){
  178. pafInterp.push_back(
  179. std::pair<float,float>(
  180. pafA.at<float>(interpCoords[l].y,interpCoords[l].x),
  181. pafB.at<float>(interpCoords[l].y,interpCoords[l].x)
  182. ));
  183. }
  184. std::vector<float> pafScores;
  185. float sumOfPafScores = 0;
  186. int numOverTh = 0;
  187. for(int l = 0; l< pafInterp.size();++l){
  188. float score = pafInterp[l].first*distance.first + pafInterp[l].second*distance.second;
  189. sumOfPafScores += score;
  190. if(score > pafScoreTh){
  191. ++numOverTh;
  192. }
  193. pafScores.push_back(score);
  194. }
  195. float avgPafScore = sumOfPafScores/((float)pafInterp.size());
  196. if(((float)numOverTh)/((float)nInterpSamples) > confTh){
  197. if(avgPafScore > maxScore){
  198. maxJ = j;
  199. maxScore = avgPafScore;
  200. found = true;
  201. }
  202. }
  203. }/* j */
  204. if(found){
  205. localValidPairs.push_back(ValidPair(candA[i].id,candB[maxJ].id,maxScore));
  206. }
  207. }/* i */
  208. validPairs.push_back(localValidPairs);
  209. } else {
  210. invalidPairs.insert(k);
  211. validPairs.push_back(std::vector<ValidPair>());
  212. }
  213. }/* k */
  214. }
  215. void getPersonwiseKeypoints(const std::vector<std::vector<ValidPair>>& validPairs,
  216. const std::set<int>& invalidPairs,
  217. std::vector<std::vector<int>>& personwiseKeypoints) {
  218. for(int k = 0; k < mapIdx.size();++k){
  219. if(invalidPairs.find(k) != invalidPairs.end()){
  220. continue;
  221. }
  222. const std::vector<ValidPair>& localValidPairs(validPairs[k]);
  223. int indexA(posePairs[k].first);
  224. int indexB(posePairs[k].second);
  225. for(int i = 0; i< localValidPairs.size();++i){
  226. bool found = false;
  227. int personIdx = -1;
  228. for(int j = 0; !found && j < personwiseKeypoints.size();++j){
  229. if(indexA < personwiseKeypoints[j].size() &&
  230. personwiseKeypoints[j][indexA] == localValidPairs[i].aId){
  231. personIdx = j;
  232. found = true;
  233. }
  234. }/* j */
  235. if(found){
  236. personwiseKeypoints[personIdx].at(indexB) = localValidPairs[i].bId;
  237. } else if(k < 17){
  238. std::vector<int> lpkp(std::vector<int>(18,-1));
  239. lpkp.at(indexA) = localValidPairs[i].aId;
  240. lpkp.at(indexB) = localValidPairs[i].bId;
  241. personwiseKeypoints.push_back(lpkp);
  242. }
  243. }/* i */
  244. }/* k */
  245. }
  246. int main(int argc,char** argv) {
  247. std::string inputFile = "./group.jpg";
  248. std::string device = "cpu";
  249. std::cout << "USAGE : ./multi-person-openpose <inputFile> <device>" << std::endl;
  250. if (argc == 2){
  251. if((std::string)argv[1] == "gpu")
  252. device = "gpu";
  253. else
  254. inputFile = argv[1];
  255. }
  256. else if (argc == 3){
  257. inputFile = argv[1];
  258. if((std::string)argv[2] == "gpu")
  259. device = "gpu";
  260. }
  261. cv::Mat input = cv::imread(inputFile, cv::IMREAD_COLOR);
  262. std::chrono::time_point<std::chrono::system_clock> startTP = std::chrono::system_clock::now();
  263. cv::dnn::Net inputNet = cv::dnn::readNetFromCaffe("./pose/coco/pose_deploy_linevec.prototxt","./pose/coco/pose_iter_440000.caffemodel");
  264. if (device == "cpu"){
  265. std::cout << "Using CPU device" << std::endl;
  266. inputNet.setPreferableBackend(cv::dnn::DNN_TARGET_CPU);
  267. }
  268. else if (device == "gpu"){
  269. std::cout << "Using GPU device" << std::endl;
  270. inputNet.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
  271. inputNet.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);
  272. }
  273. cv::Mat inputBlob = cv::dnn::blobFromImage(input,1.0/255.0,cv::Size((int)((368*input.cols)/input.rows),368),cv::Scalar(0,0,0),false,false);
  274. inputNet.setInput(inputBlob);
  275. cv::Mat netOutputBlob = inputNet.forward();
  276. std::vector<cv::Mat> netOutputParts;
  277. splitNetOutputBlobToParts(netOutputBlob,cv::Size(input.cols,input.rows),netOutputParts);
  278. std::chrono::time_point<std::chrono::system_clock> finishTP = std::chrono::system_clock::now();
  279. std::cout << "Time Taken in forward pass = " << std::chrono::duration_cast<std::chrono::milliseconds>(finishTP - startTP).count() << " ms" << std::endl;
  280. int keyPointId = 0;
  281. std::vector<std::vector<KeyPoint>> detectedKeypoints;
  282. std::vector<KeyPoint> keyPointsList;
  283. for(int i = 0; i < nPoints;++i){
  284. std::vector<KeyPoint> keyPoints;
  285. getKeyPoints(netOutputParts[i],0.1,keyPoints);
  286. std::cout << "Keypoints - " << keypointsMapping[i] << " : " << keyPoints << std::endl;
  287. for(int i = 0; i< keyPoints.size();++i,++keyPointId){
  288. keyPoints[i].id = keyPointId;
  289. }
  290. detectedKeypoints.push_back(keyPoints);
  291. keyPointsList.insert(keyPointsList.end(),keyPoints.begin(),keyPoints.end());
  292. }
  293. std::vector<cv::Scalar> colors;
  294. populateColorPalette(colors,nPoints);
  295. cv::Mat outputFrame = input.clone();
  296. for(int i = 0; i < nPoints;++i){
  297. for(int j = 0; j < detectedKeypoints[i].size();++j){
  298. cv::circle(outputFrame,detectedKeypoints[i][j].point,5,colors[i],-1,cv::LINE_AA);
  299. }
  300. }
  301. std::vector<std::vector<ValidPair>> validPairs;
  302. std::set<int> invalidPairs;
  303. getValidPairs(netOutputParts,detectedKeypoints,validPairs,invalidPairs);
  304. std::vector<std::vector<int>> personwiseKeypoints;
  305. getPersonwiseKeypoints(validPairs,invalidPairs,personwiseKeypoints);
  306. for(int i = 0; i< nPoints-1;++i){
  307. for(int n = 0; n < personwiseKeypoints.size();++n){
  308. const std::pair<int,int>& posePair = posePairs[i];
  309. int indexA = personwiseKeypoints[n][posePair.first];
  310. int indexB = personwiseKeypoints[n][posePair.second];
  311. if(indexA == -1 || indexB == -1){
  312. continue;
  313. }
  314. const KeyPoint& kpA = keyPointsList[indexA];
  315. const KeyPoint& kpB = keyPointsList[indexB];
  316. cv::line(outputFrame,kpA.point,kpB.point,colors[i],3,cv::LINE_AA);
  317. }
  318. }
  319. cv::imshow("Detected Pose",outputFrame);
  320. cv::waitKey(0);
  321. return 0;
  322. }