) Итак, начнем. Я хочу реализовать следующую идею: я хочу соединиться с другим пользователем на другом компьютере, используя webrtc (обмениваться видео и аудио данными), а затем распознать его эмоции. Поэтому в этом проекте я использую node-webrtc addon (здесь examples ). Итак, я скачал примеры и протестировал пример видео-композитинга, и все работает отлично. Вот результат тестирования
Следующая часть - узнавать эмоции лица. Для этой задачи я использую face-api.js . Я проверил это хорошее видео . Я не буду прикреплять фото, потому что сейчас я использую Ubuntu, но проверил его на Windows, просто поверьте мне, все тоже отлично работает. Настало время объединить оба модуля.
В качестве основного проекта я использую примеры node-webrtc, все последующие объяснения будут касаться этого модуля. Поэтому, чтобы запустить результат, вы должны скопировать папку weights из face-api в папку node-webrtc / examples / video-compositing, а затем просто заменить приведенный ниже код вместо node-webrtc / example / video-compositing / server.js.
'use strict';
require('@tensorflow/tfjs-node');
const tf = require('@tensorflow/tfjs');
const nodeFetch = require('node-fetch');
const fapi = require('face-api.js');
const path = require('path');
const { createCanvas, createImageData } = require('canvas');
const { RTCVideoSink, RTCVideoSource, i420ToRgba, rgbaToI420 } = require('wrtc').nonstandard;
fapi.env.monkeyPatch({ fetch: nodeFetch });
const MODELS_URL = path.join(__dirname, '/weights');
const width = 640;
const height = 480;
Promise.all([
fapi.nets.tinyFaceDetector.loadFromDisk(MODELS_URL),
fapi.nets.faceLandmark68Net.loadFromDisk(MODELS_URL),
fapi.nets.faceRecognitionNet.loadFromDisk(MODELS_URL),
fapi.nets.faceExpressionNet.loadFromDisk(MODELS_URL)
]);
function beforeOffer(peerConnection) {
const source = new RTCVideoSource();
const track = source.createTrack();
const transceiver = peerConnection.addTransceiver(track);
const sink = new RTCVideoSink(transceiver.receiver.track);
let lastFrame = null;
function onFrame({ frame }) {
lastFrame = frame;
}
sink.addEventListener('frame', onFrame);
// TODO(mroberts): Is pixelFormat really necessary?
const canvas = createCanvas(width, height);
const context = canvas.getContext('2d', { pixelFormat: 'RGBA24' });
context.fillStyle = 'white';
context.fillRect(0, 0, width, height);
let emotion = '';
const interval = setInterval(() => {
if (lastFrame) {
const lastFrameCanvas = createCanvas(lastFrame.width, lastFrame.height);
const lastFrameContext = lastFrameCanvas.getContext('2d', { pixelFormat: 'RGBA24' });
const rgba = new Uint8ClampedArray(lastFrame.width * lastFrame.height * 4);
const rgbaFrame = createImageData(rgba, lastFrame.width, lastFrame.height);
i420ToRgba(lastFrame, rgbaFrame);
lastFrameContext.putImageData(rgbaFrame, 0, 0);
context.drawImage(lastFrameCanvas, 0, 0);
const emotionsArr = { 0: 'neutral', 1: 'happy', 2: 'sad', 3: 'angry', 4: 'fearful', 5: 'disgusted', 6: 'surprised' };
async function detectEmotion() {
let frameTensor3D = tf.browser.fromPixels(lastFrameCanvas)
let face = await fapi.detectSingleFace(frameTensor3D, new fapi.TinyFaceDetectorOptions()).withFaceExpressions();
//console.log(face);
function getEmotion(face) {
try {
let mostLikelyEmotion = emotionsArr[0];
let predictionArruracy = face.expressions[emotionsArr[0]];
for (let i = 0; i < Object.keys(face.expressions).length; i++) {
if (face.expressions[emotionsArr[i]] > predictionArruracy && face.expressions[emotionsArr[i]] < 1 ){
mostLikelyEmotion = emotionsArr[i];
predictionArruracy = face.expressions[emotionsArr[i]];
}
}
return mostLikelyEmotion;
}
catch (e){
return '';
}
}
let emot = getEmotion(face);
return emot;
}
detectEmotion().then(function(res) {
emotion = res;
});
} else {
context.fillStyle = 'rgba(255, 255, 255, 0.025)';
context.fillRect(0, 0, width, height);
}
if (emotion != ''){
context.font = '60px Sans-serif';
context.strokeStyle = 'black';
context.lineWidth = 1;
context.fillStyle = `rgba(${Math.round(255)}, ${Math.round(255)}, ${Math.round(255)}, 1)`;
context.textAlign = 'center';
context.save();
context.translate(width / 2, height);
context.strokeText(emotion, 0, 0);
context.fillText(emotion, 0, 0);
context.restore();
}
const rgbaFrame = context.getImageData(0, 0, width, height);
const i420Frame = {
width,
height,
data: new Uint8ClampedArray(1.5 * width * height)
};
rgbaToI420(rgbaFrame, i420Frame);
source.onFrame(i420Frame);
});
const { close } = peerConnection;
peerConnection.close = function() {
clearInterval(interval);
sink.stop();
track.stop();
return close.apply(this, arguments);
};
}
module.exports = { beforeOffer };
А вот и результаты1 , результаты2 и результаты3 , все отлично работает)) ... Ну нет, через 2-3 минутымой компьютер просто перестал делать что-либо, я даже не могу пошевелить мышью, а затем я получаю сообщение об ошибке "Killed" в терминале. Я читал об этой ошибке здесь и, поскольку я изменил только один скрипт в проекте, я подозреваю, что где-то в моем коде у меня есть утечка данных, и моя оперативная память заполняется со временем. Может кто-нибудь помочь мне с этим вопросом? Почему программа заканчивается процессом убийства? Если кто-то захочет проверить это для себя, я оставлю пакет json, чтобы легко установить все требования.
{
"name": "node-webrtc-examples",
"version": "0.1.0",
"description": "This project presents a few example applications using node-webrtc.",
"private": true,
"main": "index.js",
"scripts": {
"lint": "eslint index.js examples lib test",
"start": "node index.js",
"test": "npm run test:unit && npm run test:integration",
"test:unit": "tape 'test/unit/**/*.js'",
"test:integration": "tape 'test/integration/**/*.js'"
},
"keywords": [
"Web",
"Audio"
],
"author": "Mark Andrus Roberts <markandrusroberts@gmail.com>",
"license": "BSD-3-Clause",
"dependencies": {
"@tensorflow/tfjs": "^1.2.9",
"@tensorflow/tfjs-core": "^1.2.9",
"@tensorflow/tfjs-node": "^1.2.9",
"Scope": "github:kevincennis/Scope",
"body-parser": "^1.18.3",
"browserify-middleware": "^8.1.1",
"canvas": "^2.6.0",
"color-space": "^1.16.0",
"express": "^4.16.4",
"face-api.js": "^0.21.0",
"node-fetch": "^2.3.0",
"uuid": "^3.3.2",
"wrtc": "^0.4.1"
},
"devDependencies": {
"eslint": "^5.15.1",
"tape": "^4.10.0"
}
}
Если у вас есть ошибка типа «someFunction is not function» или что-то в этом роде, вероятно, это может быть потому, чтовам нужно установить версию @ tenorflow / tfjs-core, tfjs и tfjs-node 1.2.9. Например, npm i @ tenorflow / tfjs-core @ 1.2.9. Для всех 3 пакетов. Спасибо за ваши ответы и понимание))