博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android Camera2 拍照入门学习
阅读量:7111 次
发布时间:2019-06-28

本文共 14838 字,大约阅读时间需要 49 分钟。

原文:

学习资料:


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的书看,知乎很多人推荐这两本,就买了,感觉确实不错

布局文件:

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); } } };}

权限:

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


2.Camera2

Camera2拍照示意图

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

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对象。

以上从摘抄


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
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
你可能感兴趣的文章
掌阅之语----》记录
查看>>
Linux下ld搜索问题:ld: cannot find -l"XX"
查看>>
C++ 常用的字符串处理函数实现
查看>>
e.key && e.which && e.keyCode
查看>>
.NET静态变量与静态方法并发的问题
查看>>
51nod 1073 约瑟夫环
查看>>
【C#公共帮助类】枚举独特类
查看>>
poj - 1469 COURSES
查看>>
Mac下配置JAVA_HOME
查看>>
Django --ORM
查看>>
docker cgroup 技术之memory(首篇)
查看>>
Java图形用户界面编程
查看>>
13. 泛型和枚举
查看>>
Django 查漏补缺
查看>>
mysql中key 、primary key 、unique key 与index区别
查看>>
俺的新书《Sencha Touch实战》终于出版了
查看>>
Word 2003从入门到精通第4讲(表格)
查看>>
docker容器故障致无法启动解决实例
查看>>
mysql数据库Too many connections报错排查
查看>>
移動型VR
查看>>