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.\n" + 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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM