Android下使用camera2和Surfaceview预览图像并取得YUV420p数据回调

本文主要是介绍Android下使用camera2和Surfaceview预览图像并取得YUV420p数据回调,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Android 5.0(21)之后,android.hardware.Camera被废弃(下面称为Camera1),还有一个android.graphics.Camera,这个android.graphics.Camera不是用来照相的,是用来处理图像的,可以做出3D的图像效果之类的,之前的Camera1则由android.hardware.Camera2来代替。

Camera2支持RAW输出,可以调节曝光,对焦模式,快门等,功能比原先Camera强大。

由于ImageFormat只有YUV420_888即YUV420系列,这里需要将其做一个转换,转成所需要的YUV420p:yyyyyyyyuuuuvvvv格式,参考文章:android camera2 拿到的yuv420数据到底是什么样的?

1、MainActivity.java文件:

package com.example.tongjiangsong.camera2base;import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.media.Image;
import android.media.ImageReader;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.annotation.RequiresApi;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.util.SparseIntArray;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;public class MainActivity extends AppCompatActivity implements View.OnClickListener {private static final SparseIntArray ORIENTATIONS = new SparseIntArray();private static final String TAG = "mainactivity" ;///为了使照片竖直显示static {ORIENTATIONS.append(Surface.ROTATION_0, 90);ORIENTATIONS.append(Surface.ROTATION_90, 0);ORIENTATIONS.append(Surface.ROTATION_180, 270);ORIENTATIONS.append(Surface.ROTATION_270, 180);}private SurfaceView mSurfaceView;private SurfaceHolder mSurfaceHolder;private ImageView iv_show;private CameraManager mCameraManager;//摄像头管理器private Handler childHandler, mainHandler;private String mCameraID;//摄像头Id 0 为后  1 为前private ImageReader mImageReaderJPG;private ImageReader mImageReaderPreview;private CameraCaptureSession mCameraCaptureSession;private CameraDevice mCameraDevice;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button capture_btn = (Button)findViewById(R.id.capture_btn);capture_btn.setOnClickListener(this);initVIew();}/*** 初始化*/private void initVIew() {iv_show = (ImageView) findViewById(R.id.image_preview);//mSurfaceViewmSurfaceView = (SurfaceView) findViewById(R.id.surface_view);mSurfaceView.setOnClickListener(this);mSurfaceHolder = mSurfaceView.getHolder();mSurfaceHolder.setKeepScreenOn(true);// mSurfaceView添加回调mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {@Overridepublic void surfaceCreated(SurfaceHolder holder) { //SurfaceView创建// 初始化CamerainitCamera2();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) { //SurfaceView销毁// 释放Camera资源if (null != mCameraDevice) {mCameraDevice.close();MainActivity.this.mCameraDevice = null;}}});}/*** 初始化Camera2*/@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)private void initCamera2() {HandlerThread handlerThread = new HandlerThread("Camera2");handlerThread.start();childHandler = new Handler(handlerThread.getLooper());mainHandler = new Handler(getMainLooper());mCameraID = "" + CameraCharacteristics.LENS_FACING_FRONT;//后摄像头mImageReaderJPG = ImageReader.newInstance(1080, 1920, ImageFormat.JPEG,1);mImageReaderJPG.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { //可以在这里处理拍照得到的临时照片 例如,写入本地@Overridepublic void onImageAvailable(ImageReader reader) {//mCameraDevice.close();//mSurfaceView.setVisibility(View.GONE);//先验证手机是否有sdcardString status = Environment.getExternalStorageState();if (!status.equals(Environment.MEDIA_MOUNTED)) {Toast.makeText(getApplicationContext(), "你的sd卡不可用。", Toast.LENGTH_SHORT).show();return;}// 获取捕获的照片数据Image image = reader.acquireNextImage();ByteBuffer buffer = image.getPlanes()[0].getBuffer();byte[] data = new byte[buffer.remaining()];buffer.get(data);//手机拍照都是存到这个路径String filePath = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/";String picturePath = System.currentTimeMillis() + ".jpg";File file = new File(filePath, picturePath);try {//存到本地相册FileOutputStream fileOutputStream = new FileOutputStream(file);fileOutputStream.write(data);fileOutputStream.close();iv_show.setVisibility(View.VISIBLE);//显示图片BitmapFactory.Options options = new BitmapFactory.Options();options.inSampleSize = 2;Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);iv_show.setImageBitmap(bitmap);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {image.close();}}}, mainHandler);mImageReaderPreview = ImageReader.newInstance(1080, 1920, ImageFormat.YUV_420_888,1);mImageReaderPreview.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { //可以在这里处理拍照得到的临时照片 例如,写入本地@Overridepublic void onImageAvailable(ImageReader reader) {// 获取捕获的照片数据Image image = reader.acquireNextImage();Log.i(TAG,"image format: " +image.getFormat());// 从image里获取三个planeImage.Plane[] planes = image.getPlanes();for (int i = 0; i < planes.length; i++) {ByteBuffer iBuffer = planes[i].getBuffer();int iSize = iBuffer.remaining();Log.i(TAG, "pixelStride  " + planes[i].getPixelStride());Log.i(TAG, "rowStride   " + planes[i].getRowStride());Log.i(TAG, "width  " + image.getWidth());Log.i(TAG, "height  " + image.getHeight());Log.i(TAG, "Finished reading data from plane  " + i);}int n_image_size = image.getWidth()*image.getHeight()*3/2;final byte[] yuv420pbuf = new byte[n_image_size];System.arraycopy(ImageUtil.getBytesFromImageAsType(image, 0), 0, yuv420pbuf, 0, n_image_size);image.close();}}, null);//获取摄像头管理mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);try {if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {return;}//打开摄像头mCameraManager.openCamera(mCameraID, stateCallback, mainHandler);} catch (CameraAccessException e) {e.printStackTrace();}}/*** 摄像头创建监听*/private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {@Overridepublic void onOpened(CameraDevice camera) {//打开摄像头mCameraDevice = camera;//开启预览takePreview();}@Overridepublic void onDisconnected(CameraDevice camera) {//关闭摄像头if (null != mCameraDevice) {mCameraDevice.close();MainActivity.this.mCameraDevice = null;}}@Overridepublic void onError(CameraDevice camera, int error) {//发生错误Toast.makeText(MainActivity.this, "摄像头开启失败", Toast.LENGTH_SHORT).show();}};/*** 开始预览*/private void takePreview() {try {// 创建预览需要的CaptureRequest.Builderfinal CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);// 将SurfaceView的surface作为CaptureRequest.Builder的目标previewRequestBuilder.addTarget(mSurfaceHolder.getSurface());previewRequestBuilder.addTarget(mImageReaderPreview.getSurface());// 创建CameraCaptureSession,该对象负责管理处理预览请求和拍照请求mCameraDevice.createCaptureSession(Arrays.asList(mSurfaceHolder.getSurface(), mImageReaderPreview.getSurface()), new CameraCaptureSession.StateCallback() // ③{@Overridepublic void onConfigured(CameraCaptureSession cameraCaptureSession) {if (null == mCameraDevice) return;// 当摄像头已经准备好时,开始显示预览mCameraCaptureSession = cameraCaptureSession;try {// 自动对焦previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);// 打开闪光灯previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);// 显示预览CaptureRequest previewRequest = previewRequestBuilder.build();mCameraCaptureSession.setRepeatingRequest(previewRequest, null, childHandler);} catch (CameraAccessException e) {e.printStackTrace();}}@Overridepublic void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {Toast.makeText(MainActivity.this, "配置失败", Toast.LENGTH_SHORT).show();}}, childHandler);} catch (CameraAccessException e) {e.printStackTrace();}}/*** 点击事件*/@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.capture_btn:takePicture();Toast.makeText(MainActivity.this, "拍照", Toast.LENGTH_SHORT).show();break;}}/*** 拍照*/private void takePicture() {if (mCameraDevice == null) return;// 创建拍照需要的CaptureRequest.Builderfinal CaptureRequest.Builder captureRequestBuilder;try {captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);// 将imageReader的surface作为CaptureRequest.Builder的目标captureRequestBuilder.addTarget(mImageReaderJPG.getSurface());// 自动对焦captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);// 自动曝光captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);// 获取手机方向int rotation = getWindowManager().getDefaultDisplay().getRotation();// 根据设备方向计算设置照片的方向captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));//拍照CaptureRequest mCaptureRequest = captureRequestBuilder.build();mCameraCaptureSession.capture(mCaptureRequest, null, childHandler);} catch (CameraAccessException e) {e.printStackTrace();}}
}

2、ImageUtil.java文件下

package com.example.tongjiangsong.camera2base;import android.graphics.ImageFormat;
import android.media.Image;
import android.util.Log;import java.nio.ByteBuffer;/*** yuv420p:  yyyyyyyyuuvv* yuv420sp: yyyyyyyyuvuv* nv21:     yyyyyyyyvuvu*/public class ImageUtil {public static final int YUV420P = 0;public static final int YUV420SP = 1;public static final int NV21 = 2;private static final String TAG = "ImageUtil";/**** 此方法内注释以640*480为例* 未考虑CropRect的*/public static byte[] getBytesFromImageAsType(Image image, int type) {try {//获取源数据,如果是YUV格式的数据planes.length = 3//plane[i]里面的实际数据可能存在byte[].length <= capacity (缓冲区总大小)final Image.Plane[] planes = image.getPlanes();//数据有效宽度,一般的,图片width <= rowStride,这也是导致byte[].length <= capacity的原因// 所以我们只取width部分int width = image.getWidth();int height = image.getHeight();//此处用来装填最终的YUV数据,需要1.5倍的图片大小,因为Y U V 比例为 4:1:1byte[] yuvBytes = new byte[width * height * ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888) / 8];//目标数组的装填到的位置int dstIndex = 0;//临时存储uv数据的byte uBytes[] = new byte[width * height / 4];byte vBytes[] = new byte[width * height / 4];int uIndex = 0;int vIndex = 0;int pixelsStride, rowStride;for (int i = 0; i < planes.length; i++) {pixelsStride = planes[i].getPixelStride();rowStride = planes[i].getRowStride();ByteBuffer buffer = planes[i].getBuffer();//如果pixelsStride==2,一般的Y的buffer长度=640*480,UV的长度=640*480/2-1//源数据的索引,y的数据是byte中连续的,u的数据是v向左移以为生成的,两者都是偶数位为有效数据byte[] bytes = new byte[buffer.capacity()];buffer.get(bytes);int srcIndex = 0;if (i == 0) {//直接取出来所有Y的有效区域,也可以存储成一个临时的bytes,到下一步再copyfor (int j = 0; j < height; j++) {System.arraycopy(bytes, srcIndex, yuvBytes, dstIndex, width);srcIndex += rowStride;dstIndex += width;}} else if (i == 1) {//根据pixelsStride取相应的数据for (int j = 0; j < height / 2; j++) {for (int k = 0; k < width / 2; k++) {uBytes[uIndex++] = bytes[srcIndex];srcIndex += pixelsStride;}if (pixelsStride == 2) {srcIndex += rowStride - width;} else if (pixelsStride == 1) {srcIndex += rowStride - width / 2;}}} else if (i == 2) {//根据pixelsStride取相应的数据for (int j = 0; j < height / 2; j++) {for (int k = 0; k < width / 2; k++) {vBytes[vIndex++] = bytes[srcIndex];srcIndex += pixelsStride;}if (pixelsStride == 2) {srcIndex += rowStride - width;} else if (pixelsStride == 1) {srcIndex += rowStride - width / 2;}}}}image.close();//根据要求的结果类型进行填充switch (type) {case YUV420P:System.arraycopy(uBytes, 0, yuvBytes, dstIndex, uBytes.length);System.arraycopy(vBytes, 0, yuvBytes, dstIndex + uBytes.length, vBytes.length);break;case YUV420SP:for (int i = 0; i < vBytes.length; i++) {yuvBytes[dstIndex++] = uBytes[i];yuvBytes[dstIndex++] = vBytes[i];}break;case NV21:for (int i = 0; i < vBytes.length; i++) {yuvBytes[dstIndex++] = vBytes[i];yuvBytes[dstIndex++] = uBytes[i];}break;}return yuvBytes;} catch (final Exception e) {if (image != null) {image.close();}Log.i(TAG, e.toString());}return null;}
}

3、activity_main.xml文件下

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><TextureViewandroid:id="@+id/tv"android:layout_above="@+id/ll_bottom"android:layout_width="match_parent"android:layout_height="match_parent" /><RelativeLayoutandroid:id="@+id/ll_bottom"android:layout_width="match_parent"android:layout_height="80dp"android:layout_alignParentBottom="true"><ImageViewandroid:id="@+id/iv"android:layout_width="wrap_content"android:layout_height="70dp"android:layout_centerVertical="true"android:layout_marginLeft="10dp"android:src="@mipmap/ic_launcher"/><Buttonandroid:id="@+id/btn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:layout_marginRight="10dp"android:text="拍照"/></RelativeLayout>
</RelativeLayout>

3、设置权限:

    <uses-permission android:name="android.permission.CAMERA" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

 

 

这篇关于Android下使用camera2和Surfaceview预览图像并取得YUV420p数据回调的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/910044

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

Hadoop集群数据均衡之磁盘间数据均衡

生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x新特性) plan后面带的节点的名字必须是已经存在的,并且是需要均衡的节点。 如果节点不存在,会报如下错误: 如果节点只有一个硬盘的话,不会创建均衡计划: (1)生成均衡计划 hdfs diskbalancer -plan hadoop102 (2)执行均衡计划 hd

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件