导言:本文是对OpenCV for Android的配置详细过程的展示,并且为在此过程中的一些坑总结整理可行的解决方案,这些解决方案大多来自网络,感谢各位大手。本文最后讲实现在Android手机上运行OpenCV的人脸识别。
一、创建一个支持NDK的Android程序
二、进行OpenCV库的导入和环境的配置
刚把库导入,Android Sdudio进行编译的时候回出现如下问题:
我们只需要点击选项框,进入Project显示,修改openCVLibraryxxx(xxx代表opencv的版本号)中的build.gradle中compileSdkVersion、minSdkVersion、targetSdkVersion后面的数字和app中的build.gradle相同。如下图:
点击最上面的Try Again之后,上述错误消失,我们需要将opencv3.so库放到我们自己的工程文件夹下,进行如下操作:
将G:\Andriod_NDK_wsp\OpenCV_demo1\app\src\mai目录下新建一个目录jniLibs,将G:\Andriod_NDK_wsp\OpenCV-android-sdk\sdk\native\libs目录下的文件复制到jniLibs目录
在Android Studio中Android目录中,app的build.gradle中添加:
sourceSets {
main {
jniLibs.srcDirs =['src/main/jniLibs']
}
}
Dependencies中添加:
implementation project(':openCVLibrary341')
compile project(':openCVLibrary341')
在MainActivity类中最开始添加:
static{
System.loadLibrary("opencv_java3");
}
这个使我们不用安装OpenCV的支持App了。
三、运行人脸识别的Demo
在布局文件activity_main.xml中添加布局:
<org.opencv.android.JavaCameraView
android:id="@+id/cameraView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:camera_id="back"/>
在AndroidManifest.xml文件中添加摄像头权限:
<uses-permissionandroid:name="android.permission.CAMERA"/>
<uses-featureandroid:name="android.hardware.camera"android:required="false"/>
<uses-featureandroid:name="android.hardware.camera.autofocus"android:required="false"/>
<uses-featureandroid:name="android.hardware.camera.front"android:required="false"/>
<uses-feature android:name="android.hardware.camera.front.autofocus"android:required="false"/>
将OpenCV官方训练的人脸识别的模型文件lbpcascade_frontalface.xml从G:\Andriod_NDK_wsp\OpenCV-android-sdk\sdk\etc\lbpcascades文件夹下复制到自己工程目录下的app\src\main\res\raw目录下(如果没有,就自己创建一个)。
在MainActivity.java文件如下:
private static final int TAKE_PHOTO_REQUEST_CODE = 1;
static{
System.loadLibrary("opencv_java3");
}
private CameraBridgeViewBase openCvCameraView;
private CascadeClassifier cascadeClassifier;
private Mat grayscaleImage;
private int absoluteFaceSize;
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
initializeOpenCVDependencies();
break;
default:
super.onManagerConnected(status);
break;
}
}
};
private void initializeOpenCVDependencies() {
try {
// Copy the resource into a temp file so OpenCV can load it
InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
File mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
FileOutputStream os = new FileOutputStream(mCascadeFile);
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
is.close();
os.close();
// Load the cascade classifier
cascadeClassifier = new CascadeClassifier(mCascadeFile.getAbsolutePath());
} catch (Exception e) {
Log.e("OpenCVActivity", "Error loading cascade", e);
}
// And we are ready to go
openCvCameraView.enableView();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
openCvCameraView = new JavaCameraView(this, -1);
setContentView(openCvCameraView);
if(Build.VERSION.SDK_INT>22){
if(ContextCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.CAMERA},TAKE_PHOTO_REQUEST_CODE);
}
}
openCvCameraView.setCvCameraViewListener(this);
}
@Override
public void onCameraViewStarted(int width, int height) {
grayscaleImage = new Mat(height, width, CvType.CV_8UC4);
// The faces will be a 20% of the height of the screen
absoluteFaceSize = (int) (height * 0.2);
}
@Override
public void onCameraViewStopped() {
}
@Override
public Mat onCameraFrame(Mat aInputFrame) {
Imgproc.cvtColor(aInputFrame, grayscaleImage, Imgproc.COLOR_RGBA2RGB);
MatOfRect faces = new MatOfRect();
// Use the classifier to detect faces
if (cascadeClassifier != null) {
cascadeClassifier.detectMultiScale(grayscaleImage, faces, 1.1, 3, 0,
new Size(absoluteFaceSize, absoluteFaceSize), new Size());
}
// If there are any faces found, draw a rectangle around it
Rect[] facesArray = faces.toArray();
for (int i = 0; i <facesArray.length; i++)
Imgproc.rectangle(aInputFrame, facesArray[i].tl(), facesArray[i].br(), new Scalar(0, 255, 0, 255), 3);
return aInputFrame;
}
@Override
public void onResume() {
super.onResume();
if (!OpenCVLoader.initDebug()) {
Log.e("log_wons", "OpenCV init error");
// Handle initialization error
}
initializeOpenCVDependencies();
//OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_6, this, mLoaderCallback);
}
@Override
public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults) {
switch (requestCode){
case TAKE_PHOTO_REQUEST_CODE:
if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
//这里已经获取到了摄像头的权限,想干嘛干嘛了可以
}else {
//这里是拒绝给APP摄像头权限,给个提示什么的说明一下都可以。
Toast.makeText(MainActivity.this,"请手动打开相机权限",Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
如果出现摄像头的预览不是竖屏的全屏,更改OpenCV的CameraBridgeViewBase类中的deliverAndDrawFrame方法,更改成如下:
<uses-permissionandroid:name="android.permission.CAMERA"/>
<uses-featureandroid:name="android.hardware.camera"android:required="false"/>
<uses-featureandroid:name="android.hardware.camera.autofocus"android:required="false"/>
<uses-featureandroid:name="android.hardware.camera.front"android:required="false"/>
<uses-feature android:name="android.hardware.camera.front.autofocus"android:required="false"/>
Mat modified;
if (mListener != null) {
modified = mListener.onCameraFrame(frame);
} else {
modified = frame.rgba();
}
boolean bmpValid = true;
if (modified != null) {
try {
Utils.matToBitmap(modified, mCacheBitmap);
} catch (Exception e) {
Log.e(TAG, "Mat type: " + modified);
Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight());
Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage());
bmpValid = false;
}
}
if (bmpValid && mCacheBitmap != null) {
Canvas canvas = getHolder().lockCanvas();
if (canvas != null) {
canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
// 修改预览旋转90度问题
canvas.rotate(90,0,0);
float scale = canvas.getWidth() / (float)mCacheBitmap.getHeight();
float scale2 = canvas.getHeight() / (float)mCacheBitmap.getWidth();
if(scale2 > scale){
scale = scale2;
}
if (scale != 0) {
canvas.scale(scale, scale,0,0);
}
canvas.drawBitmap(mCacheBitmap, 0, -mCacheBitmap.getHeight(), null);
// 修改预览旋转90度问题end
if (mFpsMeter != null) {
mFpsMeter.measure();
mFpsMeter.draw(canvas, 20, 30);
}
getHolder().unlockCanvasAndPost(canvas);
}
}
到此我们就完成了整个流程,如果在过程中或者刚打开Android Studio出现如下情况,直接点击菜单栏中Build->reBuild就好了,目前还没有发下别的有效的方法(重装都不管用)
OpenCV4Android SDK搬运(如果自己下载慢,可以选择这个):
链接:https://pan.baidu.com/s/1iunqc5NcigH7ZS45xkRvSg 密码:cd4y