日本コンピュータビジョン株式会社 (JCV) よりご提供いただいた 顔認証 SDK を使用して、顔認証アプリを実装し、その機能をご紹介します。
この SDK がどのようなものなのか、前提条件と環境構築手順は「前回の記事」をご覧ください。
どのようなアプリを作るか
前回の記事までで、
- VirtualBox 上の Ubuntu 環境に
- JDK と OpenCV をインストールし
- 顔認証 SDK のネイティブライブラリをインストール
しました。
このあと、どのような Java アプリを作っていくのか、整理します。作成するアプリは大きく2つになります。
1. 顔情報登録アプリ
顔認証処理を行うには、事前に認識したい人物の顔情報を知っておく必要があります。会員の本人確認を行うためには、予め会員証に顔写真が載っていないといけませんよね。それと同じことをします。
「顔情報登録アプリ」と題したこのアプリでは、顔写真を読み込むと SDK が 「顔情報」 を抽出し、これを JSON 形式のファイルに出力する、というものを作ります。
↑ このような「顔情報登録アプリ」を作っていきます。
顔認証 SDK は、画像や映像データから人の顔を特定し、その顔の特徴を 「特徴点」 として抽出します。目・鼻・口などの位置を点座標で保持するわけです。顔の特徴点のデータのことを、ここでは便宜的に「顔情報」と表現します。
この顔情報は Base64 文字列として保持されています。SDK を用いて同一人物かどうかを判定する際は、この「顔情報」を API に渡すことで、比較ができます。
すなわち、一度写真などから「顔情報」の文字列を取得してしまえば、以降の処理では 画像データは必要ない ということになります。永続化するデータとして画像ファイルや動画ファイルは必要なく、特徴を抽出した「顔情報」文字列のみで良いわけです。
2. 顔認証アプリ
「1. 顔情報登録アプリ」で顔情報を抽出し、JSON ファイルに保存しておきます。この JSON ファイルが「会員の顔情報データベース」の体を成すものになりますので、以降は「顔情報 DB」と表現します。
「顔認証アプリ」では、ウェブカメラからの映像をキャプチャし、映り込む人物の顔をトラッキング (追跡) します。そして顔情報 DB と比較し、映り込んでいるのが誰なのかを表示します。
↑ 「顔認証アプリ」の方はこのように実装する予定です。
この処理はリアルタイムにプレビューされ、特定した人物を枠線で囲って表示しますので、複数の人物がカメラに映り込んだ場合でも、どの人を誰と識別したかが分かるようになります。
上の画面イメージでは、右側に何やら設定項目がたくさんありますが、これは SDK が提供する 画質チェック機能 の調整項目です。どのようなチェックが行えるかは後述します。
今回は「1. 顔情報登録アプリ」を実装します
「事前登録」と「リアルタイム認証」で大きく2つのアプリを考えましたが、今回の記事では「1. 顔情報登録アプリ」を実装します。「2. 顔認証アプリ」については次回の記事でご紹介します。
Java プロジェクトを作成する
それでは実装に移ります。なお、ソースコードは実装の雰囲気が推測できる程度にしか掲載しません。IDE 用の設定や、OpenCV、JavaFX の説明なども省略します。おおよそこのような実装でアプリが作れるのだ、という参考程度にご覧ください。
Java プロジェクトは Gradle で作成しましょう。 $ gradle init
で雛形を作成し、顔認証 SDK、OpenCV、gson の JAR ファイルを依存ライブラリとして追加しておきます。
起動時の初期処理
「1. 顔情報登録アプリ」からお見せしましょう。エントリポイントから順に実装していきます。アプリ起動時に行う処理として、
- OpenCV ライブラリの準備
- SDK のライセンス認証
- JavaFX ウィンドウの表示
を行います。
OpenCV ライブラリの準備
OpenCV のネイティブライブラリを読み込むため、次の行を記述します。main()
メソッドの最初にでも行っておくと良いでしょう。
import org.opencv.core.Core;
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
SDK のライセンス認証
本 SDK は、初回利用時のみオンラインでのライセンス認証が必要になります。オンライン認証処理を呼び出す API が SDK に用意されているので、アプリ起動時にこの API をコールします。
すると「アクティベーションコード」と呼ばれる文字列が発行されますので、これをファイル等に退避しておきます。次回以降はこの「アクティベーションコード」を SDK に読み込ませることで、オフライン環境でもライセンス認証が行えます。
ライセンス認証を行わないと SDK が動作しませんのでご注意ください。また、アクティベーションコードにはハードウェア固有の情報が記録されているため、他のマシンでアクティベーションコードを使い回すことはできません。
ライセンス認証はおおよそ次のように行います。
// 顔認証 SDK のライブラリをインスタンス化する
final FaceproLibrary faceProLibrary = new FaceproLibrary();
if(Files.notExists(Paths.get("./activation_code.txt"))) {
// 「アクティベーションコード」を書き出したファイルが存在しない場合 : 初回としてオンライン認証を実行する
// 「ライセンス」ファイルを読み取る
final String licenseText = readFile(licenseFile);
// オンライン認証を行う (本メソッドを実行する際にインターネット環境が必要になります)
final StringResult onlineActivationResult = faceProLibrary.onlineActivateLicense(licenseText);
// オンライン認証が成功しているかどうか確認する
if(onlineActivationResult.getResultCode() != FaceproLibrary.STID_OK) throw new Exception("オンライン認証に失敗しました");
// オンライン認証に成功すると「アクティベーションコード」が発行される
final String activationCode = onlineActivationResult.getString();
// これをファイルに書き込出しておく
writeActivationCodeFile("./activation_code.txt", activationCode);
// 「アクティベーションコード」を基にライセンス認証を行う
final int result = faceProLibrary.loadLicenseContent(activationCode);
if(result != FaceproLibrary.STID_OK) throw new Exception("ライセンス認証に失敗しました");
}
else {
// 「アクティベーションコード」を書き出したファイルが存在する場合 : オンライン認証は省略し、ライセンス認証のみ実行する
// 「アクティベーションコード」ファイルを読み取る
final String activationCode = readFile("./activation_code.txt");
// 「アクティベーションコード」を基にライセンス認証を行う
final int result = faceProLibrary.loadLicenseContent(activationCode);
if(result != FaceproLibrary.STID_OK) throw new Exception("ライセンス認証に失敗しました");
}
以上のようにライセンス認証を行うと、インスタンス化した FaceproLibrary
クラスが提供する各種 API が使用できるようになります。
JavaFX ウィンドウの表示
OpenCV の準備と SDK のライセンス認証が済んだらアプリが使えますので、JavaFX ウィンドウを表示します。
- ウェブカメラを使って顔情報を登録したい人物を撮影しようと思いますので、JavaFX コントローラにはプレビュー表示のための
ImageView
を配置しておきます。 - 「撮影」ボタンなど、カメラを制御するためのボタンを配置します。
- 「顔情報」データと一緒に、氏名や備考を JSON に保存できるようにするため、入力フォームを設けておきます。
画面イメージとしては以下のようになります。
↑ JavaFX ウィンドウを開いたところ。
ウェブカメラのキャプチャ・撮影
続いて、ウェブカメラのキャプチャと撮影処理を実装してきます。OpenCV 公式が FXHelloCV というサンプルプロジェクトを公開しており、JavaFX と OpenCV の組み合わせでウェブカメラをキャプチャするサンプル実装が確認できます。
今回はこちらを参考に、
- ウェブカメラをキャプチャして
ImageView
に描画するループ処理 (Executors
を使用) - 「撮影」ボタン押下時に
Mat
イメージを取得する処理 (VideoCapture#read()
を使用)
を実装します。
Mat イメージを基に顔情報を抽出する
ここでようやく、顔認証 SDK の API が登場します。処理の流れとしては次のようになります。
Mat
イメージを SDK 独自形式の画像データに変換する- 画像に映り込んでいる人物の「顔の特徴点データ」を取得する (
detector()
メソッド) - 取得した「顔の特徴データ」を基に、Base64 文字列の「顔情報」を取得する (
getFeature()
メソッド)
1つ目の画像データ変換は、SDK に同梱されていたサンプル実装を拝借します。 StidImage
なる独自のクラスにデータを変換して格納します。
この StidImage
を基に、映り込んでいる人物の顔を探し出し、その特徴点を抽出します。 detector()
メソッドの呼び出し部分は次のような実装になります。
// Mat イメージを基に、独自形式の画像データへ変換する
final StidImage stidImage = convert(mat);
// 顔認識を行うための「ハンドル」と呼ばれるオブジェクトを生成する
HandleResult detectorHandle = faceProLibrary.createDetector("./models/M_Align_occlusion_106_1.6.4.model", FaceproLibrary.STID_FACEPRO_DETECTOR_CONFIG_LARGE_FACE | 1);
// 顔認識処理を行う
final DetectorResults detectorResults = faceProLibrary.detector(detectorHandle.getHandle(), stidImage, FaceproLibrary.STID_ORIENTATION_UP);
事前に、「ハンドル」と呼ばれるオブジェクトを生成しておく必要があります。SDK に同梱されている .model
ファイルを指定してインスタンスを生成しておき、これを顔認識処理 (detector()
メソッド) に渡しています。ネイティブライブラリと組み合わせるための都合と思われます。
変数 detectorResults
の中には、カメラが捉えた人物の顔の情報が含まれています。このデータを取り出して、Base64 形式の「顔情報」文字列を取得しましょう。
// 顔認識の結果から「顔の特徴点データ」を一つ取り出す
final Landmarks landmarks = detectorResults.getLandmarksResults().get(0);
// 顔情報取得用の「ハンドル」を生成する
final HandleResult featureExtractionHandle = faceProLibrary.featureExtractionCreateHandle("./models/M_Verify_Mobilenetv2Pruned_RGB_Surveillance_4.4.0.model");
// 「顔情報」を取得する
final String feature = faceProLibrary.getFeature(featureExtractionHandle.getHandle(), stidImage, landmarks);
顔認識と同じように、顔特徴の抽出にも「ハンドル」オブジェクトの生成が必要になります。このあたりは SDK に付属のドキュメントやサンプル実装を参考に、受け売りで実装していて大丈夫です。
変数 landmarks
内には、目・鼻・口などを示した点座標のデータが複数格納されています。これと画像データ (stidImage
) をセットにして getFeature()
メソッドを実行することで、Base64 文字列を得ています。
こうして抽出した文字列 (変数 fetaure
) と、フォームに入力された名前・備考情報を、JSON ファイルに書き込みます。JSON ファイルの扱いには gson という Google 製のライブラリを使いました。
「1. 顔情報登録アプリ」完成
以上のようにして、「顔情報登録アプリ」が完成しました。実際に使ってみましょう。
アプリを起動し、ウェブカメラで顔写真を撮影。
氏名と備考を入力して「登録」ボタンを押すと…
登録ができたようです。
JSON ファイルを見てみると、確かに各種情報が書き込まれています。
まとめ
JCV 社の顔認証 SDK を使用して、顔の特徴データを抽出する Java アプリを作成しました。
- SDK を使用するには、事前にライセンス認証が必要なことが確認できました
- 顔認識は
detector()
メソッド、顔情報抽出はgetFeature()
メソッドを呼び出すだけで簡単に実装できました
ここで抽出した顔情報 (Base64 文字列) を使用して、次回、ウェブカメラで捉えた人物と比較照合し、顔認証機能を実現していきます。