Android camera2 image yum 保存bitmap

轉(zhuǎn)發(fā)自:https://www.cnblogs.com/adamli/p/17187112.html
一、yuv簡介
yuv420p和yuv420sp
yuv420p(例如yv12):每兩行的4個字節(jié)對應(yīng)一個像素的y,每兩行的2個字節(jié)(uv)對應(yīng)前面的一個像素的y
yuv420sp(例如nv21):每兩行的4個字節(jié)對應(yīng)一個像素的y,每一行兩個字節(jié)(uv)對應(yīng)前面的一個像素的y
例如yv12 格式64
YYYYYY
YYYYYY
YYYYYY
YYYYYY
VVVVVV
UUUUUU
例如nv21 格式6
4
YYYYYY
YYYYYY
YYYYYY
YYYYYY
VUVUVU
VUVUVU

二、camera2 Android回調(diào)imagereader返回的 YUV_420_888 數(shù)據(jù),存儲方式
image = reader.acquireLatestImage();
Image.Plane[] planes = image.getPlanes(); //獲取yuv圖像的平面?zhèn)€數(shù),plane0返回的是y分量
Image.Plane plane = planes[i];
Buffer buffer = plane.getBuffer();
1.buffer.remaining() 獲取對應(yīng)平面字節(jié)個數(shù),
2.plane.getPixelStride() 獲取對應(yīng)平面的字節(jié)步長
3.plane.getRowStride() 獲取對應(yīng)平面的行步長
預(yù)覽分辨率為:1280720,這是獲取的 YUV_420_888格式對應(yīng)的yuv數(shù)據(jù)log日志,從log看看plane1和plane2 的getPixelStride 是2, 說明間隔的原色才是有效的元素
即plane1的行內(nèi)索引為0,2,4,6..對應(yīng)的是u分量中間插入的是v分量,且數(shù)組長度是1280
720/2 -1
即plane2的行內(nèi)索引為0,2,4,6..對應(yīng)的是v分量中間插入的是u分量,
2023-03-06 10:36:54.068 31203-31258 Camera2Fragment I getByteFromYuvReader() planes.length:3
2023-03-06 10:36:54.068 31203-31258 Camera2Fragment I getByteFromYuvReader() i:0 buffer.remaining:921600 getPixelStride:1 getRowStride:1280
2023-03-06 10:36:54.068 31203-31258 Camera2Fragment I getByteFromYuvReader() i:1 buffer.remaining:460799 getPixelStride:2 getRowStride:1280
2023-03-06 10:36:54.068 31203-31258 Camera2Fragment I getByteFromYuvReader() i:2 buffer.remaining:460799 getPixelStride:2 getRowStride:1280

三、拍攝yuv 并轉(zhuǎn)換為bitmap 保存的代碼實(shí)現(xiàn)
1.拍攝yuv格式圖片的方法
mImageReader = ImageReader.newInstance(Config.SHOOT_PIC_WIDTH,
Config.SHOOT_PIC_HEIGHT, ImageFormat.YUV_420_888, 1);

ps:Android官方 Android camera api1 默認(rèn)是:NV21,Android camera api2建議使用YUV_420_888

2.在 imagereader.onImageAvailable 回調(diào)處理

if (ImageFormat.YUV_420_888 == reader.getImageFormat()) {
Bitmap bitmap = getBitmapFromYuvReader(reader);
}

//從ImageReader中讀取yuv并轉(zhuǎn)成bitmap
private synchronized Bitmap getBitmapFromYuvReader(ImageReader reader) {
if (null == reader) {
Logger.i(TAG, "getBitmapFromYuvReader() reader is null return null");
return null;
}

Image image = null;
try {
byte[] plane0Y = null;
byte[] plane1WithU = null; //plane1 包含u
byte[] plane2WithV = null; //plane2 包含v
byte[] u = null;//真實(shí)的u
byte[] v = null;//真實(shí)的u
// fos = new FileOutputStream(file);
//獲取捕獲的照片數(shù)據(jù)
image = reader.acquireLatestImage();
if (null == image) {
Logger.w(TAG, "getBitmapFromYuvReader() image is null");
return null;
}
Image.Plane[] planes = image.getPlanes();
Logger.i(TAG, "getBitmapFromYuvReader() planes.length:" + planes.length);
if (planes.length != 3) {
return null;
}
// 重復(fù)使用同一批byte數(shù)組,減少gc頻率
if (plane0Y == null || plane1WithU == null || plane2WithV == null) {
plane0Y = new byte[planes[0].getBuffer().limit() - planes[0].getBuffer().position()];
plane1WithU = new byte[planes[1].getBuffer().limit() - planes[1].getBuffer().position()];
plane2WithV = new byte[planes[2].getBuffer().limit() - planes[2].getBuffer().position()];
}
for (int i = 0; i < planes.length; i++) {
Image.Plane plane = planes[i];
Buffer buffer = plane.getBuffer();
//1280*720
Logger.i(TAG, "getBitmapFromYuvReader() i:" + i + " buffer.remaining:" + buffer.remaining()
+ " getPixelStride:" + plane.getPixelStride() + " getRowStride:" + plane.getRowStride());
}
if (image.getPlanes()[0].getBuffer().remaining() == plane0Y.length) {
planes[0].getBuffer().get(plane0Y);
planes[1].getBuffer().get(plane1WithU);
planes[2].getBuffer().get(plane2WithV);
if (planes[1].getPixelStride() == 2) { //sp
//提取U v分量 ,這里需要+1,因?yàn)閜lane1和plane2都是少存儲一個字節(jié)
u = new byte[(plane1WithU.length + 1) / 2];
v = new byte[(plane2WithV.length + 1) / 2];
int index_u = 0;
int index_v = 0;
for (int i = 0; i < plane1WithU.length; i++) {
if (0 == (i % 2)) {
u[index_u] = plane1WithU[i];
index_u++;
}
}
for (int j = 0; j < plane2WithV.length; j++) {
if (0 == (j % 2)) {
v[index_v] = plane2WithV[j];
index_v++;
}
}
}
byte[] arrayNV21 = getArrayNV21FromYuv(plane0Y, u, v);
final int WIDTH = Config.SHOOT_PIC_WIDTH;
final int HEIGHT = Config.SHOOT_PIC_HEIGHT;
Logger.i(TAG, "getBitmapFromYuvReader() arrayNV21.length:" + arrayNV21.length);
YuvImage yuvImage = new YuvImage(arrayNV21, ImageFormat.NV21, WIDTH, HEIGHT, null);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
yuvImage.compressToJpeg(new Rect(0, 0, WIDTH, HEIGHT), 80, stream);
Bitmap newBitmap = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size());
stream.close();
return newBitmap;
}

} catch (Exception ex) {
Logger.i(TAG, "getBitmapFromYuvReader() error:" + ex);
} finally {

    //記得關(guān)閉 image
    if (image != null) {
        image.close();
    }
}
return null;
}
//將yuv 數(shù)據(jù)合并成 nv21格式的byte數(shù)組
private byte[] getArrayNV21FromYuv(byte[] y, byte[] u, byte[] v) {
//正常來說y長度是WIDTH*HEIGHT,u和v的長度是WIDTH*HEIGHT/4
final int WIDTH = Config.SHOOT_PIC_WIDTH;//圖片寬
final int HEIGHT = Config.SHOOT_PIC_HEIGHT;//圖片寬
if (WIDTH * HEIGHT != y.length) {
Logger.i(TAG, "getArrayNV21FromYuv() y length is error");
return null;
}
if ((WIDTH * HEIGHT / 4) != u.length || (WIDTH * HEIGHT / 4) != v.length) {
Logger.i(TAG, "getArrayNV21FromYuv() u or v length is error!");
return null;
}
int lengthY = y.length;
int lengthU = u.length;
int lengthV = u.length;
int newLength = lengthY + lengthU + lengthV;
byte[] arrayNV21 = new byte[newLength];
//先將所有的Y數(shù)據(jù)存儲進(jìn)去
System.arraycopy(y, 0, arrayNV21, 0, y.length);

//然后交替存儲VU數(shù)據(jù)(注意U,V數(shù)據(jù)的長度應(yīng)該是相等的,記住順序是VU VU)
for (int i = 0; i < v.length; i++) {
int index = lengthY + i * 2;
arrayNV21[index] = v[i];
}

for (int i = 0; i < u.length; i++) {
int index = lengthY + i * 2 + 1;
arrayNV21[index] = u[i];
}
Logger.i(TAG, "getArrayNV21FromYuv()");
return arrayNV21;
}

----耗時分析----------------
如log所示 將yuv byte 數(shù)組 保存成 nv21 數(shù)組耗時12毫秒,從reader取出 yuv byte 數(shù)組 到保存轉(zhuǎn)換成nv21數(shù)組總共耗時17毫秒, 將nv21 數(shù)組 轉(zhuǎn)換成bitmap 耗時24毫秒
總共 從imagereader回調(diào),到轉(zhuǎn)換成bitmap總共 耗時43毫秒,
2023-01-01 02:01:38.071 10657-10692 ImageUtil org.opencv.cameratest I getBitmapFromYuvReader 22 arrayNV21.length:1382400 diffYuv2Nv21Mills:12 diffNv21FromReaderMills:17
2023-01-01 02:01:38.096 10657-10692 CvUtil org.opencv.cameratest E getCvFaceBitmap mCacheBitmap:android.graphics.Bitmap@45ff5f6 diffMills:24
2023-01-01 02:01:38.098 10657-10692 ImageUtil org.opencv.cameratest I getCheckedBitmap end diffMills:43 format:35

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容