前言
OpenCV是Open Source Computer Vision library(开源的计算机视觉库)的缩写。它是使用最广泛的计算机视觉库。Opencv是计算机视觉领域常用的操作函数的集合,其自身由C/C++编写而成,同时也提供了对Python、Java以及任意JVM语言的封装。考虑到大部分Android应用是用C++/Java编写的,OpenCV也被移植为供开发者使用的SDK,以使他们开发的应用支持机器视觉。
一.Android平台部署OpenCV
1.下载地址:
OpenCV - Browse /opencv-android at SourceForge.netOpenCV - Browse /opencv-android/3.4.3 at SourceForge.net
点击opencv-3.4.3-android-sdk.zip下载
2.解压opencv-3.4.3-android-sdk.zip文件
3.创建Android项目命名为FirstOpenCVApp
4.找到opencv解压的文件夹
opencv-3.4.3-android-sdk下的java文件夹下的project.properties文件
例如压缩文件解压到桌面:C:\Users\SW\Desktop\opencv-3.4.3-android-sdk\OpenCV-android-sdk\sdk\java\project.properties
利用记事本打开,删除两行代码如下所示
删除后如下图所示:
由于AndroidStudio版本为4.1不删除无法编译通过。
5.Android项目导入opencv模块
点击File->New->Import Module...
选择Source directory:选择解压之后的目录C:\Users\SW\Desktop\opencv-3.4.3-android-sdk\OpenCV-android-sdk\sdk\java,点击OK
等待编辑完成,修改模块openCVLibrary343下的清单文件,去掉android:minSdkVersion="8"
删除之后的清单文件如下图所示:
修改模块openCVLibrary343下的build.gradle文件如下图所示:
以Android项目FirstOneOpencvApp下的src下的build.gradle为参考,尽量保持一致
FirstOneOpencvApp下的src下的build.gradle如下图所示:
点击同步,等待项目同步完成。
点击Android项目FirstOneOpencvApp File->Project Structure app添加Module依赖选择openCVLibrary343
检查Android项目FirstOneOpencvApp下的app下的build.gradle dependencies如下图所示:
检查settings.gradle如下图所示:
等待项目编译完成
6.Android项目新建HomeActivity
File->New->Activity->Empty Activity
Activity Name为HomeActivity
布局文件activity_home内容如下图所示
XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".HomeActivity">
<Button
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:id="@+id/bMean"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Mean Blur"></Button>
</androidx.constraintlayout.widget.ConstraintLayout>
HomeActivity内容如下所示:
java
package com.suoer.comeonhealth.firstoneopencvapp;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
public class HomeActivity extends AppCompatActivity {
public static final int MEAN_BLUR=1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
Button bMean=findViewById(R.id.bMean);
bMean.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i=new Intent(getApplicationContext(),MainActivity.class);
i.putExtra("ACTION_MODE",MEAN_BLUR);
startActivity(i);
}
});
}
}
7.修改MainActivity
布局activity_main内容如下图所示:
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<ImageView
android:layout_weight="0.5"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/ivImage"></ImageView>
<ImageView
android:layout_weight="0.5"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/ivImageProcessed"></ImageView>
</LinearLayout>
资源文件下添加菜单
menu_main内容如下图所示:
XML
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_load_image"
android:title="加载图片"
android:orderInCategory="1"
app:showAsAction="ifRoom"></item>
</menu>
MainActivity内容如下图所示:
java
package com.suoer.comeonhealth.firstoneopencvapp;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.Utils;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import java.io.FileNotFoundException;
import java.io.InputStream;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private final int SELECT_PHOTO=1;
private ImageView ivImage,ivImageProcessed;
Mat src;
static int ACTION_MODE=0;
private BaseLoaderCallback mOpenCVCallBack=new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status){
case SUCCESS:
//在这里完成
break;
default:
super.onManagerConnected(status);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ivImage=findViewById(R.id.ivImage);
ivImageProcessed=findViewById(R.id.ivImageProcessed);
Intent intent=getIntent();
if(intent.hasExtra("ACTION_MODE")){
ACTION_MODE=intent.getIntExtra("ACTION_MODE",0);
}
}
@Override
protected void onResume() {
super.onResume();
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION,this,mOpenCVCallBack);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main,menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
int id=item.getItemId();
if(id==R.id.action_load_image){
Intent photoPickerIntent=new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/*");
startActivityForResult(photoPickerIntent,SELECT_PHOTO);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case SELECT_PHOTO:
if(resultCode==RESULT_OK){
try{
//以位图载入图像,并将其转换为Mat以供处理的代码
final Uri imageUri=data.getData();
final InputStream imageStream=getContentResolver().openInputStream(imageUri);
final Bitmap selectedImage= BitmapFactory.decodeStream(imageStream);
src=new Mat(selectedImage.getHeight(),selectedImage.getWidth(), CvType.CV_8UC4);
Utils.bitmapToMat(selectedImage,src);
switch (ACTION_MODE){
case HomeActivity.MEAN_BLUR:
Imgproc.blur(src,src,new Size(3,3));
break;
}
//将Mat转换为位图,以便在ImageView中显示
Bitmap processedImage=Bitmap.createBitmap(src.cols(),src.rows(),Bitmap.Config.ARGB_8888);
Utils.matToBitmap(src,processedImage);
ivImage.setImageBitmap(selectedImage);
ivImageProcessed.setImageBitmap(processedImage);
}catch (FileNotFoundException e){
e.printStackTrace();
}
}
break;
}
}
}
8.查看手机设备的cpu架构
AndroidStudio连接手机
运行cmd.exe 输入命令:adb shell getprop ro.product.cpu.abi点击回车
arm64-v8a就是连接手机的cpu架构
找到opencv解压文件C:\Users\SW\Desktop\opencv-3.4.3-android-sdk\OpenCV-android-sdk\apk\OpenCV_3.4.3_Manager_3.43_arm64-v8a.apk
在cmd.exe中输入命令:adb install C:\Users\SW\Desktop\opencv-3.4.3-android-sdk\OpenCV-android-sdk\apk\OpenCV_3.4.3_Manager_3.43_arm64-v8a.apk
点击回车
手机安装此apk成功。安装成功以后,运行此项目。必须此项目在运行状态,进程不可以杀掉此项目,否则无法使用OpenCV提供的函数实现功能。
利用记事本打开C:\Users\SW\Desktop\opencv-3.4.3-android-sdk\OpenCV-android-sdk\apk下的readme.txt查看里面详细内容,安装哪一个apk需根据手机的cpu架构来选择安装。
9.Android项目清单
XML
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.suoer.comeonhealth.firstoneopencvapp">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.FirstOneOpencvApp">
<activity android:name=".HomeActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity">
</activity>
</application>
</manifest>
10.运行Android项目至手机
点击MEAN BLUR按钮进入MainActivity界面,点击右上角菜单按钮加载图片,选择图片,界面显示原使图像和均值模糊后的图像。