【小白求助】安卓opencv用分类器对人脸进行识别的一些问题

小白入门,最近在看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;
    }
}


1个回答

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问