Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

Nhận ra các điểm mốc với ML Kit trên Android

Bạn có thể sử dụng ML Kit để nhận ra các địa danh nổi tiếng trong hình ảnh.

Trước khi bắt đầu

  1. Nếu bạn chưa sẵn sàng, thêm căn cứ hỏa lực cho dự án Android của bạn .
  2. Thêm sự phụ thuộc cho các thư viện ML Kit Android để mô-đun của bạn (ứng dụng cấp) tập tin Gradle (thường là app/build.gradle ):
    apply plugin: 'com.android.application'
    apply plugin: 'com.google.gms.google-services'
    
    dependencies {
      // ...
    
      implementation 'com.google.firebase:firebase-ml-vision:24.0.3'
    }
    
  3. Nếu bạn chưa bật API dựa trên đám mây cho dự án của mình, hãy làm như vậy ngay bây giờ:

    1. Mở trang ML Kit API của các firebase console.
    2. Nếu bạn chưa nâng cấp dự án của bạn sang gói giá Blaze, bấm Nâng cấp để làm như vậy. (Bạn sẽ chỉ được nhắc nâng cấp nếu dự án của bạn không nằm trong gói Blaze.)

      Chỉ các dự án cấp Blaze mới có thể sử dụng API dựa trên đám mây.

    3. Nếu API dựa trên đám mây chưa được kích hoạt, nhấp chuột Enable API dựa trên đám mây.

Định cấu hình máy dò mốc

Theo mặc định, các máy dò Cloud sử dụng STABLE phiên bản của mô hình và trả lên tới 10 kết quả. Nếu bạn muốn thay đổi một trong các thiết lập này, xác định chúng với một FirebaseVisionCloudDetectorOptions đối tượng.

Ví dụ, để thay đổi cả các thiết lập mặc định, xây dựng một FirebaseVisionCloudDetectorOptions đối tượng như trong ví dụ sau:

Java

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

Kotlin + KTX

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

Để sử dụng các thiết lập mặc định, bạn có thể sử dụng FirebaseVisionCloudDetectorOptions.DEFAULT trong bước tiếp theo.

Chạy máy dò mốc

Để nhận ra các địa danh tại một hình ảnh, tạo ra một FirebaseVisionImage đối tượng từ hoặc là một Bitmap , media.Image , ByteBuffer , mảng byte, hoặc một tập tin trên thiết bị. Sau đó, vượt qua FirebaseVisionImage đối tượng để các FirebaseVisionCloudLandmarkDetector 's detectInImage phương pháp.

  1. Tạo một FirebaseVisionImage đối tượng từ hình ảnh của bạn.

    • Để tạo một FirebaseVisionImage đối tượng từ một media.Image đối tượng, chẳng hạn như khi chụp một hình ảnh từ camera của thiết bị, vượt qua media.Image đối tượng và chuyển động quay của hình ảnh để FirebaseVisionImage.fromMediaImage() .

      Nếu bạn sử dụng CameraX thư viện, các OnImageCapturedListenerImageAnalysis.Analyzer lớp tính giá trị luân chuyển cho bạn, vì vậy bạn chỉ cần phải chuyển đổi luân chuyển đến một trong ML Kit của ROTATION_ hằng trước khi gọi FirebaseVisionImage.fromMediaImage() :

      Java

      private class YourAnalyzer implements ImageAnalysis.Analyzer {
      
          private int degreesToFirebaseRotation(int degrees) {
              switch (degrees) {
                  case 0:
                      return FirebaseVisionImageMetadata.ROTATION_0;
                  case 90:
                      return FirebaseVisionImageMetadata.ROTATION_90;
                  case 180:
                      return FirebaseVisionImageMetadata.ROTATION_180;
                  case 270:
                      return FirebaseVisionImageMetadata.ROTATION_270;
                  default:
                      throw new IllegalArgumentException(
                              "Rotation must be 0, 90, 180, or 270.");
              }
          }
      
          @Override
          public void analyze(ImageProxy imageProxy, int degrees) {
              if (imageProxy == null || imageProxy.getImage() == null) {
                  return;
              }
              Image mediaImage = imageProxy.getImage();
              int rotation = degreesToFirebaseRotation(degrees);
              FirebaseVisionImage image =
                      FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
              // Pass image to an ML Kit Vision API
              // ...
          }
      }
      

      Kotlin + KTX

      private class YourImageAnalyzer : ImageAnalysis.Analyzer {
          private fun degreesToFirebaseRotation(degrees: Int): Int = when(degrees) {
              0 -> FirebaseVisionImageMetadata.ROTATION_0
              90 -> FirebaseVisionImageMetadata.ROTATION_90
              180 -> FirebaseVisionImageMetadata.ROTATION_180
              270 -> FirebaseVisionImageMetadata.ROTATION_270
              else -> throw Exception("Rotation must be 0, 90, 180, or 270.")
          }
      
          override fun analyze(imageProxy: ImageProxy?, degrees: Int) {
              val mediaImage = imageProxy?.image
              val imageRotation = degreesToFirebaseRotation(degrees)
              if (mediaImage != null) {
                  val image = FirebaseVisionImage.fromMediaImage(mediaImage, imageRotation)
                  // Pass image to an ML Kit Vision API
                  // ...
              }
          }
      }
      

      Nếu bạn không sử dụng thư viện máy ảnh cung cấp cho bạn vòng quay của hình ảnh, bạn có thể tính toán nó từ vòng quay của thiết bị và hướng của cảm biến máy ảnh trong thiết bị:

      Java

      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 + KTX

      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
      }

      Sau đó, vượt qua media.Image đối tượng và giá trị luân chuyển để FirebaseVisionImage.fromMediaImage() :

      Java

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

      Kotlin + KTX

      val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
    • Để tạo một FirebaseVisionImage đối tượng từ một tập tin URI, vượt qua bối cảnh ứng dụng và tập tin URI để FirebaseVisionImage.fromFilePath() . Này rất hữu ích khi bạn sử dụng một ACTION_GET_CONTENT ý định để nhắc nhở người dùng lựa chọn một hình ảnh từ ứng dụng bộ sưu tập của họ.

      Java

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

      Kotlin + KTX

      val image: FirebaseVisionImage
      try {
          image = FirebaseVisionImage.fromFilePath(context, uri)
      } catch (e: IOException) {
          e.printStackTrace()
      }
    • Để tạo một FirebaseVisionImage đối tượng từ một ByteBuffer hoặc một mảng byte, đầu tiên tính toán xoay hình ảnh như mô tả ở trên cho media.Image đầu vào.

      Sau đó, tạo một FirebaseVisionImageMetadata đối tượng có chứa chiều cao của hình ảnh, chiều rộng, định dạng mã hóa màu sắc, và luân chuyển:

      Java

      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 + KTX

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

      Sử dụng bộ đệm hoặc mảng, và các đối tượng siêu dữ liệu, để tạo ra một FirebaseVisionImage đối tượng:

      Java

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

      Kotlin + KTX

      val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata)
      // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
    • Để tạo một FirebaseVisionImage đối tượng từ một Bitmap đối tượng:

      Java

      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

      Kotlin + KTX

      val image = FirebaseVisionImage.fromBitmap(bitmap)
      Hình ảnh đại diện bởi các Bitmap đối tượng phải thẳng đứng, không có luân chuyển bổ sung cần thiết.

  2. Nhận một thể hiện của FirebaseVisionCloudLandmarkDetector :

    Java

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

    Kotlin + KTX

    val detector = FirebaseVision.getInstance()
            .visionCloudLandmarkDetector
    // Or, to change the default settings:
    // val detector = FirebaseVision.getInstance()
    //         .getVisionCloudLandmarkDetector(options)
  3. Cuối cùng, vượt qua hình ảnh đến detectInImage phương pháp:

    Java

    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 + KTX

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

Nhận thông tin về các địa danh được công nhận

Nếu các hoạt động công nhận mang tính bước ngoặt thành công, một danh sách các FirebaseVisionCloudLandmark đối tượng sẽ được chuyển đến người nghe thành công. Mỗi FirebaseVisionCloudLandmark đối tượng đại diện cho một bước ngoặt đã được ghi nhận trong các hình ảnh. Đối với mỗi địa danh, bạn có thể nhận được tọa độ bounding của nó trong hình ảnh đầu vào, tên địa danh, vĩ độ và kinh độ của nó, nó thực ID Đồ thị tri thức (nếu có), và điểm số tín nhiệm của trận đấu. Ví dụ:

Java

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 + KTX

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
    }
}

Bước tiếp theo