Face++のAPIを使って、PHPで顔認証をする方法

Face++のAPIを使って、PHPで顔認証をする方法

今回は、画像の顔認識、類似度判断、目や鼻の場所、表情の種類(笑顔など)を判定・抽出できる「Face++」というサービスのAPIを利用する方法を解説します。サンプル言語は、PHPを利用します。

なお、過去にJavaScript(クライアントサイド側)を使った顔認識の方法を解説しています。顔認識をユーザー側の環境で実行してほしいケースでは、下記をご参考下さい。

サンプルデモ

Face++のAPIを利用して、画像の中の顔を認識するサンプルデモです。サンプル図では、左側の女性2人が認識され、各項目の値が下記の通り、算出されています。年齢、人種、笑顔の判定、そして顔の傾き、各パーツの位置まで、詳細な情報を取得できているのが分かると思います。左側の女性は「男性」と誤判定されているようですね。各値のパーセンテージは、画像の大きさに対する割合です。なお、サンプル画像は右記の素材を使用しています(morgueFile)。

1人目2人目
年齢37 (±10)12 (±5)
性別Male (63.8%)Female (99.7%)
眼鏡None (99.5%)None (99.9%)
人種White (95.6%)White (99.3%)
笑顔85.7%86.6%
上下の傾き-6.8度-10度
水平の傾き-0度27.9度
顔の中心x:19.2%
y:55.1%
x:46.8%
y:52.3%
顔の大きさwidth:19.3%
height:29.1%
width:14.5%
height:21.8%
左目の中心位置x:14.4%
y:50%
x:42%
y:49.1%
右目の中心位置x:14.4%
y:50%
x:42%
y:49.1%
鼻の中心位置x:19.2%
y:54.7%
x:47.7%
y:51.5%
口の左端の位置x:15%
y:63.1%
x:43.3%
y:58.5%
口の右端の位置x:24%
y:62%
x:49.7%
y:57.2%

準備(APIキーの取得)

Face++のAPIを使って顔認証をするための、まずは準備としてAPIキーを取得しましょう。

ユーザーアカウントの作成

はじめに言っておきますが、このウェブサイト、APIリクエスト時はそんなことはないですが、ユーザー画面のレスポンスがとても重たいです。紅茶でも飲みながら、気長に登録作業を進めましょう。…Face++のAPIを利用するには、ユーザーアカウントを取得する必要があります。まずは、下記ページでユーザーアカウントを作成して下さい。

アプリケーションの作成

「CREATE APP」をクリックする
「CREATE APP」をクリックする

続いて、APIを利用するためのアプリケーションを作成します。まずはログイン後、「CREATE APP」をクリックして下さい。

アプリの情報を入力する
アプリの情報を入力する

アプリの登録画面に移動します。サンプル図を参考に、アプリの情報を入力して下さい。「API Server」は、APIリクエストの送り先となるサーバーの場所だと思います。どちらを選択しても問題ないかと思います。「App Type」はアプリ(プログラム)の種類、「Platform」は、この記事通りにプログラミングするならWebを選択して下さい。全て完了したら「SUBMIT」をクリックします。クリックする前に、「App Infomation」の入力内容あたりをコピーしておきましょう。何故なら、重たいのでエラーになることが多いです…。その場合は頑張って作り直して下さい…。

API KeyとAPI Secret
API KeyとAPI Secret

無事、アプリケーションの作成に成功すると、アプリケーションの情報が表示されます。この中の「API Key」と「API Secret」を利用するので、他人に知られない形で保管して下さい。

プログラミング

APIキーを取得したら、早速、Face++にリクエストを送って、顔認証のデータを取得してみましょう!! 言語はPHPを使用するので、PHPが稼働する環境を用意して下さい。

エンドポイント

GET https://apius.faceplusplus.com/v2/detection/detect

パラメータ

リクエストの際に指定するパラメータは下記の通りです。api_keyapi_secreturl(またはimg)は必須です。

api_key
アプリケーション作成で取得した、API Key。
api_secret
アプリケーション作成で取得した、API Secret。
url
顔認識をしたい画像のURLアドレス。
img
URLアドレスじゃなく、直接アップロードしたい場合はバイナリデータをPOSTリクエストで送信する。urlとの併用は不可。
mode
onefaceを指定すると、画像の中の一番大きい顔のみを取得する。normal(デフォルト)の場合は、認識した人数分を取得する。
attribute
取得したい要素を指定する。複数指定する場合は半角カンマ(,)で区切る。指定しない場合は、pose以外の全要素を取得する。
glass … 眼鏡をかけているか?
pose … 顔の傾きの情報。
gender … 性別。
age … 年齢。
race … 人種。
smiling … 笑顔。

サンプルプログラム

URLを組み立てて、リクエストをするまでのプログラムは下記の通りです。リクエストが成功すると、$jsonに、返り値のJSONデータが代入されます。

PHP

<?php

	// 設定項目
	$params = array(
		'api_key' => '' ,		// APIキー
		'api_secret' => '' ,		// APIシークレット
		'url' => get_current_dir() . 'women.jpg' ,		// 画像のURLアドレス
		'attribute' => 'glass,pose,gender,age,race,smiling' ,		// 取得要素
	) ;

	// リクエストURL
	$request_url = 'https://apius.faceplusplus.com/v2/detection/detect' . '?' . http_build_query( $params ) ;

	//cURLを使ってリクエスト
	$curl = curl_init() ;
	curl_setopt( $curl , CURLOPT_URL , $request_url ) ;
	curl_setopt( $curl , CURLOPT_HEADER, 1 ) ; 
	curl_setopt( $curl , CURLOPT_SSL_VERIFYPEER , false ) ;
	curl_setopt( $curl , CURLOPT_RETURNTRANSFER , true ) ;
	curl_setopt( $curl , CURLOPT_TIMEOUT , 15 ) ;
	$res1 = curl_exec( $curl ) ;
	$res2 = curl_getinfo( $curl ) ;
	curl_close( $curl ) ;

	// 取得したデータを整理
	$json = substr( $res1, $res2['header_size'] ) ;
	$header = substr( $res1, 0, $res2['header_size'] ) ;		// レスポンスヘッダー (検証に利用したい場合にどうぞ)

	// HTML用
	$html = '' ;

	// リクエストしたデータ
	$html .= '<h2>リクエストした画像</h2>' ;
	$html .= '<p><img class="_img" src="' . $params['url'] . '"></p>' ;

	// 取得したデータ
	$html .= '<h2>取得したデータ</h2>' ;
	$html .= '<p>リクエストの結果、下記のデータを取得できました。</p>' ;
	$html .= 	'<h3>JSON</h3>' ;
	$html .= 	'<p><textarea rows="8">' . $json . '</textarea></p>' ;
	$html .= 	'<h3>レスポンスヘッダー</h3>' ;
	$html .= 	'<p><textarea rows="8">' . $header . '</textarea></p>' ;

	// 現在のディレクトリを[http]から取得する関数
	function get_current_dir()
	{
		$ary = explode( '/' , $_SERVER['REQUEST_URI'] ) ;
		$filename = $ary[ ( count( $ary ) - 1 ) ] ;
		return str_replace( $filename , '' , ( !isset($_SERVER['HTTPS']) || empty($_SERVER['HTTPS']) ? 'http://' : 'https://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) ;
	}

?>

<?php
	// ブラウザに[$html]を出力 (HTMLのヘッダーとフッターを付けましょう)
	echo $html ;
?>

このコードの動作を確認する

取得できるJSON

冒頭のサンプルで使った写真のJSONデータは次の通りです。faceプロパティに人数分のデータが配列で格納されています。また、それぞれのデータには、年齢や性別などを含むattributeと、顔、目、口などの位置情報を含むpositionプロパティで構成されています。face_idは、その顔情報のIDです。Face++では72時間、顔画像が保存されていて、このIDを利用して再度呼び出したりすることができます。72時間経過後、このIDは意味を持たなくなります。

JSON

{"face":[{"attribute":{"age":{"range":10,"value":37},"gender":{"confidence":63.7538,"value":"Male"},"glass":{"confidence":99.4947,"value":"None"},"pose":{"pitch_angle":{"value":0.000171},"roll_angle":{"value":-6.7773},"yaw_angle":{"value":-0.001961}},"race":{"confidence":95.5922,"value":"White"},"smiling":{"value":85.687}},"face_id":"43b66e30688f865c7493ce2802077f18","position":{"center":{"x":19.166667,"y":55.137845},"eye_left":{"x":14.3913,"y":49.976942},"eye_right":{"x":23.315,"y":48.382206},"height":29.072682,"mouth_left":{"x":14.9816,"y":63.088471},"mouth_right":{"x":24.0275,"y":62.025313},"nose":{"x":19.235167,"y":54.676441},"width":19.333333},"tag":""},{"attribute":{"age":{"range":5,"value":12},"gender":{"confidence":99.6509,"value":"Female"},"glass":{"confidence":99.9069,"value":"None"},"pose":{"pitch_angle":{"value":0.001542},"roll_angle":{"value":-9.98795},"yaw_angle":{"value":27.922053}},"race":{"confidence":99.2528,"value":"White"},"smiling":{"value":86.6312}},"face_id":"3ed5e97937f0a5a6ce09e62fbde61504","position":{"center":{"x":46.75,"y":52.255639},"eye_left":{"x":41.967,"y":49.105764},"eye_right":{"x":48.270833,"y":47.436341},"height":21.804511,"mouth_left":{"x":43.258,"y":58.50401},"mouth_right":{"x":49.699333,"y":57.172682},"nose":{"x":47.685333,"y":51.54787},"width":14.5},"tag":""}],"img_height":533,"img_id":"92f5f4fdb1d7cfc96df188130a495eed","img_width":800,"session_id":"d2445038c8984840b582b992b521ca05","url":"http:\/\/syncer.jp\/images\/DHFgXv5Rfe4d1Lej1lnQfuffZtzsj\/post\/api\/face-detect-01.jpg"}

各プロパティの説明

冒頭のサンプルで使った写真のJSONデータは次の通りです。faceプロパティに人数分のデータが配列で格納されています。また、それぞれのデータには、年齢や性別などを含むattributeと、顔、目、口などの位置情報を含むpositionプロパティで構成されています。face_idは、その顔情報のIDです。Face++では72時間、顔画像が保存されていて、このIDを利用して再度呼び出したりすることができます。72時間経過後、このIDは意味を持たなくなります。

age
年齢。valueは推定年齢、rangeは±の誤差範囲。
gender
性別。valueは推定性別、confidenceは確度。
glass
眼鏡をかけているか。valueは回答、confidenceは確度。
pose
顔の傾き。roll_angleは傾きの角度(クエスチョンの時に首をひねる動作)、yaw_angleは水平方向の角度(断る時に首を振る動作)、pitch_angleは垂直方向の角度(頷く動作)。
race
人種。Whiteは白人、Blackは黒人、Asiaは黄色人。
smiling
笑顔の具合、パーセンテージ。
center
顔の中心位置。画像の横幅、縦幅に対するパーセンテージ。
width
顔の横幅。画像の横幅に対するパーセンテージ。
height
顔の高さ。画像の縦幅に対するパーセンテージ。
eye_left
左目の中心位置。画像の横幅、縦幅に対するパーセンテージ。
eye_right
右目の中心位置。画像の横幅、縦幅に対するパーセンテージ。
mouth_left
口の左端の位置。画像の横幅、縦幅に対するパーセンテージ。
mouth_right
口の右端の位置。画像の横幅、縦幅に対するパーセンテージ。
nose
鼻の中心位置。画像の横幅、縦幅に対するパーセンテージ。

スタイルシートで枠線を重ねる

取得したJSONを活用するには、PHP以外に、スタイルシートやJavaScriptの知識が必要になってくるでしょう。

position

スタイルシートのpositionを利用することで、APIを通して取得した位置情報を元に、枠線などを画像に重ね合わせることが可能になります。これ以降の解説は、スタイルシートのpositionを正確に理解していることが前提です。これなしには、画像に枠線を重ねる仕組みを把握する努力は空回りしてしまいます。ええ、本当に。

ピクセルの実数値を求める計算式

positionを利用するには、まずは顔の中心位置のX座標、Y座標と、顔の横幅、縦幅のピクセル実数値が必要ですね。APIで取得した、位置や大きさを示す値は、全て、画像の大きさに対するパーセンテージとなっています。例えば、画像のサイズが500x250だったとします。xが20%で、yが50%だった場合、顔の中心位置は、左側から100px、上部から125pxだけ離れた位置にあるということになります。

つまり、画像サイズ * (値 / 100)の計算式で、X座標、Y座標の実数となるピクセル値を算出できることが分かると思います。さらに、レスポンシブデザインなどの場合、画像の大きさは本来よりも狭めて表示させている場合があるので、そのパーセンテージ(スケーリング)の値も考慮する必要がありますね。例えば、本来の半分のサイズでブラウザに表示させているなら、0.5を掛けないといけません。総合すると、次の通りです。

( 画像サイズ * (値 / 100) ) * スケーリング値

重ね合わせの例

画像の大きさ、顔の中心座標、横幅、高さ、それぞれのピクセル値が分かれば、後は、スタイルシートとJavaScriptを使って、画像に枠となるdiv要素を重ね合わせるだけですね。例えば下記は、APIで取得した位置情報のデータを元に、それぞれの顔全体に、div要素を重ね合わせたものです。

画像に枠を重ねる場合、positiontop値に顔の中心位置のX座標、left値に顔の中心位置のY座標を重ねるだけでは右下方向にズレてしまいます。何故なら、このままだと、ちょうど枠の左上部分が顔の中心となっているからです。top値、left値からは、それぞれ、枠要素(顔の大きさ)の半分のサイズをマイナスさせる必要がある点にご注意下さいね。

枠要素(顔の大きさ)が300x200の場合、例えば、顔の中心位置のX座標が500pxだったら、「500-(300/2)」で、350がtop値に指定するべき値です。Y座標が600pxだったら、「600-(200/2)」で、500がleft値に指定するべき値ですね。

そして、最後に何故、JavaScriptが必要なのかを説明しましょう。…それは、デスクトップ環境の人は、このウィンドウを狭めてみれば分かります。画像のサイズがレスポンシブデザインによって縮小されるため、枠の位置がズレてしまうんですね。要するに、ウィンドウの幅(画像の幅)に合わせて、動的に、top値やleft値などの値を指定する必要があるわけです。スマホ全盛は、色々な手間を運んでくれています。

Face++のAPIでは、顔の中心位置と大きさだけでなく、それぞれの目の位置、口の位置、そして鼻の位置も取得することができます。これらの情報を使えば、写真を自動でデコレーションするウェブサービスなどの実現も可能です。

サンプルプログラム

Face++のAPIで取得したJSONを引数に指定することで、顔写真に枠線を付けるJavaScript(syncerFaceDetect())の関数を作成してみました。よろしければ、色々な顔写真を指定して試してみて下さいね。

JavaScript

function syncerFaceDetect( obj )
{
	// ID
	var id = 'syncer-face-detect' ;

	// 枠線の色
	var color = '#D36015' ;

	// 画像の大きさ
	var img = {
		top: $( '#' + id ).offset().top - $( '#' + id ).parent().offset().top ,
		left: $( '#' + id ).offset().left - $( '#' + id ).parent().offset().left ,
		width: $( '#' + id ).width() ,
		height: $( '#' + id ).height() ,
	} ;

	// 変数の定義
	var data = obj.face , face = [] , attr_id , attr_class_1 = id + '-' , attr_class_2 = id + '-parts-' , position ;

	// 処理
	for( var i in data )
	{
		// エイリアス
		position = data[i].position ;

		// 顔のデータをまとめる
		face = {
			width: img.width * ( position.width / 100 ) ,
			height: img.height * ( position.height / 100 ) ,
			center: {
				x: img.width * ( position.center.x / 100 ) ,
				y: img.height * ( position.center.y / 100 ) ,
			} ,
			eye: {
				left: {
					x: img.width * ( position.eye_left.x / 100 ) ,
					y: img.height * ( position.eye_left.y / 100 ) ,
				},
				right: {
					x: img.width * ( position.eye_right.x / 100 ) ,
					y: img.height * ( position.eye_right.y / 100 ) ,
				}
			},
			mouth: {
				left: {
					x: img.width * ( position.mouth_left.x / 100 ) ,
					y: img.height * ( position.mouth_left.y / 100 ) ,
				},
				right: {
					x: img.width * ( position.mouth_right.x / 100 ) ,
					y: img.height * ( position.mouth_right.y / 100 ) ,
				}
			},
			nose: {
				x: img.width * ( position.nose.x / 100 ) ,
				y: img.height * ( position.nose.y / 100 ) ,
			},
		} ;

		// 各パーツの値をセット
		var parts = [ face.eye.left , face.eye.right , face.mouth.left , face.mouth.right , face.nose ] ;

		// 顔のIDを定義
		attr_id = attr_class_1 + i ;

		// 顔全体に枠線のHTMLを付ける
		$( '#' + id ).after( '<div id="' + attr_id + '"><b>' + ( Number( i ) + 1 ) + '人目</b></div>' ) ;

		// スタイルシートを適用
		$( '#' + attr_id ).css( {
			position: 'absolute' ,
			width: face.width ,
			height: face.height ,
			top: img.top + face.center.y - ( face.height / 2 ) ,
			left: img.left + face.center.x - ( face.width / 2 ) ,
			background: 'rgba( 255,255,255 , .2 )' ,
			border: '3px solid ' + color ,
			color: '#030303' ,
		} ) ;

		// 各パーツの枠線を配置していく
		for( var ii=0 , ll=parts.length ; ll > ii ; ii++ )
		{
			// IDの決定
			attr_id = attr_class_2 + i + '-' + ii ;

			// 枠線のHTMLを追加する
			$( '#' + id ).after( '<div id="' + attr_id + '"></div>' ) ;

			// HTMLにスタイルシートを適用
			$( '#' + attr_id ).css( {
				position: 'absolute' ,
				width: 10 ,
				height: 10 ,
				top: img.top + parts[ ii ].y - 5 ,
				left: img.left + parts[ ii ].x - 5 ,
				background: color ,
			} ) ;
		}
	}

}

次のように、ID属性値(デフォルトではsyncer-face-detect)を付け、widthheightを指定したimg要素を配置して下さい。親要素には、position:relativeを指定する必要があります。

HTML

<div style="position:relative; top:0; left:0; overflow:hidden;">
	<img src="" id="syncer-face-detect" width="" height="">
</div>

このコードの動作を確認する

ダウンロード

API経由で取得した位置情報と、スタイルシートのposition、そしてJavaScript(jQuery)を利用して、画像内の顔に枠線、目、鼻、口に点を重ねるサンプルプログラムを配布します。よろしければ、プログラミングの参考にお使い下さい。なお、画像は再配布が許可されているものを利用しています。(morgueFile)

ファイル一覧

SYNCER00061
detect-request.php Download
detect-and-style.php Download
detect.js Download
women.jpg Download
man.jpg Download
girls.jpg Download
group.jpg Download

ファイル名をクリックすると内容を確認できます。「Download Zip」をクリックするとファイル一式をダウンロードできます。

Download Zip