웃지 마라. 텐소 플로우를 이용한 간단한 인공지능 게임.js와 전자

오랜 시간 동안 나는 인공지능 기술을 어떻게 이용하여 디지털 응용 프로그램의 사용자 체험을 활용하고 심지어 새로운 UI/UX 개념을 실현하는지 평가해 왔다.
최근에 나는 우연히 several articles을 발견했다. 이것은 한 사람이 얼굴 제스처로 표현하는 정서를 식별하기 위해 CNN(권적신경망)을 훈련시키는 방법을 묘사했다.이것은 마치 사용자 인터페이스를 만드는 재미있는 생각처럼 들리기 때문에 그것을 테스트하기 위해 나는 간단한 게임인'웃지 마'를 생각해냈다.
게임 자체는 매우 간단하다.유튜브에서 재미있는 동영상을 무작위로 재생하기 시작할 것이다. 유일한 일은 웃지 마라.만약 인공지능이 네가 웃는 것을 발견한다면 게임은 끝난다.그래서 나는 Vue를 사용하여 이 게임을 만들기 시작했다.js와 Electron은 최근에 내가 선택한 전단 프레임워크가 되었다.

HTML5/JavaScript를 기반으로 한 응용 프로그램에서 인공지능과 기계 학습 기술을 사용하는 것은 상당히 신선한 일이기 때문에 본고에서 제 경험을 적고 여러분과 최선의 실천을 공유하기로 했습니다.만약 네가 모든 세부 사항에 흥미가 없다면, 너도 나의 메모리 라이브러리에서 모든 원본 코드를 다운로드할 수 있다👇👇

안디포타토 / 웃지 마.


Vue 기반의 간단한 AI 게임js와 전자


전자 응용 프로그램에 TensorFlow를 추가하는 방법


만약 당신이 이전에 인공지능이나 기계 학습 코드를 해 본 적이 있다면, 아마도 TensorFlow이라는 라이브러리가 실행되고 있는 것을 보았을 것이다.TensorFlow는 구글의 인공지능 머신 학습 프레임워크(ML)로 이미지 분류 등 임무에 널리 사용되고 있다. 이것이 바로 우리 작은 게임에 필요한 것이다.유일한 단점은 파이톤 라이브러리로 nVidia GPU로만 가속할 수 있다는 것이다.네, ML과 관련된 모든 일을 할 때 GPU 가속을 절대적으로 원합니다.

TensorFlow 를 입력합니다.js 및 WebGL GPU 가속


다행히도 TensorFlow팀은 이 라이브러리를 자바스크립트에 이식하고 TensorFlow.js(TFJS)을 발표하여 전자 응용 프로그램에서 사용할 수 있도록 했다.더 좋은 것은 그들이 WebGL 기반의 GPU 가속을 추가하여 어떠한 현대 GPU도 지원하고 nVidia와 CUDA를 지원하는 하드웨어에만 국한되지 않는다는 것이다👋 👋 👋. 건배🍺!

faceapi를 사용하여 일을 더욱 간단하게 합니다.js


지금 우리는 기본적으로 모두 준비가 다 되었다.우리 손에는 정서 검측에 사용할 수 있는 기능이 강한 라이브러리가 있다.이것은 우리가 시간과 번거로움을 절약해서 우리 자신의 모형을 훈련시킬 수 있다는 것을 의미한다.
초기 코드를 작성할 때, 나는 Vincent Mühler이TensorFlow 기반의 face-api.js이라는 고급 API를 만들었다는 것을 발견했다.js는 모든 밑바닥 조작을 봉인하고 얼굴 식별과 정서 검사 등 흔한 작업에 편리한 API를 제공한다.그는 또한 그의 GitHub repository에 많은 예시 코드를 제공했기 때문에 나는 몇 분 안에 나의 첫 번째 감정 식별기를 구축할 수 있다.

애플리케이션 통합


전체적인 게임 구조부터 시작합시다.Electron을 사용하면 Chromium 렌더링 프로그램이 우리의 게임 UI가 된다는 것을 의미합니다.그것은 동영상 파일을 재생해서 실시간 동영상을 보여 주고, 물론 게임 논리도 실행할 것이다. 웃고, 긴장을 풀었다.
그렇다면 이런 상황에서 실제 정서 검측은 어디에서 일어날까?이 질문에 대답하려면 두 가지를 기억해야 한다.
실시간 정서 검측은 자원 집약형 임무이다.좋은 GPU라도 초당 약 20-30 프레임만 생성할 수 있다.이로 인해 게임의 UI가 응답하지 않을 수 있습니다.
일이 순조롭게 진행되기 위해서, 우리는 복잡한 일을 단독으로 옮기는 방법이 필요하다.Luckily Electron can do just that using hidden renderers . 따라서 Dell의 최종 게임 아키텍처는 다음과 같습니다.

이 장면에서 우리는 숨겨진 렌더링기('검측자')에서faceapi를 실행하고 실시간 카메라 흐름의 정서를 끊임없이 평가한다.정서가 감지되면 스태프는 감지된 정서가 담긴 IPC 메시지를 게임에 전송한다.게임에서 우리는 이러한 정보를 간단하게 사건으로 보고 해당하는 반응을 할 수 있다.

새 Vue를 생성합니다.js/Electron 응용 프로그램


응용 프로그램 템플릿 생성 you can follow my instructions herenpm run electron:serve을 사용하여 빈 애플리케이션을 성공적으로 실행할 수 있을 때까지 환경 준비 섹션에서 지침을 따르십시오.
다음faceapi를 설치합니다.js:
npm i --save face-api.js

배경 검사 작업 프로세스


우선, 모든 검사 작업 ('다시 불러오기') 을 처리하는 백엔드 작업 프로세스를 만듭니다.계속하여 worker.html 디렉토리에 파일 public을 만듭니다.
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Worker</title>
</head>
<body>
  <video id="cam" autoplay muted playsinline></video>
</body>
</html>
여기 <video> 라벨 주의하세요.워크맨에서 이 요소를 인용하여 카메라 흐름에서 이미지 데이터를 검색할 것입니다.
다음에 worker.js 디렉터리에 src을 만듭니다.이것은 긴 파일입니다. 당신은 full version here을 볼 수 있습니다.이를 분해하고 가장 중요한 부분을 설명하겠습니다.
import * as faceapi from 'face-api.js';
// init detection options
const minConfidenceFace = 0.5;
const faceapiOptions = new faceapi.SsdMobilenetv1Options({ minConfidenceFace });
여기에faceapi를 포함하고 설정합니다.js.내부api를 향합니다.js는 SSD MobileNet v1 모델을 사용하여 그림 속의 얼굴을 식별하기 때문에 우리는 minConfidenceFace 설정 파라미터를 제공해야 한다. 이 파라미터는 모델을 최소 50%의 신뢰도를 가진 얼굴을 식별하는 것으로 설정해야 한다.

// configure face API
faceapi.env.monkeyPatch({
  Canvas: HTMLCanvasElement,
  Image: HTMLImageElement,
  ImageData: ImageData,
  Video: HTMLVideoElement,
  createCanvasElement: () => document.createElement('canvas'),
  createImageElement: () => document.createElement('img')
});
이 부분은 faceapi를 만드는 변통 방법입니다.js는 Electron 응용 프로그램에서 정상적으로 작동합니다.정상적인 브라우저 환경에서는 필요 없습니다.하지만 숨겨진 렌더기에서 nodeIntegration을 사용하면 TensorFlow가 발생합니다.js는 우리가 NodeJS 환경에 있다고 믿는다.That’s why we need to manually monkey patch the environment back to a browser environment . 이 단계를 건너뛰면 오류 Uncaught (in promise) TypeError: Illegal constructor at createCanvasElement [...]이 표시됩니다.
let loadNet = async () => {
  let detectionNet = faceapi.nets.ssdMobilenetv1;
  await detectionNet.load('/data/weights');
  await faceapi.loadFaceExpressionModel('/data/weights');
  return detectionNet;
};
다음은 모바일넷 V1 네트워크와 얼굴 표정 모델에 미리 훈련된 모델 권한을 탑재합니다.프로그램이 오프라인으로 작동하도록 하기 때문에 로컬 URL/data/weights에서 불러오고 프로젝트 디렉터리의/public/data/weights 폴더로 변환합니다.필요한 파일은 Vincent Mühler’s GitHub repository에서 다운로드할 수 있습니다.

let cam;
let initCamera = async (width, height) => {
  cam = document.getElementById('cam');
  cam.width = width;
  cam.height = height;
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: false,
    video: {
      facingMode: "user",
      width: width,
      height: height
    }
  });
  cam.srcObject = stream;
  return new Promise((resolve) => {
    cam.onloadedmetadata = () => {
      resolve(cam);
    };
  });
};
상기 코드는 기본적으로 컴퓨터에 연결된 카메라에서 영상 흐름을 얻는 표준 코드이다.우리는 단지 그것을 편리한 약속 속에 포장했을 뿐이다.
현재 모든 것이 준비되었으니 우리는 직접 검사 부분을 계속 진행할 수 있다.게임 UI 렌더링에 이벤트 메시지를 보내기 위한 몇 가지 편리한 방법만 추가했습니다.
let onReady = () => {
  notifyRenderer('ready', {});
};
let onExpression = (type) => {
  notifyRenderer('expression', { type: type });
};
let notifyRenderer = (command, payload) => {
  ipcRenderer.send('window-message-from-worker', {
    command: command, payload: payload
  });
}
onReady 이벤트는 모델에서'예열'을 초기화하고 테스트를 준비한 후 터치합니다.이후 표현식이 감지될 때마다 onExpression 이벤트는 IPC를 통해 감지된 표현식을 주 프로세스에 전달합니다.
이제 실제 검사 섹션입니다.
let detectExpressions = async () => {
  // detect expression
  let result = await faceapi.detectSingleFace(cam, faceapiOptions)
    .withFaceExpressions();
  if(!isReady) {
    isReady = true;
    onReady();
  }
  if(typeof result !== 'undefined') {
    let happiness = 0, anger = 0;
    if(result.expressions.hasOwnProperty('happy')) {
      happiness = result.expressions.happy;
    }
    if(result.expressions.hasOwnProperty('angry')) {
      anger = result.expressions.angry;
    }
    if(happiness > 0.7) {
      onExpression('happy');
    } else if(anger > 0.7) {
      onExpression('angry');
    }
  }
  if(isRunning) {
    detectExpressions();
  }
};
이 함수는 기본적으로 무한 순환으로 카메라 사진의 한 얼굴을 먼저 검측한 다음에 이 얼굴의 얼굴 표정(=정서)을 확인하려고 시도한다.detectSingleFace().withFaceExpression()의 결과는 expressions 사전이 있는 결과 대상으로 되돌아오며'분노'나'쾌락'등 주어진 표현식의 확률(0-1)을 포함한다.나의 예에서 나는 onExpression 이벤트를 촉발하는 확률 한도값을 0.7(70%)으로 설정하기로 결정했다.
이렇게!이제 다음 코드를 사용하여 테스트 작업을 실행할 수 있습니다.
loadNet()
.then(net   => { return initCamera(640, 480); })
.then(video => { detectExpressions(); });

Vue를 구성합니다.js 숨겨진 배경 렌더링 사용하기


테스트 인원이 배치되면 두 개의 Vue를 구성합니다.js와 Electron을 사용하여 숨겨진 렌더링을 실행합니다.응용 프로그램의 루트 디렉토리에서 파일 vue.config.js을 열고 다음 구성을 삽입/첨부합니다.
module.exports = {
  pages: {
    index: {
      entry: 'src/main.js', //entry for the public page
      template: 'public/index.html', // source template
      filename: 'index.html' // output as dist/*
    },
    worker: {
      entry: 'src/worker.js',
      template: 'public/worker.html',
      filename: 'worker.html'
    }
  },
  devServer: {
    historyApiFallback: {
      rewrites: [
        { from: /\/index/, to: '/index.html' },
        { from: /\/worker/, to: '/worker.html' }
      ]
    }
  }
};
이 구성은 Vue의 WebPack 구성에 있는 worker에 두 번째 엔트리 포인트를 추가하고 개발 중에 제대로 작동하도록 별칭을 만듭니다.
마지막으로 background.js을 다음과 같이 수정했습니다.
import { app, protocol, BrowserWindow, ipcMain } from 'electron'
import {
  createProtocol,
  installVueDevtools
} from 'vue-cli-plugin-electron-builder/lib';
const isDevelopment = process.env.NODE_ENV !== 'production';
let win;
let workerWin;
// check if the "App" protocol has already been created
let createdAppProtocol = false;
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([{
  scheme: 'app', privileges: {
    secure: true,
    standard: true,
    corsEnabled: true,
    supportFetchAPI: true
  }
}])
function createWindow () {
  // create the game UI window
  win = new BrowserWindow({
    width: 1024, height: 790,
    webPreferences: { nodeIntegration: true }
  });
  if (process.env.WEBPACK_DEV_SERVER_URL) {
    win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
  } else {
    win.loadURL('app://./index.html');
  }
  win.on('closed', () => {
    // closing the main (visible) window should quit the App
    app.quit();
  });
}
function createWorker(devPath, prodPath) {
  // create hidden worker window
  workerWin = new BrowserWindow({
    show: false,
    webPreferences: { nodeIntegration: true }
  });
  if(process.env.WEBPACK_DEV_SERVER_URL) {
    workerWin.loadURL(process.env.WEBPACK_DEV_SERVER_URL + devPath);
  } else {
    workerWin.loadURL(`app://./${prodPath}`)
  }
  workerWin.on('closed', () => { workerWin = null; });
}
function sendWindowMessage(targetWindow, message, payload) {
  if(typeof targetWindow === 'undefined') {
    console.log('Target window does not exist');
    return;
  }
  targetWindow.webContents.send(message, payload);
}
[...]
app.on('ready', async () => {
  if (isDevelopment && !process.env.IS_TEST) {
    // Install Vue Devtools
    try {
      await installVueDevtools()
    } catch (e) {
      console.error('Vue Devtools failed to install:', e.toString())
    }
  }
  if(!createdAppProtocol) {
    createProtocol('app');
    createdAppProtocol = true;
  }
  // create the main application window
  createWindow();
  // create the background worker window
  createWorker('worker', 'worker.html');
  // setup message channels
  ipcMain.on('window-message-from-worker', (event, arg) => {
    sendWindowMessage(win, 'message-from-worker', arg);
  });
})
[...]
내가 여기에서 변경한 것과 추가한 것을 보여 주십시오.가장 뚜렷한 것은 두 번째 창 workerWin입니다. 이것은 우리가 숨긴 렌더링기입니다.일을 관리하기 쉽게 하기 위해 저는 함수 createWorker을 만들었습니다. 기본 createWindow 함수를 복제했고 숨겨진 작업자들의 특정한 수요를 충족시켰습니다.
그런 다음 CORS를 활성화하고 fetch API를 지원하도록 애플리케이션 프로토콜을 수정했습니다.이것은 로컬 /public 폴더에서 모델 권한을 불러오는 데 필요합니다.
마지막으로 나는 window-message-from-worker 채널에 IPC 탐지기를 추가하여 sendWindowMessage 방법을 통해 검측자로부터 전송된 메시지를 게임에 중계하였다.

게임 사용자 인터페이스 추가


나는 HTML/CSS를 어떻게 구축하는지 상세하게 소개하지 않을 것이며, 검측자로부터 받은 '감정' 메시지를 어떻게 수신하고 처리하는지 중점적으로 소개할 것이다.all the code is available on my GitHub repository은 알림으로 검토할 수 있습니다.
src/views/Play.vue의 소스 코드를 살펴보겠습니다.
this.$electron.ipcRenderer.on('message-from-worker', (ev, data) => {
  if(typeof data.command === 'undefined') {
    console.error('IPC message is missing command string');
    return;
  }
  if(data.command == 'expression') {
    if(data.payload.type == 'happy') {
      this.onLaugh();
      return;
    }
    if(data.payload.type == 'angry') {
      this.onAngry();
      return;
    }
  }
});
여기서 우리는 message-from-worker channel을 통해 입력한 IPC 메시지를 감청하기 시작했다.만약 메시지가 표현식 명령을 포함한다면, 우리는 메시지와 관련된 게임 이벤트를 터치할 것입니다.내 게임 onLaugh은 논리를 포함할 것이다. 당신이 크게 웃는 것을 발견하면 onAngry은 게임이 끝나면 다시 게임을 시작할 것이다.

이렇게!완성된 프로젝트에 관심이 있으면 download the code from my GitHub page을 클릭하세요.너는 그것을 마음대로 수정할 수도 있고, 아니면 그것을 너 자신의 게임의 기초로 삼을 수도 있다. 나는 네가 생각해 낸 모든 멋진 것을 보고 매우 기쁘다.
읽어주셔서 감사합니다!좀 남겨주세요.❤️ 만약 네가 나의 글을 좋아한다면, 만약 네가 어떤 평론, 문제, 건의가 있다면, 평론에서 대화를 시작해라

좋은 웹페이지 즐겨찾기