【问题标题】:How to convert android.media.Image to bitmap object?如何将 android.media.Image 转换为位图对象?
【发布时间】:2017-01-21 05:12:37
【问题描述】:

在 android 中,我从这里得到一个 Image 对象https://inducesmile.com/android/android-camera2-api-example-tutorial/ 这个相机教程。但我现在想循环遍历像素值,有人知道我该怎么做吗?我需要把它转换成别的东西吗?我该怎么做?

谢谢

【问题讨论】:

    标签: android image


    【解决方案1】:

    如果要循环整个像素,则需要先将其转换为Bitmap 对象。现在由于我在源代码中看到它返回一个Image,您可以直接将字节转换为位图。

        Image image = reader.acquireLatestImage();
        ByteBuffer buffer = image.getPlanes()[0].getBuffer();
        byte[] bytes = new byte[buffer.capacity()];
        buffer.get(bytes);
        Bitmap bitmapImage = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, null);
    

    然后,一旦获得位图对象,您现在就可以遍历所有像素。

    【讨论】:

    • 它将内存中的图像从缓冲区加载到字节数组中。所以get 是从图像缓冲区中检索字节。
    • 为什么只拿到第一架飞机?其他位面没有相关数据吗?
    • @hellowill89 平面直接引用图片的格式,e.g. JPEG 仅包含 1 个平面,因此仅从缓冲区检索第一个平面并将其放置到目标内存中。 developer.android.com/reference/android/media/…
    • 我明白了,所以这只适用于 JPEG 图像。相机经常输出 YUV 图像,尽管它使用 3 个(?)平面。在那种情况下,平面会按顺序附加吗?
    • IllegalStateException: BitmapFactory.decodeByte…tes, 0, bytes.size, null) must not be null
    【解决方案2】:

    YuvToRgbConverter 对于从 Image 到 Bitmap 的转换很有用。

    https://github.com/android/camera-samples/blob/master/Camera2Basic/utils/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt

    使用示例。

       val bmp = Bitmap.createBitmap(image.width, image.height, Bitmap.Config.ARGB_8888)
       yuvToRgbConverter.yuvToRgb(image, bmp)
    

    【讨论】:

    • 别忘了加val yuvToRgbConverter= YuvToRgbConverter(requireContext())
    【解决方案3】:

    其实你有两个问题合二为一 1)你如何循环抛出 android.media.Image 像素 2) 如何将android.media.image 转换为位图

    1-st 很容易。请注意,您从相机获得的 Image 对象只是一个 YUV 帧,其中 Y 和 U+V 分量位于不同的平面中。在许多图像处理案例中,您只需要 Y 平面,即图像的灰色部分。为了得到它,我建议这样的代码:

        Image.Plane[] planes = image.getPlanes();
        int yRowStride = planes[0].getRowStride();
        byte[] yImage = new byte[yRowStride];
        planes[0].getBuffer().get(yImage);
    

    yImage 字节缓冲区实际上是帧的灰色像素。 以同样的方式,你可以得到 U+V 部分。请注意,它们可以是 U 首先,V 之后,或者 V 和 U 之后,并且可能是交替存在的(这是 Camera2 API 的常见情况)。所以你得到了 UVUV....

    出于调试目的,我经常将框架写入文件,并尝试使用Vooya app (Linux) 打开它以检查格式。

    第二个问题有点复杂。 为了获得一个位图对象,我从 TensorFlow 项目here 中找到了一些代码示例。对您来说最有趣的函数是“convertImageToBitmap”,它将返回 RGB 值。

    要将它们转换为真正的位图,请执行以下操作:

      Bitmap rgbFrameBitmap;
      int[] cachedRgbBytes;
      cachedRgbBytes = ImageUtils.convertImageToBitmap(image, cachedRgbBytes, cachedYuvBytes);
      rgbFrameBitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
      rgbFrameBitmap.setPixels(cachedRgbBytes,0,image.getWidth(), 0, 0,image.getWidth(), image.getHeight());
    

    注意:将 YUV 转换为 RGB 帧的选项更多,因此如果您需要像素值,也许 Bitmap 不是最佳选择,因为它可能会消耗比您需要的更多的内存来获取 RGB 值

    【讨论】:

    • 我很想看看你的代码ImageUtils.convertImageToBitmap()
    • @SomeoneSomewhere 这不是我的,所以我提供了该项目的链接。看看那里
    【解决方案4】:

    https://docs.oracle.com/javase/1.5.0/docs/api/java/nio/ByteBuffer.html#get%28byte[]%29

    根据 java 文档:buffer.get 方法将此缓冲区中的字节传输到给定的目标数组中。 src.get(a) 形式的此方法的调用与调用的行为方式完全相同

     src.get(a, 0, a.length) 
    

    【讨论】:

      【解决方案5】:

      我假设您有相机提供的 YUV (YUV (YUV_420_888) 图像。使用这个有趣的How to use YUV (YUV_420_888) Image in Android 教程,我可以提出以下解决方案将图像转换为位图。 使用它来将 YUV Image 转换为 Bitmap:

          private Bitmap yuv420ToBitmap(Image image, Context context) {
              RenderScript rs = RenderScript.create(SpeedMeasurementActivity.this);
              ScriptIntrinsicYuvToRGB script = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
      
              // Refer the logic in a section below on how to convert a YUV_420_888 image
              // to single channel flat 1D array. For sake of this example I'll abstract it
              // as a method.
              byte[] yuvByteArray = image2byteArray(image);
      
              Type.Builder yuvType = new Type.Builder(rs, Element.U8(rs)).setX(yuvByteArray.length);
              Allocation in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);
      
              Type.Builder rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs))
                      .setX(image.getWidth())
                      .setY(image.getHeight());
              Allocation out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);
      
              // The allocations above "should" be cached if you are going to perform
              // repeated conversion of YUV_420_888 to Bitmap.
              in.copyFrom(yuvByteArray);
              script.setInput(in);
              script.forEach(out);
      
              Bitmap bitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
              out.copyTo(bitmap);
              return bitmap;
          }
      

      以及一个支持将 3 平面 YUV 图像转换为 1 维字节数组的功能:

          private byte[] image2byteArray(Image image) {
              if (image.getFormat() != ImageFormat.YUV_420_888) {
                  throw new IllegalArgumentException("Invalid image format");
              }
      
              int width = image.getWidth();
              int height = image.getHeight();
      
              Image.Plane yPlane = image.getPlanes()[0];
              Image.Plane uPlane = image.getPlanes()[1];
              Image.Plane vPlane = image.getPlanes()[2];
      
              ByteBuffer yBuffer = yPlane.getBuffer();
              ByteBuffer uBuffer = uPlane.getBuffer();
              ByteBuffer vBuffer = vPlane.getBuffer();
      
              // Full size Y channel and quarter size U+V channels.
              int numPixels = (int) (width * height * 1.5f);
              byte[] nv21 = new byte[numPixels];
              int index = 0;
      
              // Copy Y channel.
              int yRowStride = yPlane.getRowStride();
              int yPixelStride = yPlane.getPixelStride();
              for(int y = 0; y < height; ++y) {
                  for (int x = 0; x < width; ++x) {
                      nv21[index++] = yBuffer.get(y * yRowStride + x * yPixelStride);
                  }
              }
      
              // Copy VU data; NV21 format is expected to have YYYYVU packaging.
              // The U/V planes are guaranteed to have the same row stride and pixel stride.
              int uvRowStride = uPlane.getRowStride();
              int uvPixelStride = uPlane.getPixelStride();
              int uvWidth = width / 2;
              int uvHeight = height / 2;
      
              for(int y = 0; y < uvHeight; ++y) {
                  for (int x = 0; x < uvWidth; ++x) {
                      int bufferIndex = (y * uvRowStride) + (x * uvPixelStride);
                      // V channel.
                      nv21[index++] = vBuffer.get(bufferIndex);
                      // U channel.
                      nv21[index++] = uBuffer.get(bufferIndex);
                  }
              }
              return nv21;
          }
      

      【讨论】:

        【解决方案6】:

        1-将图像文件的路径存储为字符串变量。要解码图像文件的内容,您需要将文件路径作为字符串存储在代码中。使用以下语法作为指导:

        String picPath = "/mnt/sdcard/Pictures/mypic.jpg";
        

        2-创建Bitmap对象并使用BitmapFactory:

        Bitmap picBitmap;
        Bitmap picBitmap = BitmapFactory.decodeFile(picPath);
        

        【讨论】:

          猜你喜欢
          • 2015-04-10
          • 1970-01-01
          • 1970-01-01
          • 2017-07-15
          • 2010-12-04
          • 2015-06-28
          • 2017-06-07
          • 2021-08-29
          相关资源
          最近更新 更多