[Amazon Rekognition] Custom Labelsの検出結果を効率的に評価する
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のモデルを評価する仕組みを作成してみました。
オブジェクト検出で精度を上げるためには、対象となる画像や、その利用方法を一定の型にはめることが有効だと思います。
モデルの評価と同時に、利用方法自体の評価も行う、このような仕組みは、有効ではないかと妄想しています。
全部のコードは、下記に置きました。