zoukankan      html  css  js  c++  java
  • Android+openCV人脸检测2(静态图片)

    前几篇文章中有提到对openCV环境配置,这里再重新梳理导入和使用openCV进行简单的人脸检测(包括使用级联分类器)

    一 首先导入openCVLibrary320

    二 设置gradle的sdk版本号与当前项目一致

        compileSdkVersion 26
        buildToolsVersion "26.0.2"
    
        defaultConfig {
            minSdkVersion 14
            targetSdkVersion 26
        }

    三 新建 jniLibs 目录

    在 app/src/main 目录下 与 java 目录同级,新建 jniLibs 目录

    把 OpenCV-android-sdk-3.2/sdk/native/libs 目录下的所有文件夹内容拷贝到 jniLibs 目录

    四 加载so包

        private static String strLibraryName = "opencv_java3"; // 不需要添加前缀 libopencv_java3
    
        static {
            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.
    " + e);
            } catch (Exception e) {
                Log.e("loadLibrary", "Exception: " + e);
            }
        }

    五 添加xml级联分类器

    在导入的openCVLibrary320模块中,找到 src/main/res 目录,新建raw文件夹。

    在 OpenCV-android-sdk-3.2/sdk/etc 目录下,存放的则是训练好的分类器。把 haarcascades 目录和 lbpcascades 目录内的所有xml文件拷贝到 raw目录中。

    六 配置权限

        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    
        <uses-permission android:name="android.permission.CAMERA" />
        <uses-permission android:name="android.permission.FLASHLIGHT" />

    完成以上步骤,就可以使用opencv了。

    使用openCV

    首先创建检测器

        mCascadeClassifier = createDetector(context, R.raw.haarcascade_frontalface_alt);
    
    
        /**
         * 创建检测器
         *
         * @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());
                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[] detectObjectImage(Mat gray, MatOfRect object) {
            mCascadeClassifier.detectMultiScale(gray,object);
            return object.toArray();
        }
    detectFace 整个函数:
        private void detectFace() {
            try {
                // bitmapToMat
                Mat toMat = new Mat();
                Utils.bitmapToMat(bitmap, toMat);
                Mat copyMat = new Mat();
                toMat.copyTo(copyMat); // 复制
    
                // togray
                Mat gray = new Mat();
                Imgproc.cvtColor(toMat, gray, Imgproc.COLOR_RGBA2GRAY);
    
                MatOfRect mRect = new MatOfRect();
                Rect[] object = mFaceDetector.detectObjectImage(gray, mRect);
    
                Log.e("objectLength", object.length + "");
    
    
                int maxRectArea = 0 * 0;
                Rect maxRect = null;
    
                int facenum = 0;
                // Draw a bounding box around each face.
                for (Rect rect : object) {
                    Imgproc.rectangle(
                            toMat,
                            new Point(rect.x, rect.y),
                            new Point(rect.x + rect.width, rect.y + rect.height),
                            new Scalar(255, 0, 0), 3);
                    ++facenum;
                    // 找出最大的面积
                    int tmp = rect.width * rect.height;
                    if (tmp >= maxRectArea) {
                        maxRectArea = tmp;
                        maxRect = rect;
                    }
                }
    
                rectBitmap = null;
                if (facenum != 0) {
                    // 剪切最大的头像
                    Log.e("剪切的长宽", String.format("高:%s,宽:%s", maxRect.width, maxRect.height));
                    Rect rect = new Rect(maxRect.x, maxRect.y, maxRect.width, maxRect.height);
                    Mat rectMat = new Mat(copyMat, rect);  // 从原始图像拿
                    rectBitmap = Bitmap.createBitmap(rectMat.cols(), rectMat.rows(), Bitmap.Config.ARGB_8888);
                    Utils.matToBitmap(rectMat, rectBitmap);
                }
    
                textView.setText(String.format("检测到%1$d个人脸", facenum));
                Utils.matToBitmap(toMat, bitmap);
    
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }

    附所有代码:

    package com.myproject.facedetection.ui;
    
    import android.content.Intent;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.net.Uri;
    import android.os.Environment;
    import android.provider.MediaStore;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.Gravity;
    import android.view.View;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import com.myproject.facedetection.R;
    import com.myproject.facedetection.entity.CustomImageButton;
    import com.opencvlib.ObjectDetector;
    
    import org.opencv.android.Utils;
    import org.opencv.core.Mat;
    import org.opencv.core.MatOfRect;
    import org.opencv.core.Point;
    import org.opencv.core.Rect;
    import org.opencv.core.Scalar;
    import org.opencv.imgproc.Imgproc;
    
    import java.io.File;
    
    public class FaceDetectionOpenCVActivity extends AppCompatActivity {
        private ObjectDetector mFaceDetector;
    
        private static String CAMERAIMAGENAME = "image.jpg";
        private CustomImageButton imageButton;
        private CustomImageButton imageButton2;
        private TextView textView;
        private Bitmap bitmap;
        private Bitmap rectBitmap;
        private Bitmap resizeBitmap;
        private Toast toast;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_face_detection_opencv);
            textView = (TextView) findViewById(R.id.tv_face);
            imageButton = (CustomImageButton) findViewById(R.id.iv_face);
            imageButton.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
            imageButton2 = (CustomImageButton) findViewById(R.id.iv_face2);
            imageButton2.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
    
            mFaceDetector = new ObjectDetector(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.addPic:
                    // 添加照片
                    // 打开本地相册
                    Intent intent1 = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                    startActivityForResult(intent1, 101);
                    break;
    
                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(FaceDetectionOpenCVActivity.this, "未选择图像", Toast.LENGTH_SHORT);
                toast.show();
                return;
            }
    
    
            // 识别图片 并画框
            detectFace();
    
    
            // 将照片剪裁 bitmap将被释放重新赋值
            int ibWidth = imageButton.getWidth();
            int ibHeight = imageButton.getHeight();
            resizeBitmap = imageButton.resizeBitmap(bitmap, ibWidth, ibHeight);
    
            imageButton.setBitmap(resizeBitmap);
            imageButton2.setBitmap(rectBitmap);
    
    
        }
    
        private void detectFace() {
            try {
                // bitmapToMat
                Mat toMat = new Mat();
                Utils.bitmapToMat(bitmap, toMat);
                Mat copyMat = new Mat();
                toMat.copyTo(copyMat); // 复制
    
                // togray
                Mat gray = new Mat();
                Imgproc.cvtColor(toMat, gray, Imgproc.COLOR_RGBA2GRAY);
    
                MatOfRect mRect = new MatOfRect();
                Rect[] object = mFaceDetector.detectObjectImage(gray, mRect);
    
                Log.e("objectLength", object.length + "");
    
    
                int maxRectArea = 0 * 0;
                Rect maxRect = null;
    
                int facenum = 0;
                // Draw a bounding box around each face.
                for (Rect rect : object) {
                    Imgproc.rectangle(
                            toMat,
                            new Point(rect.x, rect.y),
                            new Point(rect.x + rect.width, rect.y + rect.height),
                            new Scalar(255, 0, 0), 3);
                    ++facenum;
                    // 找出最大的面积
                    int tmp = rect.width * rect.height;
                    if (tmp >= maxRectArea) {
                        maxRectArea = tmp;
                        maxRect = rect;
                    }
                }
    
                rectBitmap = null;
                if (facenum != 0) {
                    // 剪切最大的头像
                    Log.e("剪切的长宽", String.format("高:%s,宽:%s", maxRect.width, maxRect.height));
                    Rect rect = new Rect(maxRect.x, maxRect.y, maxRect.width, maxRect.height);
                    Mat rectMat = new Mat(copyMat, rect);  // 从原始图像拿
                    rectBitmap = Bitmap.createBitmap(rectMat.cols(), rectMat.rows(), Bitmap.Config.ARGB_8888);
                    Utils.matToBitmap(rectMat, rectBitmap);
                }
    
                textView.setText(String.format("检测到%1$d个人脸", facenum));
                Utils.matToBitmap(toMat, bitmap);
    
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
    }
    FaceDetectionOpenCVActivity
    package com.opencvlib;
    
    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 ObjectDetector {
        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 ObjectDetector(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());
                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;
        }
    }
    ObjectDetector
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.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="com.myproject.facedetection.ui.FaceDetectionOpenCVActivity">
    
        <com.myproject.facedetection.entity.CustomImageButton
            android:id="@+id/iv_face"
            android:layout_width="0dp"
            android:layout_height="460dp"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            tools:layout_editor_absoluteY="10dp" />
    
    
        <com.myproject.facedetection.entity.CustomImageButton
            android:id="@+id/iv_face2"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_marginRight="10dp"
            app:layout_constraintRight_toRightOf="parent"
            tools:layout_editor_absoluteY="10dp" />
    
    
        <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="0dp"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent">
    
            <Button
                android:id="@+id/takePhoto"
                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" />
    
            <Button
                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" />
    
            <Button
                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="返回(CV)"
                android:textSize="16sp"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent" />
        </LinearLayout>
    
    
    </android.support.constraint.ConstraintLayout>
    activity_face_detection_opencv.xml
  • 相关阅读:
    Chapter 5. 集合---(ArrayList、Stack、queue、哈希表)
    Chapter 4. 数组
    第一章 代码无错就是优?——简单工厂模式
    条款34:区分接口继承和实现继承
    条款33:避免遮掩继承而来的名称
    条款32:确定你的public继承塑模出is-a关系
    条款12:复制对象时勿忘其每一部分
    A:魔兽世界之一:备战
    条款09:绝不在构造和析构过程中调用virtual函数
    7、数值算法
  • 原文地址:https://www.cnblogs.com/haoxr/p/7686847.html
Copyright © 2011-2022 走看看