mirror of
https://github.com/geekwenjie/SmartJavaAI.git
synced 2026-09-14 05:38:50 +00:00
1、FaceNet 特征提取新增人脸对齐
2、人脸检测新5点人脸关键点定位 3、特征提取接口支持多人脸和最佳人脸提取 4、修复人脸框边界精度问题 5、更新 Maven 发布的 groupId
This commit is contained in:
@@ -4,9 +4,9 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>ink.numberone</groupId>
|
||||
<groupId>cn.smartjavaai</groupId>
|
||||
<artifactId>smartjavaai-parent</artifactId>
|
||||
<version>1.0.10</version>
|
||||
<version>1.0.11</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>smartjavaai-common</artifactId>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package cn.smartjavaai.common.entity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 检测结果-矩形区域
|
||||
* @author dwj
|
||||
@@ -11,9 +13,13 @@ public class DetectionRectangle {
|
||||
public int width;
|
||||
public int height;
|
||||
public float score;
|
||||
|
||||
public String className;
|
||||
|
||||
/**
|
||||
* 人脸关键点
|
||||
*/
|
||||
private List<Point> keyPoints;
|
||||
|
||||
public DetectionRectangle() {
|
||||
}
|
||||
|
||||
@@ -81,4 +87,12 @@ public class DetectionRectangle {
|
||||
public void setClassName(String className) {
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
public List<Point> getKeyPoints() {
|
||||
return keyPoints;
|
||||
}
|
||||
|
||||
public void setKeyPoints(List<Point> keyPoints) {
|
||||
this.keyPoints = keyPoints;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,27 +10,27 @@ import java.io.Serializable;
|
||||
*/
|
||||
public class Point implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private int x;
|
||||
private int y;
|
||||
private double x;
|
||||
private double y;
|
||||
|
||||
public Point(int x, int y) {
|
||||
public Point(double x, double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public void setX(int x) {
|
||||
public void setX(double x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setY(int y) {
|
||||
public void setY(double y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
package cn.smartjavaai.common.utils;
|
||||
|
||||
import ai.djl.modality.cv.BufferedImageFactory;
|
||||
import ai.djl.modality.cv.Image;
|
||||
import ai.djl.ndarray.NDArray;
|
||||
import cn.smartjavaai.common.entity.DetectionRectangle;
|
||||
import cn.smartjavaai.common.entity.DetectionResponse;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
//import java.awt.image.ColorConvertOp;
|
||||
import java.awt.image.ComponentSampleModel;
|
||||
import java.awt.image.ImageObserver;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 图片处理工具类
|
||||
@@ -88,6 +98,86 @@ public class ImageUtils {
|
||||
return image != null && image.getWidth() > 0 && image.getHeight() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 画检测框
|
||||
*
|
||||
* @param image
|
||||
* @param x
|
||||
* @param y
|
||||
* @param width
|
||||
* @param height
|
||||
*/
|
||||
public static void drawImageRect(BufferedImage image, int x, int y, int width, int height) {
|
||||
// 将绘制图像转换为Graphics2D
|
||||
Graphics2D g = (Graphics2D) image.getGraphics();
|
||||
try {
|
||||
g.setColor(new Color(0, 255, 0));
|
||||
// 声明画笔属性 :粗 细(单位像素)末端无修饰 折线处呈尖角
|
||||
BasicStroke bStroke = new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
|
||||
g.setStroke(bStroke);
|
||||
g.drawRect(x, y, width, height);
|
||||
} finally {
|
||||
g.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 画检测框
|
||||
*
|
||||
* @param image
|
||||
* @param x
|
||||
* @param y
|
||||
* @param width
|
||||
* @param height
|
||||
*/
|
||||
public static void drawImageRect(Image image, int x, int y, int width, int height) {
|
||||
// 将绘制图像转换为Graphics2D
|
||||
BufferedImage bufferedImage = (BufferedImage)image.getWrappedImage();
|
||||
Graphics2D g = (Graphics2D) bufferedImage.getGraphics();
|
||||
try {
|
||||
g.setColor(new Color(0, 255, 0));
|
||||
// 声明画笔属性 :粗 细(单位像素)末端无修饰 折线处呈尖角
|
||||
BasicStroke bStroke = new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
|
||||
g.setStroke(bStroke);
|
||||
g.drawRect(x, y, width, height);
|
||||
} finally {
|
||||
g.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 画检测框
|
||||
*
|
||||
* @param image
|
||||
* @param x
|
||||
* @param y
|
||||
* @param width
|
||||
* @param height
|
||||
*/
|
||||
public static void drawImageRect(Image image, DetectionResponse detectionResponse) {
|
||||
if(Objects.nonNull(detectionResponse) && Objects.nonNull(detectionResponse.getRectangleList()) && !detectionResponse.getRectangleList().isEmpty()){
|
||||
// 将绘制图像转换为Graphics2D'
|
||||
BufferedImage bufferedImage = (BufferedImage)image.getWrappedImage();
|
||||
Graphics2D g = (Graphics2D) bufferedImage.getGraphics();
|
||||
try {
|
||||
g.setColor(new Color(0, 255, 0));
|
||||
// 声明画笔属性 :粗 细(单位像素)末端无修饰 折线处呈尖角
|
||||
BasicStroke bStroke = new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
|
||||
g.setStroke(bStroke);
|
||||
for(DetectionRectangle detectionRectangle : detectionResponse.getRectangleList()){
|
||||
g.drawRect(detectionRectangle.getX(), detectionRectangle.getY(), detectionRectangle.getWidth(), detectionRectangle.getHeight());
|
||||
}
|
||||
} finally {
|
||||
g.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
package cn.smartjavaai.common.utils;
|
||||
|
||||
import ai.djl.ndarray.NDArray;
|
||||
import ai.djl.ndarray.NDManager;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Point;
|
||||
import org.opencv.core.Scalar;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBufferByte;
|
||||
|
||||
/**
|
||||
* OpenCV 工具类
|
||||
*/
|
||||
public class OpenCVUtils {
|
||||
/**
|
||||
* canny算法,边缘检测
|
||||
*
|
||||
* @param src
|
||||
* @return
|
||||
*/
|
||||
public static Mat canny(Mat src) {
|
||||
Mat mat = src.clone();
|
||||
Imgproc.Canny(src, mat, 100, 200);
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* 画线
|
||||
*
|
||||
* @param mat
|
||||
* @param point1
|
||||
* @param point2
|
||||
*/
|
||||
public static void line(Mat mat, Point point1, Point point2) {
|
||||
Imgproc.line(mat, point1, point2, new Scalar(255, 255, 255), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* NDArray to opencv_core.Mat
|
||||
*
|
||||
* @param manager
|
||||
* @param srcPoints
|
||||
* @param dstPoints
|
||||
* @return
|
||||
*/
|
||||
public static Mat toOpenCVMat(NDManager manager, NDArray srcPoints, NDArray dstPoints) {
|
||||
NDArray svdMat = SVDUtils.transformationFromPoints(manager, srcPoints, dstPoints);
|
||||
double[] doubleArray = svdMat.toDoubleArray();
|
||||
Mat newSvdMat = new Mat(2, 3, CvType.CV_64F);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
newSvdMat.put(i, j, doubleArray[i * 3 + j]);
|
||||
}
|
||||
}
|
||||
return newSvdMat;
|
||||
}
|
||||
|
||||
/**
|
||||
* double[][] points array to Mat
|
||||
* @param points
|
||||
* @return
|
||||
*/
|
||||
public static Mat toOpenCVMat(double[][] points) {
|
||||
Mat mat = new Mat(5, 2, CvType.CV_64F);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
mat.put(i, j, points[i * 5 + j]);
|
||||
}
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* 变换矩阵的逆矩阵
|
||||
*
|
||||
* @param src
|
||||
* @return
|
||||
*/
|
||||
public static Mat invertAffineTransform(Mat src) {
|
||||
Mat dst = src.clone();
|
||||
Imgproc.invertAffineTransform(src, dst);
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mat to BufferedImage
|
||||
*
|
||||
* @param mat
|
||||
* @return
|
||||
*/
|
||||
public static BufferedImage mat2Image(Mat mat) {
|
||||
int width = mat.width();
|
||||
int height = mat.height();
|
||||
byte[] data = new byte[width * height * (int) mat.elemSize()];
|
||||
Imgproc.cvtColor(mat, mat, 4);
|
||||
mat.get(0, 0, data);
|
||||
BufferedImage ret = new BufferedImage(width, height, 5);
|
||||
ret.getRaster().setDataElements(0, 0, width, height, data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* BufferedImage to Mat
|
||||
*
|
||||
* @param img
|
||||
* @return
|
||||
*/
|
||||
public static Mat image2Mat(BufferedImage img) {
|
||||
int width = img.getWidth();
|
||||
int height = img.getHeight();
|
||||
byte[] data = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
|
||||
Mat mat = new Mat(height, width, CvType.CV_8UC3);
|
||||
mat.put(0, 0, data);
|
||||
return mat;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package cn.smartjavaai.common.utils;
|
||||
|
||||
import Jama.Matrix;
|
||||
import Jama.SingularValueDecomposition;
|
||||
import ai.djl.ndarray.NDArray;
|
||||
import ai.djl.ndarray.NDManager;
|
||||
|
||||
/**
|
||||
* 仿射变换处理工具
|
||||
*/
|
||||
public class SVDUtils {
|
||||
/**
|
||||
* 计算仿射变换矩阵
|
||||
* Calculate affine transformation matrix
|
||||
*
|
||||
* @param manager
|
||||
* @param points1
|
||||
* @param points2
|
||||
* @return
|
||||
*/
|
||||
public static NDArray transformationFromPoints(
|
||||
NDManager manager, NDArray points1, NDArray points2) {
|
||||
// 按列计算均值
|
||||
// Calculate column-wise mean
|
||||
NDArray c1 = points1.mean(new int[]{0}); // axis=0 列操作 - axis=0 column operation
|
||||
NDArray c2 = points2.mean(new int[]{0}); // axis=0 列操作 - axis=0 column operation
|
||||
// 按列减去均值
|
||||
// Subtract column-wise mean
|
||||
points1 = points1.sub(c1);
|
||||
points2 = points2.sub(c2);
|
||||
|
||||
// 计算全局标准差
|
||||
// Calculate global standard deviation
|
||||
double s1 = std(points1);
|
||||
double s2 = std(points2);
|
||||
|
||||
// 矩阵除以全局标准差
|
||||
// Matrix divided by global standard deviation
|
||||
NDArray djl_s1 = manager.create(s1);
|
||||
NDArray djl_s2 = manager.create(s2);
|
||||
points1 = points1.div(djl_s1);
|
||||
points2 = points2.div(djl_s2);
|
||||
|
||||
double[] points1D = points1.toDoubleArray();
|
||||
double[] points2D = points2.toDoubleArray();
|
||||
|
||||
// DJL 格式转换成Jamma格式
|
||||
// Convert DJL format to Jama format
|
||||
double[][] m1 = new double[5][2];
|
||||
double[][] m2 = new double[5][2];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
m1[i][j] = points1D[i * 2 + j];
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
m2[i][j] = points2D[i * 2 + j];
|
||||
}
|
||||
}
|
||||
Matrix p1 = new Matrix(m1);
|
||||
Matrix p2 = new Matrix(m2);
|
||||
|
||||
// 进行奇异值分解
|
||||
// Perform singular value decomposition
|
||||
Matrix p3 = p1.transpose().times(p2);
|
||||
SingularValueDecomposition s = p3.svd();
|
||||
|
||||
Matrix U = s.getU();
|
||||
Matrix S = s.getS();
|
||||
Matrix V = s.getV();
|
||||
// TODO 为什么第2列的符号是反的?
|
||||
// Why is the sign of the second column opposite?
|
||||
m1 = U.getArray();
|
||||
m1[0][1] = -m1[0][1];
|
||||
m1[1][1] = -m1[1][1];
|
||||
m2 = V.getArray();
|
||||
m2[0][1] = -m2[0][1];
|
||||
m2[1][1] = -m2[1][1];
|
||||
|
||||
Matrix R = (U.times(V)).transpose();
|
||||
|
||||
double[][] rArray = R.getArray();
|
||||
NDArray newR = manager.create(rArray);
|
||||
// np.vstack([np.hstack(((s2 / s1) * R, c2.T - (s2 / s1) * R * c1.T)), np.matrix([0.,0., 1.])])
|
||||
// (s2 / s1) * R
|
||||
NDArray leftPart = djl_s2.div(djl_s1).mul(newR);
|
||||
// c2.T - (s2 / s1) * R * c1.T)
|
||||
NDArray rightPart = c2.reshape(2, 1).sub(leftPart.matMul(c1.reshape(2, 1)));
|
||||
// numpy.hstack(((s2 / s1) * R, c2.T - (s2 / s1) * R * c1.T))
|
||||
NDArray upPart = leftPart.concat(rightPart, 1);
|
||||
// np.matrix([0.,0., 1.])
|
||||
double[] downArray = {0d, 0d, 1d};
|
||||
NDArray downPart = manager.create(downArray).reshape(1, 3);
|
||||
|
||||
NDArray all = upPart.concat(downPart, 0);
|
||||
// System.out.println("all: " + all);
|
||||
|
||||
return upPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算全局标准差
|
||||
* Calculate global standard deviation
|
||||
*
|
||||
* @param points
|
||||
* @return
|
||||
*/
|
||||
public static double std(NDArray points) {
|
||||
points = points.square();
|
||||
double[] doubleResult = points.toDoubleArray();
|
||||
double std = 0;
|
||||
for (int i = 0; i < doubleResult.length; i++) {
|
||||
std = std + doubleResult[i];
|
||||
}
|
||||
std = (float) Math.sqrt(std / doubleResult.length);
|
||||
return std;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user