コンソールへ移動

ML Kit を使用してランドマークを認識する(Android)

ML Kit を使用すると、画像内にあるよく知られたランドマークを認識できます。

この API の使用例については、GitHub の ML Kit クイックスタート サンプルをご覧ください。

始める前に

  1. まだ Firebase を Android プロジェクトに追加していない場合は追加します。
  2. ML Kit Android ライブラリの依存関係をモジュール(アプリレベル)の Gradle ファイル(通常は「app/build.gradle」)に追加します。
    dependencies {
      // ...
    
      implementation 'com.google.firebase:firebase-ml-vision:19.0.3'
    }
    
  3. プロジェクトでクラウドベースの API をまだ有効にしていない場合は、ここで有効にします。

    1. Firebase コンソールの ML Kit API ページを開きます。
    2. まだプロジェクトを Blaze プランにアップグレードしていない場合は、[アップグレード] をクリックしてアップグレードします(プロジェクトをアップグレードするよう求められるのは、プロジェクトが Blaze プランでない場合のみです)。

      Blaze レベルのプロジェクトだけがクラウドベースの API を使用できます。

    3. クラウドベースの API がまだ有効になっていない場合は、[クラウドベースの API を有効化] をクリックします。

ランドマーク検出ツールを構成する

デフォルトでは、Cloud 検出ツールは STABLE バージョンのモデルを使用して、最大 10 件の結果を返します。この設定を変更したい場合には、FirebaseVisionCloudDetectorOptions オブジェクトを使用して設定し直します。

たとえば、デフォルト設定を両方とも変更するには、次の例のように FirebaseVisionCloudDetectorOptions オブジェクトをビルドします。

Java
Android

FirebaseVisionCloudDetectorOptions options =
        new FirebaseVisionCloudDetectorOptions.Builder()
                .setModelType(FirebaseVisionCloudDetectorOptions.LATEST_MODEL)
                .setMaxResults(15)
                .build();

Kotlin
Android

val options = FirebaseVisionCloudDetectorOptions.Builder()
        .setModelType(FirebaseVisionCloudDetectorOptions.LATEST_MODEL)
        .setMaxResults(15)
        .build()

デフォルト設定を使用するには、次の手順で FirebaseVisionCloudDetectorOptions.DEFAULT を使用します。

ランドマーク検出ツールを実行する

画像内のランドマークを認識するには、FirebaseVisionImage オブジェクトを Bitmapmedia.ImageByteBuffer、バイト配列、デバイス上のファイルのいずれかから作成します。次に、FirebaseVisionImage オブジェクトを FirebaseVisionCloudLandmarkDetectordetectInImage メソッドに渡します。

  1. 画像から FirebaseVisionImage オブジェクトを作成します。

    • FirebaseVisionImage オブジェクトを Bitmap オブジェクトから作成するコードは、以下のとおりです。

      Java
      Android

      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

      Kotlin
      Android

      val image = FirebaseVisionImage.fromBitmap(bitmap)
      Bitmap オブジェクトによって表される画像は、これ以上回転させる必要がないように、正しい向きになっている必要があります。
    • FirebaseVisionImage オブジェクトを media.Image オブジェクトから作成するには(デバイスのカメラから画像をキャプチャする場合など)、まずデバイスの回転と搭載されたカメラセンサーの向きの両方を補正するために必要な画像の回転角度を決定します。

      Java
      Android

      private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
      static {
          ORIENTATIONS.append(Surface.ROTATION_0, 90);
          ORIENTATIONS.append(Surface.ROTATION_90, 0);
          ORIENTATIONS.append(Surface.ROTATION_180, 270);
          ORIENTATIONS.append(Surface.ROTATION_270, 180);
      }
      
      /**
       * Get the angle by which an image must be rotated given the device's current
       * orientation.
       */
      @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
      private int getRotationCompensation(String cameraId, Activity activity, Context context)
              throws CameraAccessException {
          // Get the device's current rotation relative to its "native" orientation.
          // Then, from the ORIENTATIONS table, look up the angle the image must be
          // rotated to compensate for the device's rotation.
          int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
          int rotationCompensation = ORIENTATIONS.get(deviceRotation);
      
          // On most devices, the sensor orientation is 90 degrees, but for some
          // devices it is 270 degrees. For devices with a sensor orientation of
          // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees.
          CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE);
          int sensorOrientation = cameraManager
                  .getCameraCharacteristics(cameraId)
                  .get(CameraCharacteristics.SENSOR_ORIENTATION);
          rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360;
      
          // Return the corresponding FirebaseVisionImageMetadata rotation value.
          int result;
          switch (rotationCompensation) {
              case 0:
                  result = FirebaseVisionImageMetadata.ROTATION_0;
                  break;
              case 90:
                  result = FirebaseVisionImageMetadata.ROTATION_90;
                  break;
              case 180:
                  result = FirebaseVisionImageMetadata.ROTATION_180;
                  break;
              case 270:
                  result = FirebaseVisionImageMetadata.ROTATION_270;
                  break;
              default:
                  result = FirebaseVisionImageMetadata.ROTATION_0;
                  Log.e(TAG, "Bad rotation value: " + rotationCompensation);
          }
          return result;
      }

      Kotlin
      Android

      private val ORIENTATIONS = SparseIntArray()
      
      init {
          ORIENTATIONS.append(Surface.ROTATION_0, 90)
          ORIENTATIONS.append(Surface.ROTATION_90, 0)
          ORIENTATIONS.append(Surface.ROTATION_180, 270)
          ORIENTATIONS.append(Surface.ROTATION_270, 180)
      }/**
       * Get the angle by which an image must be rotated given the device's current
       * orientation.
       */
      @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
      @Throws(CameraAccessException::class)
      private fun getRotationCompensation(cameraId: String, activity: Activity, context: Context): Int {
          // Get the device's current rotation relative to its "native" orientation.
          // Then, from the ORIENTATIONS table, look up the angle the image must be
          // rotated to compensate for the device's rotation.
          val deviceRotation = activity.windowManager.defaultDisplay.rotation
          var rotationCompensation = ORIENTATIONS.get(deviceRotation)
      
          // On most devices, the sensor orientation is 90 degrees, but for some
          // devices it is 270 degrees. For devices with a sensor orientation of
          // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees.
          val cameraManager = context.getSystemService(CAMERA_SERVICE) as CameraManager
          val sensorOrientation = cameraManager
                  .getCameraCharacteristics(cameraId)
                  .get(CameraCharacteristics.SENSOR_ORIENTATION)!!
          rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360
      
          // Return the corresponding FirebaseVisionImageMetadata rotation value.
          val result: Int
          when (rotationCompensation) {
              0 -> result = FirebaseVisionImageMetadata.ROTATION_0
              90 -> result = FirebaseVisionImageMetadata.ROTATION_90
              180 -> result = FirebaseVisionImageMetadata.ROTATION_180
              270 -> result = FirebaseVisionImageMetadata.ROTATION_270
              else -> {
                  result = FirebaseVisionImageMetadata.ROTATION_0
                  Log.e(TAG, "Bad rotation value: $rotationCompensation")
              }
          }
          return result
      }

      次に、media.Image オブジェクトと回転値を FirebaseVisionImage.fromMediaImage() に渡します。

      Java
      Android

      FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);

      Kotlin
      Android

      val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
    • FirebaseVisionImage オブジェクトを ByteBuffer またはバイト配列から作成するには、前述のようにまず画像の回転を計算します。

      次に、画像の高さ、幅、カラー エンコーディング形式、回転を含む FirebaseVisionImageMetadata オブジェクトを作成します。

      Java
      Android

      FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
              .setWidth(480)   // 480x360 is typically sufficient for
              .setHeight(360)  // image recognition
              .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
              .setRotation(rotation)
              .build();

      Kotlin
      Android

      val metadata = FirebaseVisionImageMetadata.Builder()
              .setWidth(480) // 480x360 is typically sufficient for
              .setHeight(360) // image recognition
              .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
              .setRotation(rotation)
              .build()

      バッファまたは配列、およびメタデータ オブジェクトを使用して、FirebaseVisionImage オブジェクトを作成します。

      Java
      Android

      FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);// Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);

      Kotlin
      Android

      val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata)// Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
    • FirebaseVisionImage オブジェクトをファイルから作成するには、アプリケーション コンテキストとファイルの URI を FirebaseVisionImage.fromFilePath() に渡します。

      Java
      Android

      FirebaseVisionImage image;
      try {
          image = FirebaseVisionImage.fromFilePath(context, uri);
      } catch (IOException e) {
          e.printStackTrace();
      }

      Kotlin
      Android

      val image: FirebaseVisionImage
      try {
          image = FirebaseVisionImage.fromFilePath(context, uri)
      } catch (e: IOException) {
          e.printStackTrace()
      }

  2. FirebaseVisionCloudLandmarkDetector のインスタンスを取得します。

    Java
    Android

    FirebaseVisionCloudLandmarkDetector detector = FirebaseVision.getInstance()
            .getVisionCloudLandmarkDetector();
    // Or, to change the default settings:
    // FirebaseVisionCloudLandmarkDetector detector = FirebaseVision.getInstance()
    //         .getVisionCloudLandmarkDetector(options);

    Kotlin
    Android

    val detector = FirebaseVision.getInstance()
            .visionCloudLandmarkDetector
    // Or, to change the default settings:
    // val detector = FirebaseVision.getInstance()
    //         .getVisionCloudLandmarkDetector(options)
  3. 最後に、画像を detectInImage メソッドに渡します。

    Java
    Android

    Task<List<FirebaseVisionCloudLandmark>> result = detector.detectInImage(image)
            .addOnSuccessListener(new OnSuccessListener<List<FirebaseVisionCloudLandmark>>() {
                @Override
                public void onSuccess(List<FirebaseVisionCloudLandmark> firebaseVisionCloudLandmarks) {
                    // Task completed successfully
                    // ...
                }
            })
            .addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    // Task failed with an exception
                    // ...
                }
            });

    Kotlin
    Android

    val result = detector.detectInImage(image)
            .addOnSuccessListener { firebaseVisionCloudLandmarks ->
                // Task completed successfully
                // ...
            }
            .addOnFailureListener {
                // Task failed with an exception
                // ...
            }

認識されたランドマークに関する情報を取得する

ランドマーク認識オペレーションが成功すると、FirebaseVisionCloudLandmark オブジェクトのリストが成功リスナーに渡されます。各 FirebaseVisionCloudLandmark オブジェクトは画像内で認識されたランドマークを表します。ランドマークごとに、入力イメージの境界座標、ランドマーク名、緯度と経度、ナレッジグラフ エンティティの ID(使用できる場合)、一致の信頼スコアを取得できます。次に例を示します。

Java
Android

for (FirebaseVisionCloudLandmark landmark: firebaseVisionCloudLandmarks) {

    Rect bounds = landmark.getBoundingBox();
    String landmarkName = landmark.getLandmark();
    String entityId = landmark.getEntityId();
    float confidence = landmark.getConfidence();

    // Multiple locations are possible, e.g., the location of the depicted
    // landmark and the location the picture was taken.
    for (FirebaseVisionLatLng loc: landmark.getLocations()) {
        double latitude = loc.getLatitude();
        double longitude = loc.getLongitude();
    }
}

Kotlin
Android

for (landmark in firebaseVisionCloudLandmarks) {

    val bounds = landmark.boundingBox
    val landmarkName = landmark.landmark
    val entityId = landmark.entityId
    val confidence = landmark.confidence

    // Multiple locations are possible, e.g., the location of the depicted
    // landmark and the location the picture was taken.
    for (loc in landmark.locations) {
        val latitude = loc.latitude
        val longitude = loc.longitude
    }
}

次のステップ

Cloud API を使用するアプリを本番環境にデプロイする前に、不正な API アクセスを防いでその影響を軽減するために、いくつかの追加手順を行う必要があります。