2018年12月25日 星期二

用 TensorFlow + PoseNet 偵測骨骼位置


最近女兒參加了跳舞比賽,心想怎樣能用影像判斷出骨骼位置,從而收集動作數據呢?在網上找到以 TensorFlow + PoseNet 可以做到。以下是用 Javascript 編寫了一個簡單的偵測程序:
<html>
   <head>
      <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
      <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/posenet"></script>
   </head>

   <body>
      <canvas id="canvas"></canvas>
      <img id="photo" src="./sport_01.jpg" style="display:none;" />

      <script>
         var image = document.getElementById("photo");
         var imageScaleFactor = 0.2;
         var flipHorizontal = false;
         var outputStride = 16;

         //----------------------------------------------------------------------------------------
         function drawConnection(context, keypoints, partA, partB)  {
            var radius = 8;
            var partAPoint = null;
            var partBPoint = null;
            for (var i=0; i<keypoints.length; i++)  {

               var element = keypoints[i];
               var part = element.part;
               if (part != partA && part != partB)  {continue;}

               //  Either matches part A or part B
               if (part == partA)  {partAPoint = element.position;}
               if (part == partB)  {partBPoint = element.position;}

               //  Continue if not both position have been set
               if (partAPoint == null || partBPoint == null)  {continue;}

               //  Both parts are ready, connect them
               context.beginPath();
               context.arc(partAPoint.x, partAPoint.y, radius, 0, 2*Math.PI, false);
               context.fillStyle = 'green';
               context.fill();

               context.beginPath();
               context.moveTo(partAPoint.x, partAPoint.y);
               context.lineTo(partBPoint.x, partBPoint.y);
               context.strokeStyle = 'green';
               context.stroke();

               context.beginPath();
               context.arc(partBPoint.x, partBPoint.y, radius, 0, 2*Math.PI, false);
               context.fillStyle = 'green';
               context.fill();
            }
         }

         //----------------------------------------------------------------------------------------
         function drawConnection12(context, keypoints, partA, partB, partC)  {
            var radius = 8;
            var partAPoint = null;
            var partBPoint = null;
            var partCPoint = null;
            for (var i=0; i<keypoints.length; i++)  {

               var element = keypoints[i];
               var part = element.part;
               if (part != partA && part != partB && part != partC)  {continue;}

               //  Either matches part A or part B or part C
               if (part == partA)  {partAPoint = element.position;}
               if (part == partB)  {partBPoint = element.position;}
               if (part == partC)  {partCPoint = element.position;}

               //  Continue if not both position have been set
               if (partAPoint == null || partBPoint == null || partCPoint == null)  {continue;}

               var pointX = (partBPoint.x+partCPoint.x)/2;
               var pointY = (partBPoint.y+partCPoint.y)/2;

               //  Both parts are ready, connect them
               context.beginPath();
               context.arc(partAPoint.x, partAPoint.y, radius, 0, 2*Math.PI, false);
               context.fillStyle = 'green';
               context.fill();

               context.beginPath();
               context.moveTo(partAPoint.x, partAPoint.y);
               context.lineTo(pointX, pointY);
               context.strokeStyle = 'green';
               context.stroke();
            }
         }

         //----------------------------------------------------------------------------------------
         function drawConnection22(context, keypoints, partA, partB, partC, partD)  {
            var radius = 8;
            var partAPoint = null;
            var partBPoint = null;
            var partCPoint = null;
            var partDPoint = null;
            for (var i=0; i<keypoints.length; i++)  {

               var element = keypoints[i];
               var part = element.part;
               if (part != partA && part != partB && part != partC && part != partD)  {continue;}

               //  Either matches part A or part B or part C or part D
               if (part == partA)  {partAPoint = element.position;}
               if (part == partB)  {partBPoint = element.position;}
               if (part == partC)  {partCPoint = element.position;}
               if (part == partD)  {partDPoint = element.position;}

               //  Continue if not both position have been set
               if (partAPoint == null || partBPoint == null || partCPoint == null || partDPoint == null)  {continue;}

               var pointX1 = (partAPoint.x+partBPoint.x)/2;
               var pointY1 = (partAPoint.y+partBPoint.y)/2;
               var pointX2 = (partCPoint.x+partDPoint.x)/2;
               var pointY2 = (partCPoint.y+partDPoint.y)/2;

               //  Both parts are ready, connect them
               context.beginPath();
               context.arc(pointX1, pointY1, radius, 0, 2*Math.PI, false);
               context.fillStyle = 'green';
               context.fill();

               context.beginPath();
               context.moveTo(pointX1, pointY1);
               context.lineTo(pointX2, pointY2);
               context.strokeStyle = 'green';
               context.stroke();
            }
         }

         //----------------------------------------------------------------------------------------
         posenet.load().then(function(net)  {
            return net.estimateSinglePose(image, imageScaleFactor, flipHorizontal, outputStride);
         }).then(function(pose)  {

            var width = image.width;
            var height = image.height;

            var canvas = document.getElementById("canvas");
            canvas.width = width;
            canvas.height = height;

            var context = canvas.getContext("2d");
            context.drawImage(image, 0, 0);

            var keypoints = pose.keypoints;
            drawConnection(context, keypoints, "leftEye", "rightEye");
            drawConnection(context, keypoints, "leftEye", "nose");
            drawConnection(context, keypoints, "rightEye", "nose");

            drawConnection(context, keypoints, "leftEar", "leftEar");
            drawConnection(context, keypoints, "rightEar", "rightEar");

            drawConnection(context, keypoints, "leftShoulder", "rightShoulder");
            drawConnection(context, keypoints, "leftShoulder", "leftElbow");
            drawConnection(context, keypoints, "rightShoulder", "rightElbow");
            drawConnection(context, keypoints, "leftElbow", "leftWrist");
            drawConnection(context, keypoints, "rightElbow", "rightWrist");

            drawConnection(context, keypoints, "leftHip", "rightHip");
            drawConnection(context, keypoints, "leftHip", "leftKnee");
            drawConnection(context, keypoints, "rightHip", "rightKnee");
            drawConnection(context, keypoints, "leftKnee", "leftAnkle");
            drawConnection(context, keypoints, "rightKnee", "rightAnkle");

            drawConnection12(context, keypoints, "nose", "leftShoulder", "rightShoulder");
            drawConnection22(context, keypoints, "leftShoulder", "rightShoulder", "leftHip", "rightHip");
         });
      </script>
   </body>
</html>

沒有留言: