课设作业需要用到图像处理相关的库,想着自己知识范围内的也只是移动端开发,不如直接写个app交上去,可问题来了——能在移动端运行的图像处理库必须得满足两个要求,高效(手机摄像头每一帧捕捉到的画面都得高速处理,要不然实际应用会卡顿),轻量(不能手机上塞个几G的大模型吧….. ),思来想去好像也就openCV和 TensorFlow Lite能用(后者还能换模型,感觉会好一点),本片即记录正常使用openCV库的过程
准备工作
OpenCV SDK
- OpenCV官网
- Releases - OpenCV (在这里下载
SDK
)
- 可以看到最新的版本是截止到2023-12-28的4.9.0,我们要使用的包即
Android
包,直接下载即可
Android Studio
- 此教程写于2024.04,
Android Studio
版本为Android Studio Iguana | 2023.2.1
- Android Studio 官网
- 正常创建项目
- 目前
openCV
的预览摄像头view
还是Camera
或Camera2
实现,还没用上Google
最新的CameraX
(希望快点更新,CameraX
着实好用得多) - 注意在选择
Build configuration language
时要选择Groovy
,因为待会集成Module
时OpenCV
自己的build.gradle
里还是用的Groovy
,使用最新的Kotlin DSL
会报错
正式开始
导入SDK
- 打开
Project Structure
- 选择导入
Module
- 找到自己下好的
OpenCV
包并导入
- 更改导入后的
Module
名称,当然也可以不改,我觉得改了会好些(
- 直接
Finish
,可以看到导入完成
- 我们还需要把
Module
作为Depandency
导入到我们自己的项目中,选择Module Depandecy
- 选中
opencv
,OK
- 可以看到
opencv
被导入成功
修改文件
- 已经导入好的
opencv
模块是无法直接使用的,因为模块中build.gradle
中好多配置都和我们自己项目的build.gradle
不同,我们还得使二者同步 opencv
下的build.gradle
中已经有了注释来告诉你该如何进行相应的配置,我不是一个喜欢用旧库的人,为了保证我项目下的build.gradle
配置同步,所有能拉高版本的地方我都做了修改- 先看一下我们默认生成的
app/build.gradle
1 | gradle复制代码plugins { |
- 这里我们得到
minSdk
为24
,targetSdk
为34
,这些都是待会在opencv/build.gradle
要修改的内容 - 我们需要知道
kotlin
和gradle
的版本。每一个版本的Android Studio
都会对项目结构下的gradle
进行调整,这里目前最新的版本可以看到gradle
的版本号已经被隐藏了,我们可以通过Ctrl+鼠标左键
点击libs.plugins.jetbrainsKotlinAndroid
直接跳转到它链接的文件,最后找到这个文件
- 在这里我们可以找到相应的版本号,接下来即修改
opencv/build.gradle
1 | gradle复制代码apply plugin: 'com.android.library' |
- 其中注释的部分即为文件原来的内容,注意文件中修改的几处,
Java
版本随意,只要能跑通即可,我这里使用的是jdk17
- 如果上述操作都没啥问题,可以直接先跑一遍运行,如果操作正确
app
是可以正常启动的 - 如果遇到报错,请注意自己的gradle版本是否一致,编译所用的jdk是否一致
OpenCV 4.9
的build.gradle
已经移除了gradle
版本号部分,如果是更前的版本,需要额外注意版本号不同,两个build.gradle
和settings.gradle
中的内容不同带来的影响
编写基本布局
- 因为只是一个示例
demo
,我打算只在这里演示两个功能——OpenCV
的边缘检测和图像灰度化 - 这样我们的布局就非常简单了——
DetectFragment
和ImageFragment
,和刚开始就需要展示的MainFragment
,相关的权限申请和路由跳转也在这里完成 - 先在
app/build.gradle
中导入需要的库
1 | gradle复制代码dependencies { |
- 上面注释的内容其实是旧的写法,在
Android Studio
中会被纠正成新的写法,我给出示例只是为了考虑旧的Android Studio
中的导入形式 - 依次创建
MainFragment.kt
,DetectFragment.kt
,ImageFragment.kt
- 编写
activity_main.xml
1 | xml复制代码<?xml version="1.0" encoding="utf-8"?> |
- 编写
fragment_main.xml
1 | xml复制代码<?xml version="1.0" encoding="utf-8"?> |
- 编写
fragment_detect.xml
1 | xml复制代码<?xml version="1.0" encoding="utf-8"?> |
- 编写
fragment_image
1 | xml复制代码<?xml version="1.0" encoding="utf-8"?> |
- 这里的
android:background="@drawable/example
是我已经放在drawble
里的示例图片,这里需要你换成自己的图片
导航视图
- 在
src/res
下创建文件夹navigation
,新建一个navigation
文件( 叫什么名字不影响,这里我命名为main_navigation.xml
)
- 打开这个文件,点击
add destination
- 我们希望的是
MainFragment
可以跳转到DetectFragment
和ImageFragment
,故依次新建并连线,最后效果如下(这部分不会用需要百度,其实就是新建之后直接连线就好)
- 不同
Android Studio
版本MainActivity
的代码实现不一样,我的写法如下所示
1 | kotlin复制代码package com.ericmoin.opencv_demo |
- 此时点击运行
- 一切正常
- 编写
MainFragment
1 | kotlin复制代码package com.ericmoin.opencv_demo |
图像灰度化
openCV
处理图像的流程基本遵循三步:Bitmap
转化为Mat
- 对
Mat
进行操作 Mat
转化为Bitmap
,展示
- 图像灰度化的核心函数只有
Imgproc.cvtColor
一个,故不作过多解释
1 | kotlin复制代码package com.ericmoin.opencv_demo |
- 改变前
- 改变后
边缘检测
- 我希望实现一个实时的边缘检测效果,这就意味着我们需要摄像头的相关权限
- 在
AndroidMenifest.xml
中声明
1 | xml复制代码<?xml version="1.0" encoding="utf-8"?> |
- 权限的申请部分我使用了郭霖大神的
permissionX
,这个库我在前面gradle
部分已经导入了,这里直接使用即可 - 回到
MainActivity
1 | kotlin复制代码package com.ericmoin.opencv_demo |
- 编写
DetectFragment
1 | kotlin复制代码package com.ericmoin.opencv_demo |
- 可以发现
openCV
在Android
下的api
和python
下基本保持一致,也就是说网上通用的python
教程理论上都可以在Android
上复现 - 运行看看结果
- 感觉还不错
- 这里可以看到得到的图像莫名向左旋转了
90
度,关于这个问题openCV
并没有提供更好的地方给我们对预览得到的图像进行第一时间的旋转,但是如果在刚刚的onCameraFrame
处对得到的矩阵进行旋转,会导致报错(不信可以试试).因为实际获取到的Bitmap
和旋转后的Mat
转化为的Bitmap
宽高并不匹配 - 理论上我们可以不使用它提供的预览组件,而是使用
CameraX
对获取到的Bitmap
进行相同的图像处理就能解决问题,由于不是这里的内容不做讨论,CameraX
的使用可以看我的另一篇文章:CameraX 简单使用 - 掘金 - 那就只好在
openCV
提供的SDK
进行修改了,我找到了一篇不错的文章来解决这个问题(需要科学上网):Working with the OpenCV Camera for Android: Rotating, Orienting, and Scaling - 其中的核心部分即更改
CameraBridgeViewBase
中的deliverAndDrawFrame
函数,代码如下:
1 | java复制代码private final Matrix mMatrix = new Matrix(); |
总结
- 整个安装过程我在网上找了很多的教程,踩过很多的坑才走到了这里,如果您看到了这里,希望您能留个点赞或收藏。感谢您的观看。
本文转载自: 掘金