如何裁剪包含Y'(亮度)的byte []数组而不转换为Bitmap

时间:2018-01-16 23:03:21

标签: android arrays image-processing android-camera2 yuv

编辑:解决了!见下文。 我需要裁剪我从Camera2的onImageAvailable侦听器获得的图像(YUV422888颜色空间)。我不想或不需要将它转换为Bitmap,因为它会影响性能,而且我实际上对luma感兴趣而不是RGB信息(它包含在Image的Plane 0中)。 我提出了以下解决方案:

  1. 获取Camera2在侦听器中提供的Image对象的Plane 0中包含的Y'信息。
  2. 将Y'平面转换为中的byte []数组
  3. 将byte []数组转换为2d byte [] []数组以便裁剪。
  4. 使用一些 for 循环裁剪所需的左,右,上,下坐标。
  5. 将2d byte [] []数组折回1d byte [] array out ,包含裁剪的亮度Y'信息。
  6. 不幸的是,第4点产生了损坏的图像。 我做错了什么?

    在Camera2的 onImageAvailableListener 中(请注意,虽然我正在计算位图,但只是看看发生了什么,因为我对Bitmap / RGB数据不感兴趣):

                   Image.Plane[] planes = image.getPlanes();
                   ByteBuffer buffer = planes[0].getBuffer(); // Grab just the Y' Plane.
                   buffer.rewind();
                   byte[] data = new byte[buffer.capacity()];
                   buffer.get(data);
    
                   Bitmap bitmap = cropByteArray(data, image.getWidth(), image.getHeight()); // Just for preview/sanity check purposes. The bitmap is **corrupt**.
    
                   runOnUiThread(new bitmapRunnable(bitmap) {
                       @Override
                       public void run() {
                           image_view_preview.setImageBitmap(this.bitmap);
                       }
                   });
    

    cropByteArray 功能需要修复。它会输出一个损坏的位图,并且应该输出 out byte []数组,类似于中的,但只包含裁剪区域:

        public Bitmap cropByteArray(byte[] in, int inw, int inh) {
    
        int l = 100; // left crop start
        int r = 400; // right crop end
        int t = 400; // top crop start
        int b = 700; // top crop end
        int outw = r-l;
        int outh = b-t;
    
    
        byte[][] in2d = new byte[inw][inh]; // input width and height are 1080 x 1920.
        byte[] out = new byte[outw*outh];
        int[] pixels = new int[outw*outh];
    
        i = 0;
        for(int col = 0; col < inw; col++) {
            for(int row = 0; row < inh; row++) {
                in2d[col][row] = in[i++];
            }
        }
    
        i = 0;
        for(int col = l; col < r; col++) {
            for(int row = t; row < b; row++) {
                //out[i++] = in2d[col][row]; // out is the desired output of the function, but for now we output a bitmap instead
    
                int grey = in2d[col][row] & 0xff;
                pixels[i++] = 0xFF000000 | (grey * 0x00010101);
    
            }
        }
    
        return Bitmap.createBitmap(pixels, inw, inh, Bitmap.Config.ARGB_8888);
    }
    

    编辑感谢Eddy Talvala的建议。以下代码将Y'(来自ImageReader的亮度平面0)裁剪为所需坐标。裁剪后的数据位于 out 字节数组中。生成位图仅用于确认。我还在下面附上了方便的YUVtoGrayscale()函数。

            Image.Plane[] planes    = image.getPlanes();
            ByteBuffer buffer       = planes[0].getBuffer();
            int stride              = planes[0].getRowStride();
            buffer.rewind();
            byte[] Y = new byte[buffer.capacity()];
            buffer.get(Y);
    
            int t=200; int l=600;
            int out_h = 600; int out_w = 600;
            byte[] out = new byte[out_w*out_h];
    
            int firstRowOffset = stride * t + l;
            for (int row = 0; row < out_h; row++) {
                buffer.position(firstRowOffset + row * stride);
                buffer.get(out, row * out_w, out_w);
            }
            Bitmap bitmap = YUVtoGrayscale(out, out_w, out_h);
    

    这里是 YUVtoGrayscale()

        public Bitmap YUVtoGrayscale(byte[] yuv, int width, int height) {
    
        int[] pixels = new int[yuv.length];
    
        for (int i = 0; i < yuv.length; i++) {
            int grey = yuv[i] & 0xff;
            pixels[i] = 0xFF000000 | (grey * 0x00010101);
        }
    
        return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
    }
    

    还有一些问题。我正在使用前置摄像头,虽然在TextureView中预览方向是正确的,但ImageViewer返回的图像顺时针旋转并垂直翻转(一个人在预览中躺在他们的右脸颊上,只有右脸颊是左脸颊,因为我的设备上的垂直翻转),传感器方向为270度。 是否有可接受的解决方案,使用Camera2将预览和保存的照片保持在相同的正确方向?

    干杯。

1 个答案:

答案 0 :(得分:1)

如果您描述了图像是如何损坏的,那将会很有帮助 - 您是否看到了有效的图像,但它是否被扭曲,或者只是完全垃圾,还是只是全黑?

但我猜你没注意Y平面的行间距(https://developer.android.com/reference/android/media/Image.Plane.html#getRowStride()),这通常会导致图像偏斜(垂直线变成有角度的线条)。

当访问Y平面时,像素(x,y)的字节索引为:

y * rowStride + x

y * width + x

因为行步幅可能大于宽度。

我也避免复制这么多;你真的不需要2D数组,而且图像的大字节[]也会浪费内存。

您可以搜索()到每个输出行的开头,然后只读取您需要使用ByteBuffer.get(byte [],offset,length)直接复制到目标字节[]中的字节。 / p>

看起来像

int stride = planes[0].getRowStride();
ByteBuffer img = planes[0].getBuffer();
int firstRowOffset = stride * t + l;
for (int row = 0; row < outh; row++) {
    img.position(firstRowOffset + row * stride);
    img.get(out, row * outw, outw);
}