Android中的拍照camera和camera2

简介: 在android我们可以调用系统的相机来进行照相,当然也可以设计自己的照相页面,通过surface进行预览,通过camera api来进行拍照即可。在Android 5.0之后google推荐使用的Camera2 Api,但是由于兼容问题我们大部分还在使用Camera Api。这里我们一起简单介绍一下,仅仅是预览拍照,至于细节的设置,比如图像大小比例、对焦、图像旋转等就不一一细说了。首先我们需要在布局中加入一个SurfaceView

前言


在android我们可以调用系统的相机来进行照相,当然也可以设计自己的照相页面,通过surface进行预览,通过camera api来进行拍照即可。

在Android 5.0之后google推荐使用的Camera2 Api,但是由于兼容问题我们大部分还在使用Camera Api。这里我们一起简单介绍一下,仅仅是预览拍照,至于细节的设置,比如图像大小比例、对焦、图像旋转等就不一一细说了。

首先我们需要在布局中加入一个SurfaceView


Camera Api


var camera: Camera? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //为surfaceholder添加监听
        surface.holder.addCallback(object : SurfaceHolder.Callback{
            override fun surfaceChanged(p0: SurfaceHolder?, p1: Int, p2: Int, p3: Int) {
            }
            override fun surfaceDestroyed(p0: SurfaceHolder?) {
            }
            override fun surfaceCreated(p0: SurfaceHolder?) {
                //当surface创建完成初始化相机
                initCamera1()
            }
        })
        take.setOnClickListener {
            //拍照
            takePicture1()
        }
    }
    fun initCamera1(){
        //打开相机,开启预览
        camera = Camera.open()
        camera?.setPreviewDisplay(surface.holder)
        camera?.startPreview()
    }
    fun takePicture1(){
        //拍照,通过回调获取图片数据
        camera?.takePicture({},
                {bytes, camera ->
                    if(bytes == null){
                        return@takePicture
                    }
                    var b = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
                    imageView.setImageBitmap(b)
                },
                {bytes, camera ->
                    if(bytes == null){
                        return@takePicture
                    }
                    var b = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
                    imageView2.setImageBitmap(b)
                })
    }
复制代码

可以看到使用Camera比较简单,打开相机开启预览,拍照在回调中处理即可。


Camera2 Api


Camera2的Api相比Camera要复杂很多


var mCameraDevice : CameraDevice? = null
    var mCameraCaptureSession : CameraCaptureSession? = null
    var childHandler : Handler? = null
    @SuppressLint("NewApi")
    var imageReader : ImageReader? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //为surfaceholder添加监听
        surface.holder.addCallback(object : SurfaceHolder.Callback{
            override fun surfaceChanged(p0: SurfaceHolder?, p1: Int, p2: Int, p3: Int) {
            }
            override fun surfaceDestroyed(p0: SurfaceHolder?) {
            }
            override fun surfaceCreated(p0: SurfaceHolder?) {
                //当surface创建完成初始化相机
                initCamera2()
            }
        })
        take.setOnClickListener {
            //拍照
            takePicture2()
        }
    }
    fun initCamera2(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            //因为处理图片比较耗时,所以这里新建一个线程的handler来处理
            var handlerThread = HandlerThread("camera")
            handlerThread.start()
            childHandler = Handler(handlerThread.looper)
            //创建一个ImageReader,后面会通过这个获取拍照的图片
            imageReader = ImageReader.newInstance(500, 500, ImageFormat.JPEG,2)
            //为ImageReader添加回调,这里使用我们新建的线程的handler处理图片
            imageReader?.setOnImageAvailableListener(object : ImageReader.OnImageAvailableListener{
                override fun onImageAvailable(imageReader: ImageReader) {
                    //得到图片后,关闭相机,从ImageReader中获取图片数据
                    mCameraDevice?.close()
                    var image = imageReader.acquireNextImage()
                    var buffer = image.planes[0].buffer
                    var bytes = ByteArray(buffer.remaining())
                    buffer.get(bytes)
                    var bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
                    //由于这部分是在子线程中处理的,所以对ui操作要放到主线程中
                    runOnUiThread {
                        imageView.setImageBitmap(bitmap)
                    }
                }
            }, childHandler)
            //通过CameraManager打开相机,注意这里的handler是主线程的。
            var cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
            cameraManager.openCamera(CameraCharacteristics.LENS_FACING_FRONT.toString(), object : CameraDevice.StateCallback() {
                override fun onOpened(cameraDevice: CameraDevice) {
                    //相机打开后,得到CameraDevice并开启预览
                    mCameraDevice = cameraDevice
                    camera2Preview()
                }
                override fun onDisconnected(p0: CameraDevice) {
                }
                override fun onError(p0: CameraDevice, p1: Int) {
                }
            }, Handler(Looper.getMainLooper()))
        }
    }
    fun camera2Preview(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            mCameraDevice?.let {
                //新建一个PRIVIEW的CaptureRequest,将surface添加进去
                var builder = it.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
                builder.addTarget(surface.holder.surface)
                //通过CameraDevice新建一个CaptureSession事务,在回调中开启预览
                it.createCaptureSession(Arrays.asList(surface.holder.surface, imageReader?.surface), object : CameraCaptureSession.StateCallback() {
                    override fun onConfigureFailed(p0: CameraCaptureSession) {
                    }
                    override fun onConfigured(cameraCaptureSession: CameraCaptureSession) {
                        //获得对应的CaptureSession事务,并设置预览的request开始预览
                        mCameraCaptureSession = cameraCaptureSession
                        var previewRequest = builder.build()
                        mCameraCaptureSession?.setRepeatingRequest(previewRequest, null ,childHandler)
                    }
                }, childHandler)
            }
        }
    }
    //拍照
    fun takePicture2(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            mCameraDevice?.let {
                //新建一个拍照请求,并添加之前创建的ImageReader的surface
                var builder = it.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
                builder.addTarget(imageReader!!.surface)
                var previewRequest = builder.build()
                //通过预览是获取的CaptureSession事务,执行拍照请求,结果会通过之前设置的ImageReader的回调返回
                mCameraCaptureSession?.setRepeatingRequest(previewRequest, null ,childHandler)
            }
        }
    }
复制代码


使用Camera2 Api的流程基本与Camera一样,但是代码复杂很多,主要是需要通过事务CaptureSession来处理预览及拍照,而且为了处理图片还需要一个新建子线程,同时拍照结果也是通过ImageReader封装起来的。

Camera2拥有更多的能力,可以实现更复杂的拍照场景和设定,相对于Camera Api更专业,就好比单反和傻瓜相机的区别似的,所以使用起来也更加复杂一些。


目录
相关文章
|
数据采集 编解码 API
如何实现Android平台GB28181设备对接Camera2数据
在写如何实现Android平台GB28181设备对接Camera2数据说明之前,我在前两年的blog就有针对camera2的RTMP直播推送模块做过技术分享:
如何实现Android平台GB28181设备对接Camera2数据
|
API Android开发 计算机视觉
视觉智能平台有android人脸识别拍照demo?
视觉智能平台有android人脸识别拍照demo么?
104 0
|
5月前
|
Java API Android开发
技术经验分享:Android源码笔记——Camera系统架构
技术经验分享:Android源码笔记——Camera系统架构
60 0
|
5月前
|
API Android开发
57. 【Android教程】相机:Camera
57. 【Android教程】相机:Camera
70 0
|
6月前
|
Android开发
android camera系列
android camera系列
37 0
|
算法 API 开发工具
Android平台Camera2数据如何对接RTMP推流到服务器
在Google 推出Android 5.0的时候, Android Camera API 版本升级到了API2(android.hardware.camera2), 之前使用的API1(android.hardware.camera)就被标为 Deprecated 了。
308 0
|
API 开发工具 Android开发
如何实现RTMP推送Android Camera2数据
在Google 推出Android 5.0的时候, Android Camera API 版本升级到了API2(android.hardware.camera2), 之前使用的API1(android.hardware.camera)就被标为 Deprecated 了。
140 0
|
存储 数据可视化 Android开发
Android 实现拍照功能,并将图片保存到本地存储
Android 实现拍照功能,并将图片保存到本地存储
696 0
|
传感器 前端开发 Java
Android流媒体开发之路一:Camera2采集摄像头原始数据并手动预览
Android流媒体开发之路一:Camera2采集摄像头原始数据并手动预览
669 0
|
程序员 Android开发
Android 照相机 camera 操作封装
Android 照相机 camera 操作封装