AIを使って写真から撮影場所を特定するアプリを作ってみたので紹介します?
写真から撮影場所を特定するアプリ《AI写真診断》を作ったので紹介します。
1.AI写真診断はこんなアプリです
AI写真診断はこんなアプリです
AIが貴方の思い出を辿るお手伝いをします。AIフォトメモリーは写真を解析し何処で撮ったものか判定します。
AI写真診断の使い方
画面上部のエリアをタップして写真を選択するだけ。
AIが写真の撮られた場所を判定してくれます。
AI写真診断は場所以外のことも判定できます。
AIの写真判定の正解率
試してみたところAIの画像判定の正解率は80%以上です。
GoogleさんのAI(Google Cloud Vision)を使用しているので結構精度は良いです。
【Google Cloud Vision】
https://travel5.hateblo.jp/entry/2019/08/18/102246
2.AI写真診断のインストール
https://play.google.com/store/apps/details?id=net.zive.shibayu.aiformemorial
3.AI写真診断の活用方法
AI写真診断の活用方1.思い出を辿る
気分転換に昔の写真を見てみるのも面白いです。
大抵の場所は思い出せると思いますが、色んな名所を旅行していると、あれ?ここの地名・オブジェの名前って何だっけ?と思うこともあると思います。
そんな時は、AIフォトメモリーで地名・オブジェの名前を調べられます。
AI写真診断の活用方2.気分転換
AIフォトメモリーをインストールしてみて下さい。
何となくアプリをポチポチしたくなるので、昔の写真を見るキッカケになり気分転換になります。
4.AI写真診断の開発で苦労した点
画像解析はGoogle Cloud VisionのAPIを使ったので割と簡単に出来ました。
Google Cloud VisionのAPIの使い方は本家Googleのサイトで紹介されているので、大概の事は苦労せずに実装出来ました。
https://cloud.google.com/vision/docs/quickstart-client-libraries?hl=ja
しかし、AndroidでGoogle Cloud Vision APIを使うのに若干クセはありました。
APIキーの設定
本家のチュートリアルではAPIキーを環境変数に設定すると記載されています。
あれ?スマホ端末それぞれに環境変数を設定するなんてできなくない??
調べてみたら、Android端末かあらAPIを実行する場合はRequestInitializerでAPIキーをリクエストに埋め込む必要がありました。
◆ 実装サンプル
VisionRequestInitializer requestInitializer = new VisionRequestInitializer(CLOUD_API_KEY) {
@Override
protected void initializeVisionRequest(VisionRequest> visionRequest){
}
};
~ 中略 ~
HttpTransport httpTransport = AndroidHttp.newCompatibleTransport();
GsonFactory gsonFactory = GsonFactory.getDefaultInstance();
Vision.Builder builder = new Vision.Builder(httpTransport, gsonFactory, null);
builder.setVisionRequestInitializer(requestInitializer);
Vision vision = builder.build();
APIキーの保護
APIキーをサーバーサイドに配置すれば外部に漏れることはありませんが、今回はスタンドアロンのAndroidアプリケーションにしたのでサーバーがありません。APIキーが盗まれないか怖いなと調べていたら、特定のアプリからしかAPIキーの使用を許可しない方法がありました。
https://cloud.google.com/docs/authentication/api-keys?hl=JA&visit_id=637041958471291492-1952535614&rd=1
Google Cloud Vision APIの検索結果が日本語に非対応
Google Cloud Vision APIの検索結果は英語で返却されます。日本語では返却されません。
調べてみても返却結果を日本語にするオプションは無かったので、返却結果をGoogle Translate APIで翻訳しました。
◆ 実装サンプル
String encText = targetText;
try{
encText = URLEncoder.encode(targetText, "UTF-8");
}catch(UnsupportedEncodingException e){
}
String baseUrl = "https://www.googleapis.com/language/translate/v2?key=" + CLOUD_API_KEY;
String srcLang = "&source=en";
String targetLang = "&target=ja";
String transChar = "&q=" + encText;
String url = baseUrl + srcLang + targetLang + transChar;
new HttpRequestor(
this,
url,
getResources().getString(R.string.message_search2),
(c, b) -> {
String transStr = new String(b);
Matcher m = TRANSLATED_PATTERN.matcher(transStr);
if (m.find()){
String matchstr = m.group(1);
addDescription(String.format(message, matchstr), matchstr);
}
},
(c, e) -> {
Toast.makeText(c , getResources().getString(R.string.message_api_error), Toast.LENGTH_SHORT).show();
}
).execute();
※ HttpRequestorはぴよ猫の自作ユーティリティです。詳しくは↓↓↓の記事を参照ください。
写真の正位置の判定
写真が傾いているとGoogle Cloud Visionが正しく判定出来ませんでした。
最初、ピラミッドの画像を読み込ませたら、結果が「Angle(角度)」となってしましました。
あってはいるけど・・・、Google Cloud Visionはこの程度なのかーと思っちゃいましたが、実は、写真が傾いていることが原因でした。
写真の正位置はExifInterfaceという機能で判定出来ます。
Exifとは、デジタルカメラで撮影した画像データに、撮影条件に関する情報(メタデータ)を追加して保存できる、画像ファイル形式の規格のことで、Exifでは撮影した画像データと併せて、撮影した日時やデジタルカメラの機種、絞り値、画素数、ISO感度、色空間、といった情報がまとめて記録されています。
◆ 実装サンプル
public static Bitmap autoRotate(final Uri uri, Context context){
int degree = 0;
File file = new File(uri.getPath());
String filePath = uri.getPath().replace("file://", "");
InputStream in = null;
try {
in = context.getContentResolver().openInputStream(uri);
ExifInterface exifInterface = new ExifInterface(in);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
degree = 90;
} else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
degree = 180;
} else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
degree = 270;
}
if (degree != 0) {
exifInterface.setAttribute(ExifInterface.TAG_ORIENTATION, "0");
exifInterface.saveAttributes();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(in != null) {
try {
in.close();
} catch (IOException ignored) {}
}
}
Bitmap bitmap = null;
Bitmap rotatedImage = null;
try {
Matrix mat = new Matrix();
mat.postRotate(degree);
bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri);
rotatedImage = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), mat, true);
return rotatedImage;
} catch (Exception ex) {
return null;
} finally {
}
}
それではまたー?