1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 基于Android设备获取USB外接摄像头的图像

基于Android设备获取USB外接摄像头的图像

时间:2021-02-17 15:50:56

相关推荐

基于Android设备获取USB外接摄像头的图像

目录

背景开发环境硬件软件简介那我们开始吧导入项目布局、权限与初始化MainActivity.java运行时遇到的问题运行结果其他

背景

本人在读研期间接到的项目,需要用一个工业内窥镜(支持USBType-C接口)外接到Android设备上,获取其中图像进行目标检测等后续需求。本文主要讲Android设备如何显示、获取USB摄像头采集到的每一帧图像,后续可以通过深度学习或者Android版本的OpenCV进行目标检测。

查阅目前的技术博客,得到了几位大牛的文章指点,最终成功完成了项目。所以本文期望在前辈的基础上进行总结,给后来者提供一些帮助。

因为行文时作者还是在校学生,接触Android时间也不算长,可能用方法比较繁琐笨拙,如有不对处,望指正。

开发环境

硬件

荣耀V10手机一台(鸿蒙OS)、Redmi K30 PRO(Android 11)、工业内窥镜(支持USBType-C接口)

软件

Android studio 4.2.1

简介

UVC全称为USB Video Class,直接翻译过来的意思就是:USB视频类,它是一种专门为USB视频捕获设备定义的协议标准。

这个标准是Microsoft与另外几家设备厂商联合推出的为USB视频捕获设备定义的协议标准,已经成为USB org标准之一。

现在的主流操作系统,都已提供UVC设备驱动,因此符合UVC规格的硬件设备在不需要安装任何的驱动程序下即可在主机中正常使用。是的,目前Android系统已经支持uvc设备。(摘自小驰笔记:/p/972e05fa76a3)

本文实现的功能使参考三位大牛的文章或源码。

UVCCamera 开源项目

里面有8个例程(从易到难),需要引用作者自己的libuvccamera库,有参考价值。(/saki4510t/UVCCamera)

博主 小驰笔记 的开源项目

简书博主“小驰嘻嘻”的项目,小驰博主的文章要好好读一下,里面也是用到上面的libuvccamera库,但是里面用到了AIDL,像我这样的新手看不懂。(捂脸)(/yorkZJC/UvcCameraDemo)

博主 jiangdongguo 的开源项目

这里面需要引入博主自己的libuvccamera库,不能用UVCCamera原作者的库,照着他的demo编写程序,是可以在自己的项目中实现USB摄像头预览的。(GitHub源码地址:/jiangdongguo/AndroidUSBCamera)

最终本文参考了博主 jiangdongguo 的开源项目,并在博主无名之辈FTER文章的指导下完成。(/andrexpert/article/details/78324181)

那我们开始吧

导入项目

下载博主 jiangdongguo 的开源项目,在自己的项目中引入该项目的libuvccamera模块。

接下来会遇到一个问题

参考博客 /shenggaofei/article/details/98055433 在project目录下的build.gradle对应位置添加以下代码解决(版本号比较低,所以为黄色,不影响运行)

classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'

下一个问题

原因是libuvccamera模块中的各个版本已经定义为变量,而我们引入的时候没有给这些变量赋值,于是在开源项目中,找到声明版本号的地方,复制到自己项目的对应位置(也可以自己定义),之后重新同步一下,所有报错就消失了。

最后一步

在自己项目的 build.gradle 中添加依赖,否则 UVCCameraTextureView 图像预览控件将无法使用。

implementation project(':libusbcamera')

布局、权限与初始化

布局根据需求来吧,本文项目既要实时预览外接 USB相机拍摄的图像,又需要对图像进行分析,故首先把预览图像的控件摆上。libuvccamera模块中有一个定义好的控件 UVCCameraTextureView 直接拿来用就行。本文demo只有一个MainActivity,布局如下:

<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><FrameLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"><com.serenegiant.usb.widget.UVCCameraTextureViewandroid:id="@+id/camera_view"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_gravity="center" /></FrameLayout></androidx.constraintlayout.widget.ConstraintLayout>

权限方面,也按照开源项目配置好即可,在 AndroidManifest.xml 中加入以下权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"android:maxSdkVersion="30"tools:ignore="ScopedStorage"/><uses-permission android:name="android.permission.CAMERA" /><uses-permission android:name="android.permission.RECORD_AUDIO" /><uses-feature android:name="android.hardware.usb.host" /><uses-feature android:name="android.hardware.camera" /><uses-feature android:name="android.hardware.camera.autofocus" />

如果报红,就按 alt+Enter 解决

初始化方面,在之前下载的jiangdongguo 的开源项目里面找到一个叫 CrashHandler.java 的文件,拷贝到自己的项目中,会出现两处报错。

第12行报错,源于原作者引用自己编写的类所在文件夹的名字,由于类没有拷贝过来而报错,删除这行即可。

136行就是该类不存在而报错,可以改为自己的类的名字,也可以干脆删除MyApplication.DIRECTORY_NAME,让日志文件直接存放在根目录下,日志文件会存放调试时候出现的bug。

还有一个地方需要注意,由于 Android 10 以上系统不支持静态的写权限,所以需要把 build.gradle 中 compileSdkVersion 和 targetSdkVersion均改到 28 及以下。(这个问题不会在编译期报错,只会在插入USB摄像头的时候弹出错误提示,并且无法正常退出程序。困扰了我好久,看来还是对Android了解得不够多…)

MainActivity.java

package com.example.myapplication;import androidx.annotation.RequiresApi;import androidx.appcompat.app.AlertDialog;import androidx.appcompat.app.AppCompatActivity;import androidx.core.content.ContextCompat;import android.Manifest;import android.content.pm.PackageManager;import android.graphics.PixelFormat;import android.hardware.usb.UsbDevice;import android.os.Build;import android.os.Bundle;import android.os.Looper;import android.os.StrictMode;import android.view.Surface;import android.view.View;import android.widget.Toast;import com.jiangdg.usbcamera.UVCCameraHelper;import com.jiangdg.usbcamera.utils.FileUtils;import com.serenegiant.usb.CameraDialog;import com.serenegiant.usb.USBMonitor;import com.serenegiant.usb.UVCCamera;import com.mon.AbstractUVCCameraHandler;import com.serenegiant.usb.widget.CameraViewInterface;public class MainActivity extends AppCompatActivity implements CameraDialog.CameraDialogParent, CameraViewInterface.Callback {private CrashHandler mCrashHandler;public View mTextureView;private UVCCameraHelper mCameraHelper;private CameraViewInterface mUVCCameraView;private boolean isRequest;private boolean isPreview;private UVCCameraHelper.OnMyDevConnectListener listener = new UVCCameraHelper.OnMyDevConnectListener() {@Overridepublic void onAttachDev(UsbDevice device) {// request open permissionif (!isRequest) {isRequest = true;if (mCameraHelper != null) {mCameraHelper.requestPermission(0);}}}@Overridepublic void onDettachDev(UsbDevice device) {// close cameraif (isRequest) {isRequest = false;mCameraHelper.closeCamera();showShortMsg(device.getDeviceName() + " is out");}}@Overridepublic void onConnectDev(UsbDevice device, boolean isConnected) {if (!isConnected) {showShortMsg("fail to connect,please check resolution params");isPreview = false;} else {isPreview = true;showShortMsg("connecting");// initialize seekbar// need to wait UVCCamera initialize overnew Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(30);} catch (InterruptedException e) {e.printStackTrace();}Looper.prepare();Looper.loop();}}).start();}}@Overridepublic void onDisConnectDev(UsbDevice device) {showShortMsg("disconnecting");}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);AccessRequest();/**------- USBCamera引入 ---------*/mCrashHandler = CrashHandler.getInstance();mCrashHandler.init(getApplicationContext(), getClass());mTextureView = findViewById(R.id.camera_view);// step.1 initialize UVCCameraHelpermUVCCameraView = (CameraViewInterface) mTextureView;mUVCCameraView.setCallback(this);mCameraHelper = UVCCameraHelper.getInstance();mCameraHelper.setDefaultFrameFormat(UVCCameraHelper.FRAME_FORMAT_MJPEG);mCameraHelper.initUSBMonitor(this, mUVCCameraView, listener);mCameraHelper.setOnPreviewFrameListener(new AbstractUVCCameraHandler.OnPreViewResultListener() {@Overridepublic void onPreviewResult(byte[] data) {// 获取单帧图像回调}});}public boolean isCameraOpened() {return mCameraHelper.isCameraOpened();}private void showShortMsg(String msg) {Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();}@Overridepublic void onSurfaceCreated(CameraViewInterface view, Surface surface) {if (!isPreview && mCameraHelper.isCameraOpened()) {mCameraHelper.startPreview(mUVCCameraView);isPreview = true;}}@Overridepublic void onSurfaceChanged(CameraViewInterface view, Surface surface, int width, int height) {}@Overridepublic void onSurfaceDestroy(CameraViewInterface view, Surface surface) {if (isPreview && mCameraHelper.isCameraOpened()) {mCameraHelper.stopPreview();isPreview = false;}}@Overridepublic USBMonitor getUSBMonitor() {return mCameraHelper.getUSBMonitor();}@Overridepublic void onDialogResult(boolean canceled) {if (canceled) {showShortMsg("取消操作");}}@Overrideprotected void onStart() {super.onStart();// step.2 register USB event broadcastif (mCameraHelper != null) {mCameraHelper.registerUSB();}}@Overrideprotected void onResume() {super.onResume();}@Overrideprotected void onStop() {super.onStop();// step.3 unregister USB event broadcastif (mCameraHelper != null) {mCameraHelper.unregisterUSB();}}@Overrideprotected void onDestroy() {super.onDestroy();FileUtils.releaseFile();// step.4 release uvc camera resourcesif (mCameraHelper != null) {mCameraHelper.release();}}/*** 检查权限 方法*/private boolean checkPermission() {//是否有权限boolean haveCameraPermission = ContextCompat.checkSelfPermission(this,Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;boolean haveWritePermission = ContextCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;boolean haverReadPermission = ContextCompat.checkSelfPermission(this,Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;return haveCameraPermission && haveWritePermission && haverReadPermission;}/*** 请求权限 方法*/@RequiresApi(api = Build.VERSION_CODES.M)private void requestPermissions() {requestPermissions(new String[]{Manifest.permission.CAMERA, Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, Manifest.permission.SYSTEM_ALERT_WINDOW,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE}, 1);}private void AccessRequest() {//动态权限检测和申请if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//大于Android 6.0if (!checkPermission()) {//没有或没有全部授权requestPermissions(); //请求权限}}//加 StrictMode, Android 7.0以后,获取文件Uri需要加上这么一段if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();StrictMode.setVmPolicy(builder.build());}}}

运行时遇到的问题

编译时又出现了问题

貌似找不到一个名叫libusbcommon_v4.1.1的文件。

这个文件在引入项目的时候其实已经就放在libuvccamera模块中,把整个libs文件夹拷贝到自己的项目文件中,如下图所示。

然后在 build.gradle 中引入这个文件夹

运行结果

其他

在这里可以调整摄像头的分辨率等设置。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。