android Camera2

本文深入探讨Android Camera2 API的使用方法,包括TextureView、ImageReader的集成,以及如何处理预览帧数据,实现图像读取和保存。通过实例代码讲解关键步骤,帮助开发者掌握Camera2的高级功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

控件:

import android.view.TextureView;

这个是代码:

https://github.com/plumcot/Camera2Demo/blob/master/src/com/example/camera2demo/Camera2Demo.java

 

获取图片数据不对,应该是:

 

private void setupImageReader() {
    //前三个参数分别是需要的尺寸和格式,最后一个参数代表每次最多获取几帧数据,本例的2代表ImageReader中最多可以获取两帧图像流
    mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(),
                ImageFormat.JPEG, 2);
    //监听ImageReader的事件,当有图像流数据可用时会回调onImageAvailable方法,它的参数就是预览帧数据,可以对这帧数据进行处理
    mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
        @Override
        public void onImageAvailable(ImageReader reader) {
            Image image = reader.acquireLatestImage();
            //我们可以将这帧数据转成字节数组,类似于Camera1的PreviewCallback回调的预览帧数据
            ByteBuffer buffer = image.getPlanes()[0].getBuffer();
            byte[] data = new byte[buffer.remaining()];
            buffer.get(data);
            image.close();
        }
    }, null);
}

 

package com.mtcnn_as;

import java.nio.ByteBuffer;
import java.util.Arrays;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.SurfaceTexture;
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.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import static com.mtcnn_as.ImageUtils.saveBitmap;
public class Camera2Demo extends Activity implements
		TextureView.SurfaceTextureListener {

	private CameraDevice mCamera;
	private String mCameraID = "1";

	private TextureView mPreviewView;
	private Size mPreviewSize;
	private CaptureRequest.Builder mPreviewBuilder;
	private ImageReader mImageReader;

	private Handler mHandler;
	private HandlerThread mThreadHandler;

	// 这里定义的是ImageReader回调的图片的大小
	private int mImageWidth = 1920;
	private int mImageHeight = 1080;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_texture);

		initView();
		initLooper();

	}

	// 很多过程都变成了异步的了,所以这里需要一个子线程的looper
	private void initLooper() {
		mThreadHandler = new HandlerThread("CAMERA2");
		mThreadHandler.start();
		mHandler = new Handler(mThreadHandler.getLooper());
	}

	// 可以通过TextureView或者SurfaceView
	private void initView() {
		mPreviewView = (TextureView) findViewById(R.id.textureView);
		mPreviewView.setSurfaceTextureListener(this);
	}

	@Override
	public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
										  int height) {
		try {
			// 获得所有摄像头的管理者CameraManager
			CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
			// 获得某个摄像头的特征,支持的参数
			CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(mCameraID);
			// 支持的STREAM CONFIGURATION
			StreamConfigurationMap map = characteristics
					.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
			// 摄像头支持的预览Size数组
			mPreviewSize = map.getOutputSizes(SurfaceTexture.class)[0];

			if (ContextCompat.checkSelfPermission(Camera2Demo.this,
					Manifest.permission.CAMERA)
					!= PackageManager.PERMISSION_GRANTED ||
					ContextCompat.checkSelfPermission(Camera2Demo.this,
							Manifest.permission.WRITE_EXTERNAL_STORAGE)
							!= PackageManager.PERMISSION_GRANTED) {

				ActivityCompat.requestPermissions(Camera2Demo.this,
						new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
			}
			// 打开相机
			if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
				// TODO: Consider calling
				//    ActivityCompat#requestPermissions
				// here to request the missing permissions, and then overriding
				//   public void onRequestPermissionsResult(int requestCode, String[] permissions,
				//                                          int[] grantResults)
				// to handle the case where the user grants the permission. See the documentation
				// for ActivityCompat#requestPermissions for more details.
				return;
			}
			cameraManager.openCamera(mCameraID, mCameraDeviceStateCallback, mHandler);
		} catch (CameraAccessException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,
			int height) {

	}

	@Override
	public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
		return false;
	}

	// 这个方法要注意一下,因为每有一帧画面,都会回调一次此方法
	@Override
	public void onSurfaceTextureUpdated(SurfaceTexture surface) {

	}

	private CameraDevice.StateCallback mCameraDeviceStateCallback = new CameraDevice.StateCallback() {

		@Override
		public void onOpened(CameraDevice camera) {
			try {
				mCamera = camera;
				startPreview(mCamera);
			} catch (CameraAccessException e) {
				e.printStackTrace();
			}
		}

		@Override
		public void onDisconnected(CameraDevice camera) {

		}

		@Override
		public void onError(CameraDevice camera, int error) {

		}
	};

	// 开始预览,主要是camera.createCaptureSession这段代码很重要,创建会话
	private void startPreview(CameraDevice camera) throws CameraAccessException {
		SurfaceTexture texture = mPreviewView.getSurfaceTexture();

		// 这里设置的就是预览大小
		texture.setDefaultBufferSize(mPreviewSize.getWidth(),mPreviewSize.getHeight());
		Surface surface = new Surface(texture);
		try {
			// 设置捕获请求为预览,这里还有拍照啊,录像等
			mPreviewBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
		} catch (CameraAccessException e) {
			e.printStackTrace();
		}

		// 就是在这里,通过这个set(key,value)方法,设置曝光啊,自动聚焦等参数!! 如下举例:
		// mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);

		mImageReader = ImageReader.newInstance(mImageWidth, mImageHeight,
				ImageFormat.JPEG/* 此处还有很多格式,比如我所用到YUV等 */, 2/*
															 * 最大的图片数,
															 * mImageReader里能获取到图片数
															 * ,
															 * 但是实际中是2+1张图片,就是多一张
															 */);

		mImageReader.setOnImageAvailableListener(mOnImageAvailableListener,mHandler);
		// 这里一定分别add两个surface,一个Textureview的,一个ImageReader的,如果没add,会造成没摄像头预览,或者没有ImageReader的那个回调!!
		mPreviewBuilder.addTarget(surface);
		mPreviewBuilder.addTarget(mImageReader.getSurface());
		camera.createCaptureSession(
				Arrays.asList(surface, mImageReader.getSurface()),
				mSessionStateCallback, mHandler);
	}

	private CameraCaptureSession.StateCallback mSessionStateCallback = new CameraCaptureSession.StateCallback() {

		@Override
		public void onConfigured(CameraCaptureSession session) {
			try {
				updatePreview(session);
			} catch (CameraAccessException e) {
				e.printStackTrace();
			}
		}

		@Override
		public void onConfigureFailed(CameraCaptureSession session) {

		}
	};

	private void updatePreview(CameraCaptureSession session)
			throws CameraAccessException {
		session.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);
	}

	private ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {

		/**
		 * 当有一张图片可用时会回调此方法,但有一点一定要注意: 一定要调用
		 * reader.acquireNextImage()和close()方法,否则画面就会卡住!!!!!我被这个坑坑了好久!!!
		 * 很多人可能写Demo就在这里打一个Log,结果卡住了,或者方法不能一直被回调。
		 **/
		@Override
		public void onImageAvailable(ImageReader reader) {

			long startTime = System.currentTimeMillis(); //起始时间
			Image img = reader.acquireNextImage();
			/**
			 * 因为Camera2并没有Camera1的Priview回调!!!所以该怎么能到预览图像的byte[]呢?就是在这里了!!!
			 * 我找了好久的办法!!!
			 **/
			ByteBuffer buffer = img.getPlanes()[0].getBuffer();
			// 这里就是图片的byte数组了
			byte[] bytes = new byte[buffer.remaining()];
			buffer.get(bytes);
			img.close();
			Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
			Matrix matrix = new Matrix();
			matrix.postRotate(270);
//                        matrix.postScale( 1/2,  1/2); // 长和宽放大缩小的比例
			Bitmap bimap1 = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);

			int faceInfo[] = null;

			Bitmap rgba = bimap1.copy(Bitmap.Config.ARGB_8888, true);

			long endTime = System.currentTimeMillis(); //结束时间

			int width = rgba.getWidth();
			int height = rgba.getHeight();
			byte[] imageDate = getPixelsRGBA(rgba);
			long endTime2 = System.currentTimeMillis(); //结束时间
			saveBitmap(getApplicationContext(),rgba);

			Log.e("test", String.format("方法使用时间 %d %d ms", endTime - startTime,endTime2-startTime));


		}
	};

	//提取像素点
	private byte[] getPixelsRGBA(Bitmap image) {
		// calculate how many bytes our image consists of
		int bytes = image.getByteCount();
		ByteBuffer buffer = ByteBuffer.allocate(bytes); // Create a new buffer
		image.copyPixelsToBuffer(buffer); // Move the byte data to the buffer
		byte[] temp = buffer.array(); // Get the underlying array containing the

		return temp;
	}
	protected void onPause() {
		if (null != mCamera) {
			mCamera.close();
			mCamera = null;
		}
		if (null != mImageReader) {
			mImageReader.close();
			mImageReader = null;
		}
		super.onPause();
	}
}

 

### Android Camera2 API 使用教程 #### 1. 初步介绍 Android Camera2 API 提供了一种更灵活的方式来控制设备上的摄像头功能。相比旧版的 `Camera` API,`Camera2` 更加复杂但也更为强大[^1]。 #### 2. 配置环境 为了成功运行基于 Camera2 API 的应用,开发人员需要确保目标设备支持该 API 并设置正确的权限声明。如果项目配置失败,则可能是因为缺少必要的依赖项或未正确处理权限请求。 - **权限声明**: 在 `AndroidManifest.xml` 文件中添加以下权限: ```xml <uses-permission android:name="android.permission.CAMERA"/> <uses-feature android:name="android.hardware.camera.any" /> ``` - **动态权限申请**: 对于 Android 6.0 (API Level 23) 及以上版本的应用,需在运行时向用户请求访问相机的权限。 #### 3. 手动调整曝光参数 通过 Camera2 API 能够实现对手动曝光的支持。这通常涉及修改传感器的感光度以及调节镜头的实际开合时间来达到理想的亮度效果[^2]。 以下是简单的代码片段展示如何获取并设定当前活动中的曝光补偿等级: ```java private void setAutoExposure(CameraCharacteristics characteristics){ Range<Integer> range = characteristics.get( CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE); Float stepSize = characteristics.get( Camera Characteristics.CONTROL_AE_COMPENSATION_STEP); CaptureRequest.Builder builder; try { builder = cameraDevice.createCaptureRequest(...); int compensationIndex = ...; // Calculate desired index within 'range' float finalCompensationValue = stepSize * compensationIndex; builder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, Math.round(finalCompensationValue)); } catch(Exception e){ /* Handle exception */ } } ``` #### 4. 解决拍照过程中的典型障碍 当利用 Camera2 构建自定义摄像机界面时常会碰到一些棘手的技术难题,比如预览画面冻结或者保存图像质量差等问题[^3]。这些问题往往源于资源管理不当或是未能妥善同步异步操作的结果。 针对上述提到的闪光灯无法正常开启的情况,可以尝试如下方式修正逻辑错误[^4]: ```java public void toggleTorch(boolean enable) throws CameraAccessException{ mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), null, null); if(enable && !isFlashSupported()) return; mPreviewBuilder.set(CaptureRequest.FLASH_MODE, enable ? CameraMetadata.FLASH_MODE_TORCH : CameraMetadata.FLASH_MODE_OFF ); } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI算法网奇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值