YUV图像数据的格式转换(YV12toNV21)及base64编解码

Posted by lsq on March 11, 2019

在使用行车记录仪检测行人和车辆项目中,遇到了这样一个,相机回调返回的数据格式不是默认的NV21,而是YV12,这个硬件上的问题困扰了我好久,最终通过咨询厂商解决了这个问题。此外还有一个需求,要求将图像数据通过网络发送到指定的服务器,为了保证数据不被截取,我采用了Base64对图像数据进行了加密。下面详细介绍这两个问题。

1 YV12转NV21

YUV数据格式相关的介绍在我的这篇博文中已经进行了介绍,想进一步了解的可以移步此处:安卓相机YUV格式解析。YV12和NV21的不同之处在于UV通道的排列方式,那么我是如何发现数据格式并非默认的NV21呢?原因在于我将YUV数据转换成RGB格式时,得到的竟然是黑白图像,不由让人心生歹意。我使用的yuv转rgb的方法有如下:

//第一种来自开源库'jp.co.cyberagent.android:gpuimage:2.0.3',使用了OpenCL进行加速,墙裂建议。
GPUImageNativeLibrary.YUVtoRBGA(input, width, height, output);

第二种参见上篇博文: 安卓相机YUV格式解析

这两个方法在我的小米6上均得到了验证,可以正确的将NV21转成ARGB8888。此外还有一个现象是从YUV数据中提取到Y通道数据是正确的,这更加让人确信是YUV格式问题。通过咨询厂商,得知了相机使用的YUV格式是YV12,那么剩下的问题就是先将YV12转换成NV21,然后再进行其他处理。不直接使用YV12的原因在于,图像压缩依赖的类YUVImage并不支持。YV12转换NV21的代码如下

public static void YV12toNV21(final byte[] input, final byte[] output, final int width, final int height) {
        final int frameSize = width * height;
        final int qFrameSize = frameSize / 4;
        final int tempFrameSize = frameSize * 5 / 4;
        System.arraycopy(input, 0, output, 0, frameSize); // Y
        for (int i = 0; i < qFrameSize; i++) {
            output[frameSize + i * 2] = input[frameSize + i]; // Cb (U)
            output[frameSize + i * 2 + 1] = input[tempFrameSize + i]; // Cr (V)
        }
    }

到这里我们就得到了正常人都会使用的NV21格式数据,然后愉快地进行车辆行人检测了。

2 图像数据的Base64编解码

为了保护我们上传的数据不会被截取,或者不被一般人解析,这里对原始的图像数据进行了编码,编码方式是Base64。关于这种编码方式的资料,不需要google到处寻觅,看阮一峰大佬的这一篇就够了:Base64笔记。在安卓环境中实现Base64编解码已经有开源的轮子了,而且性能好评,就是这个 android.util.Base64,官方文档在此 Base64

该Base64编解码器的使用方法非常简单,两个方法:encode() 和 decode()。恩,感觉和没说一样,不过这样简洁易用的才是好轮子。

Reference

安卓相机YUV格式解析
Base64笔记
Base64