block by dribnet 07386225482a395e036ab60f9ca074a6

MDDN342 Assignment 3: Face Mappings

Full Screen

Assignment 3 MDDN 342 2019

Example of how to adapt assignment 2 code to assignment 3, this time using Lu Xaio’s solution. This completes part 3 by adding training data into training_values.json.

index.html

<head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/addons/p5.dom.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/2.4.3/seedrandom.min.js"></script>
    <script src="https://d3js.org/d3-random.v1.min.js"></script>
    <script src="z_clmtrackr.js"></script>
    <script src="z_model_pca_20_svm.js"></script>
    <script language="javascript" type="text/javascript" src="z_purview_helper.js"></script>
    <script language="javascript" type="text/javascript" src="z_focused_random.js"></script>
    <script language="javascript" type="text/javascript" src="kdTree.js"></script>
    <script language="javascript" type="text/javascript" src="face.js"></script>
    <script language="javascript" type="text/javascript" src="system_runner.js"></script>

    <style>
        body   { padding: 0; margin: 0; }
        .inner { position: absolute; }
        #controls {
            font: 300 12px "Helvetica Neue";
            padding: 5;
            margin: 5;
            background: #f0f0f0;
            opacity: 0.0;
            -webkit-transition: opacity 0.2s ease;
            -moz-transition: opacity 0.2s ease;
            -o-transition: opacity 0.2s ease;
            -ms-transition: opacity 0.2s ease;
        }
        #controls:hover { opacity: 0.9; }
    </style>

</head>
<body style="background-color:white">
    <div class="outer">
        <div class="inner" id="controls" height="500">
            <table>
                <tr>
                    <td>setting 1</td>
                    <td id="slider1Container"></td>
                </tr>
                <tr>
                    <td>setting 2</td>
                    <td id="slider2Container"></td>
                </tr>
                <tr>
                    <td>setting 3</td>
                    <td id="slider3Container"></td>
                </tr>
                <tr>
                    <td>setting 4</td>
                    <td id="slider4Container"></td>
                </tr>
                <tr>
                    <td>setting 5</td>
                    <td id="slider5Container"></td>
                </tr>
                <tr>
                    <td>setting 6</td>
                    <td id="slider6Container"></td>
                </tr>
                <tr>
                    <td>setting 7</td>
                    <td id="slider7Container"></td>
                </tr>
                <tr>
                    <td>setting 8</td>
                    <td id="slider8Container"></td>
                </tr>
                <tr>
                    <td>setting 9</td>
                    <td id="slider9Container"></td>
                </tr>
                <tr>
                    <td>setting 10</td>
                    <td id="slider10Container"></td>
                </tr>
                <tr>
                    <td>setting 11</td>
                    <td id="slider11Container"></td>
                </tr>
                <tr>
                    <td>setting 12</td>
                    <td id="slider12Container"></td>
                </tr>
                <tr>
                </tr>
                <tr>
                    <td>show target</td>
                    <td id="sliderTintContainer"></td>
                </tr>
                <tr>
                    <td>Draw function</td>
                    <td id="selector1Container"></td>
                </tr>
                <tr>
                    <td>Face Draw</td>
                    <td id="checkbox1Container"></td>
                </tr>
                <tr>
                    <td>Face Targets</td>
                    <td id="checkbox2Container"></td>
                </tr>
                <tr>
                    <td>Face Points</td>
                    <td id="checkbox3Container"></td>
                </tr>
                <tr>
                    <td></td>
                    <td id="button1Container"></td>
                </tr>
                <tr>
                    <td></td>
                    <td id="button2Container"></td>
                </tr>
                <tr>
                    <td></td>
                    <td id="button3Container"></td>
                </tr>
                <tr>
                    <td></td>
                    <td id="button4Container"></td>
                </tr>
            </table>
        </div>
        <div>
            <div id="canvasContainer"></div>
<a href="face.js">face code</a><br>
<a href="sketch.html">sketches</a>
        </div>
    </div>
    <pre>
        <p id="output">
        </p>
    </pre>
</body>

kdTree.js

/**
 * k-d Tree JavaScript - V 1.01
 *
 * https://github.com/ubilabs/kd-tree-javascript
 *
 * @author Mircea Pricop <pricop@ubilabs.net>, 2012
 * @author Martin Kleppe <kleppe@ubilabs.net>, 2012
 * @author Ubilabs http://ubilabs.net, 2012
 * @license MIT License <http://www.opensource.org/licenses/mit-license.php>
 */

 (function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['exports'], factory);
    } else if (typeof exports === 'object') {
        factory(exports);
    } else {
        factory((root.commonJsStrict = {}));
    }
}(this, function (exports) {
  function Node(obj, dimension, parent) {
    this.obj = obj;
    this.left = null;
    this.right = null;
    this.parent = parent;
    this.dimension = dimension;
  }

  function kdTree(points, metric, dimensions) {

    var self = this;
    
    function buildTree(points, depth, parent) {
      var dim = depth % dimensions.length,
        median,
        node;

      if (points.length === 0) {
        return null;
      }
      if (points.length === 1) {
        return new Node(points[0], dim, parent);
      }

      points.sort(function (a, b) {
        return a[dimensions[dim]] - b[dimensions[dim]];
      });

      median = Math.floor(points.length / 2);
      node = new Node(points[median], dim, parent);
      node.left = buildTree(points.slice(0, median), depth + 1, node);
      node.right = buildTree(points.slice(median + 1), depth + 1, node);

      return node;
    }

    // Reloads a serialied tree
    function loadTree (data) {
      // Just need to restore the `parent` parameter
      self.root = data;

      function restoreParent (root) {
        if (root.left) {
          root.left.parent = root;
          restoreParent(root.left);
        }

        if (root.right) {
          root.right.parent = root;
          restoreParent(root.right);
        }
      }

      restoreParent(self.root);
    }
    
    // If points is not an array, assume we're loading a pre-built tree
    if (!Array.isArray(points)) loadTree(points, metric, dimensions);
    else this.root = buildTree(points, 0, null);

    // Convert to a JSON serializable structure; this just requires removing 
    // the `parent` property
    this.toJSON = function (src) {
      if (!src) src = this.root;
      var dest = new Node(src.obj, src.dimension, null);
      if (src.left) dest.left = self.toJSON(src.left);
      if (src.right) dest.right = self.toJSON(src.right);
      return dest;
    };

    this.insert = function (point) {
      function innerSearch(node, parent) {

        if (node === null) {
          return parent;
        }

        var dimension = dimensions[node.dimension];
        if (point[dimension] < node.obj[dimension]) {
          return innerSearch(node.left, node);
        } else {
          return innerSearch(node.right, node);
        }
      }

      var insertPosition = innerSearch(this.root, null),
        newNode,
        dimension;

      if (insertPosition === null) {
        this.root = new Node(point, 0, null);
        return;
      }

      newNode = new Node(point, (insertPosition.dimension + 1) % dimensions.length, insertPosition);
      dimension = dimensions[insertPosition.dimension];

      if (point[dimension] < insertPosition.obj[dimension]) {
        insertPosition.left = newNode;
      } else {
        insertPosition.right = newNode;
      }
    };

    this.remove = function (point) {
      var node;

      function nodeSearch(node) {
        if (node === null) {
          return null;
        }

        if (node.obj === point) {
          return node;
        }

        var dimension = dimensions[node.dimension];

        if (point[dimension] < node.obj[dimension]) {
          return nodeSearch(node.left, node);
        } else {
          return nodeSearch(node.right, node);
        }
      }

      function removeNode(node) {
        var nextNode,
          nextObj,
          pDimension;

        function findMin(node, dim) {
          var dimension,
            own,
            left,
            right,
            min;

          if (node === null) {
            return null;
          }

          dimension = dimensions[dim];

          if (node.dimension === dim) {
            if (node.left !== null) {
              return findMin(node.left, dim);
            }
            return node;
          }

          own = node.obj[dimension];
          left = findMin(node.left, dim);
          right = findMin(node.right, dim);
          min = node;

          if (left !== null && left.obj[dimension] < own) {
            min = left;
          }
          if (right !== null && right.obj[dimension] < min.obj[dimension]) {
            min = right;
          }
          return min;
        }

        if (node.left === null && node.right === null) {
          if (node.parent === null) {
            self.root = null;
            return;
          }

          pDimension = dimensions[node.parent.dimension];

          if (node.obj[pDimension] < node.parent.obj[pDimension]) {
            node.parent.left = null;
          } else {
            node.parent.right = null;
          }
          return;
        }

        // If the right subtree is not empty, swap with the minimum element on the
        // node's dimension. If it is empty, we swap the left and right subtrees and
        // do the same.
        if (node.right !== null) {
          nextNode = findMin(node.right, node.dimension);
          nextObj = nextNode.obj;
          removeNode(nextNode);          
          node.obj = nextObj;
        } else {
          nextNode = findMin(node.left, node.dimension);
          nextObj = nextNode.obj;
          removeNode(nextNode);
          node.right = node.left;
          node.left = null;
          node.obj = nextObj;
        }

      }

      node = nodeSearch(self.root);

      if (node === null) { return; }

      removeNode(node);
    };

    this.nearest = function (point, maxNodes, maxDistance) {
      var i,
        result,
        bestNodes;

      bestNodes = new BinaryHeap(
        function (e) { return -e[1]; }
      );

      function nearestSearch(node) {
        var bestChild,
          dimension = dimensions[node.dimension],
          ownDistance = metric(point, node.obj),
          linearPoint = {},
          linearDistance,
          otherChild,
          i;

        function saveNode(node, distance) {
          bestNodes.push([node, distance]);
          if (bestNodes.size() > maxNodes) {
            bestNodes.pop();
          }
        }

        for (i = 0; i < dimensions.length; i += 1) {
          if (i === node.dimension) {
            linearPoint[dimensions[i]] = point[dimensions[i]];
          } else {
            linearPoint[dimensions[i]] = node.obj[dimensions[i]];
          }
        }

        linearDistance = metric(linearPoint, node.obj);

        if (node.right === null && node.left === null) {
          if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) {
            saveNode(node, ownDistance);
          }
          return;
        }

        if (node.right === null) {
          bestChild = node.left;
        } else if (node.left === null) {
          bestChild = node.right;
        } else {
          if (point[dimension] < node.obj[dimension]) {
            bestChild = node.left;
          } else {
            bestChild = node.right;
          }
        }

        nearestSearch(bestChild);

        if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) {
          saveNode(node, ownDistance);
        }

        if (bestNodes.size() < maxNodes || Math.abs(linearDistance) < bestNodes.peek()[1]) {
          if (bestChild === node.left) {
            otherChild = node.right;
          } else {
            otherChild = node.left;
          }
          if (otherChild !== null) {
            nearestSearch(otherChild);
          }
        }
      }

      if (maxDistance) {
        for (i = 0; i < maxNodes; i += 1) {
          bestNodes.push([null, maxDistance]);
        }
      }

      if(self.root)
        nearestSearch(self.root);

      result = [];

      for (i = 0; i < Math.min(maxNodes, bestNodes.content.length); i += 1) {
        if (bestNodes.content[i][0]) {
          result.push([bestNodes.content[i][0].obj, bestNodes.content[i][1]]);
        }
      }
      return result;
    };

    this.balanceFactor = function () {
      function height(node) {
        if (node === null) {
          return 0;
        }
        return Math.max(height(node.left), height(node.right)) + 1;
      }

      function count(node) {
        if (node === null) {
          return 0;
        }
        return count(node.left) + count(node.right) + 1;
      }

      return height(self.root) / (Math.log(count(self.root)) / Math.log(2));
    };
  }

  // Binary heap implementation from:
  // http://eloquentjavascript.net/appendix2.html

  function BinaryHeap(scoreFunction){
    this.content = [];
    this.scoreFunction = scoreFunction;
  }

  BinaryHeap.prototype = {
    push: function(element) {
      // Add the new element to the end of the array.
      this.content.push(element);
      // Allow it to bubble up.
      this.bubbleUp(this.content.length - 1);
    },

    pop: function() {
      // Store the first element so we can return it later.
      var result = this.content[0];
      // Get the element at the end of the array.
      var end = this.content.pop();
      // If there are any elements left, put the end element at the
      // start, and let it sink down.
      if (this.content.length > 0) {
        this.content[0] = end;
        this.sinkDown(0);
      }
      return result;
    },

    peek: function() {
      return this.content[0];
    },

    remove: function(node) {
      var len = this.content.length;
      // To remove a value, we must search through the array to find
      // it.
      for (var i = 0; i < len; i++) {
        if (this.content[i] == node) {
          // When it is found, the process seen in 'pop' is repeated
          // to fill up the hole.
          var end = this.content.pop();
          if (i != len - 1) {
            this.content[i] = end;
            if (this.scoreFunction(end) < this.scoreFunction(node))
              this.bubbleUp(i);
            else
              this.sinkDown(i);
          }
          return;
        }
      }
      throw new Error("Node not found.");
    },

    size: function() {
      return this.content.length;
    },

    bubbleUp: function(n) {
      // Fetch the element that has to be moved.
      var element = this.content[n];
      // When at 0, an element can not go up any further.
      while (n > 0) {
        // Compute the parent element's index, and fetch it.
        var parentN = Math.floor((n + 1) / 2) - 1,
            parent = this.content[parentN];
        // Swap the elements if the parent is greater.
        if (this.scoreFunction(element) < this.scoreFunction(parent)) {
          this.content[parentN] = element;
          this.content[n] = parent;
          // Update 'n' to continue at the new position.
          n = parentN;
        }
        // Found a parent that is less, no need to move it further.
        else {
          break;
        }
      }
    },

    sinkDown: function(n) {
      // Look up the target element and its score.
      var length = this.content.length,
          element = this.content[n],
          elemScore = this.scoreFunction(element);

      while(true) {
        // Compute the indices of the child elements.
        var child2N = (n + 1) * 2, child1N = child2N - 1;
        // This is used to store the new position of the element,
        // if any.
        var swap = null;
        // If the first child exists (is inside the array)...
        if (child1N < length) {
          // Look it up and compute its score.
          var child1 = this.content[child1N],
              child1Score = this.scoreFunction(child1);
          // If the score is less than our element's, we need to swap.
          if (child1Score < elemScore)
            swap = child1N;
        }
        // Do the same checks for the other child.
        if (child2N < length) {
          var child2 = this.content[child2N],
              child2Score = this.scoreFunction(child2);
          if (child2Score < (swap == null ? elemScore : child1Score)){
            swap = child2N;
          }
        }

        // If the element needs to be moved, swap it, and continue.
        if (swap != null) {
          this.content[n] = this.content[swap];
          this.content[swap] = element;
          n = swap;
        }
        // Otherwise, we are done.
        else {
          break;
        }
      }
    }
  };
  
  this.kdTree = kdTree;
  
  exports.kdTree = kdTree;
  exports.BinaryHeap = BinaryHeap;
}));

purview.json

{
  "commits": [
    {
      "sha": "4acc67790ceae47e2dc59a065d40d654ac1bdeeb",
      "name": "adapting example3"
    },
    {
      "sha": "55c1cb6e0a0af629a914a01cd122b605929a07bb",
      "name": "adapting example2"
    },
    {
      "sha": "bccc7dea1526a86c6bdcfda022fc82053ff0a904",
      "name": "adapting example1"
    },
    {
      "sha": "e5b69a958ebc7cfc4490383bc46eea4550931888",
      "name": "averaging function"
    },
    {
      "sha": "b287c9b1382496eb683a53d2b353e57edc726870",
      "name": "migrated code"
    },
    {
      "sha": "11af26044b7353d04626875bf5a819de92883680",
      "name": "original software"
    }
  ]
}

sketch.html

<head>
    <style> body {padding: 0; margin: 0;} </style>
</head>
<body style="background-color:white">
<img src="same_pose.jpg" width="960" height="500"/><br>
Same Pose
<hr>
<img src="same_subject.jpg" width="960" height="500"/><br>
Same Subject
<p>
<a href="index.html">program</a>
</body>

training_values.json

{
  "000001": [
    100
  ],
  "000002": [
    66.66666666666666
  ],
  "000005": [
    33.33333333333333
  ],
  "000006": [
    100
  ],
  "000007": [
    33.33333333333333
  ],
  "000009": [
    66.66666666666666
  ],
  "000010": [
    100
  ],
  "000013": [
    33.33333333333333
  ],
  "000014": [
    33.33333333333333
  ],
  "000015": [
    33.33333333333333
  ],
  "000016": [
    33.33333333333333
  ],
  "000018": [
    100
  ],
  "000020": [
    66.66666666666666
  ],
  "000023": [
    33.33333333333333
  ],
  "000025": [
    0
  ],
  "000028": [
    100
  ],
  "000029": [
    100
  ],
  "000030": [
    33.33333333333333
  ],
  "000031": [
    33.33333333333333
  ],
  "000032": [
    33.33333333333333
  ],
  "000035": [
    100
  ],
  "000037": [
    0
  ],
  "000038": [
    33.33333333333333
  ],
  "000040": [
    33.33333333333333
  ],
  "000041": [
    33.33333333333333
  ],
  "000042": [
    33.33333333333333
  ],
  "000043": [
    66.66666666666666
  ],
  "000044": [
    100
  ],
  "000045": [
    100
  ],
  "000047": [
    100
  ],
  "000048": [
    33.33333333333333
  ],
  "000050": [
    33.33333333333333
  ],
  "000051": [
    0
  ],
  "000052": [
    33.33333333333333
  ],
  "000054": [
    100
  ],
  "000055": [
    33.33333333333333
  ],
  "000056": [
    33.33333333333333
  ],
  "000058": [
    100
  ],
  "000060": [
    0
  ],
  "000064": [
    33.33333333333333
  ],
  "000065": [
    33.33333333333333
  ],
  "000068": [
    33.33333333333333
  ],
  "000069": [
    33.33333333333333
  ],
  "000071": [
    66.66666666666666
  ],
  "000073": [
    33.33333333333333
  ],
  "000076": [
    66.66666666666666
  ],
  "000077": [
    100
  ],
  "000078": [
    100
  ],
  "000079": [
    0
  ],
  "000080": [
    33.33333333333333
  ],
  "000081": [
    33.33333333333333
  ],
  "000083": [
    100
  ],
  "000085": [
    100
  ],
  "000086": [
    33.33333333333333
  ],
  "000088": [
    66.66666666666666
  ],
  "000091": [
    33.33333333333333
  ],
  "000092": [
    66.66666666666666
  ],
  "000096": [
    100
  ],
  "000097": [
    100
  ],
  "000099": [
    66.66666666666666
  ],
  "000100": [
    33.33333333333333
  ],
  "000103": [
    66.66666666666666
  ],
  "000104": [
    33.33333333333333
  ],
  "000106": [
    66.66666666666666
  ],
  "000108": [
    100
  ],
  "000109": [
    66.66666666666666
  ],
  "000110": [
    66.66666666666666
  ],
  "000111": [
    33.33333333333333
  ],
  "000114": [
    33.33333333333333
  ],
  "000115": [
    0
  ],
  "000116": [
    33.33333333333333
  ],
  "000117": [
    66.66666666666666
  ],
  "000118": [
    33.33333333333333
  ],
  "000121": [
    66.66666666666666
  ],
  "000122": [
    33.33333333333333
  ],
  "000125": [
    0
  ],
  "000126": [
    100
  ],
  "000129": [
    33.33333333333333
  ],
  "000131": [
    66.66666666666666
  ],
  "000132": [
    66.66666666666666
  ],
  "000133": [
    33.33333333333333
  ],
  "000134": [
    0
  ],
  "000135": [
    33.33333333333333
  ],
  "000137": [
    0
  ],
  "000140": [
    66.66666666666666
  ],
  "000142": [
    100
  ],
  "000143": [
    33.33333333333333
  ],
  "000145": [
    100
  ],
  "000146": [
    33.33333333333333
  ],
  "000147": [
    100
  ],
  "000148": [
    33.33333333333333
  ],
  "000150": [
    33.33333333333333
  ],
  "000151": [
    66.66666666666666
  ],
  "000152": [
    33.33333333333333
  ],
  "000153": [
    33.33333333333333
  ],
  "000155": [
    100
  ],
  "000156": [
    33.33333333333333
  ],
  "000157": [
    66.66666666666666
  ],
  "000160": [
    33.33333333333333
  ],
  "000161": [
    66.66666666666666
  ]
}

z_focused_random.js

function resetFocusedRandom() {
  return Math.seedrandom(arguments);
}

function focusedRandom(min, max, focus, mean) {
  // console.log("hello")
  if(max === undefined) {
    max = min;
    min = 0;
  }
  if(focus === undefined) {
    focus = 1.0;
  }
  if(mean === undefined) {
    mean = (min + max) / 2.0;
  }
  if(focus == 0) {
    return d3.randomUniform(min, max)();
  }
  else if(focus < 0) {
    focus = -1 / focus;
  }
  let sigma = (max - min) / (2 * focus);
  let val = d3.randomNormal(mean, sigma)();
  if (val >= min && val < max) {
    return val;
  }
  return d3.randomUniform(min, max)();
}

z_purview_helper.js

// note: this file is poorly named - it can generally be ignored.

// helper functions below for supporting blocks/purview

function saveBlocksImages(doZoom) {
  if(doZoom == null) {
    doZoom = false;
  }

  // generate 960x500 preview.jpg of entire canvas
  // TODO: should this be recycled?
  var offscreenCanvas = document.createElement('canvas');
  offscreenCanvas.width = 960;
  offscreenCanvas.height = 500;
  var context = offscreenCanvas.getContext('2d');
  // background is flat white
  context.fillStyle="#FFFFFF";
  context.fillRect(0, 0, 960, 500);
  context.drawImage(this.canvas, 0, 0, 960, 500);
  // save to browser
  var downloadMime = 'image/octet-stream';
  var imageData = offscreenCanvas.toDataURL('image/jpeg');
  imageData = imageData.replace('image/jpeg', downloadMime);
  p5.prototype.downloadFile(imageData, 'preview.jpg', 'jpg');

  // generate 230x120 thumbnail.png centered on mouse
  offscreenCanvas.width = 230;
  offscreenCanvas.height = 120;

  // background is flat white  
  context = offscreenCanvas.getContext('2d');
  context.fillStyle="#FFFFFF";
  context.fillRect(0, 0, 230, 120);

  if(doZoom) {
    // pixelDensity does the right thing on retina displays
    var pd = this._pixelDensity;
    var sx = pd * mouseX - pd * 230/2;
    var sy = pd * mouseY - pd * 120/2;
    var sw = pd * 230;
    var sh = pd * 120;
    // bounds checking - just displace if necessary
    if (sx < 0) {
      sx = 0;
    }
    if (sx > this.canvas.width - sw) {
      sx = this.canvas.width - sw;
    }
    if (sy < 0) {
      sy = 0;
    }
    if (sy > this.canvas.height - sh) {
      sy = this.canvas.height - sh;
    }
    // save to browser
    context.drawImage(this.canvas, sx, sy, sw, sh, 0, 0, 230, 120);
  }
  else {
    // now scaledown
    var full_width = this.canvas.width;
    var full_height = this.canvas.height;
    context.drawImage(this.canvas, 0, 0, full_width, full_height, 0, 0, 230, 120);
  }
  imageData = offscreenCanvas.toDataURL('image/png');
  imageData = imageData.replace('image/png', downloadMime);
  // call this function after 1 second
  setTimeout(function(){
    p5.prototype.downloadFile(imageData, 'thumbnail.png', 'png');
  }, 1000);
}