js_pose_estimation.html 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>Pose Estimation Example</title>
  6. <link href="js_example_style.css" rel="stylesheet" type="text/css" />
  7. </head>
  8. <body>
  9. <h2>Pose Estimation Example</h2>
  10. <p>
  11. This tutorial shows you how to write an pose estimation example with OpenCV.js.<br>
  12. To try the example you should click the <b>modelFile</b> button(and <b>configInput</b> button if needed) to upload inference model.
  13. You can find the model URLs and parameters in the <a href="#appendix">model info</a> section.
  14. Then You should change the parameters in the first code snippet according to the uploaded model.
  15. Finally click <b>Try it</b> button to see the result. You can choose any other images.<br>
  16. </p>
  17. <div class="control"><button id="tryIt" disabled>Try it</button></div>
  18. <div>
  19. <table cellpadding="0" cellspacing="0" width="0" border="0">
  20. <tr>
  21. <td>
  22. <canvas id="canvasInput" width="400" height="250"></canvas>
  23. </td>
  24. <td>
  25. <canvas id="canvasOutput" style="visibility: hidden;" width="400" height="250"></canvas>
  26. </td>
  27. </tr>
  28. <tr>
  29. <td>
  30. <div class="caption">
  31. canvasInput <input type="file" id="fileInput" name="file" accept="image/*">
  32. </div>
  33. </td>
  34. <td>
  35. <p id='status' align="left"></p>
  36. </td>
  37. </tr>
  38. <tr>
  39. <td>
  40. <div class="caption">
  41. modelFile <input type="file" id="modelFile" name="file">
  42. </div>
  43. </td>
  44. </tr>
  45. <tr>
  46. <td>
  47. <div class="caption">
  48. configFile <input type="file" id="configFile">
  49. </div>
  50. </td>
  51. </tr>
  52. </table>
  53. </div>
  54. <div>
  55. <p class="err" id="errorMessage"></p>
  56. </div>
  57. <div>
  58. <h3>Help function</h3>
  59. <p>1.The parameters for model inference which you can modify to investigate more models.</p>
  60. <textarea class="code" rows="9" cols="100" id="codeEditor" spellcheck="false"></textarea>
  61. <p>2.Main loop in which will read the image from canvas and do inference once.</p>
  62. <textarea class="code" rows="15" cols="100" id="codeEditor1" spellcheck="false"></textarea>
  63. <p>3.Get blob from image as input for net, and standardize it with <b>mean</b> and <b>std</b>.</p>
  64. <textarea class="code" rows="17" cols="100" id="codeEditor2" spellcheck="false"></textarea>
  65. <p>4.Fetch model file and save to emscripten file system once click the input button.</p>
  66. <textarea class="code" rows="17" cols="100" id="codeEditor3" spellcheck="false"></textarea>
  67. <p>5.The pairs of keypoints of different dataset.</p>
  68. <textarea class="code" rows="30" cols="100" id="codeEditor4" spellcheck="false"></textarea>
  69. <p>6.The post-processing, including get the predicted points and draw lines into the image.</p>
  70. <textarea class="code" rows="30" cols="100" id="codeEditor5" spellcheck="false"></textarea>
  71. </div>
  72. <div id="appendix">
  73. <h2>Model Info:</h2>
  74. </div>
  75. <script src="utils.js" type="text/javascript"></script>
  76. <script src="js_dnn_example_helper.js" type="text/javascript"></script>
  77. <script id="codeSnippet" type="text/code-snippet">
  78. inputSize = [368, 368];
  79. mean = [0, 0, 0];
  80. std = 0.00392;
  81. swapRB = false;
  82. threshold = 0.1;
  83. // the pairs of keypoint, can be "COCO", "MPI" and "BODY_25"
  84. dataset = "COCO";
  85. </script>
  86. <script id="codeSnippet1" type="text/code-snippet">
  87. main = async function() {
  88. const input = getBlobFromImage(inputSize, mean, std, swapRB, 'canvasInput');
  89. let net = cv.readNet(configPath, modelPath);
  90. net.setInput(input);
  91. const start = performance.now();
  92. const result = net.forward();
  93. const time = performance.now()-start;
  94. const output = postProcess(result);
  95. updateResult(output, time);
  96. input.delete();
  97. net.delete();
  98. result.delete();
  99. }
  100. </script>
  101. <script id="codeSnippet4" type="text/code-snippet">
  102. BODY_PARTS = {};
  103. POSE_PAIRS = [];
  104. if (dataset === 'COCO') {
  105. BODY_PARTS = { "Nose": 0, "Neck": 1, "RShoulder": 2, "RElbow": 3, "RWrist": 4,
  106. "LShoulder": 5, "LElbow": 6, "LWrist": 7, "RHip": 8, "RKnee": 9,
  107. "RAnkle": 10, "LHip": 11, "LKnee": 12, "LAnkle": 13, "REye": 14,
  108. "LEye": 15, "REar": 16, "LEar": 17, "Background": 18 };
  109. POSE_PAIRS = [ ["Neck", "RShoulder"], ["Neck", "LShoulder"], ["RShoulder", "RElbow"],
  110. ["RElbow", "RWrist"], ["LShoulder", "LElbow"], ["LElbow", "LWrist"],
  111. ["Neck", "RHip"], ["RHip", "RKnee"], ["RKnee", "RAnkle"], ["Neck", "LHip"],
  112. ["LHip", "LKnee"], ["LKnee", "LAnkle"], ["Neck", "Nose"], ["Nose", "REye"],
  113. ["REye", "REar"], ["Nose", "LEye"], ["LEye", "LEar"] ]
  114. } else if (dataset === 'MPI') {
  115. BODY_PARTS = { "Head": 0, "Neck": 1, "RShoulder": 2, "RElbow": 3, "RWrist": 4,
  116. "LShoulder": 5, "LElbow": 6, "LWrist": 7, "RHip": 8, "RKnee": 9,
  117. "RAnkle": 10, "LHip": 11, "LKnee": 12, "LAnkle": 13, "Chest": 14,
  118. "Background": 15 }
  119. POSE_PAIRS = [ ["Head", "Neck"], ["Neck", "RShoulder"], ["RShoulder", "RElbow"],
  120. ["RElbow", "RWrist"], ["Neck", "LShoulder"], ["LShoulder", "LElbow"],
  121. ["LElbow", "LWrist"], ["Neck", "Chest"], ["Chest", "RHip"], ["RHip", "RKnee"],
  122. ["RKnee", "RAnkle"], ["Chest", "LHip"], ["LHip", "LKnee"], ["LKnee", "LAnkle"] ]
  123. } else if (dataset === 'BODY_25') {
  124. BODY_PARTS = { "Nose": 0, "Neck": 1, "RShoulder": 2, "RElbow": 3, "RWrist": 4,
  125. "LShoulder": 5, "LElbow": 6, "LWrist": 7, "MidHip": 8, "RHip": 9,
  126. "RKnee": 10, "RAnkle": 11, "LHip": 12, "LKnee": 13, "LAnkle": 14,
  127. "REye": 15, "LEye": 16, "REar": 17, "LEar": 18, "LBigToe": 19,
  128. "LSmallToe": 20, "LHeel": 21, "RBigToe": 22, "RSmallToe": 23,
  129. "RHeel": 24, "Background": 25 }
  130. POSE_PAIRS = [ ["Neck", "Nose"], ["Neck", "RShoulder"],
  131. ["Neck", "LShoulder"], ["RShoulder", "RElbow"],
  132. ["RElbow", "RWrist"], ["LShoulder", "LElbow"],
  133. ["LElbow", "LWrist"], ["Nose", "REye"],
  134. ["REye", "REar"], ["Neck", "LEye"],
  135. ["LEye", "LEar"], ["Neck", "MidHip"],
  136. ["MidHip", "RHip"], ["RHip", "RKnee"],
  137. ["RKnee", "RAnkle"], ["RAnkle", "RBigToe"],
  138. ["RBigToe", "RSmallToe"], ["RAnkle", "RHeel"],
  139. ["MidHip", "LHip"], ["LHip", "LKnee"],
  140. ["LKnee", "LAnkle"], ["LAnkle", "LBigToe"],
  141. ["LBigToe", "LSmallToe"], ["LAnkle", "LHeel"] ]
  142. }
  143. </script>
  144. <script id="codeSnippet5" type="text/code-snippet">
  145. postProcess = function(result) {
  146. const resultData = result.data32F;
  147. const matSize = result.matSize;
  148. const size1 = matSize[1];
  149. const size2 = matSize[2];
  150. const size3 = matSize[3];
  151. const mapSize = size2 * size3;
  152. let canvasOutput = document.getElementById('canvasOutput');
  153. const outputWidth = canvasOutput.width;
  154. const outputHeight = canvasOutput.height;
  155. let image = cv.imread("canvasInput");
  156. let output = new cv.Mat(outputWidth, outputHeight, cv.CV_8UC3);
  157. cv.cvtColor(image, output, cv.COLOR_RGBA2RGB);
  158. // get position of keypoints from output
  159. let points = [];
  160. for (let i = 0; i < Object.keys(BODY_PARTS).length; ++i) {
  161. heatMap = resultData.slice(i*mapSize, (i+1)*mapSize);
  162. let maxIndex = 0;
  163. let maxConf = heatMap[0];
  164. for (index in heatMap) {
  165. if (heatMap[index] > heatMap[maxIndex]) {
  166. maxIndex = index;
  167. maxConf = heatMap[index];
  168. }
  169. }
  170. if (maxConf > threshold) {
  171. indexX = maxIndex % size3;
  172. indexY = maxIndex / size3;
  173. x = outputWidth * indexX / size3;
  174. y = outputHeight * indexY / size2;
  175. points[i] = [Math.round(x), Math.round(y)];
  176. }
  177. }
  178. // draw the points and lines into the image
  179. for (pair of POSE_PAIRS) {
  180. partFrom = pair[0];
  181. partTo = pair[1];
  182. idFrom = BODY_PARTS[partFrom];
  183. idTo = BODY_PARTS[partTo];
  184. pointFrom = points[idFrom];
  185. pointTo = points[idTo];
  186. if (points[idFrom] && points[idTo]) {
  187. cv.line(output, new cv.Point(pointFrom[0], pointFrom[1]),
  188. new cv.Point(pointTo[0], pointTo[1]), new cv.Scalar(0, 255, 0), 3);
  189. cv.ellipse(output, new cv.Point(pointFrom[0], pointFrom[1]), new cv.Size(3, 3), 0, 0, 360,
  190. new cv.Scalar(0, 0, 255), cv.FILLED);
  191. cv.ellipse(output, new cv.Point(pointTo[0], pointTo[1]), new cv.Size(3, 3), 0, 0, 360,
  192. new cv.Scalar(0, 0, 255), cv.FILLED);
  193. }
  194. }
  195. return output;
  196. }
  197. </script>
  198. <script type="text/javascript">
  199. let jsonUrl = "js_pose_estimation_model_info.json";
  200. drawInfoTable(jsonUrl, 'appendix');
  201. let utils = new Utils('errorMessage');
  202. utils.loadCode('codeSnippet', 'codeEditor');
  203. utils.loadCode('codeSnippet1', 'codeEditor1');
  204. let getBlobFromImageCode = 'getBlobFromImage = ' + getBlobFromImage.toString();
  205. document.getElementById('codeEditor2').value = getBlobFromImageCode;
  206. let loadModelCode = 'loadModel = ' + loadModel.toString();
  207. document.getElementById('codeEditor3').value = loadModelCode;
  208. utils.loadCode('codeSnippet4', 'codeEditor4');
  209. utils.loadCode('codeSnippet5', 'codeEditor5');
  210. let canvas = document.getElementById('canvasInput');
  211. let ctx = canvas.getContext('2d');
  212. let img = new Image();
  213. img.crossOrigin = 'anonymous';
  214. img.src = 'roi.jpg';
  215. img.onload = function() {
  216. ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
  217. };
  218. let tryIt = document.getElementById('tryIt');
  219. tryIt.addEventListener('click', () => {
  220. initStatus();
  221. document.getElementById('status').innerHTML = 'Running function main()...';
  222. utils.executeCode('codeEditor');
  223. utils.executeCode('codeEditor1');
  224. if (modelPath === "") {
  225. document.getElementById('status').innerHTML = 'Runing failed.';
  226. utils.printError('Please upload model file by clicking the button first.');
  227. } else {
  228. setTimeout(main, 1);
  229. }
  230. });
  231. let fileInput = document.getElementById('fileInput');
  232. fileInput.addEventListener('change', (e) => {
  233. initStatus();
  234. loadImageToCanvas(e, 'canvasInput');
  235. });
  236. let configPath = "";
  237. let configFile = document.getElementById('configFile');
  238. configFile.addEventListener('change', async (e) => {
  239. initStatus();
  240. configPath = await loadModel(e);
  241. document.getElementById('status').innerHTML = `The config file '${configPath}' is created successfully.`;
  242. });
  243. let modelPath = "";
  244. let modelFile = document.getElementById('modelFile');
  245. modelFile.addEventListener('change', async (e) => {
  246. initStatus();
  247. modelPath = await loadModel(e);
  248. document.getElementById('status').innerHTML = `The model file '${modelPath}' is created successfully.`;
  249. configPath = "";
  250. configFile.value = "";
  251. });
  252. utils.loadOpenCv(() => {
  253. tryIt.removeAttribute('disabled');
  254. });
  255. var main = async function() {};
  256. var postProcess = function(result) {};
  257. utils.executeCode('codeEditor');
  258. utils.executeCode('codeEditor1');
  259. utils.executeCode('codeEditor2');
  260. utils.executeCode('codeEditor3');
  261. utils.executeCode('codeEditor4');
  262. utils.executeCode('codeEditor5');
  263. function updateResult(output, time) {
  264. try{
  265. let canvasOutput = document.getElementById('canvasOutput');
  266. canvasOutput.style.visibility = "visible";
  267. let resized = new cv.Mat(canvasOutput.width, canvasOutput.height, cv.CV_8UC4);
  268. cv.resize(output, resized, new cv.Size(canvasOutput.width, canvasOutput.height));
  269. cv.imshow('canvasOutput', resized);
  270. document.getElementById('status').innerHTML = `<b>Model:</b> ${modelPath}<br>
  271. <b>Inference time:</b> ${time.toFixed(2)} ms`;
  272. } catch(e) {
  273. console.log(e);
  274. }
  275. }
  276. function initStatus() {
  277. document.getElementById('status').innerHTML = '';
  278. document.getElementById('canvasOutput').style.visibility = "hidden";
  279. utils.clearError();
  280. }
  281. </script>
  282. </body>
  283. </html>