google_ml_kit_flutter icon indicating copy to clipboard operation
google_ml_kit_flutter copied to clipboard

Process Camera Image Issue when using Face Detection & Pose Detection,etc with camera_android_camerax: any instead of camera_android:any

Open mrwebbeast opened this issue 9 months ago • 13 comments

Title: Process Camera Image Issue

Describe the bug Process Camera Image Issue when using Face Detection & Pose Detection,etc with camera_android_camerax: any

To Reproduce Steps to reproduce the behavior:

  1. Use default Example Code
  2. Use camera: ^0.10.6 & Add camera_android_camerax: ^0.6.4+1
  3. Run The App and Face Detection & Pose Detection will not work

Expected behavior It should work same as camera_android: ^any so it will process and return processed data according to mlkit plugin Expected InputImageFormat is nv21 but when using camera_android_camerax: any then its returning yuv_420_888 InputImageFormat

Need support for yuv_420_888 InputImageFormat when using camera_android_camerax:any

Platform (please complete the following information):

  • OS: Android 14
  • Device: Redmi Note 12 Pro Plus
  • Flutter/Dart Version 3.19.6 • channel stable | Dart 3.3.4
  • Plugin version
  1. google_mlkit_face_detection: ^0.11.0
  2. google_mlkit_pose_detection: ^0.12.0

mrwebbeast avatar May 07 '24 09:05 mrwebbeast

feel free to fork the repo and edit this:

https://github.com/flutter-ml/google_ml_kit_flutter/blob/develop/packages/google_mlkit_commons/android/src/main/java/com/google_mlkit_commons/InputImageConverter.java#L35-L41

send you PR back with your contribution

fbernaly avatar May 07 '24 16:05 fbernaly

CameraX is now the default Android camera in the Camera Package. It would be great if we could get the update.

FantaMagier avatar May 14 '24 06:05 FantaMagier

I believe yuv_420_888 format can have more than a single plane. It's unclear how to build an InputImage then. (In previous versions, probably 0.0.6 it worked with multiple planes, but it seems not anymore).

Also, the documentation of this plugin states to use `.nv21 for android.

AndreiMisiukevich avatar May 16 '24 10:05 AndreiMisiukevich

@AndreiMisiukevich : feel free to fork the repo and add multiple planes and add yuv_420_888 for Android. Then send your PR. We always welcome contributions.

fbernaly avatar May 16 '24 15:05 fbernaly

This topic is also linked to this CameraX issue: https://github.com/flutter/flutter/issues/145961

FantaMagier avatar May 17 '24 06:05 FantaMagier

Isn't it possible to use the ML KIT API with yuv_420_888 InputImageFormat? Maybe it is easier to support this third image format in the Flutter SDK?

https://developers.google.com/android/reference/com/google/mlkit/vision/common/InputImage.ImageFormat

FantaMagier avatar May 17 '24 07:05 FantaMagier

@FantaMagier : yes, it is possible. I do not have an ETA for that change. We need a volunteer to work on that. Contributions are always welcome.

fbernaly avatar May 17 '24 15:05 fbernaly

Maybe someone need a quick solution, this code helped me to convert yuv420_888 to nv21 manually: (source in java: https://blog.minhazav.dev/how-to-use-renderscript-to-convert-YUV_420_888-yuv-image-to-bitmap/#tonv21image-image-java-approach)

extension Nv21Converter on CameraImage {
  Uint8List getNv21Uint8List() {
    final width = this.width;
    final height = this.height;

    final yPlane = planes[0];
    final uPlane = planes[1];
    final vPlane = planes[2];

    final yBuffer = yPlane.bytes;
    final uBuffer = uPlane.bytes;
    final vBuffer = vPlane.bytes;

    final numPixels = (width * height * 1.5).toInt();
    final nv21 = List<int>.filled(numPixels, 0);

    // Full size Y channel and quarter size U+V channels.
    int idY = 0;
    int idUV = width * height;
    final uvWidth = width ~/ 2;
    final uvHeight = height ~/ 2;
    // Copy Y & UV channel.
    // NV21 format is expected to have YYYYVU packaging.
    // The U/V planes are guaranteed to have the same row stride and pixel stride.
    // getRowStride analogue??
    final uvRowStride = uPlane.bytesPerRow;
    // getPixelStride analogue
    final uvPixelStride = uPlane.bytesPerPixel ?? 0;
    final yRowStride = yPlane.bytesPerRow;
    final yPixelStride = yPlane.bytesPerPixel ?? 0;

    for (int y = 0; y < height; ++y) {
      final uvOffset = y * uvRowStride;
      final yOffset = y * yRowStride;

      for (int x = 0; x < width; ++x) {
        nv21[idY++] = yBuffer[yOffset + x * yPixelStride];

        if (y < uvHeight && x < uvWidth) {
          final bufferIndex = uvOffset + (x * uvPixelStride);
          //V channel
          nv21[idUV++] = vBuffer[bufferIndex];
          //V channel
          nv21[idUV++] = uBuffer[bufferIndex];
        }
      }
    }
    return Uint8List.fromList(nv21);
  }
}

PudovkinSergey avatar May 28 '24 09:05 PudovkinSergey

@PudovkinSergey Good work! How does your inputImageFromCameraImage function look like?

FantaMagier avatar May 28 '24 11:05 FantaMagier

This issue is stale because it has been open for 30 days with no activity.

github-actions[bot] avatar Jun 27 '24 12:06 github-actions[bot]

same issue here 😢

nebis-software avatar Jun 27 '24 13:06 nebis-software

Maybe someone need a quick solution, this code helped me to convert yuv420_888 to nv21 manually: (source in java: https://blog.minhazav.dev/how-to-use-renderscript-to-convert-YUV_420_888-yuv-image-to-bitmap/#tonv21image-image-java-approach)

extension Nv21Converter on CameraImage {
  Uint8List getNv21Uint8List() {
    final width = this.width;
    final height = this.height;

    final yPlane = planes[0];
    final uPlane = planes[1];
    final vPlane = planes[2];

    final yBuffer = yPlane.bytes;
    final uBuffer = uPlane.bytes;
    final vBuffer = vPlane.bytes;

    final numPixels = (width * height * 1.5).toInt();
    final nv21 = List<int>.filled(numPixels, 0);

    // Full size Y channel and quarter size U+V channels.
    int idY = 0;
    int idUV = width * height;
    final uvWidth = width ~/ 2;
    final uvHeight = height ~/ 2;
    // Copy Y & UV channel.
    // NV21 format is expected to have YYYYVU packaging.
    // The U/V planes are guaranteed to have the same row stride and pixel stride.
    // getRowStride analogue??
    final uvRowStride = uPlane.bytesPerRow;
    // getPixelStride analogue
    final uvPixelStride = uPlane.bytesPerPixel ?? 0;
    final yRowStride = yPlane.bytesPerRow;
    final yPixelStride = yPlane.bytesPerPixel ?? 0;

    for (int y = 0; y < height; ++y) {
      final uvOffset = y * uvRowStride;
      final yOffset = y * yRowStride;

      for (int x = 0; x < width; ++x) {
        nv21[idY++] = yBuffer[yOffset + x * yPixelStride];

        if (y < uvHeight && x < uvWidth) {
          final bufferIndex = uvOffset + (x * uvPixelStride);
          //V channel
          nv21[idUV++] = vBuffer[bufferIndex];
          //V channel
          nv21[idUV++] = uBuffer[bufferIndex];
        }
      }
    }
    return Uint8List.fromList(nv21);
  }
}

how to use it?

hiroppi401 avatar Jun 27 '24 23:06 hiroppi401

Have the extension code in the separate dart file

In the _inputImageFromCameraImage method ........ final format = InputImageFormatValue.fromRawValue(image.format.raw); if (format != null && (Platform.isAndroid && format == InputImageFormat.yuv_420_888)) { return InputImage.fromBytes( bytes: image.getNv21Uint8List(), metadata: InputImageMetadata( size: Size(image.width.toDouble(), image.height.toDouble()), rotation: rotation, // used only in Android format: format, // used only in iOS bytesPerRow: image.planes.first.bytesPerRow, // used only in iOS )); } .........

koppular avatar Jul 23 '24 18:07 koppular

Have the extension code in the separate dart file

In the _inputImageFromCameraImage method ........ final format = InputImageFormatValue.fromRawValue(image.format.raw); if (format != null && (Platform.isAndroid && format == InputImageFormat.yuv_420_888)) { return InputImage.fromBytes( bytes: image.getNv21Uint8List(), metadata: InputImageMetadata( size: Size(image.width.toDouble(), image.height.toDouble()), rotation: rotation, // used only in Android format: format, // used only in iOS bytesPerRow: image.planes.first.bytesPerRow, // used only in iOS )); } .........

thank u so much sir now i can use the newset camera package

hiroppi401 avatar Jul 25 '24 11:07 hiroppi401

The Flutter team discusses support for nv21 with CameraX. Upvote the topic to get more support for it 😁.

https://github.com/flutter/flutter/issues/145961

FantaMagier avatar Jul 25 '24 12:07 FantaMagier

This issue is stale because it has been open for 30 days with no activity.

github-actions[bot] avatar Aug 25 '24 12:08 github-actions[bot]

This issue was closed because it has been inactive for 14 days since being marked as stale.

github-actions[bot] avatar Sep 08 '24 12:09 github-actions[bot]