原文:Android Camera2 拍照入门学习

学习资料:


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

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


1.Camera1使用

使用步骤:

  1. 调用Camera.open(),打开相机,默认为后置,可以根据摄像头ID来指定打开前置还是后置
  2. 调用Camera.getParameters()得到一个Camera.Parameters对象
  3. 使用步骤2得到的Camera.Parameters对象,对拍照参数进行设置
  4. 调用Camera.setPreviewDispaly(SurfaceHolder holder),指定使用哪个SurfaceView来显示预览图片
  5. 调用Camera.startPreview()方法开始预览取景
  6. 调用Camera.takePicture()方法进行拍照
  7. 拍照结束后,调用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也就是Camera1Camera
  • CameraCaptureSession 这个对象控制摄像头的预览或者拍照,setRepeatingRequest()开启预览,capture()拍照,CameraCaptureSession提供了StateCallback、CaptureCallback两个接口来监听CameraCaptureSession的创建和拍照过程。
  • CameraRequest和CameraRequest.Builder,预览或者拍照时,都需要一个CameraRequest对象。CameraRequest表示一次捕获请求,用来对z照片的各种参数设置,比如对焦模式、曝光模式等。CameraRequest.Builder用来生成CameraRequest对象。

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


2.1 简单使用

使用的依然是SurfaceView来进行展示预览

主要思路:

  1. 获得摄像头管理器CameraManager mCameraManagermCameraManager.openCamera()来打开摄像头
  2. 指定要打开的摄像头,并创建openCamera()所需要的CameraDevice.StateCallback stateCallback
  3. CameraDevice.StateCallback stateCallback中调用takePreview(),这个方法中,使用CaptureRequest.Builder创建预览需要的CameraRequest,并初始化了CameraCaptureSession,最后调用了setRepeatingRequest(previewRequest, null,
    childHandler)
    进行了预览
  4. 点击屏幕,调用takePicture(),这个方法内,最终调用了capture(mCaptureRequest, null, childHandler)
  5. 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的功能很强大,暂时也只是学习了最基本的思路

住的地方,没有桌子,于是坐地上,趴在床上敲代码,腰疼。逛淘宝买桌子去

感谢极客学院和肾虚将军的学习资料

文/英勇青铜5(简书作者)

原文链接:http://www.jianshu.com/p/7f766eb2f4e7

著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

Android Camera2 拍照入门学习的更多相关文章

  1. Android Camera2 拍照(三)——切换摄像头,延时拍摄和闪光模式

    原文:Android Camera2 拍照(三)--切换摄像头,延时拍摄和闪光模式 一.切换摄像头 在前后摄像头之间切换,首先需要关闭之前打开的摄像头,关闭preview,之后重新打开新的摄像头,重新 ...

  2. Android Camera2 拍照(四)——对焦模式

    原文:Android Camera2 拍照(四)--对焦模式 本篇将重点介绍使用Camera2 API进行手动对焦的设置,以及在手动对焦与自动对焦模式之间切换. 一.手动对焦响应事件 首先我们要实现点 ...

  3. Android Camera2拍照(一)——使用SurfaceView

    原文:Android Camera2拍照(一)--使用SurfaceView Camera2 API简介 Android 从5.0(21)开始,引入了新的Camera API Camera2,原来的a ...

  4. Android Camera2 拍照(二)——使用TextureView

    原文:Android Camera2 拍照(二)--使用TextureView 上一篇博文简单介绍了使用Camera2 API拍摄照片,并使用SurfaceView作为预览界面.实际上,相对于Surf ...

  5. android的简单入门学习

    话说光配环境就整死我了, 不是说多么难, 是最近google被屏了, 很多sdk里面需要下载的东西都下不下来, 坑爹啊.  最后跟扫拉稀要了一个他配置好的,才运行了. android目录分析: ass ...

  6. Android驱动-Java入门学习(java安装)

    在ubuntu 14.04上java开发环境. 下载 jdk-7u75-linux-x64.tar.gz 使用tar xvf jdk-7u75-linux-x64.tar.gz 解压 在/usr/li ...

  7. android的入门学习

    android 入门学习. 活动:就是一个包含应用程序的用户界面的窗口.目的就是与用户交互. 意图:就是能够将来自不同应用程序的不同活动无缝连接在一起工作的"胶水",确保这些任务执 ...

  8. Android Camera2 预览,拍照,人脸检测并实时展现

    https://www.jianshu.com/p/5414ba2b5508 背景     最近需要做一个人脸检测并实时预览的功能.就是边检测人脸,边在预览界面上框出来.     当然本人并不是专门做 ...

  9. Android M中 JNI的入门学习

    今年谷歌推出了Android 6.0,作为安卓开发人员,对其学习掌握肯定是必不可少的,今天小编和大家分享的就是Android 6.0中的 JNI相关知识,这是在一个安卓教程网上看到的内容,感觉很不错, ...

随机推荐

  1. Nginx的一些介绍

    Apacheserver:http://httpd.apache.org,世界上用的最多的server,开放源码.支持跨平台,可移植性,模块支持丰富,虽速度和性能及内存消耗不及其它轻量级Webserv ...

  2. [Compose] 16. Apply multiple functors as arguments to a function (Applicatives)

    We find a couple of DOM nodes that may or may not exist and run a calculation on the page height usi ...

  3. php实现求二进制中1的个数(右移、&、int32位)(n = n & (n - 1);)

    php实现求二进制中1的个数(右移.&.int32位)(n = n & (n - 1);) 一.总结 1.PHP中的位运算符和java和c++一样 2.位移运算符看箭头方向,箭头向左就 ...

  4. ArcEngine创建IElement简单例子

    转自IT-GIS终结者原文ArcEngine创建IElement简单例子 代码下载地址:http://files.cnblogs.com/ogis/MapControlApplication2.rar ...

  5. 小强的HTML5移动开发之路(42)——HTML4与HTML5文档结构比较

    一般来说,人们在书写包括HTML在内的文档时,习惯上按照类似于"章--节--小节"这样的层次结构来进行. 在HTML4中的描述方式: <html> <head&g ...

  6. Android TextView,EditText要求固定行数自动调整TextSize

    最近项目有个需求要求文本最多显示3行,继续输入则字体变小,删除已经输入的文字,那么字体变大,不管变大变小都不能超过3行.网上怎么找也找不到相关的解决方案,自己动手,丰衣足食了! 说一下算法思路,后面给 ...

  7. Scrapy系列教程(1)------命令行工具

    默认的Scrapy项目结构 在開始对命令行工具以及子命令的探索前,让我们首先了解一下Scrapy的项目的文件夹结构. 尽管能够被改动,但全部的Scrapy项目默认有类似于下边的文件结构: scrapy ...

  8. WPF入门(四)->线形区域Path内容填充之渐变色(LinearGradientBrush)

    原文:WPF入门(四)->线形区域Path内容填充之渐变色(LinearGradientBrush) 前面我们介绍到,Path对象表示一个用直线或者曲线连接的图形,我们可以使用Path.Data ...

  9. Swift学习——Swift解释具体的基础(六)

    Optionals    可选 可选(它似乎并不如此翻译)它适用于那些值这种情况可能是空的,有两种情况一个可选:存在值并等于x,要么值不存在. 选配的概念在OC和C里面并没有.在OC中最接近的概念就是 ...

  10. 给定正整数n,计算出n个元素的集合{1,2,....,n}能够划分为多少个不同的非空集合

    给定正整数n,计算出n个元素的集合{1,2,....,n}能够划分为多少个不同的非空集合 附源码: #include<iostream> using namespace std; int ...