net.gdface.cassdk.BaseJniBridge Maven / Gradle / Ivy
package net.gdface.cassdk;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.logging.Logger;
import net.gdface.utils.ResourcePool.IntResourcePool;
public abstract class BaseJniBridge {
protected static final Logger logger = Logger.getLogger(BaseJniBridge.class.getSimpleName());
protected static final String CASSDK_HOME = getCassdkHome();
protected static final int DEFAULT_SAMPLE_SIZE;
/**
* {@link FacePos#facialData} 面部数据长度
*/
public static final int FACEPOS_FACIALDATA_LEN = 512;
protected static final String INVALID_LICENSE = "invalid license";
protected static final String INVALID_CHANNEL = "nChannelID is invalid or SDK is not initialized";
protected static final String INVALID_IMAGE = "image data is invalid,please check function parameter:pImage,bpp,nWidth,nHeight";
protected static final String INVALID_FPS = "pfps or nMaxFaceNums is invalid";
protected static final String INVALID_PBUF = "pBuf,ptfp,pFeature is NULL";
/**
* 模型数据复制标记,用于保证只在初始化时被执行一次
*/
static boolean modelCopyed = false;
protected static IntResourcePool detectChannelPool;
protected static IntResourcePool featureChannelPool;
protected static int maxDetectFaceNum;
protected static int sampleSize;
/**
* 特征码长度常量
*/
public static int FEATURE_SIZE;
static {
DEFAULT_SAMPLE_SIZE = CasConfig.getSampleSize();
maxDetectFaceNum = CasConfig.getMaxDetectFaceNum();
sampleSize = DEFAULT_SAMPLE_SIZE;
}
/**
* 复制 CASSDK运行所需的模型数据到Java当前目录user.dir
* 必须定义 cassdk_home变量,用于指定CASSDK的目录位置
* 定义cassdk_home变量的方法
* 1. 定义环境变量CASSDK_HOME
* 2. java运行时必须用-D选项定义cassdk_home
* 优先在环境变量中查找CASSDK_HOME,如果找不到,再在系统变量定义中查找cassdk_home
* 如果都找不到则抛出异常
* 假定模型数据在${cassdk_home}/bin下
*/
protected static final synchronized void copyModelToUserDir() {
if(modelCopyed){
return;
}
File cassdkPath=new File(new File(CASSDK_HOME),"bin");
// 检查 "cassdk_home"的合法性
if(!cassdkPath.exists()||!cassdkPath.isDirectory()){
throw new ExceptionInInitializerError("NOT FOUND FOLDER:".concat(CASSDK_HOME));
}
// 根据操作系统类型确定文件复制的目标路径
File destDir;
if(System.getProperty("os.name").toLowerCase().startsWith("win")){
// Windows版本SDK的加载数据文件是可执行程序所在路径(java下即JVM程序)
final String javaLibraryPath = System.getProperty("java.library.path");
// 以java.library.path中的第一个路径为jvm所在位置
destDir = new File( javaLibraryPath.substring(0, javaLibraryPath.indexOf(';')));
if(!new File(destDir,"java.exe").exists()){
throw new ExceptionInInitializerError("CANT NOT locate JVM folder(无法定位JVM路径)");
}
logger.info(String.format("JVM 路径 %s",destDir.toString()));
}else{
// linux版本SDK的加载数据文件是用户当前路径
destDir=new File(System.getProperty("user.dir"));
}
logger.info(String.format("copy model files from %s to %s", cassdkPath.getAbsolutePath(),destDir.getAbsolutePath()));
try {
// 如果destDir与"cassdk_dir"相等则放弃复制
if(!destDir.getCanonicalPath().equals(cassdkPath.getCanonicalPath())){
for(String file:CasConfig.getVersion().getModelFiles()){
File facedb = new File(cassdkPath,file);
if(needCopy(facedb,destDir)){
try{
logger.info(String.format("copy file %s", file));
copyAndDeleteOnExit(facedb,destDir);
}catch (RuntimeException e) {
if(e.getCause() instanceof IOException){
throw new ExceptionInInitializerError(
String.format("无法从\n%s\n复制模型文件(%s)到\n%s\n请手工复制\n因为:%s",
cassdkPath.toString(),
file,
destDir.toString(),
e.getMessage()));
}
throw e;
}
}
}
}
modelCopyed=true;
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
/**
* 查找 cassdk_home变量,优先在环境变量中查找CASSDK_HOME,如果找不到,再在系统变量定义中查找cassdk_home
* 如果都找不到则抛出异常
* @return
*/
static final String getCassdkHome() {
String cassdkHome=System.getenv("CASSDK_HOME");
if(null!=cassdkHome){
logger.info(String.format("environment variable CASSDK_HOME=%s", cassdkHome));
}else{
if(null==(cassdkHome=System.getProperty("cassdk_home"))){
throw new ExceptionInInitializerError("UNDEFINED cassdk_home,you can defined the variable by java -Dcassdk_home= OR set environment variable 'CASSDK_HOME'");
}
logger.info(String.format("cassdk_home=%s,defined by java -D=", cassdkHome));
}
return cassdkHome;
}
/**
* 递归判断文件/文件夹是否需要复制到目标文件夹
* 判断条件:目标不存在或大小不相等,源不存在则返回false
* @param src 原文件/文件夹
* @param dstFolder 目标文件夹
*/
static final boolean needCopy(final File src, final File dstFolder) {
if(null==src||null==dstFolder){
throw new NullPointerException("src or dstFolder is null");
}
if(!src.exists()){
return false;
}
if(!dstFolder.exists()){
return true;
}
if(src.isDirectory()){
File[] noeq = src.listFiles(new FileFilter(){
@Override
public boolean accept(File pathname) {
File folder = new File(dstFolder,src.getName());
return needCopy(pathname,folder);
}});
return noeq.length>0;
}else{
File dst=new File(dstFolder,src.getName());
return (!dst.isFile())||(dst.length()!=src.length());
}
}
/**
* 递归复制文件/文件夹到指定的目录,并且在JVM结束时删除
* @param src 原文件/文件夹
* @param dstFolder 目标文件夹
* @throws RuntimeException {@link IOException}被封装成 {@link RuntimeException}抛出
*/
static final void copyAndDeleteOnExit(final File src, final File dstFolder) throws RuntimeException {
if(null==src||null==dstFolder){
throw new NullPointerException("src or dstFolder is null");
}
if(!src.exists()){
return;
}
if(src.isDirectory()){
src.listFiles(new FileFilter(){
@Override
public boolean accept(File pathname) {
File folder = new File(dstFolder,src.getName());
folder.deleteOnExit(); // JVM结束时删除指定文件夹
copyAndDeleteOnExit(pathname,folder);
return false;
}});
}else{
try {
File dst=new File(dstFolder,src.getName());
nioCopyFile(src,dst);
// JVM结束时删除文件
dst.deleteOnExit();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
/**
* NIO方式复制文件
* 目标文件所在的文件夹如果不存在自动创建文件夹
* @param src 源文件
* @param dst 目标文件
* @throws IOException
*/
static final void nioCopyFile(File src, File dst) throws IOException {
if(null==src||null==dst){
throw new NullPointerException("src or dst is null");
}
if(!src.exists()||!src.isFile()){
throw new IllegalArgumentException(String.format("INVALID FIILE NAME(无效文件名) src=%s",src.getCanonicalPath()));
}
if (dst.exists() &&!dst.isFile()) {
throw new IllegalArgumentException(String.format("INVALID FIILE NAME(无效文件名) dst=%s",dst.getCanonicalPath()));
}
File folder = dst.getParentFile();
if (!folder.exists()){
folder.mkdirs();
}
if(((src.length()+(1<<10)-1)>>10)>(folder.getFreeSpace()>>10)){
throw new IOException(String.format("DISK ALMOST FULL(磁盘空间不足) %s",folder.getCanonicalPath()));
}
FileInputStream fin=null;
FileOutputStream fout = null;
FileChannel fic = null;
FileChannel foc = null;
try {
fin=new FileInputStream(src);
fout = new FileOutputStream(dst);
fic = fin.getChannel();
foc = fout.getChannel();
// 1MB缓冲区
ByteBuffer bb = ByteBuffer.allocate(1024<<10);
while(fic.read(bb)>0){
bb.flip();
foc.write(bb);
bb.clear();
}
} finally {
// 安全释放资源
if(null!=fic){
fic.close();
}
if(null!=foc){
foc.close();
}
if(null!=fin){
fin.close();
}
if(null!=fout){
fout.close();
}
}
}
/**
*
* 设置最大可检测人脸的数目,默认为256
* 用于新线程初始化人脸检测结果数据缓冲区大小
* 此值为全局变量,对在此方法调用之后新建的线程有效
* @param maxNum 必须>0,否则抛出异常
* @throws IllegalArgumentException 当maxNum<=0
*/
public static synchronized void setMaxDetectFaceNum(int maxNum) {
if(maxNum <= 0){
throw new IllegalArgumentException(String.format("Invalid maxNum:%d",maxNum));
}
maxDetectFaceNum = maxNum;
}
public static int getMaxDetectFaceNum() {
return maxDetectFaceNum;
}
public static int getSampleSize() {
return sampleSize;
}
/**
* 设置人脸检测时的图像归一化尺寸
* @param sampleSize 为0使用SDK提供的默认尺寸(参见SDK中THFI_DetectFace函数定义),<0则抛出异常
* @throws IllegalArgumentException sampleSize<0
*/
public static void setSampleSize(int sampleSize) {
if(sampleSize < 0){
throw new IllegalArgumentException(String.format("Invalid sampleSize:%d",sampleSize));
}
BaseJniBridge.sampleSize = sampleSize;
}
protected BaseJniBridge() {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy