小白入门,最近在看opencv的人脸识别(分类)
然后按照网上的代码,学习了之后,自己写了一段,发现运行的时候会卡很久,甚至直接退出,并且越用越卡。
我想实现的是对一张图片进行读入,然后加载多个分类器对图片内容进行识别,从而达到分类的目的。
我那个问题困扰了我一天,想来求助大神
我加载人脸识别的模型没问题,但是同时加载两个模型,比如说人脸和上身,就会卡死。
代码分3个部分,分别是MainActivity.java , execDetect.java , Detector.java
以下是MainActivity.java
package com.RinGo.IMGfenlei;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import org.opencv.android.Utils;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
public class MainActivity extends AppCompatActivity {
private Detector frontfaceDetector;
private Detector smileDetector;
private static String CAMERAIMAGENAME = "image.jpg";
private ImageButton imageButton;
private ImageButton imageButton2;
private TextView textView;
private Bitmap bitmap;
private Bitmap rectBitmap;
private Bitmap resizeBitmap;
private Toast toast;
private Button addFile;
private Button startDetect;
private Button check;
private execDetect toDetcet;
private String show;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.tv_face);
// imageButton = (ImageButton) findViewById(R.id.iv_face);
//imageButton.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
// imageButton2 = (ImageButton) findViewById(R.id.iv_face2);
// imageButton2.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
toDetcet=new execDetect();
addFile=(Button)findViewById(R.id.addPic);
startDetect=(Button)findViewById(R.id.startDetect);
check=(Button)findViewById(R.id.check);
startDetect.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// startDetect.setClickable(false);
textView.setText("正在检测");
// detect();
Thread d =new Thread(new Runnable() {
@Override
public void run() {
toDetcet.initExecDetect(MainActivity.this,bitmap);
// Toast.makeText(MainActivity.this, "initialize succeed", Toast.LENGTH_SHORT).show();
show= toDetcet.startDetect();
// Toast.makeText(MainActivity.this, " succeed", Toast.LENGTH_SHORT).show();
}
});
d.run();
textView.setText(show);
// startDetect.setClickable(true);
}
});
check.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// startDetect.setClickable(false);
textView.setText(toDetcet.getReturntoMain());
// startDetect.setClickable(true);
}
});
addFile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 添加照片
// 打开本地相册
Intent intent1 = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent1, 101);
//startActivity(intent1);
}
});
String strLibraryName = "opencv_java3"; // 不需要添加前缀 libopencv_java3
{
try {
Log.e("loadLibrary", strLibraryName);
System.loadLibrary(strLibraryName);
//System.loadLibrary(Core.NATIVE_LIBRARY_NAME); // couldn't find "libopencv_java320.so"
} catch (UnsatisfiedLinkError e) {
Log.e("loadLibrary", "Native code library failed to load.\n" + e);
} catch (Exception e) {
Log.e("loadLibrary", "Exception: " + e);
}
}
// frontfaceDetector = new Detector(this, R.raw.haarcascade_frontalface_alt, 6, 0.2F, 0.2F, new Scalar(255, 0, 0, 255));
}
/**
* 点击添加照片事件
*
* @param v
*/
public void onClick(View v) {
int bt_id = v.getId();
switch (bt_id) {
/* case R.id.takePhoto:
// 拍照
// 打开本地相机
Intent intent2 = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
Uri imageUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), CAMERAIMAGENAME));
intent2.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent2, 102);
break;
*/
case R.id.back:
this.finish();
break;
default:
break;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// 加判断 不选择照片或者不拍照时不闪退
//Log.e("data", String.valueOf(data));
//if (data == null)
//return;
bitmap = null;
switch (requestCode) {
// 选择图片库的图片
case 101:
if (resultCode == RESULT_OK) {
try {
Uri uri = data.getData();
bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
} catch (Exception e) {
e.printStackTrace();
}
}
break;
// 表示调用本地照相机拍照
case 102:
if (resultCode == RESULT_OK) {
//Bundle bundle = data.getExtras();
//bm = (Bitmap) bundle.get("data");
bitmap = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory() + "/" + CAMERAIMAGENAME);
}
break;
default:
break;
}
Log.e("bitmap", String.valueOf(bitmap));
if (bitmap == null) {
toast = Toast.makeText(MainActivity.this, "未选择图像", Toast.LENGTH_SHORT);
toast.show();
return;
}
// 识别图片 并画框
/* Thread detect=new Thread(new Runnable() {
@Override
public void run() {
}
});
detect.run();
*/
// 将照片剪裁 bitmap将被释放重新赋值
//int ibWidth = imageButton.getWidth();
// int ibHeight = imageButton.getHeight();
//resizeBitmap = imageButton.resizeBitmap(bitmap, ibWidth, ibHeight);
//imageButton.setBitmap(resizeBitmap);
//imageButton2.setBitmap(rectBitmap);
}
private void detect() {
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
toDetcet.initExecDetect(MainActivity.this,bitmap);
// Toast.makeText(MainActivity.this, "initialize succeed", Toast.LENGTH_SHORT).show();
String show= toDetcet.startDetect();
// Toast.makeText(MainActivity.this, " succeed", Toast.LENGTH_SHORT).show();
textView.setText(show);
}
});
//textView.setText(toDetcet.getReturntoMain());
}
}
以下是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">
<TextView
android:id="@+id/tv_face"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="未检测到人脸"
android:textColor="@color/colorAccent"
app:layout_constraintBottom_toTopOf="@+id/ll1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<LinearLayout
android:id="@+id/ll1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
<com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton
android:id="@+id/addPic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_weight="1"
android:onClick="onClick"
android:text=" 选择图片(CV) "
android:textSize="16sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton
android:id="@+id/startDetect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_weight="1"
android:onClick="onClick"
android:text=" 开始识别 "
android:textSize="16sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton
android:id="@+id/check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_weight="1"
android:onClick="onClick"
android:text=" 结果 "
android:textSize="16sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton
android:id="@+id/back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="0dp"
android:layout_weight="1"
android:onClick="onClick"
android:text=" 返回 "
android:textSize="16sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</LinearLayout>
</LinearLayout>
以下是execDetect.java
package com.RinGo.IMGfenlei;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
import org.opencv.android.Utils;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import com.RinGo.IMGfenlei.R;
import com.RinGo.IMGfenlei.Detector;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
public class execDetect {
private Detector frontfaceDetector;
private Detector smilefaceDetector;
private Detector[] detectors=new Detector[10];
private Detector detector;
private Bitmap bitmap;
private Bitmap rectBitmap;
private String returntoMain="";
private String[] detectorNames=new String[10];
private CascadeClassifier mFrontalFaceClassifier = null; //正脸 级联分类器
private CascadeClassifier mProfileFaceClassifier = null; //侧脸 级联分类器
private int smilefacenum=0;
private Context mcontext;
private int[] cascades=new int[10];
public void initExecDetect(Context _context,Bitmap _bitmap)
{
bitmap=_bitmap;
mcontext=_context;
cascades[0]=R.raw.haarcascade_frontalface_alt;
detectors[0] = new Detector(_context,R.raw.haarcascade_frontalface_alt, 1, 0.2F, 0.2F, new Scalar(255, 0, 0, 255));
detectorNames[0]="正面人脸";
cascades[1]=R.raw.lbpcascade_frontalface;
detectors[1] = new Detector(_context,R.raw.lbpcascade_frontalface, 1, 0.2F, 0.2F, new Scalar(255, 0, 0, 255));
detectorNames[1]="正面人脸2";
}
public String startDetect() {
// bitmapToMat
Mat toMat = new Mat();
Utils.bitmapToMat(bitmap, toMat);
// Mat copyMat = new Mat();
// toMat.copyTo(copyMat); // 复制
// togray
Mat gray = new Mat();
MatOfRect mRect = new MatOfRect();
Imgproc.cvtColor(toMat, gray, Imgproc.COLOR_RGBA2GRAY);
for (int id = 0; id < detectors.length; id++) {
//detector = new Detector(mcontext,cascades[id], 3, 0.1F, 0.1F, new Scalar(255, 0, 0, 255));
try {
int num = 0;
mRect=new MatOfRect();
Rect[] object = detectors[id].detectObjectImage(
gray, mRect);
Log.e("objectLength", object.length + "");
num=object.length;
/* for (Rect rect : object) {
num++;
}
*/
returntoMain =returntoMain+ String.format("检测到%1$d个" + detectorNames[id], num) + "\n";
} catch (Exception e) {
e.printStackTrace();
}
//textView.setText(String.format("检测到%1$d个人脸", facenum));
//Utils.matToBitmap(toMat, bitmap);
}
return returntoMain;
}
public String getReturntoMain()
{
return returntoMain;
}
}
以下是Detector.java,定义了我的级联分类器的设置
package com.RinGo.IMGfenlei;
import android.content.Context;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.objdetect.Objdetect;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Created by think-hxr on 17-10-12.
*/
public class Detector {
private CascadeClassifier mCascadeClassifier;
private int mMinNeighbors;
private float mRelativeObjectWidth;
private float mRelativeObjectHeight;
private Scalar mRectColor;
/**
* 构造方法
*
* @param context 上下文
* @param id 级联分类器ID
* @param minNeighbors 连续几帧确认目标
* @param relativeObjectWidth 最小宽度屏占比
* @param relativeObjectHeight 最小高度屏占比
* @param rectColor 画笔颜色
*/
public Detector(Context context, int id, int minNeighbors, float relativeObjectWidth, float relativeObjectHeight, Scalar rectColor) {
context = context.getApplicationContext();
mCascadeClassifier = createDetector(context, id);
mMinNeighbors = minNeighbors;
mRelativeObjectWidth = relativeObjectWidth;
mRelativeObjectHeight = relativeObjectHeight;
mRectColor = rectColor;
}
/**
* 创建检测器
*
* @param context 上下文
* @param id 级联分类器ID
* @return 检测器
*/
private CascadeClassifier createDetector(Context context, int id) {
CascadeClassifier javaDetector;
InputStream is = null;
FileOutputStream os = null;
try {
is = context.getResources().openRawResource(id);
File cascadeDir = context.getDir("cascade", Context.MODE_PRIVATE);
File cascadeFile = new File(cascadeDir, id + ".xml");
os = new FileOutputStream(cascadeFile);
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
javaDetector = new CascadeClassifier(cascadeFile.getAbsolutePath());
//javaDetector=new CascadeClassifier();
// javaDetector.load(cascadeFile.getAbsolutePath());
if (javaDetector.empty()) {
javaDetector = null;
}
boolean delete = cascadeDir.delete();
return javaDetector;
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
try {
if (null != is) {
is.close();
}
if (null != os) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 目标检测 视频
*
* @param gray 灰度图像
* @param object 识别结果的容器
* @return 检测到的目标位置集合
*/
public Rect[] detectObject(Mat gray, MatOfRect object) {
// 使用Java人脸检测
mCascadeClassifier.detectMultiScale(
gray, // 要检查的灰度图像
object, // 检测到的人脸
1.1, // 表示在前后两次相继的扫描中,搜索窗口的比例系数。默认为1.1即每次搜索窗口依次扩大10%;
mMinNeighbors, // 默认是3 控制误检测,表示默认几次重叠检测到人脸,才认为人脸存在
Objdetect.CASCADE_SCALE_IMAGE,
getSize(gray, mRelativeObjectWidth, mRelativeObjectHeight), // 目标最小可能的大小
gray.size()); // 目标最大可能的大小
return object.toArray();
}
/**
* 目标检测 图片
*
* @param gray 灰度图像
* @param object 识别结果的容器
* @return
*/
public Rect[] detectObjectImage(Mat gray, MatOfRect object) {
mCascadeClassifier.detectMultiScale(gray,object);
return object.toArray();
}
/**
* 根据屏占比获取大小
*
* @param gray gray
* @param relativeObjectWidth 最小宽度屏占比
* @param relativeObjectHeight 最小高度屏占比
* @return 大小
*/
private Size getSize(Mat gray, float relativeObjectWidth, float relativeObjectHeight) {
Size size = gray.size();
int cameraWidth = gray.cols();
int cameraHeight = gray.rows();
int width = Math.round(cameraWidth * relativeObjectWidth);
int height = Math.round(cameraHeight * relativeObjectHeight);
size.width = 0 >= width ? 0 : (cameraWidth < width ? cameraWidth : width); // width [0, cameraWidth]
size.height = 0 >= height ? 0 : (cameraHeight < height ? cameraHeight : height); // height [0, cameraHeight]
return size;
}
/**
* 获取画笔颜色
*
* @return 颜色
*/
public Scalar getRectColor() {
return mRectColor;
}
}