この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
1 はじめに
CX事業本部の平内(SIN)です。
Amazon Rekognition の Custom Labelsでは、独自のオブジェクトの検出が可能ですが、作成したモデルが、要件を満たしているかどうかを確認する作業があると思います。
この作業は、概ね以下のとおりとなりますが、Confidenceを調整したり、検出結果のサイズを検討したりする場合、3及び4の作業は、やや面倒になってしまいます。
- 評価画像の撮影
- モデルによるオブジェクト検出
- 評価結果の表示
- 評価結果の評価
今回は、この3,4の作業を効率化するための仕組みを作成してみました。
最初に、利用している様子です。Webカメラで色々撮影しながら、評価したいタイミングでボタンを押すと、その時点の画像をモデルで評価して可視化します。 また、Confidence及び、オブジェクトのサイズは、コントロールで変更が可能であり、色々を軽易に試すことができます。
なお、このアプリを利用して、先日、下記の記事を投稿させて頂きました。
[Amazon Rekognition] 部分画像の利用と、サイズのフィルタでCustom Labelsの検出精度が上げられることを確認してみました
2 detectCustomLabels
Custom Labesでのオブジェクト検出は、AWS SDK for JavaScript から Rekognition() - detectCustomLabels()を使用しています。
detectCustomLabels()では、評価対象の画像をS3のバケット若しくは、バイナリデータで指定できますが、ここでは、ブラウザからバイナリで直接送っています。
Rekognitionを扱っているコードは、下記のとおりとなっています。
class Rekognition {
constructor(model){
this.model = model
AWS.config.region = 'us-east-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx',
});
this.rekognition = new AWS.Rekognition();
console.log(this.rekognition)
}
async detectCustomLabels(buffer){
return new Promise((resolve, reject) => {
var params = {
Image: {
Bytes: buffer
},
ProjectVersionArn: this.model,
MinConfidence: 0
};
this.rekognition.detectCustomLabels(params, (err, data)=> {
if (err){
reject(err)
}
resolve(data);
});
})
}
}
3 OpenCV.js
Webカメラの画像を処理するために、今回は、OpenCVを使用しました。ここで、OpenCVは必須ではありませんが、今後の拡張を見据えて使用しています。
ブラウザ(JavaScript)から、OpenCVを利用するためには、OpenCV.jsが利用可能です。
https://docs.opencv.org/3.4/d5/d10/tutorial_js_root.html
下記のとおり、読み込むことで、利用が可能になります。
<script async src="https://docs.opencv.org/3.4.1/opencv.js" onload="main()"></script>
OpenCV.jsによる動画の取得と表示は、概ね以下の通りです。
<canvas id="output"></canvas>
<script>
// 任意のVideoデバイスを指定
const deviceIndex = 0;
const devices = await navigator.mediaDevices.enumerateDevices();
const videoDevices= devices.filter(
device => device.kind === 'videoinput'
);
// メディア(Video)の利用
navigator.mediaDevices.getUserMedia({
video: {
deviceId: videoDevices[deviceIndex].deviceId,
width: width,
height: height
},
audio: false
})
.then(function(s) {
video.srcObject = s;
video.play();
})
.catch(function(err) {
console.log("An error occured! " + err);
});
video.setAttribute("width", width);
video.setAttribute("height", height);
const vc = new cv.VideoCapture(video);
const src = new cv.Mat(height, width, cv.CV_8UC4);
while(true){
vc.read(src);
cv.imshow(name, src);
await sleep(100);
}
</script>
4 バイナリデータへの変換
OpenCVでは、画像をMatオブジェクトで管理していますが、Custom Labelsに送るために、これをバイナリデータに変換する必要があります。
バイナリデータは、作業用のコントロール(非表示)に描画してから、canvas.toDataURL()で取得しています。
// OpenCVの画像データからバイト配列を取得する
function getBytes(src){
var canvas = document.getElementById("tmpCanvas");
cv.imshow("tmpCanvas", src);
var type = 'image/jpeg';
var dataurl = canvas.toDataURL(type);
var bin = atob(dataurl.split(',')[1]);
var buffer = new Uint8Array(bin.length);
for (var i = 0; i < bin.length; i++) {
buffer[i] = bin.charCodeAt(i);
}
return buffer;
}
5 検出結果の描画
検出結果を描画しているコードです。 rangeタイプのコントロールで変更された値を元に、OpenCVで描画しています。
// 検出結果の描画
function dispDetection() {
let response = $("#response");
// 動画再生中
if(isRecoding){
response.html('');
return;
}
// テキスト表示
let text = '';
if(detectionResult && detectionResult.CustomLabels){
detectionResult.CustomLabels.forEach( label => {
if(label.Confidence > confidence){
text += `<br>Name:${label.Name} Confidence:${label.Confidence}`;
}
})
}
response.html(text);
// 静止画用の画像を複製する
if(disp != null){
delete disp;
}
disp = src.clone();
const color = new cv.Scalar(255, 255, 0);
if(detectionResult && detectionResult.CustomLabels){
detectionResult.CustomLabels.forEach( label => {
const name = label.Name;
const c = label.Confidence;
const box = label.Geometry.BoundingBox
const l = box.Left * width;
const t = box.Top * height;
const w = box.Width * width;
const h = box.Height * height;
if(c > confidence){
if(box.Height > min_h){
p1 = new cv.Point(x = l, y = t);
p2 = new cv.Point(x = l + w, y = t + h);
cv.rectangle(disp, p1, p2, color, width/200);
// p1 = new cv.Point(x = l, y = t-18)
// p2 = new cv.Point(x = l + 150, y = t);
p1 = new cv.Point(x = l+3, y = t-5);
const fontSize = height/200;
const fontWidth = height/200;
cv.putText(disp, name, p1, cv.FONT_HERSHEY_PLAIN, fontSize, color, fontWidth, cv.LINE_AA)
}
}
})
}
show("output", disp, magnification)
}
6 最後に
今回は、Amazon Rekognition Custom Labelsのモデルを評価する仕組みを作成してみました。
オブジェクト検出で精度を上げるためには、対象となる画像や、その利用方法を一定の型にはめることが有効だと思います。
モデルの評価と同時に、利用方法自体の評価も行う、このような仕組みは、有効ではないかと妄想しています。
全部のコードは、下記に置きました。