How to convert & rotate raw NV21 array image (android.media.Image) from front cam portrait mode in onImageAvailable (android Camera2)?

前端 未结 2 1622
忘了有多久
忘了有多久 2020-12-16 07:32

Note: All info in my post only goes for Samsung Galaxy S7 device. I do not know how emulators and other devices behave.

In onImageAvailable I conver

2条回答
  •  伪装坚强ぢ
    2020-12-16 08:21

    Here is the code to convert the Image to NV21 byte[]. This will work when the imgYUV420 U and V planes have pixelStride=1 (as on emulator) or pixelStride=2 (as on Nexus):

    private byte[] convertYUV420ToNV21_ALL_PLANES(Image imgYUV420) {
    
        assert(imgYUV420.getFormat() == ImageFormat.YUV_420_888);
        Log.d(TAG, "image: " + imgYUV420.getWidth() + "x" + imgYUV420.getHeight() + " " + imgYUV420.getFormat());
        Log.d(TAG, "planes: " + imgYUV420.getPlanes().length);
        for (int nplane = 0; nplane < imgYUV420.getPlanes().length; nplane++) {
            Log.d(TAG, "plane[" + nplane + "]: length " + imgYUV420.getPlanes()[nplane].getBuffer().remaining() + ", strides: " + imgYUV420.getPlanes()[nplane].getPixelStride() + " " + imgYUV420.getPlanes()[nplane].getRowStride());
        }
    
        byte[] rez = new byte[imgYUV420.getWidth() * imgYUV420.getHeight() * 3 / 2];
        ByteBuffer buffer0 = imgYUV420.getPlanes()[0].getBuffer();
        ByteBuffer buffer1 = imgYUV420.getPlanes()[1].getBuffer();
        ByteBuffer buffer2 = imgYUV420.getPlanes()[2].getBuffer();
    
        int n = 0;
        assert(imgYUV420.getPlanes()[0].getPixelStride() == 1);
        for (int row = 0; row < imgYUV420.getHeight(); row++) {
            for (int col = 0; col < imgYUV420.getWidth(); col++) {
                rez[n++] = buffer0.get();
            }
        }
        assert(imgYUV420.getPlanes()[2].getPixelStride() == imgYUV420.getPlanes()[1].getPixelStride());
        int stride = imgYUV420.getPlanes()[1].getPixelStride();
        for (int row = 0; row < imgYUV420.getHeight(); row += 2) {
            for (int col = 0; col < imgYUV420.getWidth(); col += 2) {
                rez[n++] = buffer1.get();
                rez[n++] = buffer2.get();
                for (int skip = 1; skip < stride; skip++) {
                    if (buffer1.remaining() > 0) {
                        buffer1.get();
                    }
                    if (buffer2.remaining() > 0) {
                        buffer2.get();
                    }
                }
            }
        }
    
        Log.w(TAG, "total: " + rez.length);
        return rez;
    }
    

    optimized Java code is available here.

    As you can see, it is very easy to change this code to produce a rotated image in a single step:

    private byte[] rotateYUV420ToNV21(Image imgYUV420) {
    
        Log.d(TAG, "image: " + imgYUV420.getWidth() + "x" + imgYUV420.getHeight() + " " + imgYUV420.getFormat());
        Log.d(TAG, "planes: " + imgYUV420.getPlanes().length);
        for (int nplane = 0; nplane < imgYUV420.getPlanes().length; nplane++) {
            Log.d(TAG, "plane[" + nplane + "]: length " + imgYUV420.getPlanes()[nplane].getBuffer().remaining() + ", strides: " + imgYUV420.getPlanes()[nplane].getPixelStride() + " " + imgYUV420.getPlanes()[nplane].getRowStride());
        }
    
        byte[] rez = new byte[imgYUV420.getWidth() * imgYUV420.getHeight() * 3 / 2];
        ByteBuffer buffer0 = imgYUV420.getPlanes()[0].getBuffer();
        ByteBuffer buffer1 = imgYUV420.getPlanes()[1].getBuffer();
        ByteBuffer buffer2 = imgYUV420.getPlanes()[2].getBuffer();
    
        int width = imgYUV420.getHeight();
        assert(imgYUV420.getPlanes()[0].getPixelStride() == 1);
        for (int row = imgYUV420.getHeight()-1; row >=0; row--) {
            for (int col = 0; col < imgYUV420.getWidth(); col++) {
                rez[col*width+row] = buffer0.get();
            }
        }
        int uv_offset = imgYUV420.getWidth()*imgYUV420.getHeight();
        assert(imgYUV420.getPlanes()[2].getPixelStride() == imgYUV420.getPlanes()[1].getPixelStride());
        int stride = imgYUV420.getPlanes()[1].getPixelStride();
        for (int row = imgYUV420.getHeight() - 2; row >= 0; row -= 2) {
            for (int col = 0; col < imgYUV420.getWidth(); col += 2) {
                rez[uv_offset+col/2*width+row] = buffer1.get();
                rez[uv_offset+col/2*width+row+1] = buffer2.get();
                for (int skip = 1; skip < stride; skip++) {
                    if (buffer1.remaining() > 0) {
                        buffer1.get();
                    }
                    if (buffer2.remaining() > 0) {
                        buffer2.get();
                    }
                }
            }
        }
    
        Log.w(TAG, "total rotated: " + rez.length);
        return rez;
    }
    

    I sincerely recommend the site http://rawpixels.net/ to see the actual structure of your raw images.

提交回复
热议问题