2025-04-28 16:13:34 +08:00
|
|
|
|
package cn.smartjavaai.face.seetaface;
|
2025-03-26 17:09:22 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-04-19 19:48:12 +08:00
|
|
|
|
import cn.hutool.core.io.FileUtil;
|
|
|
|
|
|
import cn.hutool.setting.dialect.Props;
|
|
|
|
|
|
import cn.hutool.system.OsInfo;
|
|
|
|
|
|
import cn.hutool.system.SystemUtil;
|
|
|
|
|
|
import cn.smartjavaai.common.config.Config;
|
|
|
|
|
|
import cn.smartjavaai.common.enums.DeviceEnum;
|
|
|
|
|
|
import cn.smartjavaai.face.FaceModelConfig;
|
|
|
|
|
|
import cn.smartjavaai.face.exception.FaceException;
|
|
|
|
|
|
import com.seeta.sdk.util.DllItem;
|
|
|
|
|
|
import com.seeta.sdk.util.LoadNativeCore;
|
2025-03-26 17:09:22 +08:00
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
|
|
|
|
|
|
|
import java.io.*;
|
|
|
|
|
|
import java.nio.file.Files;
|
|
|
|
|
|
import java.nio.file.Path;
|
2025-04-19 19:48:12 +08:00
|
|
|
|
import java.nio.file.Paths;
|
2025-03-26 17:09:22 +08:00
|
|
|
|
import java.nio.file.StandardCopyOption;
|
2025-04-19 19:48:12 +08:00
|
|
|
|
import java.util.*;
|
|
|
|
|
|
import java.util.stream.Collectors;
|
2025-03-26 17:09:22 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 依赖库加载器
|
|
|
|
|
|
* @author dwj
|
|
|
|
|
|
*/
|
|
|
|
|
|
@Slf4j
|
|
|
|
|
|
public class NativeLoader {
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-04-19 19:48:12 +08:00
|
|
|
|
private static Path seetaface6NativePath;
|
2025-03-26 17:09:22 +08:00
|
|
|
|
|
2025-04-19 19:48:12 +08:00
|
|
|
|
private static final String SEETAFACE_LIB_DIR = "seetaface6";
|
2025-03-26 17:09:22 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-04-19 19:48:12 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 定义dll 路径和加载顺序的文件
|
|
|
|
|
|
*/
|
|
|
|
|
|
private static final String PROPERTIES_FILE_NAME = "dll.properties";
|
2025-03-26 17:09:22 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-04-19 19:48:12 +08:00
|
|
|
|
public static void loadNativeLibraries(FaceModelConfig config) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
OsInfo osInfo = SystemUtil.getOsInfo();
|
|
|
|
|
|
//检查当前系统是否支持
|
|
|
|
|
|
if(!osInfo.isWindows() && !osInfo.isLinux()){
|
|
|
|
|
|
throw new FaceException("当前系统不支持:" + osInfo.getName());
|
2025-03-26 17:09:22 +08:00
|
|
|
|
}
|
2025-04-19 19:48:12 +08:00
|
|
|
|
//判断硬件架构是否支持GPU
|
|
|
|
|
|
if(config.getDevice() != null && config.getDevice().equals(DeviceEnum.GPU)){
|
|
|
|
|
|
//GPU仅支持amd64
|
|
|
|
|
|
if(!osInfo.getArch().contains("amd64") && !osInfo.getArch().contains("x86_64")){
|
|
|
|
|
|
throw new FaceException("seetaface6 GPU模型不支持当前arch:" + osInfo.getArch());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
seetaface6NativePath = Paths.get(Config.getCachePath(), SEETAFACE_LIB_DIR);
|
|
|
|
|
|
//创建目录
|
|
|
|
|
|
FileUtil.mkdir(seetaface6NativePath);
|
|
|
|
|
|
log.info("seetaface6依赖库路径: " + seetaface6NativePath.toAbsolutePath().toString());
|
|
|
|
|
|
//拷贝依赖库到缓存目录
|
|
|
|
|
|
List<File> fileList = getLibFiles(osInfo, config.getDevice());
|
|
|
|
|
|
if(fileList != null && !fileList.isEmpty()){
|
|
|
|
|
|
// 加载依赖库文件
|
|
|
|
|
|
fileList.forEach(file -> {
|
|
|
|
|
|
System.load(file.getAbsolutePath());
|
|
|
|
|
|
log.info(String.format("load %s finish", file.getAbsolutePath()));
|
|
|
|
|
|
});
|
2025-03-26 17:09:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
throw new RuntimeException("Native library loading failed", e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-19 19:48:12 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 拷贝依赖库到缓存目录
|
|
|
|
|
|
* @param osInfo
|
|
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
|
|
|
|
|
private static List<File> getLibFiles(OsInfo osInfo,DeviceEnum deviceEnum){
|
|
|
|
|
|
try {
|
|
|
|
|
|
String device = getDevice(deviceEnum);
|
|
|
|
|
|
log.info("当前设备:{}", device);
|
|
|
|
|
|
//获取dll文件列表
|
|
|
|
|
|
List<DllItem> baseList = new ArrayList<>();
|
|
|
|
|
|
List<DllItem> jniList = new ArrayList<>();
|
|
|
|
|
|
InputStream propsInputStream = LoadNativeCore.class.getResourceAsStream(getPropertiesPath());
|
|
|
|
|
|
Props props = new Props();
|
|
|
|
|
|
props.load(propsInputStream);
|
|
|
|
|
|
String prefix = getPrefix();
|
|
|
|
|
|
props.forEach((keyObj, valuObj) -> {
|
|
|
|
|
|
String key = (String) keyObj;
|
|
|
|
|
|
String value = (String) valuObj;
|
|
|
|
|
|
DllItem dllItem = new DllItem();
|
|
|
|
|
|
dllItem.setKey(key);
|
|
|
|
|
|
if (key.contains("base")) {
|
|
|
|
|
|
if (value.contains("tennis")) {
|
|
|
|
|
|
dllItem.setValue(prefix + "base/" + device + "/" + value);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
dllItem.setValue(prefix + "base/" + value);
|
|
|
|
|
|
}
|
|
|
|
|
|
baseList.add(dllItem);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
dllItem.setValue(prefix + value);
|
|
|
|
|
|
jniList.add(dllItem);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
//给dll文件排序
|
|
|
|
|
|
List<String> basePath = getSortedPath(baseList);
|
|
|
|
|
|
List<String> sdkPath = getSortedPath(jniList);
|
|
|
|
|
|
List<File> fileList = new ArrayList<>();
|
|
|
|
|
|
//拷贝文件到临时目录
|
|
|
|
|
|
for (String baseSo : basePath) {
|
|
|
|
|
|
fileList.add(extractLibrary(baseSo));
|
|
|
|
|
|
}
|
|
|
|
|
|
for (String sdkSo : sdkPath) {
|
|
|
|
|
|
fileList.add(extractLibrary(sdkSo));
|
|
|
|
|
|
}
|
|
|
|
|
|
return fileList;
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
throw new FaceException("拷贝依赖库失败",e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-03-26 17:09:22 +08:00
|
|
|
|
|
2025-04-19 19:48:12 +08:00
|
|
|
|
private static String getDevice(DeviceEnum deviceEnum) {
|
|
|
|
|
|
String device = "CPU";
|
|
|
|
|
|
if ("amd64".equals(getArch()) && deviceEnum != null) {
|
|
|
|
|
|
device = deviceEnum == DeviceEnum.GPU ? "GPU" : "CPU";
|
|
|
|
|
|
}
|
|
|
|
|
|
return device;
|
2025-03-26 17:09:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-19 19:48:12 +08:00
|
|
|
|
|
2025-03-26 17:09:22 +08:00
|
|
|
|
/**
|
2025-04-19 19:48:12 +08:00
|
|
|
|
* 返回路径文件前缀
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return
|
2025-03-26 17:09:22 +08:00
|
|
|
|
*/
|
2025-04-19 19:48:12 +08:00
|
|
|
|
private static String getPrefix() {
|
|
|
|
|
|
String arch = getArch();
|
|
|
|
|
|
//aarch64
|
|
|
|
|
|
String os = SystemUtil.getOsInfo().getName();
|
|
|
|
|
|
//Windows操作系统
|
|
|
|
|
|
if (os != null && os.toLowerCase().startsWith("windows")) {
|
|
|
|
|
|
os = "/windows/";
|
|
|
|
|
|
} else if (os != null && os.toLowerCase().startsWith("linux")) {//Linux操作系统
|
|
|
|
|
|
os = "/linux/";
|
|
|
|
|
|
} else { //其它操作系统
|
|
|
|
|
|
//安卓 乌班图等等,先不写
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
// "/seetaface6/windows/amd64"
|
|
|
|
|
|
return "/" + SEETAFACE_LIB_DIR + os + arch + "/";
|
|
|
|
|
|
}
|
2025-03-26 17:09:22 +08:00
|
|
|
|
|
2025-04-19 19:48:12 +08:00
|
|
|
|
private static String getArch() {
|
|
|
|
|
|
String arch = SystemUtil.getOsInfo().getArch().toLowerCase();
|
|
|
|
|
|
if (arch.startsWith("amd64")
|
|
|
|
|
|
|| arch.startsWith("x86_64")
|
|
|
|
|
|
|| arch.startsWith("x86-64")
|
|
|
|
|
|
|| arch.startsWith("x64")) {
|
|
|
|
|
|
arch = "amd64";
|
|
|
|
|
|
} else if (arch.contains("aarch")) {
|
|
|
|
|
|
arch = "aarch64";
|
|
|
|
|
|
} else if (arch.contains("arm")) {
|
|
|
|
|
|
arch = "arm";
|
2025-03-26 17:09:22 +08:00
|
|
|
|
}
|
2025-04-19 19:48:12 +08:00
|
|
|
|
return arch;
|
2025-03-26 17:09:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-04-19 19:48:12 +08:00
|
|
|
|
* 获取dll配置文件路径
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return String
|
2025-03-26 17:09:22 +08:00
|
|
|
|
*/
|
2025-04-19 19:48:12 +08:00
|
|
|
|
private static String getPropertiesPath() {
|
|
|
|
|
|
return getPrefix() + PROPERTIES_FILE_NAME;
|
2025-03-26 17:09:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-04-19 19:48:12 +08:00
|
|
|
|
* 拷贝依赖库到临时目录
|
|
|
|
|
|
* @param libPath
|
2025-03-26 17:09:22 +08:00
|
|
|
|
* @return
|
2025-04-19 19:48:12 +08:00
|
|
|
|
* @throws IOException
|
2025-03-26 17:09:22 +08:00
|
|
|
|
*/
|
2025-04-19 19:48:12 +08:00
|
|
|
|
private static File extractLibrary(String libPath) throws IOException {
|
|
|
|
|
|
String resourcePath = libPath;
|
2025-04-28 16:13:34 +08:00
|
|
|
|
try (InputStream in = NativeLoader.class.getResourceAsStream(resourcePath)) {
|
2025-04-19 19:48:12 +08:00
|
|
|
|
if (in == null) throw new FileNotFoundException(resourcePath);
|
|
|
|
|
|
Path path = Paths.get(resourcePath);
|
|
|
|
|
|
String fileName = path.getFileName().toString();
|
|
|
|
|
|
Path targetPath = seetaface6NativePath.resolve(fileName);
|
|
|
|
|
|
Files.copy(in, targetPath, StandardCopyOption.REPLACE_EXISTING);
|
|
|
|
|
|
log.info("copy target path success : {}", targetPath.toAbsolutePath().toString());
|
|
|
|
|
|
// 设置可执行权限
|
|
|
|
|
|
if (!SystemUtil.getOsInfo().getName().toLowerCase().contains("win")) {
|
|
|
|
|
|
targetPath.toFile().setExecutable(true);
|
2025-03-26 17:09:22 +08:00
|
|
|
|
}
|
2025-04-19 19:48:12 +08:00
|
|
|
|
return targetPath.toFile();
|
2025-03-26 17:09:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-04-19 19:48:12 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 将获得的配置进行排序 并生成路径
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param list
|
|
|
|
|
|
* @return List<String>
|
|
|
|
|
|
*/
|
|
|
|
|
|
private static List<String> getSortedPath(List<DllItem> list) {
|
|
|
|
|
|
return list.stream().sorted(Comparator.comparing(dllItem -> {
|
|
|
|
|
|
int i = dllItem.getKey().lastIndexOf(".") + 1;
|
|
|
|
|
|
String substring = dllItem.getKey().substring(i);
|
|
|
|
|
|
return Integer.valueOf(substring);
|
|
|
|
|
|
})).map(DllItem::getValue).collect(Collectors.toList());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-26 17:09:22 +08:00
|
|
|
|
}
|