Android 虹软人脸识别 NV21 和 RGB24 转换问题
博主介绍
🌊 作者主页:苏州程序大白
🌊 作者简介:🏆CSDN人工智能域优质创作者🥇,苏州市凯捷智能科技有限公司创始之一,目前合作公司富士康、歌尔等几家新能源公司
💬如果文章对你有帮助,欢迎关注、点赞、收藏(一键三连)和C#、Halcon、python+opencv、VUE、各大公司面试等一些订阅专栏哦
💅 有任何问题欢迎私信,看到会及时回复 💅关注苏州程序大白,分享粉丝福利
前言
当我们使用活体检测检测完毕后,需要上传图片给后台进行人脸识别,在 Android 里面默认的 Camera 获取的图片格式是 NV21.
怎么知道这个默认格式呢?
Camera.Parameters parameters = camera.getParameters();
parameters.getPreviewFormat();
这个格式的图片本身虹软是支持的,但是有个条件限制:SDK 对图形尺寸做了限制,宽高需要大于 0,宽度为 4 的倍数, YUYV/I420/NV21/NV12 格式的图片高度为 2 的倍数, RGB24 格式的图片高度不限制。如果你的图片是 NV21 格式不符合这个条件就需要进行转换。
private static final int VALUE_FOR_4_ALIGN = 0b11;
private static final int VALUE_FOR_2_ALIGN = 0b01;
**
* 确保传给引擎的NV21数据宽度为4的倍数,高为2的倍数
*
* @param bitmap 传入的bitmap
* @return 调整后的bitmap
*
public static Bitmap alignBitmapForNv21(Bitmap bitmap) {
if (bitmap == null || bitmap.getWidth() < 4 || bitmap.getHeight() < 2) {
return null;
}
int width = bitmap.getWidth();
int height = bitmap.getHeight();
boolean needAdjust = false;
//保证宽度是4的倍数
if ((width & VALUE_FOR_4_ALIGN) != 0) {
width &= ~VALUE_FOR_4_ALIGN;
needAdjust = true;
}
//保证高度是2的倍数
if ((height & VALUE_FOR_2_ALIGN) != 0) {
height--;
needAdjust = true;
}
if (needAdjust) {
bitmap = imageCrop(bitmap, new Rect(0, 0, width, height));
}
return bitmap;
}
如何从 onPreviewFrame(byte[] data, Camera camera)
回调中获取 Bitmap 对象呢?这个时候就需要用到 YuvImage
。
**
什么是 YuvImage**
YuvImage 包含了 YUV 数据,并且提供了一个将 YUV 数据压缩成 Jpeg 数据的方法。
什么时候使用
相机 Camera 类的 PreviewCallback 回调中,这个接口回调的是相机的预览图片,是 YUV 格式的数据,这是,利用 YuvImage 对象的 compressToJpeg 方法生成 Jpeg 格式的图片保存在本地。
Camera.Parameters parameters = camera.getParameters();
Camera.Size size = parameters.getPreviewSize();
YuvImage image = new YuvImage(data, parameters.getPreviewFormat(),
size.width, size.height, null);
File file = new File(Environment.getExternalStorageDirectory()
.getPath() + "/check_face.jpg");
FileOutputStream filecon = new FileOutputStream(file);
image.compressToJpeg(new Rect(0, 0, image.getWidth(), image.getHeight()), 90, filecon);
当然也可以使用如下方式转换为 Bitmap 对象:
Camera.Parameters parameters = camera.getParameters();
Camera.Size size = parameters.getPreviewSize();
YuvImage image = new YuvImage(data, parameters.getPreviewFormat(),
size.width, size.height, null);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
image.compressToJpeg(new Rect(0, 0, image.getWidth(), image.getHeight()), 90, outputStream);
byte[] jpegData = outputStream.toByteArray();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
Bitmap nv21Bitmap = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
得到 Bitmap 对象后我们可以先进行旋转等处理,当然上面的 compressToJpeg
中的 Rect 最好是头像的轮廓,而不是整个画布,这样可以极大的缩小图片大小。
Matrix matrix = new Matrix();
//matrix.postScale(1, 1);
matrix.postRotate(cameraOri);
Bitmap matrixedBitmap = Bitmap.createBitmap(nv21Bitmap, 0, 0, nv21Bitmap.getHeight(), nv21Bitmap.getWidth(), matrix, true);
最后进行 NV21 的宽高检测,确保传给引擎的 NV21 数据宽度为 4 的倍数,高为 2 的倍数,然后保存为文件上传给服务器。
Bitmap checkedBitmap = ImageUtils.alignBitmapForNv21(matrixedBitmap);
File file = saveBitmapToFile(checkedBitmap, Environment.getExternalStorageDirectory()
.getPath() + "/check_face.jpg");
LogPlus.d("http", "开始上传头像");
最后再给出一些常用的工具方法:
保存Bitmap到文件
private File saveBitmapToFile(Bitmap bitmap, String filePath)
{
if (bitmap == null || TextUtils.isEmpty(filePath)) {
return null;
}
try {
File file = new File(filePath);
if (!file.exists() && file.getParentFile().mkdirs() && !file.createNewFile())
{
return null;
}
OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
os.close();
return file;
} catch (IOException e)
{
e.printStackTrace();
return null;
}
}
NV21 转换为 Bitmap 对象
private Bitmap nv21ToBitmap(byte[] data, int width, int height)
{
YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, width, height, null);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
yuvImage.compressToJpeg(new Rect(0, 0, yuvImage.getWidth(), yuvImage.getHeight()), 70, outputStream);
byte[] jpegData = outputStream.toByteArray();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
return BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
}
确保传给引擎的 BGR24 数据宽度为4的倍数
public static Bitmap alignBitmapForBgr24(Bitmap bitmap
{
if (bitmap == null || bitmap.getWidth() < 4)
{
return null;
}
int width = bitmap.getWidth();
int height = bitmap.getHeight();
boolean needAdjust = false;
//保证宽度是4的倍数
if ((width & VALUE_FOR_4_ALIGN) != 0)
{
width &= ~VALUE_FOR_4_ALIGN;
needAdjust = true;
}
if (needAdjust) {
bitmap = imageCrop(bitmap, new Rect(0, 0, width, height));
}
return bitmap;
}
Bitmap 转化为 RGB 数据(格式为 Bitmap.Config#ARGB_8888)
public static byte[] bitmapToBgr(Bitmap image)
{
if (image == null)
{
return null;
}
int bytes = image.getByteCount();
ByteBuffer buffer = ByteBuffer.allocate(bytes);
image.copyPixelsToBuffer(buffer);
byte[] temp = buffer.array();
byte[] pixels = new byte[(temp.length / 4) * 3];
for (int i = 0; i < temp.length / 4; i++)
{
pixels[i * 3] = temp[i * 4 + 2];
pixels[i * 3 + 1] = temp[i * 4 + 1];
pixels[i * 3 + 2] = temp[i * 4];
}
return pixels;
}