前言
最近公司安排一个预研项目:语音跟踪&电子变焦。就是检测声音方向的人脸,如果有,则将人脸附近区域全屏显示。Camera设置4k预览,像素3840x2160,NV21(YUV420SP)格式,一帧大小为 3840x2160x1.5=12441600byte,差不多12M。人脸检测用虹软,单帧最大10M,这就超过了最大值了。试了下,人脸识别也是可以,但是一次识别时间将近50ms,太耗时了。又因为我们只需要检测声源方向,所以,可以从3840x2160图像中裁剪一个720x1620的区域用于人脸识别即可。本文记录如何从NV21图像中任意裁剪一块区域的方法。
NV21(YUV420SP)各式
YUV,是一种颜色编码方法。常使用在各个视频处理组件中。 YUV在对照片或视频编码时,考虑到人类的感知能力,允许降低色度的带宽。
YUV是编译true-color颜色空间(color space)的种类,Y’UV, YUV, YCbCr,YPbPr等专有名词都可以称为YUV,彼此有重叠。“Y”表示明亮度(Luminance或Luma),也就是灰阶值,“U”和“V”表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。
Nv21各式,一个UV对应邻近的4个Y,存储一个Y占一个字节,加上一张图像宽高分别为w和h,则Y=wh,UV=wh/2。在磁盘上是连续存储的,先存放Y,然后是UV交叉排列。
在Android中,可以给Camera设置一个PreviewCallback对象,在onPreviewFrame(byte[] data,Camera camera)中返回数据。data就是一帧图像,可以把它想像成一个而为数组,宽是w,高是1.5h,如下表所示是一个4x4像素的YUV的data[],按照步长4来分解为一个二维数组:
Y | Y | Y | Y |
Y | Y | Y | Y |
Y | Y | Y | Y |
Y | Y | Y | Y |
U | V | U | V |
U | V | U | V |
可以参考NV21与I420,有图参考方便理解。
裁剪
通过分析NV21的存储各式,使用Java的System.arraycopy(),裁剪就像用笔在表格上画矩形一样简单了。但是有一个条件,首先左上角顶点坐标(left, top)和裁剪的宽高,都得是偶数,代码很简单,如下:
/**
* @param src 原始数据
* @param width 原始图像的width
* @param height 原始图像height
* @param left 裁剪区域左上角的x