1. Camera开发流程
在这里,我们先了解下相机开发的大致流程,然后再对里面的步骤进行详细的阐述。
- 利用open(int)获取Camera实例
- 利用getParameters()获取默认设置,如果需要修改参数,利用setParameters()重新设置
- 利用setDisplayOrientation()设置相机图像旋转角度,产生正确的预览画面
- 利用setPreviewDisplay(SurfaceHolder)关联相机与SurfaceView显示图层,让视频流显示在界面上
- 设置Preview的回调函数,获取帧数据并进行图像处理逻辑
- 调用startPreview()开始预览,调用stopPreview()停止预览
- 调用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-facing
result = (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); //竖屏
else
ratio = ((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);
else
newDiff = 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_SP
myCamera.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) |
|
| 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来设定一个模式。看起来很简单的一个设置问题,在实际的项目中要想获得很好的体验,对焦这件事又变的比较麻烦。往往会结合项目的实际需求来做一些定制化的处理,比如常见的有三种实现自动对焦的方式:
4. 开启/结束预览
开启和结束预览的API比较简单,分别对应startPreview和stopPreview。开启预览后我们肯定是希望看到视频图像,因此在调用startPreview之前,需要将相机与显示图层相关联。需要用到的方法是camera.setPreviewDisplay(SurfaceHolder holder),这里就涉及到SurfaceView相关的知识。SurfaceView本身是一个View,但它与View的区别主要有几点:
基于这些特性,所以在相机预览时采用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 preview
myCamera.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主要的类和接口

若有收获,就点个赞吧
?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------
📢博客主页:https://blog.csdn.net/weixin_43197380📢欢迎点赞👍收藏⭐留言📝如有错误敬请指正!📢本文由Loewen丶原创,首发于CSDN,转载注明出处🙉📢现在的付出,都会是一种沉淀,只为让你成为更好的人✨文章预览:一.分辨率(Resolution)1、工业相机的分辨率是如何定义的?2、工业相机的分辨率是如何选择的?二.精度(Accuracy)1、像素精度(PixelAccuracy)2、定位精度和重复定位精度(RepeatPrecision)三.公差(Tolerance)四.课后作业(Post-ClassExercises)视觉行业的初学者,甚至是做了1~2年
目录H2数据库入门以及实际开发时的使用1.H2数据库的初识1.1H2数据库介绍1.2为什么要使用嵌入式数据库?1.3嵌入式数据库对比1.3.1性能对比1.4技术选型思考2.H2数据库实战2.1H2数据库下载搭建以及部署2.1.1H2数据库的下载2.1.2数据库启动2.1.2.1windows系统可以在bin目录下执行h2.bat2.1.2.2同理可以通过cmd直接使用命令进行启动:2.1.2.3启动后控制台页面:2.1.3spring整合H2数据库2.1.3.1引入依赖文件2.1.4数据库通过file模式实际保存数据的位置2.2H2数据库操作2.2.1Mysql兼容模式2.2.2Mysql模式
在本文中,我们将探讨摄影机的外参,并通过Python中的一个实践示例来加强我们的理解。相机外参摄像头可以位于世界任何地方,并且可以指向任何方向。我们想从摄像机的角度来观察世界上的物体,这种从世界坐标系到摄像机坐标系的转换被称为摄像机外参。那么,我们怎样才能找到相机外参呢?一旦我们弄清楚相机是如何变换的,我们就可以找到从世界坐标系到相机坐标系的基变换的变化。我们将详细探讨这个想法。具体来说,我们需要知道相机是如何定位的,以及它在世界空间中的位置,有两种转换可以帮助我们:有助于确定摄影机方向的旋转变换。有助于移动相机的平移变换。让我们详细看看每一个。旋转通过旋转改变坐标让我们看一下将点旋转一个角度
为什么需要服务网关传统的单体架构中只需要开放一个服务给客户端调用,但是微服务架构中是将一个系统拆分成多个微服务,如果没有网关,客户端只能在本地记录每个微服务的调用地址,当需要调用的微服务数量很多时,它需要了解每个服务的接口,这个工作量很大。有了网关之后,网关作为系统的唯一流量入口,封装内部系统的架构,所有请求都先经过网关,由网关将请求路由到合适的微服务。使用网关的好处1)简化客户端的工作。网关将微服务封装起来后,客户端只需同网关交互,而不必调用各个不同服务;(2)降低函数间的耦合度。一旦服务接口修改,只需修改网关的路由策略,不必修改每个调用该函数的客户端,从而减少了程序间的耦合性(3)解放开发
俯拍相机中心和吸嘴中心的标定文章目录俯拍相机中心和吸嘴中心的标定前言适用模型如下:一、使用一个标定片进行标定1.关键注意:2.标定步骤:二、使用一个L型的工件1.关键注意:2.标定步骤:总结前言在自动化设备领域,使用相机进行定位是很普遍存在的,而使用相机定位就必定会用到标定,本文介绍两种关于吸嘴上方的俯拍相机和吸嘴中心的标定方法(前提是带有仰拍相机和俯拍相机)。【还有很多相机的使用场景的标定方法将在以后的文章中进行阐述】适用模型如下:一、使用一个标定片进行标定1.关键注意:关键是使用两个相机的中心和识别偏差,得到两个相机的中心固定偏差。注:后续俯拍相机拍物料识别得到的偏差以吸嘴中心在俯拍相机中
相机内参标定,相机和激光雷达联合标定一、相机标定原理1.1成像过程1.2标定详解二、相机和激光雷达联合标定2.1标定方法汇总2.2Autoware的安装与运行2.2.1安装方式2.2.2安装Autoware的依赖(Ubuntu16.04/kinetic)2.2.3编译Autoware1.创造工作空间2.下载Autoware源码3.其他依赖4.编译5.效果2.3Autoware标定激光雷达和相机的外参过程一、相机标定原理1.1成像过程现实物体在相机中的成像过程离不开世界坐标系、相机坐标系、图像坐标系以及像素坐标系,只有理解了这些才能对获取的图像进行准确的分析。成像过程:四个坐标系如下图所示:世界
我发现python的细节自动完成很好RubyonRails有类似的方法描述吗? 最佳答案 有篇不错的文章"UsingVIMasacompleteRubyonRailsIDE"其中引用rails.vim.这似乎是RailsforVIM的实际标准。(不过,我还没有使用过它,但很快就会尝试。)这允许你做很多与Rails相关的任务,但对自动完成没有帮助。还有一篇"RubyAutocompleteinVim"(遗憾的是不再可用)这就是您要搜索的内容。我不知道,理解Rails的所有插件魔法和元编程的东西是否足够聪明。它至少在vim的配置中提到了
防火墙防火墙分类第一代防火墙:包过滤防火墙包过滤防火墙的缺点第二代防火墙:代理防火墙第三代防火墙:状态防火墙第四代防火墙:UTM防火墙第五代防火墙:下一代防火墙华为防火墙介绍安全策略防火墙的会话表防火墙分类第一代防火墙:包过滤防火墙属于第一代防火墙技术,在没有专用防火墙设备时,一般由路由器实现该功能。将网络上传送数据包的IP首部以及TCP/UDP首部,获取发送源的IP地址和端口号,以及目的地的IP地址和端口号,并将这些信息作为过滤条件,决定是否将该分组转发至目的地网络分组过滤的执行需要设置访问控制列表。访问控制列表也可以称为安全策略(简称策略)或安全规则(简称规则)。类似于进站检票的做法,符合
内容来自Qt样式表之QSS语法介绍-3YL的博客Qt样式表是一个可以自定义部件外观的十分强大的机制,可以用来美化部件。Qt样式表的概念、术语和语法都受到了HTML的层叠样式表(CascadingStyleSheets, CSS教程)的启发,不过与CSS不同的是,Qt样式表应用于部件的世界。类型选择器QPushButton匹配QPushButton及其子类的实例ID选择器QPushButton#okButton匹配所有objectName为okButton的QPushButton实例。 CSS常用样式1CSS文字属性注:px:相对长度单位,像素(Pixel)。pt:绝对长度单位,点(Point