Обнаружение эмоций на лице в реальном времени с помощью веб-камеры в браузере с использованием TensorFlow.js. Часть 3

от автора

Мы уже научились использовать искусственный интеллект (ИИ) в веб-браузере для отслеживания лиц в реальном времени и применять глубокое обучение для обнаружения и классификации эмоций на лице. Итак, мы собрали эти два компонента вместе и хотим узнать, сможем ли мы в реальном времени обнаруживать эмоции с помощью веб-камеры. В этой статье мы, используя транслируемое с веб-камеры видео нашего лица, узнаем, сможет ли модель реагировать на выражение лица в реальном времени.


Вы можете загрузить демоверсию этого проекта. Для обеспечения необходимой производительности может потребоваться включить в веб-браузере поддержку интерфейса WebGL. Вы также можете загрузить код и файлы для этой серии. Предполагается, что вы знакомы с JavaScript и HTML и имеете хотя бы базовое представление о нейронных сетях.

Добавление обнаружения эмоций на лице

В этом проекте мы протестируем нашу обученную модель обнаружения эмоций на лице на видео, транслируемом с веб-камеры. Мы начнём со стартового шаблона с окончательным кодом из проекта отслеживания лиц и внесём в него части кода для обнаружения эмоций на лице.

Давайте загрузим и применим нашу предварительно обученную модель выражений на лице. Начала мы определим некоторые глобальные переменные для обнаружения эмоций, как мы делали раньше:

const emotions = [ "angry", "disgust", "fear", "happy", "neutral", "sad", "surprise" ]; let emotionModel = null;

Затем мы можем загрузить модель обнаружения эмоций внутри блока async:

(async () => {     ...      // Load Face Landmarks Detection     model = await faceLandmarksDetection.load(         faceLandmarksDetection.SupportedPackages.mediapipeFacemesh     );     // Load Emotion Detection     emotionModel = await tf.loadLayersModel( 'web/model/facemo.json' );      ... })();

А для модельного прогнозирования по ключевым точкам лица мы можем добавить служебную функцию:

async function predictEmotion( points ) {     let result = tf.tidy( () => {         const xs = tf.stack( [ tf.tensor1d( points ) ] );         return emotionModel.predict( xs );     });     let prediction = await result.data();     result.dispose();     // Get the index of the maximum value     let id = prediction.indexOf( Math.max( ...prediction ) );     return emotions[ id ]; }

Наконец, нам нужно получить ключевые точки лица от модуля обнаружения внутри функции trackFace и передать их модулю прогнозирования эмоций.

async function trackFace() {     ...      let points = null;     faces.forEach( face => {         ...          // Add just the nose, cheeks, eyes, eyebrows & mouth         const features = [             "noseTip",             "leftCheek",             "rightCheek",             "leftEyeLower1", "leftEyeUpper1",             "rightEyeLower1", "rightEyeUpper1",             "leftEyebrowLower", //"leftEyebrowUpper",             "rightEyebrowLower", //"rightEyebrowUpper",             "lipsLowerInner", //"lipsLowerOuter",             "lipsUpperInner", //"lipsUpperOuter",         ];         points = [];         features.forEach( feature => {             face.annotations[ feature ].forEach( x => {                 points.push( ( x[ 0 ] - x1 ) / bWidth );                 points.push( ( x[ 1 ] - y1 ) / bHeight );             });         });     });      if( points ) {         let emotion = await predictEmotion( points );         setText( `Detected: ${emotion}` );     }     else {         setText( "No Face" );     }      requestAnimationFrame( trackFace ); }

Это всё, что нужно для достижения нужной цели. Теперь, когда вы открываете веб-страницу, она должна обнаружить ваше лицо и распознать эмоции. Экспериментируйте и получайте удовольствие!

Вот полный код, нужный для завершения этого проекта
<html>     <head>         <title>Real-Time Facial Emotion Detection</title>         <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@2.4.0/dist/tf.min.js"></script>         <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/face-landmarks-detection@0.0.1/dist/face-landmarks-detection.js"></script>     </head>     <body>         <canvas id="output"></canvas>         <video id="webcam" playsinline style="             visibility: hidden;             width: auto;             height: auto;             ">         </video>         <h1 id="status">Loading...</h1>         <script>         function setText( text ) {             document.getElementById( "status" ).innerText = text;         }          function drawLine( ctx, x1, y1, x2, y2 ) {             ctx.beginPath();             ctx.moveTo( x1, y1 );             ctx.lineTo( x2, y2 );             ctx.stroke();         }          async function setupWebcam() {             return new Promise( ( resolve, reject ) => {                 const webcamElement = document.getElementById( "webcam" );                 const navigatorAny = navigator;                 navigator.getUserMedia = navigator.getUserMedia ||                 navigatorAny.webkitGetUserMedia || navigatorAny.mozGetUserMedia ||                 navigatorAny.msGetUserMedia;                 if( navigator.getUserMedia ) {                     navigator.getUserMedia( { video: true },                         stream => {                             webcamElement.srcObject = stream;                             webcamElement.addEventListener( "loadeddata", resolve, false );                         },                     error => reject());                 }                 else {                     reject();                 }             });         }          const emotions = [ "angry", "disgust", "fear", "happy", "neutral", "sad", "surprise" ];         let emotionModel = null;          let output = null;         let model = null;          async function predictEmotion( points ) {             let result = tf.tidy( () => {                 const xs = tf.stack( [ tf.tensor1d( points ) ] );                 return emotionModel.predict( xs );             });             let prediction = await result.data();             result.dispose();             // Get the index of the maximum value             let id = prediction.indexOf( Math.max( ...prediction ) );             return emotions[ id ];         }          async function trackFace() {             const video = document.querySelector( "video" );             const faces = await model.estimateFaces( {                 input: video,                 returnTensors: false,                 flipHorizontal: false,             });             output.drawImage(                 video,                 0, 0, video.width, video.height,                 0, 0, video.width, video.height             );              let points = null;             faces.forEach( face => {                 // Draw the bounding box                 const x1 = face.boundingBox.topLeft[ 0 ];                 const y1 = face.boundingBox.topLeft[ 1 ];                 const x2 = face.boundingBox.bottomRight[ 0 ];                 const y2 = face.boundingBox.bottomRight[ 1 ];                 const bWidth = x2 - x1;                 const bHeight = y2 - y1;                 drawLine( output, x1, y1, x2, y1 );                 drawLine( output, x2, y1, x2, y2 );                 drawLine( output, x1, y2, x2, y2 );                 drawLine( output, x1, y1, x1, y2 );                  // Add just the nose, cheeks, eyes, eyebrows & mouth                 const features = [                     "noseTip",                     "leftCheek",                     "rightCheek",                     "leftEyeLower1", "leftEyeUpper1",                     "rightEyeLower1", "rightEyeUpper1",                     "leftEyebrowLower", //"leftEyebrowUpper",                     "rightEyebrowLower", //"rightEyebrowUpper",                     "lipsLowerInner", //"lipsLowerOuter",                     "lipsUpperInner", //"lipsUpperOuter",                 ];                 points = [];                 features.forEach( feature => {                     face.annotations[ feature ].forEach( x => {                         points.push( ( x[ 0 ] - x1 ) / bWidth );                         points.push( ( x[ 1 ] - y1 ) / bHeight );                     });                 });             });              if( points ) {                 let emotion = await predictEmotion( points );                 setText( `Detected: ${emotion}` );             }             else {                 setText( "No Face" );             }              requestAnimationFrame( trackFace );         }          (async () => {             await setupWebcam();             const video = document.getElementById( "webcam" );             video.play();             let videoWidth = video.videoWidth;             let videoHeight = video.videoHeight;             video.width = videoWidth;             video.height = videoHeight;              let canvas = document.getElementById( "output" );             canvas.width = video.width;             canvas.height = video.height;              output = canvas.getContext( "2d" );             output.translate( canvas.width, 0 );             output.scale( -1, 1 ); // Mirror cam             output.fillStyle = "#fdffb6";             output.strokeStyle = "#fdffb6";             output.lineWidth = 2;              // Load Face Landmarks Detection             model = await faceLandmarksDetection.load(                 faceLandmarksDetection.SupportedPackages.mediapipeFacemesh             );             // Load Emotion Detection             emotionModel = await tf.loadLayersModel( 'web/model/facemo.json' );              setText( "Loaded!" );              trackFace();         })();         </script>     </body> </html>

Что дальше? Когда мы сможем носить виртуальные очки?

Взяв код из первых двух статей этой серии, мы смогли создать детектор эмоций на лице в реальном времени, используя лишь немного кода на JavaScript. Только представьте, что ещё можно сделать с помощью библиотеки TensorFlow.js! В следующей статье мы вернёмся к нашей цели – создать фильтр для лица в стиле Snapchat, используя то, что мы уже узнали об отслеживании лиц и добавлении 3D-визуализации посредством ThreeJS. Оставайтесь с нами! До встречи завтра, в это же время!

Отслеживание лиц в реальном времени в браузере с использованием TensorFlow.js. Часть 2

Узнайте подробности, как получить Level Up по навыкам и зарплате или востребованную профессию с нуля, пройдя онлайн-курсы SkillFactory со скидкой 40% и промокодом HABR, который даст еще +10% скидки на обучение.

ссылка на оригинал статьи https://habr.com/ru/company/skillfactory/blog/545332/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *