背景
在onPreviewFrame方法中获取的byte[] data数据为420sp格式,排列顺序为width*height
个Y(亮度信息,就是我们常见的灰度图像),后面是UV(颜色信息),4个Y共享一个U和V
,故byte数组的总大小是width*height*2/3
。
420sp通常是如下形式(UV交替属于NV12)(或者VU交替属于NV21):
[ [
Y Y Y Y Y Y Y Y
Y Y Y Y 或 Y Y Y Y
U V U V V U V U
] ]
先通过JNI把YUV420sp数据转换成YUV420p数据。420p就是YYYYYYYYUUVV这样存放的格式,相当于数组中的数据交换位置而已,很好实现。(当然,后来发现Opencv可以直接处理sp的数据格式)。
https://www.cnblogs.com/samaritan/p/YUV.html,这个博客详细介绍了YUV数据的格式。
###opencv数据类型
Mat的数据类型分为8U,16S,32F,64F等。在这里我们使用8U类型,YUV数据也是8位无符号类型的。C1,C2,C3,C4为通道数,YUV为单通道,即8UC1。
尝试一(认为可行,结果失败,求大神解答)
想着OpencvForAndroid中,初始化Mat中有一个方法为:
Mat mat = new Mat(int rows, int cols, int type, ByteBuffer data);
其中ByteBuffer为Nio包中的类,可以使用静态方法ByteBuffer.wrap(data)
进行转换。使用如下代码:
Mat mat = new Mat((int)frameHeight*1.5,frameWidth,CvType.CV_8UC1,ByteBuffer.wrap(data));
执行这一句的时候,一直报CvException错误,查看错误中是Mat的本地代码报的错,显示data.total为0。一直不明白怎么回事,放弃。
尝试二
OpencvForAndroid中还有一个方法可以初始化矩阵,或者称给矩阵赋值。
Mat mat = new Mat(frameHeight*1.5,frameWidth,CvType.CV_8UC1);//初始化一个矩阵,没数据
mat.put(0,0,data);//从(0,0)开始放数据,直到data放完或者矩阵被填满(若是多通道,则把当前位置的通道全部填满,才继续下一个位置,data长度必须整除通道数).
Mat bgr_i420 = new Mat();
Imgproc.cvtColor(mat , bgr_i420, Imgproc.COLOR_YUV2BGR_I420);//转换颜色空间
//对bgr格式的mat做一些常见的处理.
Mat result = new Mat();
Imgproc.cvtColor(bgr_i420, result , Imgproc.COLOR_BGR2YUV_I420);//转换回YUV420p格式
byte[] data_result = new byte[(int)frameHeight*frameWidth*1.5];
result.get(0,0,data_result);//从(0,0)开始取数据,过程同上
通过上面的代码,可以把YUV数据转成BGR格式,进行一系列处理,在转换成YUV数据,保存或者推流走。
结语
Imgproc.cvtColor(src, dst , Imgproc.COLOR_XXXXXX);
其中XXX有多种模式可选,可以直接将420SP(NV12/NV21)转换成BGR,具体的常量,自己打开工程试试就知道了。
data转成Mat,基于Mat可以用来做人脸识别、美颜、贴图、换脸等功能。