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
Android+openCV人脸检测2(静态图片)的更多相关文章
- Android人脸检测1(静态图片)
搭建Android人脸识别环境花了很长时间(可以查看之前的文章),解决Android开发中的杂七杂八小问题也耗时不少. 今天记录一下,点击选择照片或者拍照上传照片进行人脸检测的小demo. (andr ...
- OpenCV人脸检测并把图片写成avi视频
读出某一个文件夹下“jpg”后缀的全部图片后,用的OpenCV自带的人脸检测检测图片中的人脸,调整图片的大小写成一个avi视频. 主要是要记录一下CvVideoWriter的用法和如何从文件夹中读取某 ...
- android opencv 人脸检测
转载自http://blog.csdn.net/jesse__zhong/article/details/24889709 .......省略包 public class Staticdetectio ...
- keras系列︱人脸表情分类与识别:opencv人脸检测+Keras情绪分类(四)
引自:http://blog.csdn.net/sinat_26917383/article/details/72885715 人脸识别热门,表情识别更加.但是表情识别很难,因为人脸的微表情很多,本节 ...
- Android—基于OpenCV+Android实现人脸检测
导读 OpenCV 是一个开源的跨平台计算机视觉库, 采C++语言编写,实现了图像处理和计算机视觉方面的很多通用算法,同时也提供对Python,Java,Android等的支持,这里利用Android ...
- opencv人脸检测分类器训练小结
这两天在初学目标检测的算法及步骤,其中人脸检测作为最经典的算法,于是进行了重点研究.该算法最重要的是建立人脸检测分类器,因此我用了一天的时间来学习分类器的训练.这方面的资料很多,但是能按照一个资料运行 ...
- opencv人脸检测,旋转处理
年会签到,拍自己的大头照,有的人可能会拍成横向的,需要旋转,用人脸检测并修正它(图片). 1. 无脑检测步骤为: 1. opencv 读取图片,灰度转换 2. 使用CascadeClassifier( ...
- 人脸检测学习笔记(数据集-DLIB人脸检测原理-DLIB&OpenCV人脸检测方法及对比)
1.Easily Create High Quality Object Detectors with Deep Learning 2016/10/11 http://blog.dlib.net/201 ...
- OpenCV——人脸检测
OpenCV支持的目标检测的方法: 利用样本的Haar特征进行的分类器训练,得到的级联boosted分类器(Cascade Classification) 1.加载级联分类器 CascadeClass ...
随机推荐
- Building Forms with PowerShell – Part 1 (The Form)
For those of you familiar with Scripting languages you are probably used to using alternate applicat ...
- Atcoder Beginner Contest 124 解题报告
心态爆炸.本来能全做出来的.但是由于双开了Comet oj一个比赛,写了ABC就去搞那个的B题 还被搞死了. 回来写了一会D就过了.可惜比赛已经结束了.真的是作死. A - Buttons #incl ...
- BZOJ4817[Sdoi2017]树点涂色——LCT+线段树
题目描述 Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色.Bob可能会进 ...
- Magento 2 Block模板终极指南
/view/frontend/page_layout/2columns-left.xml <layout xmlns:xsi="http://www.w3.org/2001/XMLSc ...
- ovs之组网实验
介绍 本示例将创建两个OVS实例和两个主机,其中每个OVS上接入一个主机,OVS实例之间有链路连接,形成一个链状拓扑,如图.在OVS组网完成之后,再通过手动方式添加流表,实现网络通信,从而验证实验可行 ...
- 20190211 模拟训练 A. 大猫咪
好题 2.11考试
- centos7 LNMP
Nginx1.13.5 + PHP7.1.10 + MySQL5.7.19 一.安装Nginx 1.安装依赖扩展 # yum -y install wget openssl* gcc gcc-c++ ...
- 1.3浅谈Spring(IOC容器的实现)
这一节我们来讨论IOC容器到底做了什么. 还是借用之前的那段代码 ClassPathXmlApplicationContext app = new ClassPathXmlApplicationCon ...
- css长度单位学习(em,rem,px,vw,vh)
绝对长度单位 绝对长度单位代表一个物理测量 [像素px(pixels)] 像素,为影像显示的基本单位,译自英文"pixel",pix是英语单词picture的常用简写,加上英语单词 ...
- tensorflow 模型保存和加载
使用 tf.train.Saver 保存:tf.train.Saver.save(sess, save_path, global_step=None, latest_filename=None, me ...