開発環境
- Debian 7.8(仮想マシン)
- PHP 5.4.45-0+deb7u4 (cli)
手順
1.Cloud Vision APIの使い方まとめ (サンプルコード付き) を参考に、APIキーを取得する。
2.下記スクリプトをcloudVisionDemo.phpというファイル名で保存する。APIキーだけは取得したキーに書き換える。
<?php
# cloudVisionDemo.php
// APIキー
$api_key = "APIキー";
// 画像へのパス
$image_path = $argv[1];
// Feature Type
$feature = $argv[2];
// パラメータ設定
$param = array("requests" => array());
$item["image"] = array("content" => base64_encode(file_get_contents($image_path)));
$item["features"] = array(array("type" => $feature, "maxResults" => 3));
$param["requests"][] = $item;
// リクエスト用のJSONを作成
$json = json_encode($param);
// リクエストを実行
$curl = curl_init() ;
curl_setopt($curl, CURLOPT_URL, "https://vision.googleapis.com/v1/images:annotate?key=" . $api_key);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-Type: application/json"));
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_TIMEOUT, 15);
curl_setopt($curl, CURLOPT_POSTFIELDS, $json);
$res1 = curl_exec($curl);
$res2 = curl_getinfo($curl);
curl_close($curl);
// 取得したデータ
$json = substr($res1, $res2["header_size"]);
$array = json_decode($json, true);
// 出力
var_dump($array);
3.cloudVisionDemo.phpと同じ階層に分類したい画像ファイルを保存する。
4.スクリプトの第一引数にファイル名、第二引数に検出方式を指定する。下記は、image.pngというファイルのカテゴリ検出をしたい場合の例。
$ php cloudVisionDemo.php image.png LABEL_DETECTION
実験
カテゴリ検出
- グラス
$ php cloudVisionDemo.php 'water.jpg' LABEL_DETECTION
array(1) {
["responses"]=>
array(1) {
[0]=>
array(1) {
["labelAnnotations"]=>
array(3) {
[0]=>
array(3) {
["mid"]=>
string(9) "/m/04shl0"
["description"]=>
string(18) "distilled beverage"
["score"]=>
float(0.9329527)
}
[1]=>
array(3) {
["mid"]=>
string(8) "/m/039jq"
["description"]=>
string(5) "glass"
["score"]=>
float(0.78407258)
}
[2]=>
array(3) {
["mid"]=>
string(8) "/m/012mj"
["description"]=>
string(18) "alcoholic beverage"
["score"]=>
float(0.75640213)
}
}
}
}
}
蒸留酒、グラス、酒と認識
- りんご
$ php cloudVisionDemo.php 'apple.jpg' LABEL_DETECTION
array(1) {
["responses"]=>
array(1) {
[0]=>
array(1) {
["labelAnnotations"]=>
array(3) {
[0]=>
array(3) {
["mid"]=>
string(9) "/m/036qh8"
["description"]=>
string(7) "produce"
["score"]=>
float(0.98105073)
}
[1]=>
array(3) {
["mid"]=>
string(8) "/m/05s2s"
["description"]=>
string(5) "plant"
["score"]=>
float(0.93924618)
}
[2]=>
array(3) {
["mid"]=>
string(8) "/m/02xwb"
["description"]=>
string(5) "fruit"
["score"]=>
float(0.936089)
}
}
}
}
}
作物、植物、果物と認識
- コーヒーカップ
RaspberryPi3にカメラモジュールつけて撮影した画像をDeepLearningで分類してみた
にて、コーヒーカップとして分類された画像
$ php cloudVisionDemo.php 'camera5.png' LABEL_DETECTION
array(1) {
["responses"]=>
array(1) {
[0]=>
array(1) {
["labelAnnotations"]=>
array(3) {
[0]=>
array(3) {
["mid"]=>
string(8) "/m/07yv9"
["description"]=>
string(7) "vehicle"
["score"]=>
float(0.80549806)
}
[1]=>
array(3) {
["mid"]=>
string(8) "/m/057gn"
["description"]=>
string(7) "minivan"
["score"]=>
float(0.77592212)
}
[2]=>
array(3) {
["mid"]=>
string(9) "/m/017z18"
["description"]=>
string(15) "small appliance"
["score"]=>
float(0.62339073)
}
}
}
}
}
車両、ミニバン、小型電化製品と認識
スコア0.8以上でこのレベルか・・・最初から「ミッションクリティカルなシステムには使えないと思うし、今後その方向で頑張っていくつもりもないよ」って白旗上げてるだけのことはある。
OCR
次にOCRを試してみた。
- コワーキングスペースWakU2
$ php cloudVisionDemo.php 'waku2.png' TEXT_DETECTION
array(1) {
["responses"]=>
array(1) {
[0]=>
array(1) {
["textAnnotations"]=>
array(5) {
[0]=>
array(3) {
["locale"]=>
string(2) "ja"
["description"]=>
string(36) "コワーキングスペースWakua
"
["boundingPoly"]=>
array(1) {
["vertices"]=>
array(4) {
[0]=>
array(2) {
["x"]=>
int(43)
["y"]=>
int(42)
}
[1]=>
array(2) {
["x"]=>
int(464)
["y"]=>
int(42)
}
[2]=>
array(2) {
["x"]=>
int(464)
["y"]=>
int(71)
}
[3]=>
array(2) {
["x"]=>
int(43)
["y"]=>
int(71)
}
}
}
}
[1]=>
array(2) {
["description"]=>
string(3) "コ"
["boundingPoly"]=>
array(1) {
["vertices"]=>
array(4) {
[0]=>
array(2) {
["x"]=>
int(43)
["y"]=>
int(46)
}
[1]=>
array(2) {
["x"]=>
int(65)
["y"]=>
int(46)
}
[2]=>
array(2) {
["x"]=>
int(65)
["y"]=>
int(68)
}
[3]=>
array(2) {
["x"]=>
int(43)
["y"]=>
int(68)
}
}
}
}
[2]=>
array(2) {
["description"]=>
string(15) "ワーキング"
["boundingPoly"]=>
array(1) {
["vertices"]=>
array(4) {
[0]=>
array(2) {
["x"]=>
int(76)
["y"]=>
int(42)
}
[1]=>
array(2) {
["x"]=>
int(230)
["y"]=>
int(42)
}
[2]=>
array(2) {
["x"]=>
int(230)
["y"]=>
int(71)
}
[3]=>
array(2) {
["x"]=>
int(76)
["y"]=>
int(71)
}
}
}
}
[3]=>
array(2) {
["description"]=>
string(12) "スペース"
["boundingPoly"]=>
array(1) {
["vertices"]=>
array(4) {
[0]=>
array(2) {
["x"]=>
int(233)
["y"]=>
int(45)
}
[1]=>
array(2) {
["x"]=>
int(354)
["y"]=>
int(45)
}
[2]=>
array(2) {
["x"]=>
int(354)
["y"]=>
int(69)
}
[3]=>
array(2) {
["x"]=>
int(233)
["y"]=>
int(69)
}
}
}
}
[4]=>
array(2) {
["description"]=>
string(5) "Wakua"
["boundingPoly"]=>
array(1) {
["vertices"]=>
array(4) {
[0]=>
array(2) {
["x"]=>
int(358)
["y"]=>
int(45)
}
[1]=>
array(2) {
["x"]=>
int(464)
["y"]=>
int(45)
}
[2]=>
array(2) {
["x"]=>
int(464)
["y"]=>
int(70)
}
[3]=>
array(2) {
["x"]=>
int(358)
["y"]=>
int(70)
}
}
}
}
}
}
}
}
検索ボタンがQとされてるのは百歩譲るが、「コワーキングスペースWakua」・・・(#^ω^)ピキピキ
- コワーキングスペースWakU2(2を全角にしてみた)
$ php cloudVisionDemo.php 'waku2_2.png' TEXT_DETECTION
array(1) {
["responses"]=>
array(1) {
[0]=>
array(1) {
["textAnnotations"]=>
array(7) {
[0]=>
array(3) {
["locale"]=>
string(2) "ja"
["description"]=>
string(39) "コワーキングスペースWakU 2
Q
"
["boundingPoly"]=>
array(1) {
["vertices"]=>
array(4) {
[0]=>
array(2) {
["x"]=>
int(44)
["y"]=>
int(34)
}
[1]=>
array(2) {
["x"]=>
int(1248)
["y"]=>
int(34)
}
[2]=>
array(2) {
["x"]=>
int(1248)
["y"]=>
int(70)
}
[3]=>
array(2) {
["x"]=>
int(44)
["y"]=>
int(70)
}
}
}
}
[1]=>
array(2) {
["description"]=>
string(3) "コ"
["boundingPoly"]=>
array(1) {
["vertices"]=>
array(4) {
[0]=>
array(2) {
["x"]=>
int(44)
["y"]=>
int(39)
}
[1]=>
array(2) {
["x"]=>
int(65)
["y"]=>
int(39)
}
[2]=>
array(2) {
["x"]=>
int(65)
["y"]=>
int(62)
}
[3]=>
array(2) {
["x"]=>
int(44)
["y"]=>
int(62)
}
}
}
}
[2]=>
array(2) {
["description"]=>
string(15) "ワーキング"
["boundingPoly"]=>
array(1) {
["vertices"]=>
array(4) {
[0]=>
array(2) {
["x"]=>
int(76)
["y"]=>
int(35)
}
[1]=>
array(2) {
["x"]=>
int(231)
["y"]=>
int(35)
}
[2]=>
array(2) {
["x"]=>
int(231)
["y"]=>
int(64)
}
[3]=>
array(2) {
["x"]=>
int(76)
["y"]=>
int(64)
}
}
}
}
[3]=>
array(2) {
["description"]=>
string(12) "スペース"
["boundingPoly"]=>
array(1) {
["vertices"]=>
array(4) {
[0]=>
array(2) {
["x"]=>
int(234)
["y"]=>
int(38)
}
[1]=>
array(2) {
["x"]=>
int(355)
["y"]=>
int(38)
}
[2]=>
array(2) {
["x"]=>
int(355)
["y"]=>
int(62)
}
[3]=>
array(2) {
["x"]=>
int(234)
["y"]=>
int(62)
}
}
}
}
[4]=>
array(2) {
["description"]=>
string(4) "WakU"
["boundingPoly"]=>
array(1) {
["vertices"]=>
array(4) {
[0]=>
array(2) {
["x"]=>
int(358)
["y"]=>
int(39)
}
[1]=>
array(2) {
["x"]=>
int(441)
["y"]=>
int(39)
}
[2]=>
array(2) {
["x"]=>
int(441)
["y"]=>
int(63)
}
[3]=>
array(2) {
["x"]=>
int(358)
["y"]=>
int(63)
}
}
}
}
[5]=>
array(2) {
["description"]=>
string(1) "2"
["boundingPoly"]=>
array(1) {
["vertices"]=>
array(4) {
[0]=>
array(2) {
["x"]=>
int(451)
["y"]=>
int(37)
}
[1]=>
array(2) {
["x"]=>
int(469)
["y"]=>
int(37)
}
[2]=>
array(2) {
["x"]=>
int(469)
["y"]=>
int(63)
}
[3]=>
array(2) {
["x"]=>
int(451)
["y"]=>
int(63)
}
}
}
}
[6]=>
array(2) {
["description"]=>
string(1) "Q"
["boundingPoly"]=>
array(1) {
["vertices"]=>
array(4) {
[0]=>
array(2) {
["x"]=>
int(1218)
["y"]=>
int(35)
}
[1]=>
array(2) {
["x"]=>
int(1246)
["y"]=>
int(34)
}
[2]=>
array(2) {
["x"]=>
int(1248)
["y"]=>
int(69)
}
[3]=>
array(2) {
["x"]=>
int(1220)
["y"]=>
int(70)
}
}
}
}
}
}
}
}
「コワーキングスペースWakU 2」
スペース入っちゃったけど、Uの大文字までちゃんと読めてるのはなかなか。
顔認識
次に顔認識をやってみた。
- 自分のfacebook写真
$ php cloudVisionDemo.php 'http://1.gravatar.com/avatar/73683d9774cbe57766ed47b52ec83539?s=160&d=mm&r=g' FACE_DETECTION
array(1) {
["responses"]=>
array(1) {
[0]=>
array(1) {
["faceAnnotations"]=>
array(1) {
[0]=>
array(15) {
["boundingPoly"]=>
array(1) {
["vertices"]=>
array(4) {
[0]=>
array(2) {
["x"]=>
int(28)
["y"]=>
int(34)
}
[1]=>
array(2) {
["x"]=>
int(122)
["y"]=>
int(34)
}
[2]=>
array(2) {
["x"]=>
int(122)
["y"]=>
int(143)
}
[3]=>
array(2) {
["x"]=>
int(28)
["y"]=>
int(143)
}
}
}
["fdBoundingPoly"]=>
array(1) {
["vertices"]=>
array(4) {
[0]=>
array(2) {
["x"]=>
int(40)
["y"]=>
int(62)
}
[1]=>
array(2) {
["x"]=>
int(103)
["y"]=>
int(62)
}
[2]=>
array(2) {
["x"]=>
int(103)
["y"]=>
int(125)
}
[3]=>
array(2) {
["x"]=>
int(40)
["y"]=>
int(125)
}
}
}
["landmarks"]=>
array(34) {
[0]=>
array(2) {
["type"]=>
string(8) "LEFT_EYE"
["position"]=>
array(3) {
["x"]=>
float(63.979912)
["y"]=>
float(81.75769)
["z"]=>
float(4.1787585E-5)
}
}
[1]=>
array(2) {
["type"]=>
string(9) "RIGHT_EYE"
["position"]=>
array(3) {
["x"]=>
float(90.976967)
["y"]=>
float(83.886482)
["z"]=>
float(1.8890204)
}
}
[2]=>
array(2) {
["type"]=>
string(20) "LEFT_OF_LEFT_EYEBROW"
["position"]=>
array(3) {
["x"]=>
float(54.393)
["y"]=>
float(74.171753)
["z"]=>
float(2.1800623)
}
}
[3]=>
array(2) {
["type"]=>
string(21) "RIGHT_OF_LEFT_EYEBROW"
["position"]=>
array(3) {
["x"]=>
float(71.9689)
["y"]=>
float(75.312912)
["z"]=>
float(-4.8891692)
}
}
[4]=>
array(2) {
["type"]=>
string(21) "LEFT_OF_RIGHT_EYEBROW"
["position"]=>
array(3) {
["x"]=>
float(85.924217)
["y"]=>
float(76.534096)
["z"]=>
float(-3.9212897)
}
}
[5]=>
array(2) {
["type"]=>
string(22) "RIGHT_OF_RIGHT_EYEBROW"
["position"]=>
array(3) {
["x"]=>
float(102.28504)
["y"]=>
float(78.370239)
["z"]=>
float(5.4902554)
}
}
[6]=>
array(2) {
["type"]=>
string(21) "MIDPOINT_BETWEEN_EYES"
["position"]=>
array(3) {
["x"]=>
float(78.483757)
["y"]=>
float(82.0245)
["z"]=>
float(-5.0081019)
}
}
[7]=>
array(2) {
["type"]=>
string(8) "NOSE_TIP"
["position"]=>
array(3) {
["x"]=>
float(77.280457)
["y"]=>
float(100.00699)
["z"]=>
float(-14.250449)
}
}
[8]=>
array(2) {
["type"]=>
string(9) "UPPER_LIP"
["position"]=>
array(3) {
["x"]=>
float(76.004669)
["y"]=>
float(109.30354)
["z"]=>
float(-7.6722078)
}
}
[9]=>
array(2) {
["type"]=>
string(9) "LOWER_LIP"
["position"]=>
array(3) {
["x"]=>
float(75.299316)
["y"]=>
float(118.34825)
["z"]=>
float(-5.9648252)
}
}
[10]=>
array(2) {
["type"]=>
string(10) "MOUTH_LEFT"
["position"]=>
array(3) {
["x"]=>
float(64.95462)
["y"]=>
float(111.50229)
["z"]=>
float(-1.0042747)
}
}
[11]=>
array(2) {
["type"]=>
string(11) "MOUTH_RIGHT"
["position"]=>
array(3) {
["x"]=>
float(85.673538)
["y"]=>
float(113.70555)
["z"]=>
float(0.44637543)
}
}
[12]=>
array(2) {
["type"]=>
string(12) "MOUTH_CENTER"
["position"]=>
array(3) {
["x"]=>
float(75.651222)
["y"]=>
float(113.09444)
["z"]=>
float(-5.7008615)
}
}
[13]=>
array(2) {
["type"]=>
string(17) "NOSE_BOTTOM_RIGHT"
["position"]=>
array(3) {
["x"]=>
float(84.2441)
["y"]=>
float(101.86198)
["z"]=>
float(-2.5993559)
}
}
[14]=>
array(2) {
["type"]=>
string(16) "NOSE_BOTTOM_LEFT"
["position"]=>
array(3) {
["x"]=>
float(68.973358)
["y"]=>
float(100.56936)
["z"]=>
float(-3.6547837)
}
}
[15]=>
array(2) {
["type"]=>
string(18) "NOSE_BOTTOM_CENTER"
["position"]=>
array(3) {
["x"]=>
float(76.659081)
["y"]=>
float(104.23148)
["z"]=>
float(-7.5894523)
}
}
[16]=>
array(2) {
["type"]=>
string(21) "LEFT_EYE_TOP_BOUNDARY"
["position"]=>
array(3) {
["x"]=>
float(63.90242)
["y"]=>
float(79.692146)
["z"]=>
float(-1.8653158)
}
}
[17]=>
array(2) {
["type"]=>
string(21) "LEFT_EYE_RIGHT_CORNER"
["position"]=>
array(3) {
["x"]=>
float(69.408012)
["y"]=>
float(82.212387)
["z"]=>
float(0.49274716)
}
}
[18]=>
array(2) {
["type"]=>
string(24) "LEFT_EYE_BOTTOM_BOUNDARY"
["position"]=>
array(3) {
["x"]=>
float(63.567158)
["y"]=>
float(83.6236)
["z"]=>
float(-0.31856096)
}
}
[19]=>
array(2) {
["type"]=>
string(20) "LEFT_EYE_LEFT_CORNER"
["position"]=>
array(3) {
["x"]=>
float(57.7246)
["y"]=>
float(81.390335)
["z"]=>
float(2.2203603)
}
}
[20]=>
array(2) {
["type"]=>
string(14) "LEFT_EYE_PUPIL"
["position"]=>
array(3) {
["x"]=>
float(63.19186)
["y"]=>
float(81.6635)
["z"]=>
float(-0.72718245)
}
}
[21]=>
array(2) {
["type"]=>
string(22) "RIGHT_EYE_TOP_BOUNDARY"
["position"]=>
array(3) {
["x"]=>
float(92.604721)
["y"]=>
float(82.194939)
["z"]=>
float(0.12301356)
}
}
[22]=>
array(2) {
["type"]=>
string(22) "RIGHT_EYE_RIGHT_CORNER"
["position"]=>
array(3) {
["x"]=>
float(97.745613)
["y"]=>
float(84.879196)
["z"]=>
float(4.9835973)
}
}
[23]=>
array(2) {
["type"]=>
string(25) "RIGHT_EYE_BOTTOM_BOUNDARY"
["position"]=>
array(3) {
["x"]=>
float(91.944443)
["y"]=>
float(86.011841)
["z"]=>
float(1.6645448)
}
}
[24]=>
array(2) {
["type"]=>
string(21) "RIGHT_EYE_LEFT_CORNER"
["position"]=>
array(3) {
["x"]=>
float(86.695686)
["y"]=>
float(83.624924)
["z"]=>
float(1.7033099)
}
}
[25]=>
array(2) {
["type"]=>
string(15) "RIGHT_EYE_PUPIL"
["position"]=>
array(3) {
["x"]=>
float(92.668434)
["y"]=>
float(84.239929)
["z"]=>
float(1.2963727)
}
}
[26]=>
array(2) {
["type"]=>
string(27) "LEFT_EYEBROW_UPPER_MIDPOINT"
["position"]=>
array(3) {
["x"]=>
float(63.508991)
["y"]=>
float(71.190506)
["z"]=>
float(-3.1210399)
}
}
[27]=>
array(2) {
["type"]=>
string(28) "RIGHT_EYEBROW_UPPER_MIDPOINT"
["position"]=>
array(3) {
["x"]=>
float(94.656624)
["y"]=>
float(73.9012)
["z"]=>
float(-0.95416707)
}
}
[28]=>
array(2) {
["type"]=>
string(16) "LEFT_EAR_TRAGION"
["position"]=>
array(3) {
["x"]=>
float(43.44656)
["y"]=>
float(96.239311)
["z"]=>
float(32.42128)
}
}
[29]=>
array(2) {
["type"]=>
string(17) "RIGHT_EAR_TRAGION"
["position"]=>
array(3) {
["x"]=>
float(106.64991)
["y"]=>
float(101.18263)
["z"]=>
float(37.983433)
}
}
[30]=>
array(2) {
["type"]=>
string(17) "FOREHEAD_GLABELLA"
["position"]=>
array(3) {
["x"]=>
float(79.049408)
["y"]=>
float(75.546112)
["z"]=>
float(-5.4698133)
}
}
[31]=>
array(2) {
["type"]=>
string(13) "CHIN_GNATHION"
["position"]=>
array(3) {
["x"]=>
float(73.93647)
["y"]=>
float(131.90903)
["z"]=>
float(-2.2904294)
}
}
[32]=>
array(2) {
["type"]=>
string(16) "CHIN_LEFT_GONION"
["position"]=>
array(3) {
["x"]=>
float(44.158802)
["y"]=>
float(113.82098)
["z"]=>
float(21.304802)
}
}
[33]=>
array(2) {
["type"]=>
string(17) "CHIN_RIGHT_GONION"
["position"]=>
array(3) {
["x"]=>
float(102.82076)
["y"]=>
float(119.06871)
["z"]=>
float(25.30686)
}
}
}
["rollAngle"]=>
float(4.8305407)
["panAngle"]=>
float(3.9433053)
["tiltAngle"]=>
float(2.0712221)
["detectionConfidence"]=>
float(0.62428904)
["landmarkingConfidence"]=>
float(0.41789523)
["joyLikelihood"]=>
string(13) "VERY_UNLIKELY"
["sorrowLikelihood"]=>
string(13) "VERY_UNLIKELY"
["angerLikelihood"]=>
string(13) "VERY_UNLIKELY"
["surpriseLikelihood"]=>
string(13) "VERY_UNLIKELY"
["underExposedLikelihood"]=>
string(13) "VERY_UNLIKELY"
["blurredLikelihood"]=>
string(13) "VERY_UNLIKELY"
["headwearLikelihood"]=>
string(13) "VERY_UNLIKELY"
}
}
}
}
}
下の方に表情から感情認識してる部分もあります。
["joyLikelihood"]=>
string(13) "VERY_UNLIKELY"
["sorrowLikelihood"]=>
string(13) "VERY_UNLIKELY"
["angerLikelihood"]=>
string(13) "VERY_UNLIKELY"
["surpriseLikelihood"]=>
string(13) "VERY_UNLIKELY"
["underExposedLikelihood"]=>
string(13) "VERY_UNLIKELY"
["blurredLikelihood"]=>
string(13) "VERY_UNLIKELY"
["headwearLikelihood"]=>
string(13) "VERY_UNLIKELY"
これはすげぇ。
顔を傾けてる角度とか、左目の瞳孔とか、右眉毛の右端とか、めちゃめちゃ細かいパラメータまで返してくれる。
あと、表情分析みたいなこともできて、笑顔の人の写真とか認識すると、["joyLikelihood"]が「VERY_LIKELY」になったりする。
他にも
・ランドマーク検出(LANDMARK_DETECTION)
・ロゴ検出(LOGO_DETECTION)
・有害な画像検出(SAFE_SEARCH_DETECTION)
・色解析(IMAGE_PROPERTIES)
などがあって、色々かけてみると結構楽しめる。
注意
「表情判定でいろんな結果出したくて、延々と色んな顔して自撮りしまくる」という奇行を他人に見られる危険性があります。
参考
・Cloud Vision APIの使い方まとめ (サンプルコード付き)
・Google Cloud Visionを使ってなんか作ってみた話