Android集成native库libyuv转换图像旋转图像
最近的项目,用到了人脸识别,虽然谷歌推出的CameraX极大简化了相机数据的获取,但我们使用的SDK只能处理 NV21 数据,还要方向正确才能识别人脸,更专业的YUV知识我是不懂的,刚开始用CameraX获取的 YUV420 数据喂给SDK,能识别出人脸,就以为 NV21 是 YUV420 的一种特例(实际上也是),但CameraX获得的是I420,也是YUV420的一种,不过跟NV21是有区别的,直到测试发现活体检测会误把图片检测成活体,但SDK提供方提供的示例代码不会,查看代码发现他们使用的是Android5.0就已经过时的Camera API,获取的是NV21(YCbCr)数据,后面搜索了解了一下这两种格式的区别,就想着把原始数据转成Bitmap看看,官方有YUV420转Bitmap的Java示例代码,但在我们使用的设备上耗时较多,然后果断选择大家推荐的专业的图像处理库libyuv,没点专业知识,使用起来还是有一丢丢难度的,所以写个文章记录一下,集成libyuv参考这位网友的文章,下面是我项目中用到的代码,主要功能有:
1、NV21转Bitmap
2、旋转NV21
3、旋转Image并输出NV21
4、旋转Image并输出Bitmap
NativeLib.java
package com.ttcble.libyuv;import android.graphics.Bitmap;
import android.media.Image;import java.nio.ByteBuffer;public class NativeLib {// Used to load the 'libyuv' library on application startup.static {System.loadLibrary("libyuv");}private native int nv21ToABGR(byte[] nv21, int width, int height, Bitmap outbmp);/*** NV21转Bitmap*/public Bitmap nv21ToABGR(byte[] nv21, int width, int height) {Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);nv21ToABGR(nv21, width, height, bitmap);return bitmap;}/*** 旋转NV21*/public native byte[] rotateNV21(byte[] data, int width, int height, int degrees);private native byte[] rotateToNV21(ByteBuffer yData, int src_stride_y,ByteBuffer uData, int src_stride_u,ByteBuffer vData, int src_stride_v,int src_pixel_stride_uv,int width, int height, int degrees);/*** 旋转Image并输出NV21*/public NV21Data rotateToNV21(Image image, int degrees) {int srcWidth = image.getWidth();int srcHeight = image.getHeight();int dstWidth;int dstHeight;if (degrees == 90 || degrees == 270) {dstWidth = srcHeight;dstHeight = srcWidth;} else {dstWidth = srcWidth;dstHeight = srcHeight;}Image.Plane yPlane = image.getPlanes()[0];Image.Plane uPlane = image.getPlanes()[1];Image.Plane vPlane = image.getPlanes()[2];byte[] data = rotateToNV21(yPlane.getBuffer(), yPlane.getRowStride(),uPlane.getBuffer(), uPlane.getRowStride(),vPlane.getBuffer(), vPlane.getRowStride(),uPlane.getPixelStride(),srcWidth, srcHeight, degrees);return new NV21Data(data, dstWidth, dstHeight);}private native void rotateToABGR(ByteBuffer yData, int src_stride_y,ByteBuffer uData, int src_stride_u,ByteBuffer vData, int src_stride_v,int src_pixel_stride_uv,int width, int height, int degrees, Bitmap outbmp);/*** 旋转Image并输出Bitmap** @param degrees 取值 0, 90, 180, 270*/public Bitmap rotateToABGR(Image image, int degrees) {int srcWidth = image.getWidth();int srcHeight = image.getHeight();int dstWidth;int dstHeight;if (degrees == 90 || degrees == 270) {dstWidth = srcHeight;dstHeight = srcWidth;} else {dstWidth = srcWidth;dstHeight = srcHeight;}Image.Plane yPlane = image.getPlanes()[0];Image.Plane uPlane = image.getPlanes()[1];Image.Plane vPlane = image.getPlanes()[2];Bitmap bitmap = Bitmap.createBitmap(dstWidth, dstHeight, Bitmap.Config.ARGB_8888);rotateToABGR(yPlane.getBuffer(), yPlane.getRowStride(),uPlane.getBuffer(), uPlane.getRowStride(),vPlane.getBuffer(), vPlane.getRowStride(),uPlane.getPixelStride(),srcWidth, srcHeight, degrees, bitmap);return bitmap;}
}
libyuv.cpp
#include
#include
#include
#include
#include #define TAG "libyuv-jni"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__)extern "C"
JNIEXPORT jint JNICALL
Java_com_ttcble_libyuv_NativeLib_nv21ToABGR(JNIEnv *env, jobject thiz, jbyteArray nv21, jint width, jint height, jobject outbmp) {jboolean jb = JNI_FALSE;jbyte *nv21_data = (env->GetByteArrayElements(nv21, &jb));if (outbmp != nullptr) {AndroidBitmapInfo info;AndroidBitmap_getInfo(env, outbmp, &info);if (info.format != 1) {LOGE("only support ANDROID_BITMAP_FORMAT_RGBA_8888");return -1;}uint8_t *ptr_jbitmap = nullptr;AndroidBitmap_lockPixels(env, outbmp, (void **) &ptr_jbitmap);AndroidBitmap_unlockPixels(env, outbmp);if (ptr_jbitmap == nullptr) return -1;u_int8_t *ptr_y = (u_int8_t *) nv21_data;u_int8_t *ptr_uv = ptr_y + width * height;libyuv::NV21ToABGR(ptr_y, width, ptr_uv, width, ptr_jbitmap, info.stride, info.width,info.height);(*env).ReleaseByteArrayElements(nv21, nv21_data, 0);} else return -1;return 0;
}extern "C"
JNIEXPORT void JNICALL
Java_com_ttcble_libyuv_NativeLib_rotateToABGR(JNIEnv *env, jobject thiz, jobject y_data, jint src_stride_y, jobject u_data, jint src_stride_u,jobject v_data, jint src_stride_v, jint src_pixel_stride_uv, jint width, jint height, jint degrees,jobject outbmp) {auto *src_y = (uint8_t *) env->GetDirectBufferAddress(y_data);auto *src_u = (uint8_t *) env->GetDirectBufferAddress(u_data);auto *src_v = (uint8_t *) env->GetDirectBufferAddress(v_data);int i420_stride_y;switch (degrees) {case 90:case 270:i420_stride_y = height;break;case 180:default:i420_stride_y = width;break;}auto *src_i420_data = (uint8_t *) malloc(sizeof(uint8_t) * width * height * 3 / 2);jint src_y_size = width * height;jint src_u_size = (width >> 1) * (height >> 1);uint8_t *src_i420_y_data = src_i420_data;uint8_t *src_i420_u_data = src_i420_data + src_y_size;uint8_t *src_i420_v_data = src_i420_data + src_y_size + src_u_size;libyuv::Android420ToI420Rotate(src_y, src_stride_y,src_u, src_stride_u,src_v, src_stride_v,src_pixel_stride_uv,src_i420_y_data, i420_stride_y,src_i420_u_data, i420_stride_y >> 1,src_i420_v_data, i420_stride_y >> 1,width, height, libyuv::RotationMode(degrees));AndroidBitmapInfo info;AndroidBitmap_getInfo(env, outbmp, &info);uint8_t *ptr_jbitmap = nullptr;AndroidBitmap_lockPixels(env, outbmp, (void **) &ptr_jbitmap);AndroidBitmap_unlockPixels(env, outbmp);libyuv::I420ToABGR(src_i420_y_data, i420_stride_y,src_i420_u_data, i420_stride_y >> 1,src_i420_v_data, i420_stride_y >> 1,ptr_jbitmap, (int) info.stride,(int) info.width, (int) info.height);free(src_i420_data);
}extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_ttcble_libyuv_NativeLib_rotateNV21(JNIEnv *env, jobject thiz, jbyteArray data, jint width, jint height, jint degrees) {static jboolean jb = JNI_FALSE;uint8_t *nv21_data = (uint8_t *) (env->GetByteArrayElements(data, &jb));uint8_t *src_y = nv21_data;uint8_t *src_uv = src_y + width * height;auto length = sizeof(uint8_t) * width * height * 3 / 2;auto *src_i420_data = (uint8_t *) malloc(length);jint src_y_size = width * height;jint src_u_size = (width >> 1) * (height >> 1);uint8_t *src_i420_y_data = src_i420_data;uint8_t *src_i420_u_data = src_i420_data + src_y_size;uint8_t *src_i420_v_data = src_i420_data + src_y_size + src_u_size;libyuv::NV21ToI420(src_y, width,src_uv, width,src_i420_y_data, width,src_i420_u_data, width >> 1,src_i420_v_data, width >> 1,width, height);auto *dst_i420_data = (uint8_t *) malloc(length);uint8_t *dst_i420_y_data = dst_i420_data;uint8_t *dst_i420_u_data = dst_i420_data + src_y_size;uint8_t *dst_i420_v_data = dst_i420_data + src_y_size + src_u_size;int dst_stride;int dst_width;int dst_height;if (degrees == 0 || degrees == 180) {dst_stride = width;dst_width = width;dst_height = height;} else {dst_stride = height;dst_width = height;dst_height = width;}libyuv::I420Rotate(src_i420_y_data, width,src_i420_u_data, width >> 1,src_i420_v_data, width >> 1,dst_i420_y_data, dst_stride,dst_i420_u_data, dst_stride >> 1,dst_i420_v_data, dst_stride >> 1,width, height, libyuv::RotationMode(degrees));auto *dst_nv21_data = (uint8_t *) malloc(length);uint8_t *dst_y = dst_nv21_data;uint8_t *dst_uv = dst_y + width * height;libyuv::I420ToNV21(dst_i420_y_data, dst_stride,dst_i420_u_data, dst_stride >> 1,dst_i420_v_data, dst_stride >> 1,dst_y, dst_stride,dst_uv, dst_stride,dst_width, dst_height);jbyteArray result = (*env).NewByteArray(length);jbyte *arr = (*env).GetByteArrayElements(result, nullptr);jint i;for (i = 0; i < length; i++) {arr[i] = (jbyte) (dst_y[i]);}//下面这行很关键, JNI array reference table最多容纳1024个指针(*env).ReleaseByteArrayElements(result, arr, 0);free(src_i420_data);free(dst_i420_y_data);free(dst_nv21_data);return result;
}extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_ttcble_libyuv_NativeLib_rotateToNV21(JNIEnv *env, jobject thiz, jobject y_data, jint src_stride_y, jobject u_data, jint src_stride_u,jobject v_data, jint src_stride_v, jint src_pixel_stride_uv, jint width, jint height, jint degrees) {auto *src_y = (uint8_t *) env->GetDirectBufferAddress(y_data);auto *src_u = (uint8_t *) env->GetDirectBufferAddress(u_data);auto *src_v = (uint8_t *) env->GetDirectBufferAddress(v_data);int i420_stride_y;int dst_width;int dst_height;if (degrees == 0 || degrees == 180) {i420_stride_y = width;dst_width = width;dst_height = height;} else {i420_stride_y = height;dst_width = height;dst_height = width;}auto *src_i420_data = (uint8_t *) malloc(sizeof(uint8_t) * width * height * 3 / 2);jint src_y_size = width * height;jint src_u_size = (width >> 1) * (height >> 1);uint8_t *src_i420_y_data = src_i420_data;uint8_t *src_i420_u_data = src_i420_data + src_y_size;uint8_t *src_i420_v_data = src_i420_data + src_y_size + src_u_size;libyuv::Android420ToI420Rotate(src_y, src_stride_y,src_u, src_stride_u,src_v, src_stride_v,src_pixel_stride_uv,src_i420_y_data, i420_stride_y,src_i420_u_data, i420_stride_y >> 1,src_i420_v_data, i420_stride_y >> 1,width, height, libyuv::RotationMode(degrees));auto length = sizeof(uint8_t) * width * height * 3 / 2;auto *dst_nv21_data = (uint8_t *) malloc(length);uint8_t *dst_y = dst_nv21_data;uint8_t *dst_uv = dst_y + width * height;libyuv::I420ToNV21(src_i420_y_data, i420_stride_y,src_i420_u_data, i420_stride_y >> 1,src_i420_v_data, i420_stride_y >> 1,dst_y, i420_stride_y,dst_uv, i420_stride_y,dst_width, dst_height);jbyteArray result = (*env).NewByteArray(length);jbyte *arr = (*env).GetByteArrayElements(result, nullptr);jint i;for (i = 0; i < length; i++) {arr[i] = (jbyte) (dst_y[i]);}//下面这行很关键, JNI array reference table最多容纳1024个指针(*env).ReleaseByteArrayElements(result, arr, 0);free(src_i420_data);free(dst_nv21_data);return result;
}
NV21Data.java
package com.ttcble.libyuv;public class NV21Data {private final byte[] data;private final int width;private final int height;public NV21Data(byte[] data, int width, int height) {this.data = data;this.width = width;this.height = height;}public byte[] getData() {return data;}public int getWidth() {return width;}public int getHeight() {return height;}
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
