您的位置:首页 > 移动开发 > Android开发

Android Camera2 拍照入门学习

2017-05-25 09:25 316 查看


Android Camera2 拍照入门学习



 

作者 英勇青铜5 关注

2016.09.25 14:52* 字数 1293 阅读 5003评论
43喜欢 27

学习资料:
肾虚将军android camera2 详解说明
极客学院android.hardware.camera2
使用指南
20170513 16:25
注意权限,注意权限,注意权限
5.0在配置文件申明一下就好,6.0以上的系统需要考虑动态权限问题
评论里2个同学说因为权限问导致了黑屏,排查代码却没有发现问题,对刚刚接触安卓不久的同学造成困扰,还耽搁了不少时间,抱歉了
具体的相机设置、预览控件大小以及样张的参数之类的,需要根据自己的手机、系统做些相应调整。我测试手机是5.1系统,使用博客中的代码只是简单地拍了个照片测试了下,除了照片样张质量一般外,暂时没有发现其他问题。我并没有在项目中实际开发过拍照相关的功能,这里我也是简单地学习下。若哪位同学发现代码里的问题,请留言指出,免得造成误导浪费时间
Android 5.0(21)之后,
android.hardware.Camera
被废弃(下面称为
Camera1
),还有一个
android.graphics.Camera
,这个
android.graphics.Camera
不是用来照相的,是用来处理图像的,可以做出
3D
的图像效果
1926f
之类的,之前的
Camera1
则由
android.hardware.Camera2
来代替
Camera2
支持
RAW
输出,可以调节曝光,对焦模式,快门等,功能比原先
Camera
强大


1.Camera1使用

使用步骤:
调用
Camera.open()
,打开相机,默认为后置,可以根据摄像头
ID
来指定打开前置还是后置
调用
Camera.getParameters()
得到一个
Camera.Parameters
对象
使用
步骤2
得到的
Camera.Parameters
对象,对拍照参数进行设置
调用
Camera.setPreviewDispaly(SurfaceHolder
holder)
,指定使用哪个
SurfaceView
来显示预览图片
调用
Camera.startPreview()
方法开始预览取景
调用
Camera.takePicture()
方法进行拍照
拍照结束后,调用
Camera.stopPreview()
结束取景预览,之后再
replease()
方法释放资源
这几个步骤从
疯狂Android讲义
中学到


1.1简单使用

使用
SurfaceView
进行取景的预览,点击屏幕进行拍照,用
ImageView
来展示拍的照片



取景



拍照预览

想买关于操作系统和C的书看,知乎很多人推荐这两本,就买了,感觉确实不错。然而,深入理解操作系统买早了,啃不动

布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<SurfaceView
android:id="@+id/surface_view_camera2_activity"
android:layout_width="match_parent"
android:layout_height="match_parent" />

<ImageView
android:id="@+id/iv_show_camera2_activity"
android:layout_width="180dp"
android:layout_height="320dp"
android:visibility="gone"
android:layout_centerInParent="true"
android:scaleType="centerCrop" />
</RelativeLayout>

Activity
代码:
public class CameraActivity extends AppCompatActivity implements View.OnClickListener {

private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private Camera mCamera;
private ImageView iv_show;
private int viewWidth, viewHeight;//mSurfaceView的宽和高

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera2);
initView();
}

/**
* 初始化控件
*/
private void initView() {
iv_show = (ImageView) findViewById(R.id.iv_show_camera2_activity);
//mSurfaceView
mSurfaceView = (SurfaceView) findViewById(R.id.surface_view_camera2_activity);
mSurfaceHolder = mSurfaceView.getHolder();
// mSurfaceView 不需要自己的缓冲区
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
// mSurfaceView添加回调
mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) { //SurfaceView创建
// 初始化Camera
initCamera();
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) { //SurfaceView销毁
// 释放Camera资源
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
}
}
});
//设置点击监听
mSurfaceView.setOnClickListener(this);
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (mSurfaceView != null) {
viewWidth = mSurfaceView.getWidth();
viewHeight = mSurfaceView.getHeight();
}
}

/**
* SurfaceHolder 回调接口方法
*/
private void initCamera() {
mCamera = Camera.open();//默认开启后置
mCamera.setDisplayOrientation(90);//摄像头进行旋转90°
if (mCamera != null) {
try {
Camera.Parameters parameters = mCamera.getParameters();
//设置预览照片的大小
parameters.setPreviewFpsRange(viewWidth, viewHeight);
//设置相机预览照片帧数
parameters.setPreviewFpsRange(4, 10);
//设置图片格式
parameters.setPictureFormat(ImageFormat.JPEG);
//设置图片的质量
parameters.set("jpeg-quality", 90);
//设置照片的大小
parameters.setPictureSize(viewWidth, viewHeight);
//通过SurfaceView显示预览
mCamera.setPreviewDisplay(mSurfaceHolder);
//开始预览
mCamera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
}

/**
* 点击回调方法
*/
@Override
public void onClick(View v) {
if (mCamera == null) return;
//自动对焦后拍照
mCamera.autoFocus(autoFocusCallback);
}

/**
* 自动对焦 对焦成功后 就进行拍照
*/
Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
if (success) {//对焦成功

camera.takePicture(new Camera.ShutterCallback() {//按下快门
@Override
public void onShutter() {
//按下快门瞬间的操作
}
}, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {//是否保存原始图片的信息

}
}, pictureCallback);
}
}
};
/**
* 获取图片
*/
Camera.PictureCallback pictureCallback = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
final Bitmap resource = BitmapFactory.decodeByteArray(data, 0, data.length);
if (resource == null) {
Toast.makeText(CameraActivity.this, "拍照失败", Toast.LENGTH_SHORT).show();
}
final Matrix matrix = new Matrix();
matrix.setRotate(90);
final Bitmap bitmap = Bitmap.createBitmap(resource, 0, 0, resource.getWidth(), resource.getHeight(), matrix, true);
if (bitmap != null && iv_show != null && iv_show.getVisibility() == View.GONE) {
mCamera.stopPreview();
iv_show.setVisibility(View.VISIBLE);
mSurfaceView.setVisibility(View.GONE);
Toast.makeText(CameraActivity.this, "拍照", Toast.LENGTH_SHORT).show();
iv_show.setImageBitmap(bitmap);
}
}
};
}

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

在获得图片后,想要显示的效果是照片是竖直显示,
resource
显示的却是逆时针旋转了
90°
,照片是横着的,就使用
matrix.setRotate(90)
进行旋转


2.Camera2



Camera2拍照示意图

这里引用了管道的概念将安卓设备和摄像头之间联通起来,系统向摄像头发送 Capture 请求,而摄像头会返回 CameraMetadata。这一切建立在一个叫作 CameraCaptureSession 的会话中。



camera2中主要的类

以上从极客学院android.hardware.camera2
使用指南摘抄

CameraManaer 摄像头管理器,用于检测摄像头,打开系统摄像头,调用
CameraManager.getCameraCharacteristics(String)
可以获取指定摄像头的相关特性
CameraCharacteristics 摄像头的特性
CameraDevice 摄像头,类似
android.hardware.Camera
也就是
Camera1
Camera

CameraCaptureSession 这个对象控制摄像头的预览或者拍照,
setRepeatingRequest()
开启预览,
capture()
拍照,
CameraCaptureSession
提供了StateCallback、CaptureCallback两个接口来监听
CameraCaptureSession
的创建和拍照过程。
CameraRequest和CameraRequest.Builder,预览或者拍照时,都需要一个
CameraRequest
对象。CameraRequest表示一次捕获请求,用来对z照片的各种参数设置,比如对焦模式、曝光模式等。CameraRequest.Builder用来生成CameraRequest对象。

以上从肾虚将军的android camera2 详解说明摘抄


2.1 简单使用

使用的依然是
SurfaceView
来进行展示预览
主要思路:
获得摄像头管理器
CameraManager
mCameraManager
mCameraManager.openCamera()
来打开摄像头
指定要打开的摄像头,并创建
openCamera()
所需要的
CameraDevice.StateCallback
stateCallback

CameraDevice.StateCallback
stateCallback
中调用
takePreview()
,这个方法中,使用
CaptureRequest.Builder
创建预览需要的
CameraRequest
,并初始化了
CameraCaptureSession
,最后调用了
setRepeatingRequest(previewRequest,
null, childHandler)
进行了预览
点击屏幕,调用
takePicture()
,这个方法内,最终调用了
capture(mCaptureRequest,
null, childHandler)

new ImageReader.OnImageAvailableListener(){}
回调方法中,将拍照拿到的图片进行展示
代码:
public class Camera2Activity extends AppCompatActivity implements View.OnClickListener {
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();

///为了使照片竖直显示
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 mImageReader;
private CameraCaptureSession mCameraCaptureSession;
private CameraDevice mCameraDevice;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera2);
initVIew();
}

/**
* 初始化
*/
private void initVIew() {
iv_show = (ImageView) findViewById(R.id.iv_show_camera2_activity);
//mSurfaceView
mSurfaceView = (SurfaceView) findViewById(R.id.surface_view_camera2_activity);
mSurfaceView.setOnClickListener(this);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.setKeepScreenOn(true);
// mSurfaceView添加回调
mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) { //SurfaceView创建
// 初始化Camera
initCamera2();
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) { //SurfaceView销毁
// 释放Camera资源
if (null != mCameraDevice) {
mCameraDevice.close();
Camera2Activity.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;//后摄像头
mImageReader = ImageReader.newInstance(1080, 1920, ImageFormat.JPEG,1);
mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { //可以在这里处理拍照得到的临时照片 例如,写入本地
@Override
public void onImageAvailable(ImageReader reader) {
mCameraDevice.close();
mSurfaceView.setVisibility(View.GONE);
iv_show.setVisibility(View.VISIBLE);
// 拿到拍照照片数据
Image image = reader.acquireNextImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);//由缓冲区存入字节数组
final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
if (bitmap != null) {
iv_show.setImageBitmap(bitmap);
}
}
}, mainHandler);
//获取摄像头管理
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() {
@Override
public void onOpened(CameraDevice camera) {//打开摄像头
mCameraDevice = camera;
//开启预览
takePreview();
}

@Override
public void onDisconnected(CameraDevice camera) {//关闭摄像头
if (null != mCameraDevice) {
mCameraDevice.close();
Camera2Activity.this.mCameraDevice = null;
}
}

@Override
public void onError(CameraDevice camera, int error) {//发生错误
Toast.makeText(Camera2Activity.this, "摄像头开启失败", Toast.LENGTH_SHORT).show();
}
};

/**
* 开始预览
*/
private void takePreview() {
try {
// 创建预览需要的CaptureRequest.Builder
final CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
// 将SurfaceView的surface作为CaptureRequest.Builder的目标
previewRequestBuilder.addTarget(mSurfaceHolder.getSurface());
// 创建CameraCaptureSession,该对象负责管理处理预览请求和拍照请求
mCameraDevice.createCaptureSession(Arrays.asList(mSurfaceHolder.getSurface(), mImageReader.getSurface()), new CameraCaptureSession.StateCallback() // ③
{
@Override
public 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();
}
}

@Override
public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
Toast.makeText(Camera2Activity.this, "配置失败", Toast.LENGTH_SHORT).show();
}
}, childHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}

/**
* 点击事件
*/
@Override
public void onClick(View v) {
takePicture();
}

/**
* 拍照
*/
private void takePicture() {
if (mCameraDevice == null) return;
// 创建拍照需要的CaptureRequest.Builder
final CaptureRequest.Builder captureRequestBuilder;
try {
captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
// 将imageReader的surface作为CaptureRequest.Builder的目标
captureRequestBuilder.addTarget(mImageReader.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();
}
}
}

布局代码以及权限与
Camera1
中一样,效果一样
预览时,是将
mSurfaceHolder.getSurface()
作为目标

显示拍照结果时,是将
mImageReader.getSurface()
作为目标


3.最后

Camera2
的功能很强大,暂时也只是学习了最基本的思路
住的地方,没有桌子,于是坐地上,趴在床上敲代码,腰疼。逛淘宝买桌子去
感谢极客学院和肾虚将军的学习资料
共勉 : )
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: