史上最强的YUV转换RenderScript,支持转换成RGBA,BGRA,并且同时支持旋转,翻转,YUV数据格式支持NV21, YV12, YUV420_888, 分别对应Android Camera1API 以及Camera2 API 的YUV处理

个人觉得libyuv性能更好,建议使用libyuv,地址:https://120.27.239.50/wordpress/index.php/2020/05/25/androidlibyuv/

好了,回到正题,先放Demo工程地址:https://github.com/bookzhan/YUVRenderScript.git

史上最强的YUV转换RenderScript,支持转换成RGBA,BGRA,并且同时支持旋转,翻转,YUV数据格式支持NV21, YV12, YUV420_888, 分别对应Android Camera1API 以及Camera2 API 的YUV处理

​ 这段时间在做一些Android Camera数据处理的相关工作,涉及到YUV转RGBA的事情,由于对性能要求极高我先后采用了多种方案,最开始的时候是试用OpenCV里面的方法,最后发现性能不是很理想,最后又使用了libyuv,这个库性能要比OpenCV要好,对性能要求不是特别高的可以采用,这个库有一定的缺陷,YUV转换后图像还原度很不好,明显感觉到有色差,而且在Android手机上长时间运行,性能下降明显,最后没有办法了开始采用RenderScript,这里不得不吐槽一下Google 他仅仅提供了一个ScriptIntrinsicYuvToRGB,而且不支持YUV420_888,也不支持旋转,翻转,我呸渣男! 还能怎么办自己动手吧!

话不多说直接上代码吧(里面涉及到很多YUV的知识我就不讲了):

#pragma version(1)
#pragma rs java_package_name(com.luoye.bzyuv)
#pragma rs_fp_relaxed

rs_allocation mInput;

rs_allocation mInY;
rs_allocation mInU;
rs_allocation mInV;
rs_allocation mInNV21;
rs_allocation mInYV12;
int inWidth=0,inHeight=0;
int orientation=0;
bool flipY=false;
int uvPixelStride=1;

static uchar4 yuvToRGBA4(uchar y, uchar u, uchar v) {
    short Y = ((short)y) - 16;
    short U = ((short)u) - 128;
    short V = ((short)v) - 128;

    short4 p;
    p.x = (Y * 298 + V * 409 + 128) >> 8;
    p.y = (Y * 298 - U * 100 - V * 208 + 128) >> 8;
    p.z = (Y * 298 + U * 516 + 128) >> 8;
    p.w = 255;
    if(p.x < 0) {
        p.x = 0;
    }
    if(p.x > 255) {
        p.x = 255;
    }
    if(p.y < 0) {
        p.y = 0;
    }
    if(p.y > 255) {
        p.y = 255;
    }
    if(p.z < 0) {
        p.z = 0;
    }
    if(p.z > 255) {
        p.z = 255;
    }
    return (uchar4){p.x, p.y, p.z, p.w};
}

static float4 yuvToRGBA_f4(uchar y, uchar u, uchar v) {
    float4 yuv_U_values = {0.f, -0.392f * 0.003921569f, +2.02 * 0.003921569f, 0.f};
    float4 yuv_V_values = {1.603f * 0.003921569f, -0.815f * 0.003921569f, 0.f, 0.f};

    float4 color = (float)y * 0.003921569f;
    float4 fU = ((float)u) - 128.f;
    float4 fV = ((float)v) - 128.f;

    color += fU * yuv_U_values;
    color += fV * yuv_V_values;
    color = clamp(color, 0.f, 1.f);
    return color;
}

void makeRef(rs_allocation ay, rs_allocation au, rs_allocation av, rs_allocation aout) {
    uint32_t w = rsAllocationGetDimX(ay);
    uint32_t h = rsAllocationGetDimY(ay);

    for (int y = 0; y < h; y++) {
        //rsDebug("y", y);
        for (int x = 0; x < w; x++) {

            int py = rsGetElementAt_uchar(ay, x, y);
            int pu = rsGetElementAt_uchar(au, x >> 1, y >> 1);
            int pv = rsGetElementAt_uchar(av, x >> 1, y >> 1);

            //rsDebug("py", py);
            //rsDebug(" u", pu);
            //rsDebug(" v", pv);

            uchar4 rgb = yuvToRGBA4(py, pu, pv);
            //rsDebug("  ", rgb);

            rsSetElementAt_uchar4(aout, rgb, x, y);
        }
    }
}

void makeRef_f4(rs_allocation ay, rs_allocation au, rs_allocation av, rs_allocation aout) {
    uint32_t w = rsAllocationGetDimX(ay);
    uint32_t h = rsAllocationGetDimY(ay);

    for (int y = 0; y < h; y++) {
        //rsDebug("y", y);
        for (int x = 0; x < w; x++) {

            uchar py = rsGetElementAt_uchar(ay, x, y);
            uchar pu = rsGetElementAt_uchar(au, x >> 1, y >> 1);
            uchar pv = rsGetElementAt_uchar(av, x >> 1, y >> 1);

            //rsDebug("py", py);
            //rsDebug(" u", pu);
            //rsDebug(" v", pv);

            float4 rgb = yuvToRGBA_f4(py, pu, pv);
            //rsDebug("  ", rgb);

            rsSetElementAt_float4(aout, rgb, x, y);
        }
    }
}

static void setLocation(int x,int y, int*targetX,int *targetY){
    int finalX=x;
    int finalY=y;
    if(orientation!=0){
        if(orientation==90){
            finalX=y;
            finalY=inHeight-1-x;
            if(flipY){
               finalX=inWidth-1-finalX;
            }
        }else if(orientation==180){
             finalX=inWidth - 1 - x;
             finalY=inHeight - 1 - y;
             if(flipY){
                finalY=inHeight-1-finalY;
             }
        }else if(orientation==270){
             finalX=inWidth - 1 - y;
             finalY=x;
             if(flipY){
                finalY=inHeight-1-finalY;
             }
        }
    }else{
        if(flipY){
           finalY=inHeight-1-finalY;
        }
    }
    *targetX=finalX;
    *targetY=finalY;
}

uchar4 __attribute__((kernel)) yuv_420_888_2_rgba(uint32_t x, uint32_t y) {
    if(inWidth<=0||inHeight<=0){
        rsDebug("bz_inWidth<=0||inHeight<=0", inWidth,inHeight);
        return '0';
    }
    int targetX=x;
    int targetY=y;
    setLocation(x,y,&targetX,&targetY);
    uint uvIndex=  uvPixelStride * (targetX/2) + inWidth*(targetY/2);
    uchar yps= rsGetElementAt_uchar(mInY,targetX,targetY);
    uchar u= rsGetElementAt_uchar(mInU,uvIndex);
    uchar v= rsGetElementAt_uchar(mInV,uvIndex);
    return yuvToRGBA4(yps, u, v);
}

uchar4 __attribute__((kernel)) yuv_420_888_2_bgra(uint32_t x, uint32_t y) {
    if(inWidth<=0||inHeight<=0){
        rsDebug("bz_inWidth<=0||inHeight<=0", inWidth,inHeight);
        return '0';
    }
    int targetX=x;
    int targetY=y;
    setLocation(x,y,&targetX,&targetY);
    uint uvIndex=  uvPixelStride * (targetX/2) + inWidth*(targetY/2);
    uchar yps= rsGetElementAt_uchar(mInY,targetX,targetY);
    uchar u= rsGetElementAt_uchar(mInU,uvIndex);
    uchar v= rsGetElementAt_uchar(mInV,uvIndex);

    uchar4 rgba= yuvToRGBA4(yps, u, v);
    return (uchar4){rgba.z, rgba.y, rgba.x,rgba.w};
}

uchar4 __attribute__((kernel)) yuv_yv12_2_rgba(uint32_t x, uint32_t y) {
    if(inWidth<=0||inHeight<=0){
        rsDebug("bz_inWidth<=0||inHeight<=0", inWidth,inHeight);
        return '0';
    }
    int targetX=x;
    int targetY=y;
    setLocation(x,y,&targetX,&targetY);
    uint yIndex=targetX+inWidth*targetY;
    uint uIndex=inWidth*inHeight+ (targetX/2) + inWidth/2*(targetY/2);
    uint vIndex=inWidth*inHeight+inWidth*inHeight/4+ (targetX/2) + inWidth/2*(targetY/2);
    uchar yps= rsGetElementAt_uchar(mInYV12,yIndex);
    uchar u= rsGetElementAt_uchar(mInYV12,uIndex);
    uchar v= rsGetElementAt_uchar(mInYV12,vIndex);

    return yuvToRGBA4(yps, v, u);
}

uchar4 __attribute__((kernel)) yuv_yv12_2_bgra(uint32_t x, uint32_t y) {
    if(inWidth<=0||inHeight<=0){
        rsDebug("bz_inWidth<=0||inHeight<=0", inWidth,inHeight);
        return '0';
    }
    int targetX=x;
    int targetY=y;
    setLocation(x,y,&targetX,&targetY);
    uint yIndex=targetX+inWidth*targetY;
    uint uIndex=inWidth*inHeight+ (targetX/2) + inWidth/2*(targetY/2);
    uint vIndex=inWidth*inHeight+inWidth*inHeight/4+ (targetX/2) + inWidth/2*(targetY/2);
    uchar yps= rsGetElementAt_uchar(mInYV12,yIndex);
    uchar u= rsGetElementAt_uchar(mInYV12,uIndex);
    uchar v= rsGetElementAt_uchar(mInYV12,vIndex);

    uchar4 rgba= yuvToRGBA4(yps, v, u);
    return (uchar4){rgba.z, rgba.y, rgba.x,rgba.w};
}

uchar4 __attribute__((kernel)) yuv_nv21_2_rgba(uint32_t x, uint32_t y) {
    if(inWidth<=0||inHeight<=0){
        rsDebug("bz_inWidth<=0||inHeight<=0", inWidth,inHeight);
        return '0';
    }
    int targetX=x;
    int targetY=y;
    setLocation(x,y,&targetX,&targetY);
    uint yIndex=targetX+inWidth*targetY;
    uint uIndex=inWidth*inHeight+ 2*(targetX/2) + inWidth*(targetY/2);
    uint vIndex=inWidth*inHeight+ 2*(targetX/2) + inWidth*(targetY/2)+1;
    uchar yps= rsGetElementAt_uchar(mInNV21,yIndex);
    uchar u= rsGetElementAt_uchar(mInNV21,uIndex);
    uchar v= rsGetElementAt_uchar(mInNV21,vIndex);

    return yuvToRGBA4(yps, v, u);
}

uchar4 __attribute__((kernel)) yuv_nv21_2_bgra(uint32_t x, uint32_t y) {
    if(inWidth<=0||inHeight<=0){
        rsDebug("bz_inWidth<=0||inHeight<=0", inWidth,inHeight);
        return '0';
    }
    int targetX=x;
    int targetY=y;
    setLocation(x,y,&targetX,&targetY);
    uint yIndex=targetX+inWidth*targetY;
    uint uIndex=inWidth*inHeight+ 2*(targetX/2) + inWidth*(targetY/2);
    uint vIndex=inWidth*inHeight+ 2*(targetX/2) + inWidth*(targetY/2)+1;
    uchar yps= rsGetElementAt_uchar(mInNV21,yIndex);
    uchar u= rsGetElementAt_uchar(mInNV21,uIndex);
    uchar v= rsGetElementAt_uchar(mInNV21,vIndex);

    uchar4 rgba= yuvToRGBA4(yps, v, u);
    return (uchar4){rgba.z, rgba.y, rgba.x,rgba.w};
}

float4 __attribute__((kernel)) cvt_f4(uint32_t x, uint32_t y) {

    uchar py = rsGetElementAtYuv_uchar_Y(mInput, x, y);
    uchar pu = rsGetElementAtYuv_uchar_U(mInput, x, y);
    uchar pv = rsGetElementAtYuv_uchar_V(mInput, x, y);

    //rsDebug("py2", py);
    //rsDebug(" u2", pu);
    //rsDebug(" v2", pv);

    return rsYuvToRGBA_float4(py, pu, pv);
}

The supported methods are as follows:

  1. yuv_yv12_2_Bitmap
  2. yuv_yv12_2_RGBA
  3. yuv_yv12_2_BGRA
  4. yuv_nv21_2_Bitmap
  5. yuv_nv21_2_RGBA
  6. yuv_nv21_2_BGRA
  7. yuv420_2_Bitmap
  8. yuv420_2_RGBA
  9. yuv420_2_BGRA
此条目发表在Android分类目录。将固定链接加入收藏夹。

史上最强的YUV转换RenderScript,支持转换成RGBA,BGRA,并且同时支持旋转,翻转,YUV数据格式支持NV21, YV12, YUV420_888, 分别对应Android Camera1API 以及Camera2 API 的YUV处理》有4条回应

  1. Danielexhip说:

    нло информация iphone информация https://opjgrotip.com казахстан информация

  2. Pingback引用通告: Android YUV转换,支持Camera1,Camera2 | 绕云技术笔记

  3. 小蜗牛说:

    1、在Camera2Activity中onImageAvailable,u和v数据错位了:
    if (null == yBuffer) {
    yBuffer = new byte[width * height];
    }
    if (null == uBuffer) {
    uBuffer = new byte[planes[1].getBuffer().capacity()];
    }
    if (null == vBuffer) {
    vBuffer = new byte[planes[2].getBuffer().capacity()];
    }
    planes[0].getBuffer().get(yBuffer);
    planes[1].getBuffer().get(vBuffer);
    planes[2].getBuffer().get(uBuffer);
    2、在YuvConvertUtil的convertYuv420中,U,V数据也错位:
    scriptC_yuv.set_mInU(vIn);
    scriptC_yuv.set_mInV(uIn);
    3、运行,把YUV420_8888的数据转化为RGB,图片失去颜色变成会灰度图

    • luoye说:

      我在我手机上试了一下,没有问题,这个工程就不支持转换成RGB, 只是支持转换成RGBA, 也有可能是你手机上的兼容问题RowStride大小不一样导致的,现阶段不打算做支持了,建议你用这个工程https://120.27.239.50/wordpress/index.php/2020/05/25/androidlibyuv/,效果性能更好

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注