JCV社の顔属性SDKでカメラに写った人の年齢や性別などの属性情報を取得する_currentTrackingFace

Pocket

JCV社の顔属性SDKでカメラに写った人の年齢や性別などの属性情報を取得する:currentTrackingFace編

SDK紹介

JCV属性分析SDKはオフライン店舗における、顧客の行動分析の提供をコンセプトに開発されたSDKで、以下の3つのAPIが提供されています。本記事ではSDKの導入とcurrentTrackingFace APIについて記載します。

  1. currentTrackingFace
  • 現時点の画像を分析し、ユーザごとの顔に関する属性情報を取得できる
  • 取得できる主な属性情報
    • トラッキング顔ID(同一人物と判定された場合は同じ値となる)
    • 顔ID
    • 入退館時間
    • 年齢
    • 性別
    • 注視した時間
    • 表情
    • メガネ有無
    • マスク有無
    • 笑顔度
    • 魅力度
    • 顔画像のクオリティスコア
    • 検知した顔の座標情報
  1. facesOfPastTimeRange
  • 過去一定期間で分析されたユーザごとの顔に関する属性情報を取得できる
  • 取得できる主な属性情報
    • 顔ID
    • 検知した顔の画像
    • 顔画像のクオリティスコア
    • 年齢
    • 性別
    • 注視した時間
    • 表情
    • メガネ有無
    • マスク有無
  1. currentTrackingBody
  • 現時点の画像を分析し、ユーザごとの身体に関する属性情報を取得できる
  • 取得できる主な属性情報
    • トラッキング身体ID
    • 身体ID
    • 性別
    • 身体画像
    • 身体画像のクオリティスコア
    • 検知した身体の座標情報
    • トップスの色、形状など
    • ボトムスの色、形状など
    • 年齢層

画像解析で年齢や性別を分析するためのWebAPIはいろいろ公開されていますが、JCV属性分析SDKは画像解析が端末内で完結していることが大きな特徴です。ネットワーク接続が難しい状況での画像解析や、リアルタイムで画像解析したい場合に便利です。

一方で横顔の認識は苦手なので、画像を解析する人物がカメラに対して正対している状況ではスムーズに解析できますが、真横を向いているような状況だとうまく解析できないこともあります。

動作環境

Android向けのSDKは以下の環境で動作します

  • OS:Android7.1以上
  • CPU:RK3399相当以上
  • RAM:2GB以上

導入方法

現状でCameraX APIでの動作確認ができていないため、Camera APIを利用して顔属性SDKを利用してみます。

環境は下記のとおりです

  • Android Studio 4.0

新規アプリ作成

新しいプロジェクトを作成します。

screenshot1

JCV顔属性SDKは7.1以上が必要ですので7.1以上を選択します。ここではAndroid 7.1.1を選択しています。

screenshot2

ライセンス、設定ファイル、ライブラリの設定

ライセンスファイル(License.lic)と設定ファイル(device_en.cfg)を app/src/main/assets以下にコピーします。

screenshot3

次にライブラリをapp/libs以下にコピーします。

screenshot4

build.gradleの設定

app/build.gradleを編集します。

まずはrepositoriesにlibsを追加します。

android {
    // 中略
    repositories {
        flatDir {
            dirs 'libs'
        }
    }
}

次にdependenciesを追加します

dependencies {
    // 中略
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.6'
    implementation (name: 'ST SenseInsight Android service SDK -GE V2.0.2', ext: 'aar')
}

パーミッションの設定

以下のパーミッションをAndroidManifest.xmlに追加します

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • android.permission.INTERNET:ライセンス認証のためインターネット接続が必要です
  • android.permission.CAMERA:カメラのプレビュー画像を解析するためにはこのパーミッションが必要です
  • android.permission.READ_EXTERNAL_STORAGE/WRITE_EXTERNAL_STORAGE:SDK内でファイルの読み書きが行われるため必要です(解析した情報を端末内に保持しているようです)

また、オートフォーカスを実装する場合は以下のfeatureも追加する必要があります。

<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

SDKの初期化

SDKの初期化はアプリ起動時に1回だけ行います。少し初期化に時間がかかるため、スプラッシュ画面を表示している間に初期化を行うのがよいでしょう。初期化のサンプルコードは下記のとおりです。

    private fun initSdk() {
        // 設定ファイル
        val f =
            File(Environment.getDataDirectory(), "device_en.cfg")
        // パラメータ生成
        val param = SDKParams.builder()
            .isDebugMode(true)// trueにすると詳細なログが出力されます
            .context(requireContext().applicationContext)
            .licensePath("License.lic") // ライセンスファイル(assets以下に配置)
            .businessConfigPath(f.absolutePath)// 設定ファイルのパス
            .build()
        // SDK初期化
        InsightSDK.init(param, object : SDKInitCallback {
            override fun success() {
                // SDK初期化に成功→カメラ画面に遷移
                Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(
                    SplashFragmentDirections.actionSplashToCamera()
                )
            }

            override fun failed(i: Int, s: String) {
                // SDK初期化に失敗→アプリ終了
                Toast.makeText(context, "Failed init InsightSDK", Toast.LENGTH_LONG).show()
                requireActivity().finish()
            }
        })
    }

InsightSDK.initで設定するSDK初期化成功後のコールバック内でカメラ画面に遷移しています。

注意点としてSDK初期化の前にパーミッションの許可をもらう必要があります。パーミッションが許可されていることを確認してから上記SDKの初期化を行ってください。

属性情報を取得

属性情報を取得するためには、①解析する画像をSDKにわたす、②解析結果をSDKから受け取る、の2つのステップが必要です。ここではcurrentTrackingFace APIを利用して顔に関する属性情報を取得してみます。

まずはAndroidのCamera APIを利用してカメラプレビューを画面に表示させます。CameraX APIで属性を取得できなかったため古いCamera APIを利用します。

①解析する画像をSDKにわたす

val tmpBuffer1 = ByteArray(previewWidth * previewHeight * 3 / 2)
val tmpBuffer2 = ByteArray(previewWidth * previewHeight * 3 / 2)
val tmpBuffer3 = ByteArray(previewWidth * previewHeight * 3 / 2)
mCamera.addCallbackBuffer(tmpBuffer1)
mCamera.addCallbackBuffer(tmpBuffer2)
mCamera.addCallbackBuffer(tmpBuffer3)
val params = mCamera.parameters
params.setPreviewSize(previewWidth, previewHeight)
mCamera.parameters = params
mCamera.setPreviewCallbackWithBuffer { data, camera ->
	InsightSDK.service().offer(data)
  camera.addCallbackBuffer(data)
}

CameraのCallbackBufferにByteArrayを追加します。追加したByteArrayにプレビューデータが設定されてsetPreviewCallbackWithBufferで設定したコールバックメソッドが呼ばれるので、渡されたdataをSDKに渡します。

読み込んだByteArrayは再度CallbackBufferに戻して再利用します。

なお、本SDKで利用できるフォーマットはNV21フォーマットになります。Camera APIではNV21フォーマットで画像データが取得できますが、Camera2 API等利用時はNV21フォーマットに変換する必要があります。

②解析結果をSDKから受け取る

currentTrackingFace APIから顔に関する属性値を取得する実装の例は以下のとおりです。

scope.launch {
  while (!isRelease) {
    try {
      // 現在の顔情報を取得
      val trackingFaces =
     	 InsightSDK.service().currentTrackingFace()
      // 検出した顔に四角、属性を描画
      drawFaces(trackingFaces)
      SystemClock.sleep(interval)
    } catch (e: Exception) {
      e.printStackTrace()
    }
  }
}

属性はKotlinのオブジェクトとして返されます。SDK初期化中はnull、初期化後に認識できた顔情報がない場合は空のListが返されます。

取得したオブジェクトを画面に表示する

currentTrackingFaceからはTrackingFaceのリストが取得できます。

public class TrackingFace extends AbsAttr implements Serializable, Cloneable {
    private int trackingFaceId;
    private int faceId;
    private long enter;
    private long leave;
    private String gender;
    private byte[] faceFeature;
    private int age;
    private float quality;
    private StRect faceRect;
    private long watchTime;
    private String expression;
    private int glasses;
    private int mask;
    private int smileScore;
    private int charmScore;
    private StImage faceImage;
    private float faceImageQuality;
    private int beard;
    private int associatedBodyTrackID;
  // 中略
}

取得できる属性の詳細は下記のとおりです。

No. 物理名 説明
1 faceId 人が同じであれば同じfaceIdが設定される(ただし前回と同じfaceIdが割り振られるまで1秒弱かかる) 0,1,2,8,111等の整数値
2 trackingFaceId ユーザマージ前のID 整数値
3 enter チェックイン時刻(顔が認識された日時) 整数値
4 leave チェックアウト時刻(顔が認識された日時、ずっと顔がカメラに写っていると時刻が更新されていく。次回同じ人が写ってもまたカウントし直しで前回のleave時刻がもらえるわけではない) 整数値
5 gender 性別 M:男性;F:女性
6 age 年齢 整数値
7 quality 顔認識クオリティスコア 0〜1
8 watchTime 注視時間(ms) 整数値
9 expression 表情 文字列(calm, happy,others)
10 glasses メガネ 0:利用せず;1:メガネ;2:サングラ ス;-1:不明
11 mask マスク 0:no;1:yes;-1:不明
12 smileScore 笑顔スコア 整数値(73, 80…)
13 charmScore 魅力スコア 整数値(84,98…)
14 faceImageQuality クロップされた顔画像のクオリティスコア 0〜1
15 beard ひげ 0:no;1:yes;-1:不明
16 associateBodyTrackID 関連付けられたbody id 整数値

TrackingFaceのfaceRectを画面に描画すると以下のよう検出された顔部分に四角を表示できます。

face2
子供も認識できます。年齢の精度はそこまで高くはありませんが、子供ということは識別できそうです。

face1

マスクをしていても属性を取得できます。

face3

メガネやサングラスを掛けた人も検出できます。

face5

face4

ただ、横顔は苦手なようです。顔としては認識していますが属性は取得できません。

横顔の例は以下のとおりです。

face6

face7

faceIdが-1となっているデータはqualityが低く属性が取得できないデータです。

まとめ

以上、JCV社の顔属性SDKを利用して顔に関する属性情報を取得してみました。

currentTrackingFaceはサーバへの問い合わせが発生しないため高速に動作します。また1フレームいくらという課金体系でないためリアルタイムに属性情報を取り続けたいようなシーンに適用しやすいと感じました。1点気になるのは、端末によるかもしれませんがカメラを起動して属性情報を取得し続けることで端末の温度が上がってしまいアラートが上がる点です。長時間動作させたい場合端末温度が上がり続けるとシャットダウンしてしまうなど不具合が生じると思いますので、何らかの改善が必要かもしれません。

冒頭でも述べましたが、JCV属性分析SDKは画像解析が端末内で完結していることが大きな特徴です。ネットワーク接続が難しい状況での画像解析や、リアルタイムで画像解析したい場合に便利です。様々なシーンで活用できそうです。

Pocket

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です