-
Notifications
You must be signed in to change notification settings - Fork 88
/
Copy pathImageAgeDetect.java
226 lines (212 loc) · 7.81 KB
/
ImageAgeDetect.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
package com.biubiu.example;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.imageio.ImageIO;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfRect;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.dnn.Dnn;
import org.opencv.dnn.Net;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
/**
* @author :张音乐
* @date :Created in 2021/4/18 下午1:25
* @description:年龄识别
* @email: [email protected]
* @version: 1.0
*/
public class ImageAgeDetect {
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
/**
* 年龄识别模型
*/
private final static String ageProto = "D:/workspace/opencv/data/models/age_deploy.prototxt";
private final static String ageModel = "D:/workspace/opencv/data/models/age_net.caffemodel";
/**
* 年龄预测返回的是8个年龄的阶段!
*/
private final static List<String> ageList = new ArrayList<>(Arrays.asList("(0-2)", "(4-6)", "(8-12)", "(15-20)", "(25-32)", "(38-43)", "(48-53)", "(60-100)"));
/**
* 模型均值
*/
private final static Scalar MODEL_MEAN_VALUES = new Scalar(78.4263377603, 87.7689143744, 114.895847746);
public static void main(String[] args) {
// 加载网络模型
Net ageNet = Dnn.readNetFromCaffe(ageProto, ageModel);
if (ageNet.empty()) {
System.out.println("无法打开网络模型...\n");
return;
}
// 加载图片矩阵
String filePath = "D:\\upload\\gather.png";
Mat img = Imgcodecs.imread(filePath);
// 人脸检测
MatOfRect faceRects = facePick(img);
// 定义一个颜色
Scalar color = new Scalar(0, 0, 255);
// 遍历检测到的图片
for(Rect rect : faceRects.toArray()) {
// 人脸画矩形框
drawRect(rect, img, color);
// 检测年龄
String gender = getAge(img, rect, ageNet);
// 图片上显示中文的年龄 ,因为原生的 opencv putText 显示中文会乱码, 所以需要特殊处理一下
img = putChineseTxt(img, gender, rect.x + rect.width / 2 - 5, rect.y - 10);
// Imgproc.putText(img, new String(gender.getBytes(StandardCharsets.UTF_8)), new Point(x, y), 2, 2, color);
}
// 显示图像
HighGui.imshow("预览", img);
HighGui.waitKey(0);
// 释放所有的窗体资源
HighGui.destroyAllWindows();
}
/**
* 在图片上的人脸区域画上矩形框
* @param rect
* @param img
* @param color
*/
private static void drawRect(Rect rect, Mat img, Scalar color) {
int x = rect.x;
int y = rect.y;
int w = rect.width;
int h = rect.height;
Imgproc.rectangle(img, new Point(x, y), new Point(x + h, y + w), color, 2);
}
/**
* 年龄检测
* @param img
* @param rect
* @param genderNet
* @return
*/
private static String getAge(Mat img, Rect rect, Net genderNet) {
Mat face = new Mat(img, rect);
// Resizing pictures to resolution of Caffe model
Imgproc.resize(face, face, new Size(140, 140));
// 灰度化
Imgproc.cvtColor(face, face, Imgproc.COLOR_RGBA2BGR);
// blob输入网络进行年龄的检测
Mat inputBlob = Dnn.blobFromImage(face, 1.0f, new Size(227, 227), MODEL_MEAN_VALUES, false, false);
genderNet.setInput(inputBlob, "data");
// 年龄检测进行前向传播
Mat probs = genderNet.forward("prob").reshape(1, 1);
Core.MinMaxLocResult mm = Core.minMaxLoc(probs);
// Result of gender recognition prediction.
double index = mm.maxLoc.x;
return ageList.get((int) index);
}
/**
* 图片人脸检测
* @return
*/
private static MatOfRect facePick(Mat img) {
// 存放灰度图
Mat tempImg = new Mat();
// 摄像头获取的是彩色图像,所以先灰度化下
Imgproc.cvtColor(img, tempImg, Imgproc.COLOR_BGRA2GRAY);
// OpenCV人脸识别分类器
CascadeClassifier classifier = new CascadeClassifier("D:\\workspace\\opencv\\data\\haarcascades\\haarcascade_frontalface_default.xml");
// # 调用识别人脸
MatOfRect faceRects = new MatOfRect();
// 特征检测点的最小尺寸, 根据实际照片尺寸来选择, 不然测量结果可能不准确。
Size minSize = new Size(140, 140);
// 图像缩放比例,可理解为相机的X倍镜
double scaleFactor = 1.2;
// 对特征检测点周边多少有效点同时检测,这样可避免因选取的特征检测点太小而导致遗漏
int minNeighbors = 3;
// 人脸检测
// CV_HAAR_DO_CANNY_PRUNING
classifier.detectMultiScale(tempImg, faceRects, scaleFactor, minNeighbors, 0, minSize);
return faceRects;
}
/**
* Mat二维矩阵转Image
* @param matrix
* @param fileExtension
* @return
*/
public static BufferedImage matToImg(Mat matrix, String fileExtension) {
// convert the matrix into a matrix of bytes appropriate for
// this file extension
MatOfByte mob = new MatOfByte();
Imgcodecs.imencode(fileExtension, matrix, mob);
// convert the "matrix of bytes" into a byte array
byte[] byteArray = mob.toArray();
BufferedImage bufImage = null;
try {
InputStream in = new ByteArrayInputStream(byteArray);
bufImage = ImageIO.read(in);
} catch (Exception e) {
e.printStackTrace();
}
return bufImage;
}
/**
* BufferedImage转换成 Mat
* @param original
* @param imgType
* @param matType
* @return
*/
public static Mat imgToMat(BufferedImage original, int imgType, int matType) {
if (original == null) {
throw new IllegalArgumentException("original == null");
}
if (original.getType() != imgType){
// Create a buffered image
BufferedImage image = new BufferedImage(original.getWidth(), original.getHeight(), imgType);
// Draw the image onto the new buffer
Graphics2D g = image.createGraphics();
try {
g.setComposite(AlphaComposite.Src);
g.drawImage(original, 0, 0, null);
} finally {
g.dispose();
}
}
byte[] pixels = ((DataBufferByte) original.getRaster().getDataBuffer()).getData();
Mat mat = Mat.eye(original.getHeight(), original.getWidth(), matType);
mat.put(0, 0, pixels);
return mat;
}
/**
* 在图片上显示中文
* @param img
* @param gender
* @param x
* @param y
* @return
*/
private static Mat putChineseTxt(Mat img, String gender, int x, int y) {
Font font = new Font("微软雅黑", Font.PLAIN, 20);
BufferedImage bufImg = matToImg(img,".png");
Graphics2D g = bufImg.createGraphics();
g.drawImage(bufImg, 0, 0, bufImg.getWidth(), bufImg.getHeight(), null);
// 设置字体
g.setColor(new Color(255, 10, 52));
g.setFont(font);
// 设置水印的坐标
g.drawString(gender, x, y);
g.dispose();
// 加完水印再转换回来
return imgToMat(bufImg, BufferedImage.TYPE_3BYTE_BGR, CvType.CV_8UC3);
}
}