OpenCV for Android开发笔记(二)相机的使用

您所在的位置:网站首页 ccd相机怎么用前置摄像头拍照的呢 OpenCV for Android开发笔记(二)相机的使用

OpenCV for Android开发笔记(二)相机的使用

2024-07-16 09:13:14| 来源: 网络整理| 查看: 265

绪论

对于Android移动端的相机调用及数据读取,OpenCV是把原来C++的部分与本地AndroidSDK进行了整合,通过桥接的方式调用Android手机摄像头,最重要的一个类是JavaCameraView,它是OpenCV中调用Android手机摄像头的接口类,支持以代码和XMLView配置的方式使用,可以在Android设备中使用摄像头完成前置与后置摄像头的预览与拍照功能,下面就对这些内容逐一加以说明,并完成代码演示。

目录 绪论 一、显示预览帧 1、修改AndroidManifest.xml文件: 2、为界面布局文件添加显示相机内容的组件: 3、在MainActivity中调用API 完整代码 二、横屏与竖屏切换 1、横竖屏切换 ① 方法一:在AndroidManifest.xml中配置 ② 方法二:在java代码中设置 3、竖屏预览图像自动旋转问题 问题 解决方法 三、前后置摄像头切换 四、拍照和读取相册 一、显示预览帧 1、修改AndroidManifest.xml文件: 添加相机的相关权限:

因为在Android中使用SD卡、相机卡等本地硬件资源的时候会涉及授权问题,而且Android的低版本与高版本的授权方式有点不一样。这里首先需要说明一下Android在不同版本上的相机授权差别,Android在低版本中是通过向AndroidManifests.xml中添加文本的方式来完成授权的,添加内容如下:

123456

这种方式在Android 6.0以下的版本上使用没有问题,但是在Android 6.0以上的版本中(包括6.0)就会出现问题,原因是Android系统的授权方式改变升级了,所以需要在onCreat中添加如下的代码进行授权:

123if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {             requestPermissions( new String[]{Manifest.permission.CAMERA}, 203);         } 设置应用的界面主题为没有顶部标题栏且全屏显示的,在application标签中添加: 123456android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@android:style/Theme.NoTitleBar.Fullscreen">   设置横竖屏显示,在application-activity标签中添加: 1 android:screenOrientation="landscape"     //屏幕的控制

2、为界面布局文件添加显示相机内容的组件:

打开res/layout下面的activity_main.xml布局文件,往布局中添加一个OpenCV的视觉组件JavaCameraView:

123456789101112131415         3、在MainActivity中调用API

打开src目录下面的MainActivity,由于我们的目标是在应用中通过OpenCV的Java API实现打开相机全屏显示,并获取预览框,所以MainActivity需要实现CvCameraViewListener2接口,可以实现三个方法,分别是:onCameraViewStarted、onCameraViewStopped和onCameraFrame,关键的图像处理写在onCameraFrame函数中:

调用API 声明一个JavaCameraView对象,用于存放activity_main.xml中的JavaCameraView组件,并在OnCreate中实现绑定和添加事件监听:

12mCVCamera = (JavaCameraView) findViewById(R.id.camera_view); mCVCamera.setCvCameraViewListener(this);

修改public Mat onCameraFrame(CvCameraViewFrame inputFrame)回调函数的内容了,这个函数在相机刷新每一帧都会调用一次,而且每次的输入参数就是当前相机视图信息,我们直接获取其中的RGBA信息作为Mat数据返回给显示组件即可:

12345678/**      * 图像处理都写在此处      */     @Override     public Mat onCameraFrame(CvCameraViewFrame inputFrame) {         //直接返回输入视频预览图的RGBA数据并存在Mat数据中         return inputFrame.rgba();     }

以上操作中,我们在OnCreate函数中已经获取到mCVCamera对象,只有调用mCVCamera.enableView()之后,预览组件才会显示每一帧的Mat图像,但是在显示之前我们必须先确保OpenCV的库文件已经加载完成,所以调用此方法需要进行异步处理:

12345678910111213141516/**  * 通过OpenCV管理Android服务,异步初始化OpenCV  */ BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {     @Override     public void onManagerConnected(int status){         switch (status) {         case LoaderCallbackInterface.SUCCESS:             Log.i(TAG,"OpenCV loaded successfully");             mCVCamera.enableView();             break;         default:             break;         }     } };

所以只有当mLoaderCallback收到LoaderCallbackInterface.SUCCESS消息的时候,才会打开预览显示,那么这个消息是从哪里发出来的呢,这就需要我们重写Activity的onRusume方法了,因为每次当前Activity激活都会调用此方法,所以可以在此处检测OpenCV的库文件是否加载完毕:

12345678910@Override     public void onResume() {         super.onResume();         if (!OpenCVLoader.initDebug()) {             Log.d(TAG,"OpenCV library not found!");         } else {             Log.d(TAG, "OpenCV library found inside package. Using it!");             mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);         }     }; 完整代码 MainActivity 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273package com.example.asus.opencvcamerademo; import android.Manifest; import android.os.Build; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import org.opencv.android.BaseLoaderCallback; import org.opencv.android.CameraBridgeViewBase; import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame; import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2; import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; import org.opencv.android.JavaCameraView; import org.opencv.core.Mat; public class MainActivity extends AppCompatActivity implements CvCameraViewListener2 {     private String TAG = "OpenCV_Test";     private JavaCameraView mOpenCvCameraView;     private int M_REQUEST_CODE = 203;     private String[] permissions = {Manifest.permission.CAMERA};     // Initialize OpenCV manager.     private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {         @Override         public void onManagerConnected(int status) {             switch (status) {                 case LoaderCallbackInterface.SUCCESS: {                     mOpenCvCameraView.enableView();                     break;                 }                 default:                     break;             }         }     };     @Override     public void onResume() {         super.onResume();         if (!OpenCVLoader.initDebug()) {             Log.d(TAG,"OpenCV library not found!");         } else {             Log.d(TAG, "OpenCV library found inside package. Using it!");             mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);         }     };     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         // Set up camera listener.         mOpenCvCameraView = (JavaCameraView)findViewById(R.id.CameraView);         mOpenCvCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);         mOpenCvCameraView.setCvCameraViewListener(this);         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {             requestPermissions(permissions, M_REQUEST_CODE);         }     }     // Load a network.     public void onCameraViewStarted(int width, int height) {     }     public Mat onCameraFrame(CvCameraViewFrame inputFrame) {         return inputFrame.rgba();     }     public void onCameraViewStopped() {} AndroidManifest.xml 12345678910111213141516171819202122232425                                                                                                            

二、横屏与竖屏切换

1、横竖屏切换 ① 方法一:在AndroidManifest.xml中配置

假设不想让软件在横竖屏之间切换,最简单的办法就是在项目的AndroidManifest.xml中找到你所指定的activity中加上android:screenOrientation属性。

12android:screenOrientation="landscape"是限制此页面横屏显示,   android:screenOrientation="portrait"是限制此页面数竖屏显示。 12android:screenOrientation="landscape"是限制此页面横屏显示, android:screenOrientation="portrait"是限制此页面数竖屏显示。

android:screenOrientation设定该活动的方向,该值可以是任何一个下面的字符串:

“unspecified”:默认值 由系统来推断显示方向.判定的策略是和设备相关的,所以不同的设备会有不同的显示方向. “landscape”:横屏显示(宽比高要长) “portrait”:竖屏显示(高比宽要长) “user”:用户当前首选的方向 “behind”:和该Activity以下的那个Activity的方向一致(在Activity堆栈中的) “sensor”:有物理的感应器来决定。假设用户旋转设备这屏幕会横竖屏切换。 “nosensor”:忽略物理感应器。这样就不会随着用户旋转设备而更改了("unspecified"设置除外)。 ② 方法二:在java代码中设置 1设置横屏代码:setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//横屏 1设置竖屏代码:setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//竖屏 3、竖屏预览图像自动旋转问题

Android的Camera相关应用开发中,有一个必须搞清楚的知识点,就是Camera的预览方向和拍照方向:

图像的Sensor方向:手机Camera的图像数据都是来自于摄像头硬件的图像传感器(Image Sensor),这个Sensor被固定到手机之后是有一个默认的取景方向的,这个方向如下图所示,坐标原点位于手机横放时的左上角: Camera的预览方向:由于手机屏幕可以360度旋转,为了保证用户无论怎么旋转手机都能看到“正确”的预览画面(这个“正确”是指显示在UI预览界面的画面与你人眼看到的眼前的画面是一致的),Android系统底层根据当前手机屏幕的方向对图像Sensor采集到的数据进行了旋转处理,然后后才送给显示系统,因此,打开Camera应用后,无论怎么旋转手机,你都能看到“正确”的画面,Android系统提供一个API来手动设置Camera的预览方向,叫做setDisplayOrientation,默认情况下,这个值是0,与图像Sensor方向一致,所以对于横屏应用来说,就不需要更改这个Camera预览方向。但是,如果你的应用是竖屏应用,就必须通过这个API将Camera的预览方向旋转90,与手机屏幕方向一致,这样才会得到正确的预览画面。 问题

Camera的拍照方向:当你点击拍照按钮,得到的图片方向不一定与画面中预览的方向一致,这是因为拍摄的照片是将图像Sensor采集到的数据直接存储到SDCard上的,因此,Camera的拍照方向与上述的Camera的图像Sensor方向一致。 由此可见,如果横向拿手机拍照,由于正好与Camera的拍照方向一致,因此得到的照片是“正确”的;而竖着拿手机拍照的话,Camera的图像Sensor依然以上面描述的角度在采集图像并存储到SDCard上,所以得到的图片就是右图这样的,因为竖着拿手机正好与图像Sensor的方向相差了90度。由此,大家应该明白了为什么我们用手机拍出的照片经常需要旋转90度才能看到“正确”的画面了吧?

解决方法

① 首先在onCameraFrame方法中将每帧图像顺时针旋转90°以后再显示,代码如下:

12345678910111213@Override public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {     if (isFrontCamera) {             Core.rotate(frame, frame, Core.ROTATE_90_COUNTERCLOCKWISE);             Core.flip(frame, frame, 1);                 } else {             Core.rotate(frame, frame, Core.ROTATE_90_CLOCKWISE);         }     return frame; }

这里我们根据是否是前置摄像头,对帧所对应的矩阵进行旋转,其中后置摄像头需要将矩阵顺时针旋转90°得到竖向的图像,而前置摄像头则需要逆时针旋转90°,同时进行一次镜像翻转才能得到想要的图像。 PS:由于矩阵的乘法不满足交换律,所以此处前置摄像头的旋转与翻转顺序不可更改!!

123456789public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {         Mat frame= inputFrame.rgba();         Core.rotate(frame,frame,Core.ROTATE_90_CLOCKWISE);         return frame;     }

② 当运行上述代码的时候就会发现PORTRAIT显示模式运行的时候,JavaCameraView没有实现相机预览显示功能,原因是其继承的基类CameraBridgeViewBase的deliverAndDrawFrame方法中有一个缓存图像对象,它的大小与onCameraFrame方法中旋转之后的frame大小不一致,导致无法填充缓冲区,实现绘制,这个时候可以在deliverAndDrawFrame方法中进行手动修改。

请在455行的AllocateCache方法中将 mFrameHeight, mFrameWidth对调成如下:

1234protected void AllocateCache() {       mCacheBitmap = Bitmap.createBitmap( mFrameHeight, mFrameWidth,Bitmap.Config.ARGB_8888); }

【注:如果是横屏显示,则不能对调。】

三、前后置摄像头切换

对于大多数的Android移动设备来说都有前置与后置摄像头,JavaCameraView通过setCameraIndex方法根据所输入摄像头的索引值来决定是开启前置还是后置摄像头,具体如下。

0:开启后置摄像头。 1:开启前置摄像头。

在View定义的XML文件中添加如下两个RadioButton对象作为开启前置或者后置摄像头的选项,相关的XML定义如下:

12345678910111213141516171819202122232425262728293031323334353637                            

当横屏显示的时候,若使用的是前置摄像头,则需要在返回之前完成图像的镜像变换,否则图像会呈镜像显示。最后在选择前后摄像头、横屏与竖屏相互切换的时候,我们应该在destory方法中禁用JavaCameraView对象,然后在resume方法中再次启用它,不然就会导致切换之后显示不正确。

1234567891011121314151617181920212223242526272829303132333435 @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         // Set up camera listener.         mOpenCvCameraView = (JavaCameraView)findViewById(R.id.CameraView);         mOpenCvCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);         mOpenCvCameraView.setCvCameraViewListener(this);         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {             requestPermissions( new String[]{Manifest.permission.CAMERA}, 203);         }         RadioGroup radioGroup=findViewById(R.id.radioGroup);         RadioButton frontOption = (RadioButton) findViewById(R.id.frontCameraBtn);         RadioButton backOption = (RadioButton) findViewById(R.id.backCameraBtn);         radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {             @Override             public void onCheckedChanged(RadioGroup group, int checkedId) {                 switch (checkedId){                     case R.id.frontCameraBtn:                         cameraIndex=1;                         break;                     case R.id.backCameraBtn:                         cameraIndex=0;                         break;                 }                 mOpenCvCameraView.setCameraIndex(cameraIndex);                 mOpenCvCameraView.disableView();                 mOpenCvCameraView.enableView();             }         });     } 若是竖屏显示,则 1 android:screenOrientation="portrait">  

在onCameraFrame方法中加入

123456789101112131415@Override public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {     Mat frame= inputFrame.rgba();     if(cameraIndex==1) {         Core.rotate(frame, frame, Core.ROTATE_90_CLOCKWISE);         //倒过来         Core.flip(frame,frame,0);         return frame;     }else {         Core.rotate(frame, frame, Core.ROTATE_90_CLOCKWISE);         return frame;     } } 若是横屏显示,则 1 android:screenOrientation="landscape">  

记得修改第455行中的AllocateCache()方法 在onCameraFrame方法中加入

1234567891011121314@Override public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {     Mat frame= inputFrame.rgba();     if(cameraIndex==1) {         Core.flip(frame,frame,1);         //镜像翻转         return frame;     }else {         return frame;     } }

四、拍照和读取相册

参考



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭