Rotating YUV image data for Portrait Mode Using RenderScript

后端 未结 1 1167
遥遥无期
遥遥无期 2020-12-19 10:51

for a video image processing project, I have to rotate the incoming YUV image data so that the data is not shown horizontally but vertically. I used this project which gave

相关标签:
1条回答
  • 2020-12-19 11:33

    See my YuvConverter. It was inspired by android - Renderscript to convert NV12 yuv to RGB.

    Its rs part is very simple:

    #pragma version(1)
    #pragma rs java_package_name(whatever)
    #pragma rs_fp_relaxed
    
    rs_allocation Yplane;
    uint32_t Yline;
    uint32_t UVline;
    rs_allocation Uplane;
    rs_allocation Vplane;
    rs_allocation NV21;
    uint32_t Width;
    uint32_t Height;
    
    uchar4 __attribute__((kernel)) YUV420toRGB(uint32_t x, uint32_t y)
    {
        uchar Y = rsGetElementAt_uchar(Yplane, x + y * Yline);
        uchar V = rsGetElementAt_uchar(Vplane, (x & ~1) + y/2 * UVline);
        uchar U = rsGetElementAt_uchar(Uplane, (x & ~1) + y/2 * UVline);
        // https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion
        short R = Y + (512           + 1436 * V) / 1024; //             1.402
        short G = Y + (512 -  352 * U - 731 * V) / 1024; // -0.344136  -0.714136
        short B = Y + (512 + 1815 * U          ) / 1024; //  1.772
        if (R < 0) R == 0; else if (R > 255) R == 255;
        if (G < 0) G == 0; else if (G > 255) G == 255;
        if (B < 0) B == 0; else if (B > 255) B == 255;
        return (uchar4){R, G, B, 255};
    }
    
    uchar4 __attribute__((kernel)) YUV420toRGB_180(uint32_t x, uint32_t y)
    {
        return YUV420toRGB(Width - 1 - x, Height - 1 - y);
    }
    
    uchar4 __attribute__((kernel)) YUV420toRGB_90(uint32_t x, uint32_t y)
    {
        return YUV420toRGB(y, Width - x - 1);
    }
    
    uchar4 __attribute__((kernel)) YUV420toRGB_270(uint32_t x, uint32_t y)
    {
        return YUV420toRGB(Height - 1 - y, x);
    }
    

    My Java wrapper was used in Flutter, but this does not really matter:

    public class YuvConverter implements AutoCloseable {
    
        private RenderScript rs;
        private ScriptC_yuv2rgb scriptC_yuv2rgb;
        private Bitmap bmp;
    
        YuvConverter(Context ctx, int ySize, int uvSize, int width, int height) {
            rs = RenderScript.create(ctx);
            scriptC_yuv2rgb = new ScriptC_yuv2rgb(rs);
            init(ySize, uvSize, width, height);
        }
    
        private Allocation allocY, allocU, allocV, allocOut;
    
        @Override
        public void close() {
            if (allocY != null) allocY.destroy();
            if (allocU != null) allocU.destroy();
            if (allocV != null) allocV.destroy();
            if (allocOut != null) allocOut.destroy();
            bmp = null;
            allocY = null;
            allocU = null;
            allocV = null;
            allocOut = null;
            scriptC_yuv2rgb.destroy();
            scriptC_yuv2rgb = null;
            rs = null;
        }
    
        private void init(int ySize, int uvSize, int width, int height) {
            if (bmp == null || bmp.getWidth() != width || bmp.getHeight() != height) {
                bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                if (allocOut != null) allocOut.destroy();
                allocOut = null;
            }
            if (allocY == null || allocY.getBytesSize() != ySize) {
                if (allocY != null) allocY.destroy();
                Type.Builder yBuilder = new Type.Builder(rs, Element.U8(rs)).setX(ySize);
                allocY = Allocation.createTyped(rs, yBuilder.create(), Allocation.USAGE_SCRIPT);
            }
            if (allocU == null || allocU.getBytesSize() != uvSize || allocV == null || allocV.getBytesSize() != uvSize ) {
                if (allocU != null) allocU.destroy();
                if (allocV != null) allocV.destroy();
                Type.Builder uvBuilder = new Type.Builder(rs, Element.U8(rs)).setX(uvSize);
                allocU = Allocation.createTyped(rs, uvBuilder.create(), Allocation.USAGE_SCRIPT);
                allocV = Allocation.createTyped(rs, uvBuilder.create(), Allocation.USAGE_SCRIPT);
            }
            if (allocOut == null || allocOut.getBytesSize() != width*height*4) {
                Type rgbType = Type.createXY(rs, Element.RGBA_8888(rs), width, height);
                if (allocOut != null) allocOut.destroy();
                allocOut = Allocation.createTyped(rs, rgbType, Allocation.USAGE_SCRIPT);
            }
        }
    
        @Retention(RetentionPolicy.SOURCE)
        // Enumerate valid values for this interface
        @IntDef({Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_180, Surface.ROTATION_270})
        // Create an interface for validating int types
        public @interface Rotation {}
    
        /**
         * Converts an YUV_420 image into Bitmap.
         * @param yPlane  byte[] of Y, with pixel stride 1
         * @param uPlane  byte[] of U, with pixel stride 2
         * @param vPlane  byte[] of V, with pixel stride 2
         * @param yLine   line stride of Y
         * @param uvLine  line stride of U and V
         * @param width   width of the output image (note that it is swapped with height for portrait rotation)
         * @param height  height of the output image
         * @param rotation  rotation to apply. ROTATION_90 is for portrait back-facing camera.
         * @return RGBA_8888 Bitmap image.
         */
    
        public Bitmap YUV420toRGB(byte[] yPlane, byte[] uPlane, byte[] vPlane,
                                  int yLine, int uvLine, int width, int height,
                                  @Rotation int rotation) {
            init(yPlane.length, uPlane.length, width, height);
    
            allocY.copyFrom(yPlane);
            allocU.copyFrom(uPlane);
            allocV.copyFrom(vPlane);
            scriptC_yuv2rgb.set_Width(width);
            scriptC_yuv2rgb.set_Height(height);
            scriptC_yuv2rgb.set_Yline(yLine);
            scriptC_yuv2rgb.set_UVline(uvLine);
            scriptC_yuv2rgb.set_Yplane(allocY);
            scriptC_yuv2rgb.set_Uplane(allocU);
            scriptC_yuv2rgb.set_Vplane(allocV);
    
            switch (rotation) {
                case Surface.ROTATION_0:
                    scriptC_yuv2rgb.forEach_YUV420toRGB(allocOut);
                    break;
                case Surface.ROTATION_90:
                    scriptC_yuv2rgb.forEach_YUV420toRGB_90(allocOut);
                    break;
                case Surface.ROTATION_180:
                    scriptC_yuv2rgb.forEach_YUV420toRGB_180(allocOut);
                    break;
                case Surface.ROTATION_270:
                    scriptC_yuv2rgb.forEach_YUV420toRGB_270(allocOut);
                    break;
            }
    
            allocOut.copyTo(bmp);
            return bmp;
        }
    }
    

    The key to performance is that renderscript can be initialized once (that's why YuvConverter.init() is public) and the following calls are very fast.

    0 讨论(0)
提交回复
热议问题