libfacedetection是深圳大学于仕途琪老师开发的一个用于人脸检测的算法,并且开源到了GitHub上面,它是基于CNN模型的开源库,可以在Linux、ARM、Windows等任何平台下使用C++编译器编译源代码。
可以到GitHub直接搜索libfacedetection,选择于仕琪教授的。
有的小伙伴可能无法访问到GitHub,我这里也将于仕琪教授开源到GitHub上的源码转移到了gitee上,需要的小伙伴可以私聊我获取源码。
COMPILE.md文件中有介绍如何在各个平台编译libfacedetection。
将编译好的.dll文件或者.so文件放在/resource/lib目录下;
导入JNA依赖(接触过JNI的人应该知道,它允许Java代码和其他语言(尤其C/C++)写的代码进行交互,只要遵守调用约定即可,不过使用JNI进行交叉编译步骤非常繁多,所以sun公司则推出了一种可以简化调用本地方法过程的Java开源框架-JNA,使用JNA之后我们只需要写Java代码,而不用写JNI或本地代码,大大降低了开发难度);
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.9.0</version>
</dependency>
public interface CLibrary extends Library {
// 通过反射创建CLibrary实例
CLibrary faceDetect = Native.load("libFaceDetection", CLibrary.class);
// 声明源码中的facedetect_cnn方法
Pointer facedetect_cnn(Pointer result_buffer, Pointer rgb_image_data, int width, int height, int step);
}
public class FacePoint {
Integer X;
Integer Y;
public Integer getX() {
return X;
}
public void setX(Integer x) {
X = x;
}
public Integer getY() {
return Y;
}
public void setY(Integer y) {
Y = y;
}
}
public class FaceInfo {
Integer Confidence;
Integer X;
Integer Y;
Integer Width;
Integer Height;
ArrayList<FacePoint> points;
public Integer getConfidence() {
return Confidence;
}
public void setConfidence(Integer confidence) {
Confidence = confidence;
}
public Integer getX() {
return X;
}
public void setX(Integer x) {
X = x;
}
public Integer getY() {
return Y;
}
public void setY(Integer y) {
Y = y;
}
public Integer getWidth() {
return Width;
}
public void setWidth(Integer width) {
Width = width;
}
public Integer getHeight() {
return Height;
}
public void setHeight(Integer height) {
Height = height;
}
public ArrayList<FacePoint> getPoints() {
return points;
}
public void setPoints(ArrayList<FacePoint> points) {
this.points = points;
}
}
public class FaceResult {
Integer FaceCount;
ArrayList<FaceInfo> faceInfos;
public Integer getFaceCount() {
return FaceCount;
}
public void setFaceCount(Integer faceCount) {
FaceCount = faceCount;
}
public ArrayList<FaceInfo> getFaceInfos() {
return faceInfos;
}
public void setFaceInfos(ArrayList<FaceInfo> faceInfos) {
this.faceInfos = faceInfos;
}
}
public class FaceDetectUtil {
//定义缓冲区大小:0X20000字节
public static final Long DETECT_BUFFER_SIZE = 0x20000L;
/**
* @param imgPath 服务器中文件位置
* @param fileName 文件名
* @return 结果集
*/
public static FaceResult faceDetect(String imgPath, String fileName) {
//读取图片资源
Mat mat = opencv_imgcodecs.imread(imgPath);
if (mat.cols() > 600) {
float temp = (float) mat.rows() / mat.cols();
//重设图片大小,(输入图像,输出图像,新的大小)
opencv_imgproc.resize(mat, mat, new Size(600, Math.round(600 * temp)));
}
//获取mat对象的地址
Pointer bgrData = new Pointer(mat.data().address());
//分配缓冲区内存
Pointer resultBuffer = new Memory(DETECT_BUFFER_SIZE);
//调用人脸识别方法
Pointer pResults = CLibrary.faceDetect.facedetect_cnn(resultBuffer, bgrData, mat.cols(), mat.rows(), (int) mat.step1());
//opencv处理图片,获取结果集
FaceResult faceResult = new FaceResult();
faceResult.setFaceCount(pResults.getInt(0L));
System.err.println("FaceDetectUtil.faceDetect:faceCount:" + faceResult.getFaceCount());
ArrayList<FaceInfo> faceInfos = new ArrayList<>();
for (int i = 0; i < faceResult.getFaceCount(); i++) {
Pointer p = pResults.share(4L).share(142L * i * 2L);
FaceInfo faceInfo = new FaceInfo();
faceInfo.setConfidence((int) p.getShort(0L));
faceInfo.setX((int) p.getShort(2L));
faceInfo.setY((int) p.getShort(4L));
faceInfo.setWidth((int) p.getShort(6L));
faceInfo.setHeight((int) p.getShort(8L));
ArrayList<FacePoint> points = new ArrayList<>();
for (int j = 0; j < 5; j++) {
FacePoint facePoint = new FacePoint();
facePoint.setX((int) p.getShort(10L + j * 4L));
facePoint.setY((int) p.getShort(12L + j * 4L));
points.add(j, facePoint);
}
faceInfo.setPoints(points);
faceInfos.add(i, faceInfo);
}
faceResult.setFaceInfos(faceInfos);
return faceResult;
}
}
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>1.5.8</version>
<classifier>${opencv.platform}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.21-1.5.8</version>
<classifier>${opencv.platform}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.6.0-1.5.8</version>
<classifier>${opencv.platform}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.6.0-1.5.8</version>
</dependency>
public File testDetect(String imgPath, String fileName) {
//读取图片资源
Mat mat = opencv_imgcodecs.imread(imgPath);
if (mat.cols() > 600) {
float temp = (float) mat.rows() / mat.cols();
//重设图片大小,(输入图像,输出图像,新的大小)
opencv_imgproc.resize(mat, mat, new Size(600, Math.round(600 * temp)));
}
//opencv处理图片,标记人脸以及人脸特征点
Mat resultImage = mat.clone();
FaceResult faceResult = FaceDetectUtil.faceDetect(imgPath, fileName);
for (int i = 0; i < faceResult.getFaceCount(); i++) {
//矩形框标记人脸
opencv_imgproc.rectangle(resultImage,
new Rect(faceResult.getFaceInfos().get(i).getX(),
faceResult.getFaceInfos().get(i).getY(),
faceResult.getFaceInfos().get(i).getWidth(),
faceResult.getFaceInfos().get(i).getHeight()),
new Scalar(0d, 255d, 0d, 0));
Scalar[] scalars = new Scalar[5];
scalars[0] = new Scalar(255, 0, 0, 0);
scalars[1] = new Scalar(0, 0, 255, 0);
scalars[2] = new Scalar(0, 255, 0, 0);
scalars[3] = new Scalar(255, 0, 255, 0);
scalars[4] = new Scalar(0, 255, 255, 0);
//标记人脸特征点
for (int j = 0; j < 5; j++) {
try (Scalar scalar = scalars[j]) {
opencv_imgproc.circle(resultImage, new Point(faceResult.getFaceInfos().get(i).getPoints().get(j).getX(),
faceResult.getFaceInfos().get(i).getPoints().get(j).getY()), 1, scalar);
}
}
}
//返回处理后的临时文件
String hzName = fileName.substring(fileName.lastIndexOf("."));
fileName = UUID.randomUUID() + hzName;
String tmpPath = System.getProperty("java.io.tmpdir");
String finalPath = tmpPath + fileName;
opencv_imgcodecs.imwrite(finalPath, resultImage);
return new File(finalPath);
}
使用libfacedetection库进行人脸检测时相应速度平均只有200毫秒,相较于使用JNA自带的人脸检测,执行效率有很明显的提升。