需求:使用虹软活体检测时,需要截取检测框中的人脸
Camera.PreviewCallback中onPreviewFrame(byte[] data, Camera camera)返回的data字节数组不是bitmap 的编码,需要转移一下:
下面列出几种方法:
1.有可能发生内存溢出:
YuvImage image = new YuvImage(bytes, ImageFormat.NV21, width, height, null);
ByteArrayOutputStream os = new ByteArrayOutputStream(bytes.length);
if (!image.compressToJpeg(rect, 100, os)) {
return null;
}
byte[] tmp = os.toByteArray();
Bitmap bmp = BitmapFactory.decodeByteArray(tmp, 0, tmp.length);
2.转码:
//YCbCr_420_SP-->(解码,yuv420sp转为RGB格式 )
static public void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width,
int height) {
final int frameSize = width * height;
for (int j = 0, yp = 0; j < height; j++) {
int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
for (int i = 0; i < width; i++, yp++) {
int y = (0xff & ((int) yuv420sp[yp])) - 16;
if (y < 0)
y = 0;
if ((i & 1) == 0) {
v = (0xff & yuv420sp[uvp++]) - 128;
u = (0xff & yuv420sp[uvp++]) - 128;
}
int y1192 = 1192 * y;
int r = (y1192 + 1634 * v);
int g = (y1192 - 833 * v - 400 * u);
int b = (y1192 + 2066 * u);
if (r < 0)
r = 0;
else if (r > 262143)
r = 262143;
if (g < 0)
g = 0;
else if (g > 262143)
g = 262143;
if (b < 0)
b = 0;
else if (b > 262143)
b = 262143;
rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000)
| ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
}
}
}
或者
//图片字节转换
public static Bitmap rawByteArray2RGBABitmap2(byte[] data, int width, int height) {
int frameSize = width * height;
int[] rgba = new int[frameSize];
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++) {
int y = (0xff & ((int) data[i * width + j]));
int u = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1)]));
int v = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 1]));
y = y < 16 ? 16 : y;
int r = Math.round(1.164f * (y - 16) + 1.596f * (v - 128));
int g = Math.round(1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128));
int b = Math.round(1.164f * (y - 16) + 2.018f * (u - 128));
r = r < 0 ? 0 : (r > 255 ? 255 : r);
g = g < 0 ? 0 : (g > 255 ? 255 : g);
b = b < 0 ? 0 : (b > 255 ? 255 : b);
rgba[i * width + j] = 0xff000000 + (b << 16) + (g << 8) + r;
}
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
bmp.setPixels(rgba, 0, width, 0, 0, width, height);
return bmp;
}
3.这种需要sdk 17以上
public Bitmap convertYUVtoRGB(byte[] yuvData, int width, int height) {
if (yuvType == null) {
yuvType = new Type.Builder(rs, Element.U8(rs)).setX(yuvData.length);
in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);
Type.Builder rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height);
out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);
}
in.copyFrom(yuvData);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
yuvToRgbIntrinsic.setInput(in);
yuvToRgbIntrinsic.forEach(out);
Bitmap bmpout = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
out.copyTo(bmpout);
return bmpout;
}
return null;
}
其中的对象初始化:
RenderScript rs = RenderScript.create(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
ScriptIntrinsicYuvToRGB= yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
}
现在说正题了:
前置左右镜像问题:
final YuvImage image = new YuvImage(bytes, ImageFormat.NV21, width, height, null);
ByteArrayOutputStream os = new ByteArrayOutputStream(bytes.length);
if (!image.compressToJpeg(rect, 100, os)) {
return null;
}
byte[] tmp = os.toByteArray();
Bitmap bmp = BitmapFactory.decodeByteArray(tmp, 0, tmp.length);
Matrix matrix = new Matrix();
Log.i(TAG, "getBitmapFromByte: displayOrientation-->" + displayOrientation);
matrix.preRotate(360 - displayOrientation);
//重点--->前置摄像头生成图片 左右镜像问题
matrix.postScale(-1, 1);
return Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp
.getHeight(), matrix, true);
镜像上下颠倒,需要设置相机预览的显示方向
public static void setCameraDisplayOrientation(Activity activity,
int cameraId, android.hardware.Camera camera) {
try {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
} catch (Exception e) {
CommonUtil.printStackTrace(e);
}
}
之后做旋转:
Matrix matrix = new Matrix();
matrix.postRotate(CamParaUtil.getCameraDisplayOrientation(StickerMakeActivity.this, mCameraId, mCamera));
matrix.postScale(scale, isCameraFront() ? -scale : scale);
Bitmap cropRotateScaled = Bitmap.createBitmap(origin, (int) resultRect.left, (int) resultRect.top, (int) resultRect.width(), (int) resultRect.height(), matrix, true);
其中用到的方法:
public static int getCameraDisplayOrientation(Activity activity,
int cameraId, android.hardware.Camera camera) {
int result = 90;
try {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
} catch (Exception e) {
CommonUtil.printStackTrace(e);
}
return result;
}
镜像来自自android开发步步为营之112:关于Camera镜像上下左右颠倒问题的解决办法