在安卓上使用ML Kit识别地标

您可以使用ML kit来识别图片中的著名地标。

有关此API使用的示例,请参阅GitHub上的ML Kit快速入门示例。

在您开始之前

  1. 如果您还没有将Firebase添加到您的程序当中,那您可以从开始指南来开始您的工作。

  2. 在app-level的build.gradle 文件中为ML kit添加依赖:

    1. dependencies {
    2. // ...
    3. implementation 'com.google.firebase:firebase-ml-vision:15.0.0'
    4. }
  3. 如果您想使用基于云的模型,并且尚未将项目升级到Blaze计划,请在Firebase控制台中执行此操作。只有Blaze计划的项目才能使用Cloud Vision API。

  4. 如果您想使用基于云的模型,您也需要开启Cloud Vision API:

    • 在云API列表管理平台中打开Cloud Vision API
    • 确保您的Firebase项目已经在当前菜单页面中被置于顶端。
    • 如果API依旧还是显示为enabled,请点击Enable。

配置地标识别器

默认情况下,云识别器使用STABLE版本的模型并返回多达10个结果。如果您想要更改这些设置中的任何一个,请使用下例中的 FirebaseVisionCloudDetectorOptions 对象指定它们。

例如,要更改这两个默认设置,请按照以下示例构建一个FirebaseVisionCloudDetectorOptions 对象:

  1. FirebaseVisionCloudDetectorOptions options =
  2. new FirebaseVisionCloudDetectorOptions.Builder()
  3. .setModelType(FirebaseVisionCloudDetectorOptions.LATEST_MODEL)
  4. .setMaxResults(15)
  5. .build();

要使用默认设置,可以在下一步中使用FirebaseVisionCloudDetectorOptions.DEFAULT

运行地标识别器

为了进行地标识别,从任一个Bitmapmedia.ImageByteBuffer,字节阵列,或在设备上的文件中创建一个FirebaseVisionImage对象。然后,传递FirebaseVisionImage对象到 FirebaseVisionCloudLandmarkDetectordetectInImage方法。

  1. 从您的图像中创建一个 FirebaseVisionImage 对象。

    • Bitmap对象创建FirebaseVisionImage对象:

      1. FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

      Bitmap 对象指示的图像必须是直立的,不能旋转。

    • 要从 media.Image对象创建FirebaseVisionImage对象(例如从设备的相机捕捉图像时),首先要确定图像必须旋转的角度,以补偿设备的旋转和相机传感器在设备中的方向:

      1. private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
      2. static {
      3. ORIENTATIONS.append(Surface.ROTATION_0, 90);
      4. ORIENTATIONS.append(Surface.ROTATION_90, 0);
      5. ORIENTATIONS.append(Surface.ROTATION_180, 270);
      6. ORIENTATIONS.append(Surface.ROTATION_270, 180);
      7. }
      8. /**
      9. * 得到当前图像需要补偿的角度
      10. */
      11. @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
      12. private int getRotationCompensation(String cameraId, Activity activity, Context context)
      13. throws CameraAccessException {
      14. // 得到设备当前与原始的角度的旋转差值
      15. // 然后照片一定要旋转回去相对的差值
      16. int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
      17. int rotationCompensation = ORIENTATIONS.get(deviceRotation);
      18. // 在大多数的设备上,传感器的方向是90度。但是对于
      19. // 少数设备,这个值是270度。那么对于这些270度的设备
      20. // 必须让照片旋转额外的180 ((270 + 270) % 360) 度.
      21. CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE);
      22. int sensorOrientation = cameraManager
      23. .getCameraCharacteristics(cameraId)
      24. .get(CameraCharacteristics.SENSOR_ORIENTATION);
      25. rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360;
      26. // 返回相关的FirebaseVisionImageMetadata rotation值。
      27. int result;
      28. switch (rotationCompensation) {
      29. case 0:
      30. result = FirebaseVisionImageMetadata.ROTATION_0;
      31. break;
      32. case 90:
      33. result = FirebaseVisionImageMetadata.ROTATION_90;
      34. break;
      35. case 180:
      36. result = FirebaseVisionImageMetadata.ROTATION_180;
      37. break;
      38. case 270:
      39. result = FirebaseVisionImageMetadata.ROTATION_270;
      40. break;
      41. default:
      42. result = FirebaseVisionImageMetadata.ROTATION_0;
      43. Log.e(TAG, "Bad rotation value: " + rotationCompensation);
      44. }
      45. return result;
      46. }

      然后,将media.Image对象和旋转值传递给FirebaseVisionImage.fromMediaImage()

      1. FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
    • 要从一个字节数组或ByteBuffer创建一个FirebaseVisionImage对象,首先按照上面的描述计算图像旋转角度。

      然后,创建一个包含图像高度,宽度,颜色编码格式和旋转度的FirebaseVisionImageMetadata对象:

      1. FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
      2. .setWidth(1280)
      3. .setHeight(720)
      4. .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
      5. .setRotation(rotation)
      6. .build();

      使用缓冲区或数组以及元数据对象来创建一个 FirebaseVisionImage对象:

      1. FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
      2. // Or:
      3. // FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
    • 要从文件创建FirebaseVisionImage对象,请将应用context和文件URI传递给FirebaseVisionImage.fromFilePath()

      1. FirebaseVisionImage image;
      2. try {
      3. image = FirebaseVisionImage.fromFilePath(context, uri);
      4. } catch (IOException e) {
      5. e.printStackTrace();
      6. }
  2. 得到一个 FirebaseVisionCloudLandmarkDetector 的实例:

    1. FirebaseVisionCloudLandmarkDetector detector = FirebaseVision.getInstance()
    2. .getVisionCloudLandmarkDetector();
    3. // 或者,您可以使用默认设定:
    4. // FirebaseVisionCloudLandmarkDetector detector = FirebaseVision.getInstance()
    5. // .getVisionCloudLandmarkDetector(options);
  3. 最后传递图片到 detectInImage 方法:

    1. Task<List<FirebaseVisionCloudLandmark>> result = detector.detectInImage(image)
    2. .addOnSuccessListener(new OnSuccessListener<List<FirebaseVisionCloudLandmark>>() {
    3. @Override
    4. public void onSuccess(List<FirebaseVisionCloudLandmark> firebaseVisionCloudLandmarks) {
    5. // 任务成功
    6. // ...
    7. }
    8. })
    9. .addOnFailureListener(new OnFailureListener() {
    10. @Override
    11. public void onFailure(@NonNull Exception e) {
    12. // 任务失败并且报异常
    13. // ...
    14. }
    15. });

获取著名地标的有关信息

如果地标识别操作成功,FirebaseVisionCloudLandmark对象列表 将传递给成功侦听器(success listener)。每个FirebaseVisionCloudLandmark对象代表在图像中识别的地标。对于每个地标,您可以输入图像,得到它的图像中的坐标,地标名称,经度和纬度,知识图谱实体ID (如果可用)以及匹配的置信度得分。例如:

  1. for (FirebaseVisionCloudLandmark landmark: firebaseVisionCloudLandmarks) {
  2. Rect bounds = landmark.getBoundingBox();
  3. String landmarkName = landmark.getLandmark();
  4. String entityId = landmark.getEntityId();
  5. float confidence = landmark.getConfidence();
  6. // 多个位置是可能的,例如,所描绘的地标的位置和照片的拍摄位置。
  7. for (FirebaseVisionLatLng loc: landmark.getLocations()) {
  8. double latitude = loc.getLatitude();
  9. double longitude = loc.getLongitude();
  10. }
  11. }