CV深度学习模型Android端落地第一步:使用Android Camera2接口获得实时预览图像
这个系列的博客主要介绍如何在Android设备上移植你训练的cv神经网络模型。
主要过程如下: 1、使用Android Camera2 APIs获得摄像头实时预览的画面。 2、如果是对人脸图像进行处理,使用Android Camera2自带的Face类来对人脸检测,并完成在预览画面上画框将人脸框出、添加文字的功能。
3、使用Tensorflow Lite 将自己的训练得到的模型移植到Android上。
以上三个步骤会分为三个博客,同时也会提供示例代码。步骤二可以根据你的实际需求跳过或修改。
搭建Android Studio开发环境
在Win10或Ubuntu平台上搭建Android Stuido开发环境过程都是一样的。
1、安装JDK1.8并配置环境变量 输入java --version
提示版本则成功
2、下载AndroidSDK并解压,Win10下SDK目录下启动SDK Manager.exe,Ubuntu下在tool目录下执行可执行文件android可以打开SDK manager。可以在这里面配置sdk版本、NDK版本、Cmake、LLDB等,一般装好SKD就可以了,其他的Tools按需安装即可。
3、前往官网下载安装Android Studio,在开始使用前要配置一下JDK和SDK的路径。
需要注意的是,在Ubuntu下,最好前往/opt/android/bin目录(替换成你的AS安装目录)下,执行sudo sh studio.sh
,以root权限打开Android studio,这样一来会避免很多莫名其妙的问题出现。
以下是我的项目的配置情况
- Android Studio版本: 1
2
3
4
5Android Studio 3.2.1
Build #AI-181.5540.7.32.5056338, built on October 9, 2018
JRE: 1.8.0_152-release-1136-b06 amd64
JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
Windows 10 10.01
2
3java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)1
HUAWEI MATE 9(4G+64G)
1
2
3Android 8.1(Oreo)
API Level:27
Revision:31
18.1506
根据CameraDemo修改代码获得实时预览图像
项目准备
git clone或者下载代码解压到工作文件夹下,用AS打开,下载Gradle,Syc项目(Syc按钮在Run按钮边上),版本和插件版本是有对应关系的。
Plugin version | Required Gradle version |
---|---|
1.0.0 - 1.1.3 | 2.2.1 - 2.3 |
1.2.0 - 1.3.1 | 2.2.1 - 2.9 |
1.5.0 | 2.2.1 - 2.13 |
2.0.0 - 2.1.2 | 2.10 - 2.13 |
2.1.3 - 2.2.3 | 2.14.1+ |
2.3.0+ | 3.3+ |
3.0.0+ | 4.1+ |
3.1.0+ | 4.4+ |
3.2.0+ | 4.6+ |
appliction的build.gradle下面写的是gradle插件版本 1
2
3
4
5buildscript{
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
}1
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
代码修改
完成Syc(同步)之后 ,将手机连接上,使用debug run模式将代码部署到手机上,出现的效果应该是带前置预览画面和执行聚焦的一个button。
切换摄像头(optional)
修改openCamera方法,使用前置摄像头。
原openCamera方法代码 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24/**
* Opens the camera specified by {@link Camera2BasicFragment#mCameraId}.
*/
private void openCamera(int width, int height) {
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
requestCameraPermission();
return;
}
setUpCameraOutputs(width, height);
configureTransform(width, height);
Activity activity = getActivity();
CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
try {
if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26private void openCamera(int width, int height, int isSwitch) {
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
requestCameraPermission();
return;
}
setUpCameraOutputs(width, height);
configureTransform(width, height);
Activity activity = getActivity();
CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
try {
if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
// isSwitch==0 使用后置摄像头 isSwitch==1 使用前置摄像头
if(isSwitch==1){
mCameraId = "1";
}
Log.d(TAG, "openCamera is : "+mCameraId);
manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
openCamera(width, height, 1);
}
@Override
public void onResume() {
super.onResume();
startBackgroundThread();
if (mTextureView.isAvailable()) {
openCamera(mTextureView.getWidth(), mTextureView.getHeight(), 1);
} else {
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
}
}
在Android视图下编辑 Application/res/layout
的 xml
文件 设置button 和** image button**属性 android:visibility="invisible"
。
Android Camera2通过建立会话(session)机制来协调程序调用相机,CameraCaptureSession.CaptureCallbac
是一个抽象类,通过对这个抽象类的实现,我们可以处理 CapterSession
的回调内容。要获得每一帧的实时画面,应该在CameraCaptureSession.CaptureCallback中写相关代码。在这个Demo中,实时预览画面是展示在一个基于Android Texture
的 Surface Vie
控件的,我们通过调用Texture控件的getBitmap方法,就能得到当前Texture上的实时画面,想通过Camera2的API获得实时的帧数据比较麻烦,建议使用这样的方法,延迟也比较小。
1 |
|
至此,我们就能够完成实时切换摄像头并获得了实时预览画面,后面会说明如何检测人脸并在预览画面上添加矩形框和文字以展示神经网络的处理结果。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!