今回は、TensorFlow.js の Body Segmentation を試していきたいと思います。コードは前回のものを引き続き使っていきます。
完成しているコードは下記にあります。
セグメンテーションとは
ピクセル単位で物体と物体とを分割する技術のことです。
今回の Body Segmentation は自撮りなどが前提で、カメラから 2m 以内の人物を対象に検出します。人物と背景を分割できるのでビデオ会議システムの背景エフェクトなどに使用できます。
環境
- node: 18.20.0
- Angular CLI:17.0.0
- TensorFlow.js:4.22.0
- TensorFlow-model / Body Segmentation:1.0.2
パッケージのインストール
$ npm install @tensorflow/tfjs
$ npm install @tensorflow-models/body-segmentation
tsconfig.json
に "skipLibCheck": true
を記載して型チェックをスキップさせるのを忘れないでください。
サービスの作成
$ ng generate service service/canvas
public async uploadImage(imgStr: string, maxWidth: number, canvas: HTMLCanvasElement): Promise<void> {
const img = new Image();
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
let scaleRaito: number = 1;
img.src = imgStr;
return new Promise((resolve, reject) => {
img.onload = () => {
let imageWidth = img.naturalWidth
if (imageWidth > maxWidth) {
imageWidth = maxWidth;
scaleRaito = imageWidth / img.naturalWidth;
}
canvas.width = imageWidth;
let imageHeight = img.naturalHeight * scaleRaito;
canvas.height = imageHeight;
resolve(ctx.drawImage(img, 0, 0, imageWidth, imageHeight));
};
img.onerror = (error) => {
console.log(error);
reject;
}
});
}
uploadImage
base64形式の画像を canvas に描画するためのメソッドです。
コンポーネントの作成
$ ng generate component component/page/body-segmentation
import { Component } from '@angular/core';
import * as tf from '@tensorflow/tfjs';
import * as bodySegmentation from '@tensorflow-models/body-segmentation';
import { ImageUploaderComponent } from '../../share/image-uploader/image-uploader.component';
import { LoadingSpinnerComponent } from '../../share/loading-spinner/loading-spinner.component';
import { LoadingSpinnerService } from '../../../service/loading-spinner.service';
import { CanvasService } from '../../../service/canvas.service';
@Component({
selector: 'app-body-segmentation',
standalone: true,
imports: [ImageUploaderComponent, LoadingSpinnerComponent],
templateUrl: './body-segmentation.component.html',
styleUrl: './body-segmentation.component.css'
})
export class BodySegmentationComponent {
private canvas!: HTMLCanvasElement;
private SEGMENTER_CONFIG = {
runtime: 'tfjs',
} as bodySegmentation.MediaPipeSelfieSegmentationTfjsModelConfig;
private segmenter!: bodySegmentation.BodySegmenter;
constructor(
private loadingSpinnerService: LoadingSpinnerService,
private canvasService: CanvasService
) { }
private async loadModel(): Promise<void> {
this.loadingSpinnerService.show();
await tf.ready();
const model = bodySegmentation.SupportedModels.MediaPipeSelfieSegmentation;
this.segmenter = await bodySegmentation.createSegmenter(model, this.SEGMENTER_CONFIG);
this.loadingSpinnerService.hide();
}
private async detect(canvas: HTMLCanvasElement): Promise<any> {
const predictions = await this.segmenter.segmentPeople(canvas);
return predictions;
}
public async startDetected(image: string): Promise<void> {
this.loadingSpinnerService.show();
const parent = this.canvas.parentElement as HTMLElement;
const width = parent.clientWidth;
const canvas = document.createElement('canvas') as HTMLCanvasElement;
await this.canvasService.uploadImage(image, width, canvas);
const results = await this.detect(canvas);
const foregroundColor = { r: 0, g: 0, b: 255, a: 255 };
const backgroundColor = { r: 0, g: 0, b: 0, a: 0 };
const backgroundDarkeningMask = await bodySegmentation.toBinaryMask(
results, foregroundColor, backgroundColor);
const opacity = 0.7;
const maskBlurAmount = 3;
const flipHorizontal = false;
await bodySegmentation.drawMask(
this.canvas, canvas, backgroundDarkeningMask, opacity, maskBlurAmount, flipHorizontal);
this.loadingSpinnerService.hide();
}
async ngOnInit() {
this.loadModel();
}
ngAfterViewInit() {
this.canvas = document.getElementById('myCanvas') as HTMLCanvasElement;
}
}
loadModel
モデルをロードします。
detect
セグメンテーションを実行します。
Body Segmentation の場合は返ってきた結果をそのまま canvas に描画するメソッドが準備されているので、そのメソッドを使用してセグメンテーションの結果を描画します。
bodySegmentation.toBinaryMask(segmentation: Segmentation | Segmentation[], foreground: Color, background: Color)
セグメンテーション結果の ImageData
を取得します。
segmentation
・・・segmentPeople
で取得したセグメンテーションの結果を渡します。foreground
・・・ マスク部分の色を rgba 形式で設定します。background
・・・ マスク部分以外の色を rgba 形式で設定します。
bodySegmentation.drawMask(canvas: Canvas, image: ImageType, maskImage: ImageData | null, maskOpacity?: number, maskBlurAmount?: number, flipHorizontal?: boolean)
canvas
に image
と maskImage
を重ねて描画します。
canvas
・・・ 描画する canvas を渡します。image
・・・ セグメンテーションに使用した元画像を渡します。maskImage
・・・bodySegmentation.toBinaryMask()
などで作成したセグメンテーション結果のImageData
を渡します。maskOpacity
・・・maskImage
の透過率を設定します。maskBlurAmount
・・・maskImage
のぼかしのピクセル数を 0 ~ 20 で設定します。flipHorizontal
・・・ 左右反転させるかを設定します。カメラの種類によっては左右反転が必要になります。
実行結果
元画像
https://pixabay.com/photos/beard-face-man-model-mustache-1845166/
実行結果

コメント