1. Camera开发流程
在这里,我们先了解下相机开发的大致流程,然后再对里面的步骤进行详细的阐述。

  1. 利用open(int)获取Camera实例
  2. 利用getParameters()获取默认设置,如果需要修改参数,利用setParameters()重新设置
  3. 利用setDisplayOrientation()设置相机图像旋转角度,产生正确的预览画面
  4. 利用setPreviewDisplay(SurfaceHolder)关联相机与SurfaceView显示图层,让视频流显示在界面上
  5. 设置Preview的回调函数,获取帧数据并进行图像处理逻辑
  6. 调用startPreview()开始预览,调用stopPreview()停止预览
  7. 调用release()释放相机资源。

2. 打开相机
一般手机上都有多个相机,支持前置或后置相机来拍照,利用Camera.getNumberOfCameras()获取相机的个数;然后通过Camera.getCameraInfo()获取每个相机的朝向,该返回值有两种:
后置:CameraInfo.CAMERA_FACING_BACK
前置:CameraInfo.CAMERA_FACING_FRONT
我们根据项目需求选择对应朝向的相机,默认是开启后置相机,再通过Camera.open()打开即可。需要注意的是打开相机可能失败,所以一定要检查相机是否打开成功,判断Camera是否为null。

Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
for(int id=0;id<Camera.getNumberOfCameras();id++)
{Camera.getCameraInfo(id, cameraInfo);if(cameraInfo.facing==Camera.CameraInfo.CAMERA_FACING_BACK){mCamera = Camera.open(id);break;}
}   
3. 设置参数
相机涉及到的参数较多,通过Camera.getParameters()获取当前相机的参数信息,返回值为Parameters对象;再通过Camera.setParamerters()来实现相机参数的重新设置。下面介绍几个主要的参数设置。3.1 设置帧率
我们可以通过getSupportedPreviewFpsRange获取手机支持的预览帧率,如下代码找出测试机(Huawei nova2)支持的帧率范围。
List<int[]> fpsSupport = parameters.getSupportedPreviewFpsRange();for (int i = 0; i < fpsSupport.size(); i++)
{int[] fps = fpsSupport.get(i);Log.i("FPS", String.format("fps: %d, %d",fps[0],fps[1]));
}

输出结果:

fps: 30000, 30000
fps: 14000, 30000
fps: 14000, 20000
fps: 20000, 20000
fps: 14000, 25000
fps: 25000, 25000
fps: 12000, 15000
fps: 15000, 15000
fps: 14000, 14000

在知道了手机支持的最小和最大帧率后,通过parameters.setPreviewFpsRange来设定我们需要的视频帧率。

3.2 设置预览角度
为了在程序界面上看到与人眼观察一致的视频图像,我们需要对相机图像做一定的旋转才能达到目的,这就是本小结将要介绍的内容。我们设计的应用程序有横屏或竖屏等不同的布局方式,同时相机在出厂时也设定了一个默认的安装方式与成像角度;因此为了在界面上看到正常的图像,需要结合这两个信息来计算一个预览图像的旋转角度。为了说清楚这个问题,涉及到几个概念:自然方向、屏幕方向和相机图像方向,可能比较绕,下面做详细的介绍。

自然方向:一般我们使用手机都是采用竖屏的方式,因此定义手机的自然方向就是竖屏的状态。在这个“自然方向”,手机的左上角为它的渲染坐标系原点,向右为x方向,向下为y方向。而平板的自然方向是横屏,在此不再做介绍,后续都是围绕手机为例做的说明。

屏幕方向(rotation):大多数情况下,我们都采用“自然方向”的竖屏布局模式;也存在很多场景设定不同的布局方式,比如拍证件采用横屏,游戏时根据传感器来自动调整界面布局。为了知道屏幕当前的切换方向,我们可以通过activity.getWindowManager().getDefaultDisplay().getRotation() 方法来获取。这角度值是指从“自然方向”切换到当前画面正方向的顺时针旋转角度,这个旋转方向正好与实际手机的旋转方向相反。下图我们给出了详细的示例图,会有个更直观的理解。

相机图像方向(info.orientation):预览视频数据来自于手机的图像传感器硬件,这个传感器在被安装到手机上后有一个默认的取景方向,与屏幕旋转、横竖屏无关。在“自然方向”下,大多数后置图像传感器的上边与右侧屏幕平行,因此它的坐标系为:右上角为原点,沿右侧屏幕向下为x正方向,向左为y正方向,因此我们获得的图像是横向的。为了在手机屏幕上正确显示图像,相机图像需要顺时针旋转90度。因此,大多情况下使用后置相机时,info.orientation=90,前置相机为270。当然,并不是所有设备都遵循这一规则,也存在一些特殊机器他们的orientation会是0或者180,所以需要通过代码来获取当前手机相机图像方向的真实值。

通过下图,可以更直接的看到测试机(Huawei nova2)获取的图像与现实世界图像的关系。

预览旋转角度:通过setDisplayOrientation设定预览图像旋转的角度,使得视频数据在界面上正常显示。这个操作本身只是改变了预览的角度,相机回调获得的帧图像数据角度并没有发生改变,所以后续对图像的处理仍需要做旋转处理。下图更直观的给出了后置摄像头在不同屏幕角度下,相机成像的画面,以及想要在屏幕上正常显示时需要设置的顺时针旋转角度值。

官方文档给出了获取预览方向的计算方法:

public static void setCameraDisplayOrientation(Activity activity,int cameraId, android.hardware.Camera camera)
{android.hardware.Camera.CameraInfo info =new android.hardware.Camera.CameraInfo();android.hardware.Camera.getCameraInfo(cameraId, info);int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();int degrees = 0;switch (rotation) {case Surface.ROTATION_0: degrees = 0; break;case Surface.ROTATION_90: degrees = 90; break;case Surface.ROTATION_180: degrees = 180; break;case Surface.ROTATION_270: degrees = 270; break;}int result;if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {result = (info.orientation + degrees) % 360;result = (360 - result) % 360;  // compensate the mirror} else {  // back-facingresult = (info.orientation - degrees + 360) % 360;}camera.setDisplayOrientation(result);}

3.3 设置预览大小
一般相机支持的预览大小会有很多种,通过getSupportedPreviewSize来获取实际支持的图片大小。比如通过下述代码获取测试机(Huawei nova2)支持的预览尺寸。

List<Size> previewSizes = parameters.getSupportedPreviewSizes();for (int i = 0; i < previewSizes.size(); i++)
{Size sz = previewSizes.get(i);Log.i("size", String.format("w: %d, h: %d",sz.width,sz.height));
}

输出结果:

w: 1920, h: 1080

w: 1440, h:1080

w: 1280, h: 960

w: 1280, h:720

w: 960, h: 720

w: 960, h: 554

......

w: 176, h:144

既然手机支持这么多的尺寸,到底选择哪个合适呢?一般建议选择与预览界面宽高等比例的尺寸,这样可以确保相机预览的画面不变形,但这仅考虑了画面形变的适配性问题。对于图像检测识别等场合,图像分辨率是个很重要的参数,小分辨率会导致目标像素占比过低不利于检测,太大分辨率则导致计算耗时较多。因此,最佳的选择完全依赖于项目的需求。实践中,我们采取下面规则寻找最合适的尺寸: 1. 图像宽高比与屏幕宽高比例接近 2. 图像宽或高满足最小分辨率的要求

public Size getPreviewSize(Camera.Parameters para){List<Size> previewSizes = para.getSupportedPreviewSizes();float ratio;    if(bPortraint)ratio = ((float)ScreenHeight)/((float)ScreenWidth);       //竖屏    elseratio = ((float)ScreenWidth)/((float)ScreenHeight);        //横屏int   minDim = Math.max(ScreenWidth,ScreenHeight);    //最小尺寸float min_diff = Float.MAX_VALUE;int min_diff_index = -1;for (int j=0; j<previewSizes.size(); j++){float r = ((float)previewSizes.get(j).width)/((float)previewSizes.get(j).height);float diff = Math.abs(r - ratio);if(diff<min_diff && previewSizes.get(j).width>=minDim){min_diff = diff;min_diff_index = j;         }}              if(min_diff_index != -1)return previewSizes.get(min_diff_index);//如果没找到合适的,则寻找尺寸与屏幕分辨率最接近的作为兜底int diff = Integer.MAX_VALUE;for (int j=0; j<previewSizes.size(); j++){int newDiff;if(bPortraint)newDiff = Math.abs(previewSizes.get(j).width - ScreenHeight) + Math.abs(previewSizes.get(j).height - ScreenWidth);elsenewDiff = Math.abs(previewSizes.get(j).width - ScreenWidth) + Math.abs(previewSizes.get(j).height - ScreenHeight);if (newDiff < diff) {min_diff_index = j;diff = newDiff;}     }return previewSizes.get(min_diff_index);
}

3.4 设置数据格式
一般相机生产的视频数据都是按照一定格式来组织的,通过getSupportedPreviewFormats来获取实际支持的数据格式。比如通过下述代码获取测试机(Huawei nova2)支持的数据格式。

List<Integer> formats = parameters.getSupportedPreviewFormats();for (int i = 0; i < formats.size(); i++)
{int v = formats.get(i);Log.i("format", String.format("%d",v));
}

输出结果:

842094169         //Planar 4:2:0 YCrCb format
17                       //NV21 encoding format

利用setPreviewFormat方法可以设置想要的数据格式,最常见的数据格式为NV21(YCbCr_420_SP)。如果不设置,默认返回数据也是NV21编码的数据格式。为了获得预览的数据,还需要给相机设置一个预览回调函数(PreviewCallback),在这个回调中实现一个方法 onPreviewFrame(byte[] data, Camera camera),然后就可以获取Camera预览时的视频帧数据。

public void initCamera(){......//设置数据格式Camera.Parameters para = myCamera.getParameters();para.setPreviewFormat(PixelFormat.NV21); //PixelFormat.YCbCr_420_SPmyCamera.setParameters(para);//设置监听预览回调myCamera.setPreviewCallback(videocb);
}public PreviewCallback videocb = new Camera.PreviewCallback() {public void onPreviewFrame(byte[] data, Camera camera) {// TODO Auto-generated method stub//video process algorithm}
};

需要注意的是,onPreviewFrame函数返回的data数据格式是NV21,并不同于我们日常熟悉的RGB格式。在这里,我们有必要对该格式做一个详细介绍,后续解析灰度图或RGB彩色图都依赖于对该数据格式的理解。NV21图像格式属于YUV颜色空间的YUV420SP格式,每四个Y分量共用一组U分量和V分量。整个数据的大小为(w*h+w*h/2),其中w*h对应Y通道的数据大小,UV通道占的大小为 w*h/2=2*(w*h/4)。因为四个Y分量共用一组UV分量,因此U分量和V分量大小各占w*h/4。数据的排序方式如下所示,前w*h为Y通道的数据,后面w*h/2为UV通道的数据,其中UV是按顺序交叉存储。

通过下图可以更好的理解4个Y分量是如何共享一组UV分量的,我们利用相同的颜色块来说明Y与UV通道数据的关联性。比如,Y0、Y1、Y8、Y9四个Y分量,共享U0、V0分量;Y22、Y23、Y30和Y31四个Y分量共享 U7、V7分量。

在知道了YUV数据各通道间的关联关系后,我们就可以比较容易的获取每个像素位置的颜色值。首先获得YUV空间下的值,然后利用颜色转换公式计算RGB空间下的值。

像素位置

YUV颜色值

RGB颜色值

0

(Y0, U0, V0)

R = Y+1.4075*(V-128)
G = Y-0.3455*(U-128)-0.7169*(V-128)
B = Y+1.779*(U-128)

1

(Y1, U0, V0)

8

(Y8, U0, V0)

9

(Y9, U0, V0)

18

(Y19, U5, V5)

22

(Y22, U7, V7)

31

(Y31, U7, V7)

3.5 设置对焦
通过getSupportedFocusModes()可以获取到手机支持的对焦模式,比如通过下述代码可以打印出测试机(Huawei nova2)后置相机支持的对焦模型。

List<String> focusModeList = parameters.getSupportedFocusModes();
for (int i = 0; i < focusModeList.size(); i++)
{String focusMode = focusModeList.get(i);Log.i("FOCUS_MODE", String.format("%s",focusMode));
}

输出结果:

infinity
auto
continuous-video
continuous-picture

知道了相机支持的对焦模式后,就可以通过setFocusMode来设定一个模式。看起来很简单的一个设置问题,在实际的项目中要想获得很好的体验,对焦这件事又变的比较麻烦。往往会结合项目的实际需求来做一些定制化的处理,比如常见的有三种实现自动对焦的方式:

  1. 图像识别:通过对拍摄的图像进行分析,判断是否模糊,如果模糊调用1次自动对焦功能。这也符合人的主观认知,真正实现时会涉及图像模糊判断的算法准确度问题。
  2. 定时对焦:相对容易的方式,设定一个定时器,每隔一定的时间(比如2s)让相机做一次对焦操作。
  3. 传感器:基于手机传感器来判断运动状态,当运动结束时调用一次对焦,重点关注相机对准目标那一刻的清晰度。

4. 开启/结束预览
开启和结束预览的API比较简单,分别对应startPreview和stopPreview。开启预览后我们肯定是希望看到视频图像,因此在调用startPreview之前,需要将相机与显示图层相关联。需要用到的方法是camera.setPreviewDisplay(SurfaceHolder holder),这里就涉及到SurfaceView相关的知识。SurfaceView本身是一个View,但它与View的区别主要有几点:

  • View绘图效率不高,属于系统主动刷新绘制,适合界面动画较少的程序。而SurfaceView的效率较高,适合被动更新且界面刷新频繁的程序,比如游戏、视频播放和相机预览。
  • View在主线程,而SurfaceView在子线程进行页面刷新,因此不占用主线程资源。
  • SurfaceView实现了双缓冲机制,因此播放视频时画面更流畅。

基于这些特性,所以在相机预览时采用SurfaceView来实现。 SurfaceView有两个成员变量:一个是Surface对象,另一个是SurfaceHolder对象。通过getHolder()方法来获取当前SurfaceView的SurfaceHolder对象;通过SurfaceHolder中的回调可以知道Surface的状态(创建、变化、销毁)。SurfaceView的使用虽然比View要复杂,但是SurfaceView在使用时有一套使用的模板代码,大部分的SurfaceView绘图操作都可以套用这样的模板代码来进行编写。

private SurfaceView mysurface;
private SurfaceHolder myholder;public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mysurface = (SurfaceView)findViewById(R.id.surface);myholder = mysurface.getHolder();myholder.addCallback(this);    ......
}public void surfaceCreated(SurfaceHolder holder) {; //surface第一次创建的时候回调
}public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {//surface变化的时候回调(格式/大小)initCamera();
}public void surfaceDestroyed(SurfaceHolder holder) {; //surface销毁的时候回调
}  private void initCamera(){......myCamera = Camera.open(id);//Sets the Surface to be used for live previewmyCamera.setPreviewDisplay(myholder);
}

备注:更多有关SurfaceView和SurfaceHolder的内容可以参考官方介绍:Surface 和 SurfaceHolder  |  Android 开源项目  |  Android Open Source Project

5. 关闭相机
在程序退出或满足业务条件后希望关闭相机,先停止预览和回调,然后释放相机即可。

//Removes a previously added Callback interface from this holder
surfaceHolder.removeCallback(this);mCamera.setPreviewCallback(null);
mCamera.stopPreview();Re-locks the camera to prevent other processes from accessing it
mCamera.lock();    //Disconnects and releases the Camera object resources
mCamera.release();
mCamera = null;

6. 附Camera主要的类和接口

若有收获,就点个赞吧

Camera相机研发介绍相关推荐

  1. 手机相机里面的m_荣耀V30 PRO详细评测:Matrix Camera相机矩阵开启5G视频时代

    [IT168 评测]随着手机摄像头的配置越来越高,手机上已经能实现不俗的拍照效果,甚至超越了数码相机(DC)成为了人们最常用的拍照工具.进入今年的下半年,随着5G的商用,手机厂商对于手机的摄像头功能优 ...

  2. 【Camera相机开发】实现相机预览

    文章目录 认识 Parameters 设置预览尺寸 添加预览 Surface 开启和关闭预览 校正预览画面方向 自然方向 设备方向 局部坐标系 屏幕方向 摄像头传感器方向 画面方向校正 适配预览比例 ...

  3. 深度相机分类介绍:结构光+立体深度+飞行时间

    深度相机分类介绍:结构光+立体深度+飞行时间 摘要 结构光(Structured Light and Coded Light) 双目立体视觉或者直译为立体深度(Stereo Depth) 飞行时间(T ...

  4. 高帧频CMOS相机研发方案

    文章目录 前言 一.感光芯片 二.RJ45万兆以太网 三.FPGA 前言 项目需要,花2个月采用K7 FPGA+IMX4XX(索尼的感光芯片)+88X3310(美满的10G PHY)方案完成了一个76 ...

  5. QT中Camera相机调用

    一:摄像头配置 1.创建摄像头类,获取电脑端的所有摄像头 cameras = QCameraInfo::availableCameras(); //获取所有相机的列表 if (cameras.coun ...

  6. vue3.x +Cesium Cesium camera相机的使用(二)

    Cesium camera 相机系统 setView flyTo lookAt viewBoundingSphere setView 的使用 通过设定目的地的三维坐标和视线方向将视角切换到设定视域范围 ...

  7. Camera5 MTK camera驱动框架介绍

    一.概序: 前文已经介绍了camera驱动部分的内容Camera4 MTK camera驱动结构介绍,这里在回顾下之前的框架图, 这篇主要介绍红框部分的内容: imgsensor起到承上启下的作用,在 ...

  8. Android自定义camera相机 系列(一)

    该文章 主要使用 自定义 surfaceview 及 camera 知识点,来实现一个自定义的拍照 .切换闪光灯 和 前后摄像头的功能.阅读需要消耗时间 :15分钟+ .内容比较简单算是 开发相机的过 ...

  9. Webots 机器人仿真平台(十) 添加camera相机

    添加camera相机 1. 添加camera实体 2. 添加camera控制接口代码 3. 运行效果 参考资料 1. 添加camera实体 step1: 在Robot中添加 camera(camera ...

  10. 【转】Android Camera 相机开发详解

    在Android 5.0(SDK 21)中,Google使用Camera2替代了Camera接口.Camera2在接口和架构上做了巨大的变动, 但是基于众所周知的原因,我们还必须基于 Android ...

最新文章

  1. mongodb android,如何在Android中连接到MongoDB数据库?
  2. 一起学asp.net基础文章二 服务器控件、客户端控件和html表单控件
  3. android p获取通话记录_[android] 取得最近通话记录的方法
  4. IOS开发之GCD的基本知识
  5. 对GCD的一些理解和实践
  6. ae saber插件_【AE插件】 用于做动画制作/设计的五个免费插件 非常好用
  7. 输入url后的加载过程
  8. android+浮层布局,如何使用Android实现单页面浮层可拖动view
  9. OpenBmc开发5:bitbake介绍与使用
  10. mp4格式-播放和断点续播
  11. 【云驻共创】华为云数据库之大数据入门与应用(全)
  12. python打印不换行_python打印后如何不换行
  13. Android App Dark Theme(暗黑模式)适配指南,android实战mysql
  14. 在xp中不能查看或更改文件夹的“只读”属性或“系统”属性解决方法
  15. 安卓 蓝牙遥控器键值配对 kl文件
  16. DHCP Snooping IPSG
  17. OpenResty实现限流的几种方式
  18. Jenkins自动构建(CI/DI)项目(一)
  19. 2019蓝桥杯国赛E-第八大奇迹
  20. 计算机教育cscd,工程技术方向论文投稿,这几本CSCD期刊值得推荐

热门文章

  1. python3 中文繁体转换简体,简体转换为繁体,汉字转换拼音
  2. 编辑中的word变成只读_教大家word文档变成只读模式怎么改
  3. 解决vscode的任务栏白色图标的问题
  4. windows系统用cmd命令开启WiFi共享功能
  5. 10月20日前!武汉市科技成果转化中试平台(基地)备案申报条件及流程梳理
  6. Android Studio如何导出apk文件
  7. 致敬2021——中国汽车,拆掉思维里的墙
  8. 小区物业管理系统计算机科学技术,智能化小区物业管理系统关键技术研究
  9. 雅戈尔关于媒体报道出澄清公告 谨防股价变动
  10. 出没干日月鸿蒙之内翻译,李白《大鹏遇希有鸟赋》原文及翻译赏析