()
| 83 | |
| 84 | let cachedData = null; |
| 85 | const predict = async () => { |
| 86 | // Put original video content on the input texture. |
| 87 | inputTextureFrameBuffer.bindTexture(); |
| 88 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB8, gl.RGB, gl.UNSIGNED_BYTE, video); |
| 89 | |
| 90 | let data; |
| 91 | if (numInferences % STATE.DepthCachedFrames === 0) { |
| 92 | if (cachedData) { |
| 93 | // Make sure to dispose all tensors, otherwise there will be memory |
| 94 | // leak. |
| 95 | cachedData.tensorRef.dispose(); |
| 96 | } |
| 97 | |
| 98 | // Extract face. |
| 99 | const faces = await faceDetector.estimateFaces(video); |
| 100 | |
| 101 | let output4D; |
| 102 | if (faces.length === 0) { |
| 103 | output4D = tf.zeros([videoHeight, videoWidth, 1]); |
| 104 | } else { |
| 105 | const face = faces[0]; |
| 106 | |
| 107 | const rightEye = face.keypoints[0]; |
| 108 | const leftEye = face.keypoints[1]; |
| 109 | const nose = face.keypoints[2]; |
| 110 | |
| 111 | const rightEyePoint = [rightEye.x, rightEye.y]; |
| 112 | const leftEyePoint = [leftEye.x, leftEye.y]; |
| 113 | const eyesDistance = |
| 114 | Math.sqrt(tf.util.distSquared(rightEyePoint, leftEyePoint)); |
| 115 | |
| 116 | const scaleDistTo64 = 64 / eyesDistance; |
| 117 | |
| 118 | let portraitHeight, portraitWidth; |
| 119 | let sliceStart, sliceSize; |
| 120 | let rescaledEyeHeight, rescaledEyeWidth; |
| 121 | const portraitPadded = tf.tidy(() => { |
| 122 | const image1Tensor = tf.browser.fromPixels(video); |
| 123 | const image1Float = tf.cast(image1Tensor, 'float32'); |
| 124 | // Resize until distance of eyes is 192 / 3 = 94. |
| 125 | const rescaledEye = tf.image.resizeBilinear(image1Float, [ |
| 126 | Math.round(videoHeight * scaleDistTo64), |
| 127 | Math.round(videoWidth * scaleDistTo64) |
| 128 | ]); |
| 129 | [rescaledEyeHeight, rescaledEyeWidth] = rescaledEye.shape; |
| 130 | |
| 131 | // Find position of nose in rescaled image. |
| 132 | const nosePoint = [ |
| 133 | Math.round(nose.x * scaleDistTo64), |
| 134 | Math.round(nose.y * scaleDistTo64) |
| 135 | ]; |
| 136 | |
| 137 | sliceStart = [ |
| 138 | Math.max(0, nosePoint[1] - 256 / 2), |
| 139 | Math.max(0, nosePoint[0] - 192 / 2), 0 |
| 140 | ]; |
| 141 | sliceSize = [ |
| 142 | Math.min(rescaledEyeHeight - sliceStart[0], 256), |
no test coverage detected