Краткое руководство по TypeScript и машинному обучению
Сегодня я хочу поделиться кратким руководством о том, как создать собственное приложение с функцией распознавания лиц, используя следующий стек технологий:
Ionic, Capacitor и MediaPipe.
Ну, я буду представлять не Ionic или Capacitor, а MediaPipe, потому что это свежая и открытая технология, предоставленная Google. MediaPipe — это, по сути, SDK для разработчиков, созданный на основе TensorFlow для предоставления возможностей машинного обучения на любом устройстве.
Зайдите и проверьте Центр MediaPipe, чтобы узнать больше. Первой общедоступной версии MediaPipe 6 месяцев, а официально о ней было объявлено менее недели назад на мероприятии Google I/O.
Это было введение! Что вы найдете в этом документе:
1) Initial Setup - Create Your Project - Install Packages - Add the Mobile version of your App 2) Implementing Machine Learning - Template & Styles - Imports - Declarations - Functions
Начальная настройка
Прежде всего, нам нужно создать новый проект и настроить несколько конфигураций для начала. Если у вас уже есть проект, перейдите в раздел «Машинное обучение» ниже.
Создайте свой проект
Мы создадим базовое «пустое» приложение-шаблон, для него вам понадобится одна строка:
npx ionic start ml-pol blank --type=angular-standalone --capacitor --package-id=ml.pol
Установить пакеты
Теперь установим tasks-vision от MediaPipe на проект как зависимость:
npm i @mediapipe/tasks-vision
Поскольку мы работаем с проектом Typescript, вам необходимо предоставить типы для Offscreen Canvas, так как мы будем использовать их позже для правильной работы с MediaPipe:
npm i @types/offscreencanvas -D // analyze the source which uses offscreencanvas if you still having trouble: node_modules\@mediapipe\tasks-vision/vision.d.ts
Добавьте мобильную версию вашего приложения.
Вы можете продолжить здесь, создав версию приложения для Android, iOS или обеих версий. Я продолжу сборку версии Android для этого примера:
npm install @capacitor/[email protected] --save-exact npx cap add android && npx cap sync android
И, наконец, установите разрешение камеры в файле AndroidManifest.xml
.
<uses-permission android:name="android.permission.CAMERA" />
Поздравляем! 🎉 На данный момент у нас есть настройка с использованием Angular v15, Ionic v7, Capacitor v5 и Tasks-Vision v0.10.
Вы можете попробовать запустить свое приложение в Интернете или на Android! Чтобы протестировать его на Android, используйте Android Studio (эмулируемое устройство) или подключите телефон через USB в режиме отладки.
npx ionic build && npx cap copy android && npx cap run android --target=[your_mobile_id] --external --no-build --no-sync --configuration=production
Внедрение машинного обучения
Во втором разделе мы увидим, как создать экземпляр контекста видения задач и модель для использования распознавания лиц, но сначала — шаблон :)
Шаблон и стили
Сделаем несколько корректировок в шаблоне. Замените все, что находится внутри div#container
в домашнем компоненте, следующим фрагментом HTML:
<ion-button [color]="tracking ? 'danger' : 'primary'"> <ion-label class="ion-padding" (click)="toggleTracking()">Start Tracking</ion-label> <ion-icon [name]="tracking ? 'close' : 'finger-print'"></ion-icon> </ion-button> <video class="user-media" id="user-video" autoplay playsinline></video> <canvas class="user-media" id="user-canvas" ></canvas>
Обновите CSS с помощью этих правил:
#container{ /* comment these positioning rules generated by Ionic template top: 50%; transform: translateY(-50%); (...other default styles) */ display: flex; flex-direction: column; align-items: center; } .user-media{ min-height: 500px; max-width: 400px; margin-inline: auto; } #user-canvas{ position: absolute; /* Will overlay the video stream. */ top: 45px; /* Place it aprox. after the button space */ object-fit: contain; }
Теперь переключитесь на Typescript.
Импорт
Мы будем использовать следующий импорт из tasks-vision в компоненте, в котором мы хотим запустить распознавание лиц. В этом примере я размещу его на домашнем компоненте.
import { Category, DrawingUtils, FaceLandmarker, FilesetResolver } from '@mediapipe/tasks-vision'; // Category: a classification of a detected object or feature // DrawingUtils: utility functions for drawing on a canvas // FaceLandmarker: a machine learning model for facial landmark detection // FilesetResolver: a utility class for resolving file paths for ML models
Декларации
Теперь давайте создадим переменные, которые мы будем использовать.
// ML Model and properties (WASM & Model provided by Google, you can place your own). faceLandmarker!: FaceLandmarker; wasmUrl: string = "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"; modelAssetPath: string = "https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task"; // Native elements we need to interact to later. video!: HTMLVideoElement; canvasElement!: HTMLCanvasElement; canvasCtx!: CanvasRenderingContext2D; // A state to toggle functionality. showingPreview: boolean = false; // A challenge state for the user. userDidBlink: boolean = false;
Функции
Настройте экземпляр FaceLandmarker в хуке OnInit и получите держатели HTML после монтирования представления:
async ngOnInit(): Promise<void> { this.faceLandmarker = await FaceLandmarker.createFromOptions(await FilesetResolver.forVisionTasks(this.wasmUrl), { baseOptions: { modelAssetPath: this.modelAssetPath, delegate: "GPU" }, outputFaceBlendshapes: true, // We will draw the face mesh in canvas. runningMode: "VIDEO", }); // When FaceLandmarker is ready, you'll see in the console: Graph successfully started running. } async ngAfterViewInit(): Promise<void> { this.video = document.getElementById("user-video") as HTMLVideoElement; this.canvasElement = document.getElementById("user-canvas") as HTMLCanvasElement; this.canvasCtx = this.canvasElement.getContext("2d") as CanvasRenderingContext2D; }
Это событие переключения, срабатывающее при нажатии кнопки:
toggleTracking = () => (this.tracking = !this.tracking, this.tracking ? this.startTracking() : this.stopTracking());
И, наконец, функции startTracking
и stopTracking
:
startTracking() { // Check if we can access user media api. (!(!!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia)) || !this.faceLandmarker) && (console.warn("user media or ml model is not available"), false); // Everything is ready to go! navigator.mediaDevices.getUserMedia({ video: true }).then((stream) => (this.video.srcObject = stream, this.video.addEventListener("loadeddata", predictWebcam))); let lastVideoTime = -1; let results: any = undefined; const drawingUtils = new DrawingUtils(this.canvasCtx!); let predictWebcam = async () => { // Resize the canvas to match the video size. this.canvasElement.width = this.video.videoWidth; this.canvasElement.height = this.video.videoHeight; // Send the video frame to the model. lastVideoTime !== this.video.currentTime && (lastVideoTime = this.video.currentTime, results = this.faceLandmarker.detectForVideo(this.video, Date.now())); // Draw the results on the canvas (comment this out to improve performance or add even more markers like mouth, etc). if (results.faceLandmarks) for (const landmarks of results.faceLandmarks) { [FaceLandmarker.FACE_LANDMARKS_TESSELATION, FaceLandmarker.FACE_LANDMARKS_RIGHT_EYE, FaceLandmarker.FACE_LANDMARKS_LEFT_EYE] .every((type, i) => drawingUtils.drawConnectors(landmarks, type, { color: "#C0C0C070", lineWidth: i == 0 ? 1 : 4 })) }; // Check if the user blinked (you can customize this to expect a smile, etc). Let's assume there is only one face. if (results.faceLandmarks && results.faceBlendshapes && results.faceBlendshapes[0] && results.faceBlendshapes![0].categories?.find( (shape: Category) => shape?.categoryName == "eyeBlinkRight")?.score > 0.4) (this.userDidBlink = true, alert('Guiño Guiño')); // Call this function again to keep predicting when the browser is ready. this.tracking == true && window.requestAnimationFrame(predictWebcam); } } stopTracking() { // Stop and clear the video & canvas this.tracking = false; (this.video.srcObject as MediaStream).getTracks().forEach(track => track.stop()); this.video.srcObject = null; this.canvasCtx.clearRect(0, 0, this.canvasElement.width, this.canvasElement.height); }
Мы сделали! все готово для запуска и тестирования нашего приложения (веб-сайта или мобильного приложения), а также для его дальнейшей настройки по нашему вкусу.
🐱👤Вы можете проверить исходный код здесь: https://github.com/GiustiRo/ml-pol
Если вы любите технологии, рекомендую вам проверить все анонсы Google I/O 2023, там есть удивительные вещи!
Увидимся в следующий раз! Ро.