0


【Android App】人脸识别中OpenCV根据人脸估算性别和年龄实战(附源码和演示 超详细)

需要源码请点赞关注收藏后评论区留言私信~~~

人脸蕴含的信息量巨大,不管是青春还是年少,还是老年沧桑,都能体现出来,不过从人脸估算年龄估算年龄全凭经验,毕竟计算机无法根据固定框架判断年龄,那么计算机的经验从何而来呢?当然是要人类把经验传授给它,这种经验在机器学习领域称作模型,通过海量的原始样本训出结果模型,然后由计算机依据模型执行辨别操作

一、根据人脸估算性别和年龄

在App工程中使用年龄模型和性别模型需要按照以下步骤处理

(1)导入年龄模型文件,以及性别模型文件。

(2)把assets目录下的模型资源复制到存储卡。

(3)在代码中初始化年龄模型和性别模型。

(4)根据模型网络对人脸矩阵分别猜测年龄和性别。

但是根据这样的方法猜测出来的年龄和性别都带有中文字符,虽然OpenCV的imgproc工具提供了putText方法,但是该方法还不支持往图像上写中文,若调用putText方法写中文的话只会看到一堆乱码,为了在图像上看到年龄和性别,可以采取以下两种方法

1:修改OpenCV的C语言源码,在putText函数旁边增加putTextZH函数,新函数专门用来添加中文字符,同时还要修改sdk的Imgproc.java 补充native类型的putTextZH方法声明

2:第一种方法设计C代码修改与so库编译,操作十分麻烦,为此考虑先将OpenCV的mat结构转换为位图对象,再借助画布往位图上描绘文字

二、效果展示

先上一张神仙姐姐刘亦菲的照片

可见性别判断的非常准确 但是年龄只能给出一个区间 有待改进

这个就有点抽象了 年轻时候的贝克汉姆竟然识别到51-65这个区间肯定是不合理的

马儿的判断还是比较准确的

梅西判断的也还比较准确

三、代码

部分代码如下 需要源码请点赞关注收藏后评论区留言私信~~~

  1. package com.example.face;
  2. import android.content.Context;
  3. import android.content.Intent;
  4. import android.graphics.Bitmap;
  5. import android.graphics.PointF;
  6. import android.net.Uri;
  7. import android.os.Bundle;
  8. import android.os.Environment;
  9. import android.util.Log;
  10. import android.widget.ImageView;
  11. import androidx.appcompat.app.AppCompatActivity;
  12. import com.example.face.util.BitmapUtil;
  13. import com.example.face.util.FaceUtil;
  14. import com.example.face.util.FaceUtil.FaceText;
  15. import org.opencv.android.BaseLoaderCallback;
  16. import org.opencv.android.LoaderCallbackInterface;
  17. import org.opencv.android.OpenCVLoader;
  18. import org.opencv.android.Utils;
  19. import org.opencv.core.Core;
  20. import org.opencv.core.Mat;
  21. import org.opencv.core.MatOfRect;
  22. import org.opencv.core.Rect;
  23. import org.opencv.core.Scalar;
  24. import org.opencv.core.Size;
  25. import org.opencv.dnn.Dnn;
  26. import org.opencv.dnn.Net;
  27. import org.opencv.imgproc.Imgproc;
  28. import org.opencv.objdetect.CascadeClassifier;
  29. import java.io.File;
  30. import java.io.FileOutputStream;
  31. import java.io.InputStream;
  32. import java.util.ArrayList;
  33. import java.util.List;
  34. public class GuessAgeActivity extends AppCompatActivity {
  35. private final static String TAG = "GuessAgeActivity";
  36. private int CHOOSE_CODE = 3; // 只在相册挑选图片的请求码
  37. private ImageView iv_face; // 声明一个图像视图对象
  38. private CascadeClassifier mJavaDetector; // OpenCV的人脸检测器
  39. private Net mAgeNet, mGenderNet; // 年龄模型,性别模型
  40. @Override
  41. protected void onCreate(Bundle savedInstanceState) {
  42. super.onCreate(savedInstanceState);
  43. setContentView(R.layout.activity_guess_age);
  44. iv_face = findViewById(R.id.iv_face);
  45. findViewById(R.id.btn_choose).setOnClickListener(v -> {
  46. // 创建一个内容获取动作的意图(准备跳到系统相册)
  47. Intent albumIntent = new Intent(Intent.ACTION_GET_CONTENT);
  48. albumIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false); // 是否允许多选
  49. albumIntent.setType("image/*"); // 类型为图像
  50. startActivityForResult(albumIntent, CHOOSE_CODE); // 打开系统相册
  51. });
  52. }
  53. @Override
  54. protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
  55. super.onActivityResult(requestCode, resultCode, intent);
  56. if (resultCode == RESULT_OK && requestCode == CHOOSE_CODE) { // 从相册返回
  57. if (intent.getData() != null) { // 从相册选择一张照片
  58. Uri uri = intent.getData(); // 获得已选择照片的路径对象
  59. // 根据指定图片的uri,获得自动缩小后的位图对象
  60. Bitmap bitmap = BitmapUtil.getAutoZoomImage(this, uri);
  61. guessAgeAndSex(bitmap); // 根据人脸猜测年龄和性别
  62. }
  63. }
  64. }
  65. // 根据人脸猜测年龄和性别
  66. private void guessAgeAndSex(Bitmap orig) {
  67. Mat rgba = new Mat();
  68. Utils.bitmapToMat(orig, rgba); // 把位图对象转为Mat结构
  69. Mat gray = new Mat();
  70. Imgproc.cvtColor(rgba, gray, Imgproc.COLOR_RGB2GRAY); // 全彩矩阵转灰度矩阵
  71. Mat three = new Mat();
  72. Imgproc.cvtColor(rgba, three, Imgproc.COLOR_RGBA2RGB); // 四通道转三通道
  73. // 下面识别人脸并预测年龄和性别
  74. MatOfRect faces = new MatOfRect();
  75. int height = gray.rows();
  76. int absoluteFaceSize = 0;
  77. if (Math.round(height * 0.2f) > 0) {
  78. absoluteFaceSize = Math.round(height * 0.2f);
  79. }
  80. if (mJavaDetector != null) { // 检测器开始识别人脸
  81. mJavaDetector.detectMultiScale(gray, faces, 1.1, 2, 2,
  82. new Size(absoluteFaceSize, absoluteFaceSize), new Size());
  83. }
  84. Rect[] faceArray = faces.toArray();
  85. List<FaceText> textList = new ArrayList<>();
  86. int lineWidth = Math.max(orig.getWidth()/600 + 1, orig.getHeight()/600 + 1);
  87. for (Rect rect : faceArray) { // 给找到的人脸标上相框
  88. String ageText = predictAge(mAgeNet, three.submat(rect)); // 猜测年龄
  89. String genderText = predictGender(mGenderNet, three.submat(rect)); // 猜测性别
  90. Scalar scalar = new Scalar(0, 255, 0, 255);
  91. Imgproc.rectangle(rgba, rect.tl(), rect.br(), scalar, lineWidth);
  92. PointF pos = new PointF((float) rect.tl().x / rgba.width(), (float) rect.tl().y / rgba.height());
  93. textList.add(new FaceText(pos, genderText + "," + ageText));
  94. //OpenCV的putText方法写中文会乱码,目前OpenCV的Java开发包还不支持中文
  95. //Imgproc.putText(rgba, ageText, rect.tl(), FONT_HERSHEY_PLAIN, 1.2, new Scalar(0, 0, 255), 1);
  96. }
  97. Bitmap mark = Bitmap.createBitmap(orig.getWidth(), orig.getHeight(), Bitmap.Config.ARGB_8888);
  98. Utils.matToBitmap(rgba, mark); // 把Mat结构转为位图对象
  99. mark = FaceUtil.drawTextList(this, mark, textList); // 往位图添加多个文字
  100. iv_face.setImageBitmap(mark);
  101. }
  102. // 获取年龄列表
  103. private List<String> ageLabels() {
  104. List<String> ageList = new ArrayList<>();
  105. ageList.add("0 - 3");
  106. ageList.add("4 - 7");
  107. ageList.add("8 - 14");
  108. ageList.add("15 - 24");
  109. ageList.add("25 - 37");
  110. ageList.add("38 - 50");
  111. ageList.add("51 - 65");
  112. ageList.add("66 -");
  113. return ageList;
  114. }
  115. // 根据模型网络分析预测图像矩阵
  116. private Core.MinMaxLocResult predictResult(Net modelNet, Mat imageMat) {
  117. // 输入图像矩阵
  118. Mat blob = Dnn.blobFromImage(imageMat, 1.0, new Size(227, 227));
  119. modelNet.setInput(blob, "data");
  120. Mat prob = modelNet.forward("prob"); // 模型网络开始预测
  121. Mat probMat = prob.reshape(1, 1);
  122. return Core.minMaxLoc(probMat);
  123. }
  124. // 猜测年龄
  125. private String predictAge(Net modelNet, Mat imageMat) {
  126. Core.MinMaxLocResult result = predictResult(modelNet, imageMat);
  127. return ageLabels().get((int) result.maxLoc.x);
  128. }
  129. // 猜测性别
  130. private String predictGender(Net modelNet, Mat imageMat) {
  131. Core.MinMaxLocResult result = predictResult(modelNet, imageMat);
  132. //return ((int) result.maxLoc.x)==1 ? "男" : "女";
  133. return ((int) result.maxLoc.x)==1 ? "女" : "男";
  134. }
  135. @Override
  136. protected void onResume() {
  137. super.onResume();
  138. if (!OpenCVLoader.initDebug()) {
  139. Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
  140. OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
  141. } else {
  142. Log.d(TAG, "OpenCV library found inside package. Using it!");
  143. mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
  144. }
  145. }
  146. private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
  147. @Override
  148. public void onManagerConnected(int status) {
  149. if (status == LoaderCallbackInterface.SUCCESS) {
  150. new Thread(() -> importModel()).start(); // 启动分线程导入年龄模型和性别模型
  151. Log.d(TAG, "OpenCV loaded successfully");
  152. // 在OpenCV初始化完成后加载so库
  153. System.loadLibrary("detection_based_tracker");
  154. File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
  155. File cascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
  156. // 从应用程序资源加载级联文件
  157. try (InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
  158. FileOutputStream os = new FileOutputStream(cascadeFile)) {
  159. byte[] buffer = new byte[4096];
  160. int bytesRead;
  161. while ((bytesRead = is.read(buffer)) != -1) {
  162. os.write(buffer, 0, bytesRead);
  163. }
  164. } catch (Exception e) {
  165. e.printStackTrace();
  166. }
  167. // 根据级联文件创建OpenCV的人脸检测器
  168. mJavaDetector = new CascadeClassifier(cascadeFile.getAbsolutePath());
  169. if (mJavaDetector.empty()) {
  170. Log.d(TAG, "Failed to load cascade classifier");
  171. mJavaDetector = null;
  172. } else {
  173. Log.d(TAG, "Loaded cascade classifier from " + cascadeFile.getAbsolutePath());
  174. }
  175. cascadeDir.delete();
  176. } else{
  177. super.onManagerConnected(status);
  178. }
  179. }
  180. };
  181. // 导入年龄模型和性别模型
  182. private void importModel() {
  183. String prePath = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() + "/";
  184. String age_model = prePath + "age_net.caffemodel";
  185. String age_text = prePath + "age_deploy.prototxt";
  186. String gender_model = prePath + "gender_net.caffemodel";
  187. String gender_text = prePath + "gender_deploy.prototxt";
  188. mAgeNet = Dnn.readNetFromCaffe(age_text, age_model);
  189. mGenderNet = Dnn.readNetFromCaffe(gender_text, gender_model);
  190. }
  191. }

创作不易 觉得有帮助请点赞关注收藏~~~


本文转载自: https://blog.csdn.net/jiebaoshayebuhui/article/details/128158368
版权归原作者 showswoller 所有, 如有侵权,请联系我们删除。

“【Android App】人脸识别中OpenCV根据人脸估算性别和年龄实战(附源码和演示 超详细)”的评论:

还没有评论