Full Code of xikuqi/OpenCV for AI

master d21521c3b556 cached
29 files
52.3 KB
14.8k tokens
104 symbols
1 requests
Download .txt
Repository: xikuqi/OpenCV
Branch: master
Commit: d21521c3b556
Files: 29
Total size: 52.3 KB

Directory structure:
gitextract_yuq3c2t4/

├── LICENSE
├── README.md
├── a.txt
├── build.pom
├── doc/
│   └── lib-linux-x64.tar.bz2
├── pom.xml
├── seetafaceJNI-1.1.jar
├── seetafaceJNI-2.0.jar
├── seetafaceJNI-3.0.jar
└── src/
    ├── main/
    │   ├── java/
    │   │   └── com/
    │   │       ├── cnsugar/
    │   │       │   ├── ai/
    │   │       │   │   └── face/
    │   │       │   │       ├── FaceHelper.java
    │   │       │   │       ├── SeetafaceBuilder.java
    │   │       │   │       ├── bean/
    │   │       │   │       │   ├── FaceIndex.java
    │   │       │   │       │   └── Result.java
    │   │       │   │       ├── dao/
    │   │       │   │       │   └── FaceDao.java
    │   │       │   │       └── utils/
    │   │       │   │           └── ImageUtils.java
    │   │       │   └── common/
    │   │       │       └── sqlite/
    │   │       │           ├── JdbcPool.java
    │   │       │           ├── JdbcPoolFactory.java
    │   │       │           ├── RowMapper.java
    │   │       │           └── SqliteUtils.java
    │   │       └── seetaface2/
    │   │           ├── SeetaFace2JNI.java
    │   │           └── model/
    │   │               ├── FaceLandmark.java
    │   │               ├── RecognizeResult.java
    │   │               ├── RegisterData.java
    │   │               ├── SeetaImageData.java
    │   │               ├── SeetaPointF.java
    │   │               └── SeetaRect.java
    │   └── resources/
    │       ├── log4j2.xml
    │       └── seetaface.properties
    └── test/
        └── java/
            └── Test.java

================================================
FILE CONTENTS
================================================

================================================
FILE: LICENSE
================================================
BSD 2-Clause License

Copyright (c) 2019, cnsugar
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: README.md
================================================
# seetafaceJNI

#### 项目介绍
基于中科院seetaface2进行封装的JAVA人脸识别算法库,支持人脸识别、1:1比对、1:N比对。
seetaface2:https://github.com/seetaface/SeetaFaceEngine2

#### 环境配置
1、下载model( https://pan.baidu.com/s/1HJj8PEnv3SOu6ZxVpAHPXg ) 文件到本地,并解压出来;

2、下载doc目录中对应的lib包到本地并解压:Windows(64位)环境下载lib-win-x64.zip、Linux(64位)下载lib-linux-x64.tar.bz2,Linux环境还需要安装依赖库,详见:https://my.oschina.net/u/1580184/blog/3042404 ;

3、将doc中的faces-data.db下载到本地;(PS:如果不需要使用1:N人脸搜索,不需要此文件,需要将seetafce.properties中的sqlite.db.file配置注释掉);

4、将src/main/resources/中的seetaface.properties文件放到项目的resources根目录中;

```properties
#linux系统中依赖的lib名称
libs=holiday,SeetaFaceDetector200,SeetaPointDetector200,SeetaFaceRecognizer200,SeetaFaceCropper200,SeetaFace2JNI
#Windows系统中依赖的lib名称
#libs=libgcc_s_sjlj-1,libeay32,libquadmath-0,ssleay32,libgfortran-3,libopenblas,holiday,SeetaFaceDetector200,SeetaPointDetector200,SeetaFaceRecognizer200,SeetaFaceCropper200,SeetaFace2JNI

#lib存放目录
libs.path=/usr/local/seetaface2/lib
#model存放目录
bindata.dir=/usr/local/seetaface2/bindata

##sqlite配置(如果不用1:N人脸搜索功能,请删除下面5项sqlite开头的配置)
sqlite.db.file=/data/faces-data.db
sqlite.conn.maxTotal=50
sqlite.conn.maxIdle=5
sqlite.conn.minIdle=0
sqlite.conn.maxWaitMillis=60000
```


5、将seetafaceJNI-2.0.jar和依赖包导入到项目中,pom如下:

```xml
   <properties>
       <spring.version>4.2.8.RELEASE</spring.version>
       <log4j.version>2.8.2</log4j.version>
       <slf4j.version>1.7.25</slf4j.version>
   </properties>
  
   <dependencies>
       <dependency>
            <groupId>com.cnsugar.ai</groupId>
            <artifactId>seetafaceJNI</artifactId>
            <version>2.0</version>
            <!--<scope>system</scope>-->
            <!--<systemPath>${project.basedir}/lib/seetafaceJNI-2.0.jar</systemPath>-->
       </dependency>
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-core</artifactId>
           <version>${spring.version}</version>
       </dependency>
  
       <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-api</artifactId>
           <version>${slf4j.version}</version>
       </dependency>
  
       <!-- sqlite -->
       <dependency>
           <groupId>org.xerial</groupId>
           <artifactId>sqlite-jdbc</artifactId>
           <version>3.25.2</version>
       </dependency>
       <dependency>
           <groupId>org.apache.commons</groupId>
           <artifactId>commons-pool2</artifactId>
           <version>2.4.2</version>
       </dependency>
   </dependencies> 
```

6、调用FaceHelper中的方法。


#### 使用方法
所有方法都封装到了FaceHelper工具类中
```java
    /**
     * 人脸比对
     *
     * @param img1
     * @param img2
     * @return 相似度
     */
    float compare(File img1, File img2);
    float compare(byte[] img1, byte[] img2);
    float compare(BufferedImage image1, BufferedImage image2);
    
    /**
     * 注册人脸(会裁剪图片)
     *
     * @param key 人脸照片唯一标识
     * @param img 人脸照片
     * @return 
     */
    boolean register(String key, byte[] img);
    /**
     * 注册人脸(不裁剪图片)
     *
     * @param key 人脸照片唯一标识
     * @param image 人脸照片
     * @return 
     */
    boolean register(String key, BufferedImage image)
    
    /**
     * 搜索人脸
     *
     * @param img 人脸照片
     * @return
     */
    Result search(byte[] img);
    Result search(BufferedImage image);
    
    /**
     * 人脸提取(裁剪)
     *
     * @param img
     * @return return cropped face
     */
    BufferedImage crop(byte[] img);
    BufferedImage crop(BufferedImage image);
    
    /**
     * 人脸识别
     *
     * @param img
     * @return
     */
    SeetaRect[] detect(byte[] img);
    SeetaRect[] detect(BufferedImage image);

    /**
     * 人脸识别(包含5个特征点位置)
     *
     * @param image
     * @return
     */
    FaceLandmark detectLandmark(BufferedImage image);
    
    /**
     * 删除已注册的人脸
     * @param keys
     */
    void removeRegister(String... keys);  
    
    /**
     * 清除人脸库数据
     */
    void clear();    
    
```

- 示例代码:1:1人脸比对
```java
    @org.junit.Test
    public void testCompare() throws Exception {
        String img1 = "F:\\ai\\demo-pic39.jpg";
        String img2 = "F:\\ai\\left_pic_one.jpg";
        System.out.println("result:"+FaceHelper.compare(new File(img1), new File(img2)));
    }
```

- 示例代码:1:N人脸搜索
  先调用FaceHelper.register()方法将人脸图片注册到seetaface2的人脸库(内存)中,同时会将图片存在sqlite数据库中进行持久化,下次应用程序启动时会自动从sqlite中把图片读取出来重新注册到seetafce2的内存库中

```java
    @org.junit.Test
    public void testRegister() throws IOException {
        //将F:\ai\star目录下的jpg、png图片都注册到人脸库中,以文件名为key
        Collection<File> files = FileUtils.listFiles(new File("F:\\ai\\star"), new String[]{"jpg", "png"}, false);
        for (File file : files) {
            String key = file.getName();
            try {
                FaceHelper.register(key, FileUtils.readFileToByteArray(file));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @org.junit.Test
    public void testSearch() throws IOException {
        SeetafaceBuilder.build();//系统启动时先调用初始化方法

        //等待初始化完成
        while (SeetafaceBuilder.getFaceDbStatus() == SeetafaceBuilder.FacedbStatus.LOADING || SeetafaceBuilder.getFaceDbStatus() == SeetafaceBuilder.FacedbStatus.READY) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        long l = System.currentTimeMillis();
        Result result = FaceHelper.search(FileUtils.readFileToByteArray(new File("F:\\ai\\gtl.jpg")));
        System.out.println("搜索结果:" + result + ", 耗时:" + (System.currentTimeMillis() - l));
    }
```

================================================
FILE: a.txt
================================================
111


================================================
FILE: build.pom
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         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>

    <groupId>com.cnsugar.ai</groupId>
    <artifactId>seetafaceJNI</artifactId>
    <version>3.0</version>

    <properties>
        <spring.version>4.2.8.RELEASE</spring.version>
        <log4j.version>2.8.2</log4j.version>
        <slf4j.version>1.7.25</slf4j.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

        <!-- sqlite -->
        <dependency>
            <groupId>org.xerial</groupId>
            <artifactId>sqlite-jdbc</artifactId>
            <version>3.25.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.4.2</version>
        </dependency>
    </dependencies>
</project>

================================================
FILE: pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         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>

    <groupId>com.cnsugar.ai</groupId>
    <artifactId>seetafaceJNI</artifactId>
    <version>3.0</version>

    <properties>
        <spring.version>4.2.8.RELEASE</spring.version>
        <log4j.version>2.8.2</log4j.version>
        <slf4j.version>1.7.25</slf4j.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <!-- log4j2 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <!--  log4j to slf4j bridge -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>${log4j.version}</version>
        </dependency>

        <!-- sqlite -->
        <dependency>
            <groupId>org.xerial</groupId>
            <artifactId>sqlite-jdbc</artifactId>
            <version>3.25.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.4.2</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13-beta-1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>${artifactId}-${version}</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <includes>
                        <!-- 打jar包时,只打包class文件 -->
                        <include>**/*.class</include>
                    </includes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

================================================
FILE: src/main/java/com/cnsugar/ai/face/FaceHelper.java
================================================
package com.cnsugar.ai.face;

import com.cnsugar.ai.face.bean.FaceIndex;
import com.cnsugar.ai.face.bean.Result;
import com.cnsugar.ai.face.dao.FaceDao;
import com.cnsugar.ai.face.utils.ImageUtils;
import com.seetaface2.SeetaFace2JNI;
import com.seetaface2.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;

/**
 * @Author Sugar
 * @Version 2019/4/22 15:56
 */
public class FaceHelper {
    private static Logger logger = LoggerFactory.getLogger(FaceHelper.class);

    private static int CROP_SIZE = 256 * 256 * 3;

    private static SeetaFace2JNI seeta = SeetafaceBuilder.build();

    /**
     * 人脸比对
     *
     * @param img1
     * @param img2
     * @return 相似度
     * @throws IOException
     */
    public static float compare(File img1, File img2) throws IOException {
        BufferedImage image1 = ImageIO.read(img1);
        BufferedImage image2 = ImageIO.read(img2);
        return compare(image1, image2);
    }

    /**
     * 人脸比对
     *
     * @param img1
     * @param img2
     * @return 相似度
     */
    public static float compare(byte[] img1, byte[] img2) throws IOException {
        BufferedImage image1 = ImageIO.read(new ByteArrayInputStream(img1));
        BufferedImage image2 = ImageIO.read(new ByteArrayInputStream(img2));
        return compare(image1, image2);
    }

    /**
     * 人脸比对
     *
     * @param image1
     * @param image2
     * @return 相似度
     */
    public static float compare(BufferedImage image1, BufferedImage image2) {
        if (image1 == null || image2 == null) {
            return 0;
        }
        SeetaImageData imageData1 = new SeetaImageData(image1.getWidth(), image1.getHeight(), 3);
        imageData1.data = ImageUtils.getMatrixBGR(image1);

        SeetaImageData imageData2 = new SeetaImageData(image2.getWidth(), image2.getHeight(), 3);
        imageData2.data = ImageUtils.getMatrixBGR(image2);

        return seeta.compare(imageData1, imageData2);
    }

    /**
     * 注册人脸(会对人脸进行裁剪)
     *
     * @param key 人脸照片唯一标识
     * @param img 人脸照片
     * @return
     * @throws IOException
     */
    public static boolean register(String key, byte[] img) throws IOException {
        BufferedImage image = ImageIO.read(new ByteArrayInputStream(img));
        //先对人脸进行裁剪
        SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
        imageData.data = ImageUtils.getMatrixBGR(image);
        byte[] bytes = seeta.crop(imageData);

        if (bytes == null || bytes.length != CROP_SIZE) {
            logger.info("register face fail: key={}, error=no valid face", key);
            return false;
        }
        imageData = new SeetaImageData(256, 256, 3);
        imageData.data = bytes;
        int index = seeta.register(imageData);
        if (index < 0) {
            logger.info("register face fail: key={}, index={}", key, index);
            return false;
        }
        FaceIndex face = new FaceIndex();
        face.setKey(key);
        face.setImgData(imageData.data);
        face.setWidth(imageData.width);
        face.setHeight(imageData.height);
        face.setChannel(imageData.channels);
        face.setIndex(index);
        FaceDao.saveOrUpdate(face);
        logger.info("Register face success: key={}, index={}", key, index);
        return true;
    }

    /**
     * 注册人脸(不裁剪图片)
     *
     * @param key 人脸照片唯一标识
     * @param image 人脸照片
     * @return
     * @throws IOException
     */
    public static boolean register(String key, BufferedImage image) throws IOException {
        SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
        imageData.data = ImageUtils.getMatrixBGR(image);
        int index = seeta.register(imageData);
        if (index < 0) {
            logger.info("register face fail: key={}, index={}", key, index);
            return false;
        }
        FaceIndex face = new FaceIndex();
        face.setKey(key);
        face.setImgData(imageData.data);
        face.setWidth(imageData.width);
        face.setHeight(imageData.height);
        face.setChannel(imageData.channels);
        FaceDao.saveOrUpdate(face);
        face.setIndex(index);
        logger.info("Register face success: key={}, index={}", key, index);
        return true;
    }

    /**
     * 搜索人脸
     *
     * @param img 人脸照片
     * @return
     * @throws IOException
     */
    public static Result search(byte[] img) throws IOException {
        BufferedImage image = ImageIO.read(new ByteArrayInputStream(img));
        return search(image);
    }

    /**
     * 搜索人脸
     *
     * @param image 人脸照片
     * @return
     * @throws IOException
     */
    public static Result search(BufferedImage image) {
        if (image == null) {
            return null;
        }
        SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
        imageData.data = ImageUtils.getMatrixBGR(image);
        RecognizeResult rr = seeta.recognize(imageData);

        if (rr == null || rr.index == -1) {
            return null;
        }
        Result result = new Result(rr);
        result.setKey(FaceDao.findKeyByIndex(rr.index));
        return result;
    }

    /**
     * 人脸提取(裁剪)
     *
     * @param img
     * @return return cropped face
     */
    public static BufferedImage crop(byte[] img) throws IOException {
        BufferedImage image = ImageIO.read(new ByteArrayInputStream(img));
        return crop(image);
    }

    /**
     * 人脸提取(裁剪)
     *
     * @param image
     * @return return cropped face
     */
    public static BufferedImage crop(BufferedImage image) {
        if (image == null) {
            return null;
        }
        SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
        imageData.data = ImageUtils.getMatrixBGR(image);
        byte[] bytes = seeta.crop(imageData);
        if (bytes == null || bytes.length != CROP_SIZE) {
            return null;
        }
        return ImageUtils.bgrToBufferedImage(bytes, 256,256);
    }

    /**
     * 人脸识别
     *
     * @param img
     * @return
     */
    public static SeetaRect[] detect(byte[] img) throws IOException {
        BufferedImage image = ImageIO.read(new ByteArrayInputStream(img));
        return detect(image);
    }

    /**
     * 人脸识别
     *
     * @param image
     * @return
     */
    public static SeetaRect[] detect(BufferedImage image) {
        if (image == null) {
            return null;
        }
        SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
        imageData.data = ImageUtils.getMatrixBGR(image);
        return seeta.detect(imageData);
    }

    /**
     * 人脸特征识别
     *
     * @param image
     * @return
     */
    public static FaceLandmark detectLandmark(BufferedImage image) {
        if (image == null) {
            return null;
        }
        SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
        imageData.data = ImageUtils.getMatrixBGR(image);
        SeetaRect[] rects = seeta.detect(imageData);
        if (rects == null) {
            return null;
        }
        FaceLandmark faces = new FaceLandmark();
        faces.rects = rects;
        faces.points = seeta.detect(imageData, rects);
        return faces;
    }

    /**
     * 删除已注册的人脸
     * @param keys
     */
    public static void removeRegister(String... keys) {
        FaceDao.deleteFaceImg(keys);//删除数据库的人脸
        SeetafaceBuilder.buildIndex();//重新建立索引
    }

    /**
     * 清除人脸库数据
     */
    public static void clear() {
        FaceDao.clearImg();
        FaceDao.clearIndex();
        seeta.clear();
    }
}


================================================
FILE: src/main/java/com/cnsugar/ai/face/SeetafaceBuilder.java
================================================
package com.cnsugar.ai.face;

import com.cnsugar.ai.face.bean.FaceIndex;
import com.cnsugar.ai.face.dao.FaceDao;
import com.cnsugar.common.sqlite.JdbcPool;
import com.seetaface2.SeetaFace2JNI;
import com.seetaface2.model.SeetaImageData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.DefaultResourceLoader;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Properties;

/**
 * @Author Sugar
 * @Version 2019/4/22 14:28
 */
public class SeetafaceBuilder {
    private static Logger logger = LoggerFactory.getLogger(SeetafaceBuilder.class);
    private volatile static SeetaFace2JNI seeta = null;

    public enum FacedbStatus {
        READY, LOADING, OK, INACTIV;
    }

    private volatile static FacedbStatus face_db_status = FacedbStatus.READY;

    public static SeetaFace2JNI build() {
        if (seeta == null) {
            synchronized (SeetafaceBuilder.class) {
                if (seeta != null) {
                    return seeta;
                }
                init();
            }
        }
        return seeta;
    }

    /**
     * 建立人脸库索引
     */
    public static void buildIndex() {
        synchronized (SeetafaceBuilder.class) {
            while (face_db_status == FacedbStatus.LOADING || face_db_status == FacedbStatus.READY) {
                //等待之前的任务初始化完成
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                }
            }
            face_db_status = FacedbStatus.READY;
            new Thread(() -> {
                seeta.clear();
                loadFaceDb();
            }).start();
        }
    }

    /**
     * 返回人脸数据库状态
     * @return
     */
    public static FacedbStatus getFaceDbStatus() {
        return face_db_status;
    }

    private static void init() {
        Properties prop = getConfig();
        String separator = System.getProperty("path.separator");
        String sysLib = System.getProperty("java.library.path");
        if (sysLib.endsWith(separator)) {
            System.setProperty("java.library.path", sysLib + prop.getProperty("libs.path", ""));
        } else {
            System.setProperty("java.library.path", sysLib + separator + prop.getProperty("libs.path", ""));
        }
        try {//使java.library.path生效
            Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
            sysPathsField.setAccessible(true);
            sysPathsField.set(null, null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }

        String[] libs = prop.getProperty("libs", "").split(",");
        for (String lib : libs) {
            logger.debug("load library: {}", lib);
            System.loadLibrary(lib);
        }
        String bindata = prop.getProperty("bindata.dir");
        logger.debug("bindata dir: {}", bindata);
        seeta = new SeetaFace2JNI();
        seeta.initModel(bindata);
        String db_file = prop.getProperty("sqlite.db.file");
        if (db_file != null) {
            System.setProperty("seetaface.db", db_file);
            System.setProperty(JdbcPool.MAX_TOTAL, prop.getProperty(JdbcPool.MAX_TOTAL));
            System.setProperty(JdbcPool.MAX_IDLE, prop.getProperty(JdbcPool.MAX_IDLE));
            System.setProperty(JdbcPool.MIN_IDLE, prop.getProperty(JdbcPool.MIN_IDLE));
            System.setProperty(JdbcPool.MAX_WAIT_MILLIS, prop.getProperty(JdbcPool.MAX_WAIT_MILLIS));

            new Thread(() -> loadFaceDb()).start();
        } else {
            face_db_status = FacedbStatus.INACTIV;
            logger.warn("没有配置sqlite.db.file,人脸注册(register)及人脸搜索(1 v N)功能将无法使用!!!");
        }
        logger.info("Seetaface init completed!!!");
    }

    /**
     * 加载人脸库
     */
    private static void loadFaceDb() {
        if (face_db_status != FacedbStatus.READY) {
            return;
        }

        if (System.getProperty("seetaface.db") == null) {
            face_db_status = FacedbStatus.INACTIV;
            logger.error("没有配置sqlite.db.file!!!");
            return;
        }

        face_db_status = FacedbStatus.LOADING;
        logger.info("load face data...");
        FaceDao.clearIndex();
        int pageNo = 0, pageSize = 100;
        while (true) {
            List<FaceIndex> list = FaceDao.findFaceImgs(pageNo, pageSize);
            if (list == null) {
                break;
            }
            list.forEach(face -> {
                try {
                    register(face.getKey(), face);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            if (list.size() < pageSize) {
                break;
            }
            pageNo++;
        }
        face_db_status = FacedbStatus.OK;
    }

    /**
     * 将历史注册过的所有人脸重新加载到内存库中
     *
     * @param key  人脸照片唯一标识
     * @param face 人脸照片
     * @return
     * @throws IOException
     */
    private static void register(String key, FaceIndex face) {
        SeetaImageData imageData = new SeetaImageData(face.getWidth(), face.getHeight(), face.getChannel());
        imageData.data = face.getImgData();
        int index = seeta.register(imageData);
        if (index < 0) {
            logger.info("Register face fail: key={}, index={}", key, index);
            return;
        }
        FaceIndex faceIndex = new FaceIndex();
        faceIndex.setKey(key);
        faceIndex.setIndex(index);
        FaceDao.saveOrUpdateIndex(faceIndex);
        logger.info("Register face success: key={}, index={}", key, index);
    }

    private static Properties getConfig() {
        Properties properties = new Properties();
        String location = "classpath:/seetaface.properties";
        try (InputStream is = new DefaultResourceLoader().getResource(location).getInputStream()) {
            properties.load(is);
            logger.debug("seetaface config: {}", properties.toString());
        } catch (IOException ex) {
            logger.error("Could not load property file:" + location, ex);
        }
        return properties;
    }
}


================================================
FILE: src/main/java/com/cnsugar/ai/face/bean/FaceIndex.java
================================================
package com.cnsugar.ai.face.bean;

import java.io.Serializable;

/**
 * @Author Sugar
 * @Version 2019/4/22 17:14
 */
public class FaceIndex implements Serializable {
    private String key;

    private int index;

    private byte[] imgData;
    private int width;
    private int height;
    private int channel;

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public byte[] getImgData() {
        return imgData;
    }

    public void setImgData(byte[] imgData) {
        this.imgData = imgData;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getChannel() {
        return channel;
    }

    public void setChannel(int channel) {
        this.channel = channel;
    }
}


================================================
FILE: src/main/java/com/cnsugar/ai/face/bean/Result.java
================================================
package com.cnsugar.ai.face.bean;

import com.seetaface2.model.RecognizeResult;

import java.io.Serializable;

/**
 * @Author Sugar
 * @Version 2019/4/22 17:50
 */
public class Result implements Serializable {
    private String key;
    private float similar;

    public Result() {

    }

    public Result(RecognizeResult result) {
        this.similar = result.similar;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public float getSimilar() {
        return similar;
    }

    public void setSimilar(float similar) {
        this.similar = similar;
    }

    @Override
    public String toString() {
        return "{" +
                "" + key +
                ": " + similar +
                '}';
    }
}


================================================
FILE: src/main/java/com/cnsugar/ai/face/dao/FaceDao.java
================================================
package com.cnsugar.ai.face.dao;

import com.cnsugar.ai.face.bean.FaceIndex;
import com.cnsugar.common.sqlite.RowMapper;
import com.cnsugar.common.sqlite.SqliteUtils;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

/**
 * @Author Sugar
 * @Version 2019/4/22 17:14
 */
public class FaceDao {
    public static String findKeyByIndex(int index) {
        try {
            return SqliteUtils.queryForString("select \"key\" from face_index where \"index\"=" + index);
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static boolean saveOrUpdate(FaceIndex index) {
        try {
            SqliteUtils.executeUpdate("INSERT OR REPLACE INTO face_img (\"key\",\"img_data\",\"width\",\"height\",\"channel\") VALUES (?,?,?,?,?)", new Object[]{index
                    .getKey(), index.getImgData(), index.getWidth(), index.getHeight(), index.getChannel()});
            SqliteUtils.executeUpdate("INSERT OR REPLACE INTO face_index (\"index\",\"key\") VALUES (?,?)", new Object[]{index
                    .getIndex(), index.getKey()});
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }

    public static boolean saveOrUpdateIndex(FaceIndex index) {
        try {
            SqliteUtils.executeUpdate("INSERT OR REPLACE INTO face_index (\"index\",\"key\") VALUES (?,?)", new Object[]{index
                    .getIndex(), index.getKey()});
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }

    public static boolean clearIndex() {
        try {
            SqliteUtils.executeUpdate("DELETE FROM face_index");
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }

    public static List<FaceIndex> findFaceImgs(int pageNo, int pageSize) {
        String sql = "select \"key\",\"img_data\",\"width\",\"height\",\"channel\" from face_img " +
                " limit " + pageNo * pageSize + "," + pageSize;
        try {
            return SqliteUtils.queryForList(sql, new RowMapper<FaceIndex>() {
                @Override
                public FaceIndex mapRow(ResultSet rs) throws SQLException {
                    FaceIndex face = new FaceIndex();
                    face.setKey(rs.getString("key"));
                    face.setImgData(rs.getBytes("img_data"));
                    face.setWidth(rs.getInt("width"));
                    face.setHeight(rs.getInt("height"));
                    face.setChannel(rs.getInt("channel"));
                    return face;
                }
            });
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static boolean deleteFaceImg(String... keys) {
        StringBuilder in = new StringBuilder();
        for (int i = 0; i < keys.length; i++) {
            if (i > 0) {
                in.append(",");
            }
            in.append("?");
        }
        try {
            SqliteUtils.executeUpdate("DELETE FROM face_img where key in ("+in+")", keys);
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }

    public static boolean clearImg() {
        try {
            SqliteUtils.executeUpdate("DELETE FROM face_img");
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }
}


================================================
FILE: src/main/java/com/cnsugar/ai/face/utils/ImageUtils.java
================================================
package com.cnsugar.ai.face.utils;

//import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
//import java.awt.image.ColorConvertOp;
import java.awt.image.ComponentSampleModel;
import java.util.Arrays;

/**
 * @Author Sugar
 * @Version 2019/4/4 16:05
 */
public class ImageUtils {
    /**
     * @param image
     * @param bandOffset 用于推断通道顺序
     * @return
     */
    private static boolean equalBandOffsetWith3Byte(BufferedImage image, int[] bandOffset) {
        if (image.getType() == BufferedImage.TYPE_3BYTE_BGR) {
            if (image.getData().getSampleModel() instanceof ComponentSampleModel) {
                ComponentSampleModel sampleModel = (ComponentSampleModel) image.getData().getSampleModel();
                if (Arrays.equals(sampleModel.getBandOffsets(), bandOffset)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 推断图像是否为BGR格式
     *
     * @return
     */
    public static boolean isBGR3Byte(BufferedImage image) {
        return equalBandOffsetWith3Byte(image, new int[]{0, 1, 2});
    }

    /**
     * 对图像解码返回BGR格式矩阵数据
     *
     * @param image
     * @return
     */
    public static byte[] getMatrixBGR(BufferedImage image) {
        byte[] matrixBGR;
        if (isBGR3Byte(image)) {
            matrixBGR = (byte[]) image.getData().getDataElements(0, 0, image.getWidth(), image.getHeight(), null);
        } else {
            // ARGB格式图像数据
            int intrgb[] = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
            matrixBGR = new byte[image.getWidth() * image.getHeight() * 3];
            // ARGB转BGR格式
            for (int i = 0, j = 0; i < intrgb.length; ++i, j += 3) {
                matrixBGR[j] = (byte) (intrgb[i] & 0xff);
                matrixBGR[j + 1] = (byte) ((intrgb[i] >> 8) & 0xff);
                matrixBGR[j + 2] = (byte) ((intrgb[i] >> 16) & 0xff);
            }
        }
        return matrixBGR;
    }

    /**
     * 判断图像是否为RGB格式
     *
     * @return
     */
//    public static boolean isRGB3Byte(BufferedImage image) {
//        return equalBandOffsetWith3Byte(image, new int[]{2, 1, 0});
//    }

    /**
     *   * 对图像解码返回RGB格式矩阵数据
     *   * @param image
     *   * @return
     *   
     */
//    public static byte[] getMatrixRGB(BufferedImage image) {
//        byte[] matrixRGB;
//        if (isRGB3Byte(image)) {
//            matrixRGB = (byte[]) image.getData().getDataElements(0, 0, image.getWidth(), image.getHeight(), null);
//        } else {
//            // 转RGB格式
//            BufferedImage rgbImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
//            new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_sRGB), null).filter(image, rgbImage);
//            matrixRGB = (byte[]) rgbImage.getData().getDataElements(0, 0, image.getWidth(), image.getHeight(), null);
//        }
//        return matrixRGB;
//    }

    public static BufferedImage bgrToBufferedImage(byte[] data, int width, int height) {
        int type = BufferedImage.TYPE_3BYTE_BGR;
        // bgr to rgb
        byte b;
        for (int i = 0; i < data.length; i = i + 3) {
            b = data[i];
            data[i] = data[i + 2];
            data[i + 2] = b;
        }
        BufferedImage image = new BufferedImage(width, height, type);
        image.getRaster().setDataElements(0, 0, width, height, data);
        return image;
    }

    /**
     * 裁剪图片方法
     *
     * @param bufferedImage 图像源
     * @param startX        裁剪开始x坐标
     * @param startY        裁剪开始y坐标
     * @param endX          裁剪结束x坐标
     * @param endY          裁剪结束y坐标
     * @return
     */
//    public static BufferedImage cropImage(BufferedImage bufferedImage, int startX, int startY, int endX, int endY) {
//        int width = bufferedImage.getWidth();
//        int height = bufferedImage.getHeight();
//        if (startX == -1) {
//            startX = 0;
//        }
//        if (startY == -1) {
//            startY = 0;
//        }
//        if (endX == -1) {
//            endX = width - 1;
//        }
//        if (endY == -1) {
//            endY = height - 1;
//        }
//        BufferedImage result = new BufferedImage(endX - startX, endY - startY, 4);
//        for (int x = startX; x < endX; ++x) {
//            for (int y = startY; y < endY; ++y) {
//                int rgb = bufferedImage.getRGB(x, y);
//                result.setRGB(x - startX, y - startY, rgb);
//            }
//        }
//        return result;
//    }
}


================================================
FILE: src/main/java/com/cnsugar/common/sqlite/JdbcPool.java
================================================
package com.cnsugar.common.sqlite;

import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @Author Sugar
 * @Version 2019/4/23 17:11
 */
public class JdbcPool {
    private volatile static JdbcPool pool;
    public static final String MAX_TOTAL = "sqlite.conn.maxTotal";
    public static final String MAX_IDLE = "sqlite.conn.maxIdle";
    public static final String MIN_IDLE = "sqlite.conn.minIdle";
    public static final String MAX_WAIT_MILLIS = "sqlite.conn.maxWaitMillis";

    public static JdbcPool getInstance() {
        if (pool == null) {
            synchronized (JdbcPool.class) {
                if (pool == null) {
                    pool = new JdbcPool();
                }
            }
        }
        return pool;
    }

    private static GenericObjectPool<Connection> connPool;

    private JdbcPool() {
        connPool = new GenericObjectPool<Connection>(new JdbcPoolFactory(), getDefaultConfig());
    }

    private GenericObjectPoolConfig getDefaultConfig() {
        GenericObjectPoolConfig conf = new GenericObjectPoolConfig();
        conf.setMaxTotal(Integer.parseInt(System.getProperty(MAX_TOTAL, "100")));
        conf.setMaxIdle(Integer.parseInt(System.getProperty(MAX_IDLE, "0")));
        conf.setMinIdle(Integer.parseInt(System.getProperty(MIN_IDLE, "0")));
        conf.setMaxWaitMillis(Integer.parseInt(System.getProperty(MAX_WAIT_MILLIS, "60000")));
        return conf;
    }

    public Connection getConnection() {
        try {
            return connPool.borrowObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void close(PreparedStatement ps, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
            }
        }
        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException e) {
            }
        }
    }

    public static void returnConnection(Connection conn) {
        connPool.returnObject(conn);
    }

    public static void returnConnectionAndClose(Connection conn, PreparedStatement ps, ResultSet rs) {
        close(ps, rs);
        returnConnection(conn);
    }
}


================================================
FILE: src/main/java/com/cnsugar/common/sqlite/JdbcPoolFactory.java
================================================
package com.cnsugar.common.sqlite;

import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.DriverManager;

/**
 * @Author Sugar
 * @Version 2019/4/23 17:16
 */
public class JdbcPoolFactory extends BasePooledObjectFactory<Connection> {
    private static final Logger logger = LoggerFactory.getLogger(JdbcPoolFactory.class);

    private static final String DRIVER = "org.sqlite.JDBC";
    public static final String DB_NAME = "seetaface.db";

    private static String URL;

    static {
        String db = System.getProperty(DB_NAME);
        if (db == null || db.isEmpty()) {
            logger.error("System.getProperty(\"{}\") is null", DB_NAME);
        } else {
            URL = "jdbc:sqlite:" + db;
            logger.info(URL);
        }
        try {
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Connection create() throws Exception {
        if (URL == null) {
            return null;
        }
        return DriverManager.getConnection(URL);
    }

    @Override
    public PooledObject<Connection> wrap(Connection conn) {
        return new DefaultPooledObject<Connection>(conn);
    }
}


================================================
FILE: src/main/java/com/cnsugar/common/sqlite/RowMapper.java
================================================
package com.cnsugar.common.sqlite;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @Author Sugar
 * @Version 2019/4/23 17:46
 */
public interface RowMapper<T> {

    /**
     * Implementations must implement this method to map each row of data
     * in the ResultSet. This method should not call {@code next()} on
     * the ResultSet; it is only supposed to map values of the current row.
     *
     * @param rs     the ResultSet to map (pre-initialized for the current row)
     * @return the result object for the current row
     * @throws SQLException if a SQLException is encountered getting
     *                      column values (that is, there's no need to catch SQLException)
     */
    T mapRow(ResultSet rs) throws SQLException;

}


================================================
FILE: src/main/java/com/cnsugar/common/sqlite/SqliteUtils.java
================================================
package com.cnsugar.common.sqlite;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @Author Sugar
 * @Version 2019/4/23 16:38
 */
public class SqliteUtils {
    private static final Logger logger = LoggerFactory.getLogger(SqliteUtils.class);

    /**
     * 执行sql查询
     *
     * @param sql select 语句
     * @return 查询结果
     * @throws SQLException
     */
    public static String queryForString(String sql) throws SQLException {
        Connection conn = JdbcPool.getInstance().getConnection();
        try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql);) {
            if (rs.next()) {
                return rs.getString(1);
            }
            return null;
        } finally {
            JdbcPool.returnConnection(conn);
        }
    }

    /**
     * 执行select查询,返回对象
     *
     * @param sql    select 语句
     * @param mapper 结果集的行数据处理类对象
     * @return
     * @throws SQLException
     */
    public static <T> T queryForObject(String sql, RowMapper<T> mapper) throws SQLException {
        Connection conn = JdbcPool.getInstance().getConnection();
        try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql);) {
            if (rs.next()) {
                return mapper.mapRow(rs);
            }
        } finally {
            JdbcPool.returnConnection(conn);
        }
        return null;
    }

    /**
     * 执行select查询,返回结果列表
     *
     * @param sql    select 语句
     * @param mapper 结果集的行数据处理类对象
     * @return
     * @throws SQLException
     */
    public static <T> List<T> queryForList(String sql, RowMapper<T> mapper) throws SQLException {
        List<T> list = new ArrayList<T>();
        logger.debug("queryForList: sql={}", sql);
        Connection conn = JdbcPool.getInstance().getConnection();
        try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql);) {
            while (rs.next()) {
                list.add(mapper.mapRow(rs));
            }
        } finally {
            JdbcPool.returnConnection(conn);
        }
        return list;
    }

    /**
     * 执行数据库更新sql语句
     *
     * @param sql
     * @return 更新行数
     * @throws SQLException
     */
    public static int executeUpdate(String sql) throws SQLException {
        return executeUpdate(sql, null);
    }

    /**
     * 执行数据库更新sql语句
     *
     * @param sql
     * @return 更新行数
     * @throws SQLException
     */
    public static int executeUpdate(String sql, Object[] args) throws SQLException {
        logger.debug("executeUpdate: sql={}, parameter={}", sql, arrayToString(args));
        Connection conn = JdbcPool.getInstance().getConnection();
        if (args == null || args.length == 0) {
            try (Statement stmt = conn.createStatement()) {
                return stmt.executeUpdate(sql);
            } finally {
                JdbcPool.returnConnection(conn);
            }
        } else {
            try (PreparedStatement stmt = conn.prepareStatement(sql)) {
                for (int i = 0; i < args.length; i++) {
                    stmt.setObject(i + 1, args[i]);
                }
                return stmt.executeUpdate();
            } finally {
                JdbcPool.returnConnection(conn);
            }
        }
    }

    /**
     * Object[] 转成 String
     *
     * @param objs 对象数组
     * @return
     */
    protected static String arrayToString(Object[] objs) {
        if (objs == null) {
            return "[]";
        }
        StringBuffer buf = new StringBuffer();
        buf.append("[");
        for (int j = 0; j < objs.length; j++) {
            if (j > 0) {
                buf.append(", ");
            }
            buf.append(String.valueOf(objs[j]));
        }
        buf.append("]");
        return buf.toString();
    }
}


================================================
FILE: src/main/java/com/seetaface2/SeetaFace2JNI.java
================================================
package com.seetaface2;

import com.seetaface2.model.*;

/**
 * SeetaFace2JNI接口
 */
public class SeetaFace2JNI {
    /**
     * 初始化,指定人脸识别模型文件目录,该目录下应当包括这3个文件:
     * SeetaFaceDetector2.0.ats,
     * SeetaFaceRecognizer2.0.ats,
     * SeetaPointDetector2.0.pts5.ats
     *
     * @param modelDir
     * @return
     */

    public native synchronized boolean initModel(String modelDir);

    /**
     * 检测人脸
     *
     * @param img
     * @return
     */
    public native synchronized SeetaRect[] detect(SeetaImageData img);

    /**
     * 特征对齐
     *
     * @param img
     * @param faces
     * @return
     */
    public native synchronized SeetaPointF[] detect(SeetaImageData img, SeetaRect[] faces);

    /**
     * 1 v 1 人脸比对
     *
     * @param img1
     * @param img2
     * @return 相似度范围在0~1,返回负数表示出错
     */
    public native synchronized float compare(SeetaImageData img1, SeetaImageData img2);

    /**
     * 注册人脸
     *
     * @param img
     * @return The returned value is the index of face database. Reture -1 if failed
     */
    public native synchronized int register(SeetaImageData img);

    /**
     * Recognize face and get the most similar face index
     *
     * @param img
     * @return index saves the index of face databese, which is same as the retured value by Register. similar saves the most similar.
     */
    public native synchronized RecognizeResult recognize(SeetaImageData img);


    /**
     * Clear face database
     */
    public native synchronized void clear();


    /**
     * 人脸提取
     *
     * @param img
     * @return The returned value is face data. Reture null if failed
     */
    public native synchronized byte[] crop(SeetaImageData img);
}

================================================
FILE: src/main/java/com/seetaface2/model/FaceLandmark.java
================================================
package com.seetaface2.model;

public class FaceLandmark {
    public SeetaRect[] rects;
    public SeetaPointF[] points;
}


================================================
FILE: src/main/java/com/seetaface2/model/RecognizeResult.java
================================================
package com.seetaface2.model;

/**
 * @Author Sugar
 * @Version 2019/4/4 18:07
 */
public class RecognizeResult {
    public int index;
    public float similar;
}


================================================
FILE: src/main/java/com/seetaface2/model/RegisterData.java
================================================
package com.seetaface2.model;

/**
 * @Author Sugar
 * @Version 2019/4/23 11:03
 */
public class RegisterData {
    public byte[] data;
    public int index;
}


================================================
FILE: src/main/java/com/seetaface2/model/SeetaImageData.java
================================================
package com.seetaface2.model;

public class SeetaImageData {
    public SeetaImageData() {

    }

    public SeetaImageData(int width, int height, int channels) {
        this.data = new byte[width * height * channels];
        this.width = width;
        this.height = height;
        this.channels = channels;
    }

    public SeetaImageData(int width, int height) {
        this(width, height, 3);
    }

    public byte[] data;
    public int width;
    public int height;
    public int channels;
}


================================================
FILE: src/main/java/com/seetaface2/model/SeetaPointF.java
================================================
package com.seetaface2.model;

public class SeetaPointF {
    public double x;
    public double y;
}


================================================
FILE: src/main/java/com/seetaface2/model/SeetaRect.java
================================================
package com.seetaface2.model;

public class SeetaRect {
    public int x;
    public int y;
    public int width;
    public int height;
}


================================================
FILE: src/main/resources/log4j2.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="WARN" monitorInterval="300">
	<properties>
		<property name="LOG_HOME">logs</property>
	</properties>
	<Appenders>
		<!-- 控制台 -->
		<Console  name="console" target="SYSTEM_OUT">
			<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] %p [%c-%L] %m%n" />
		</Console>
		<!-- 默认日志文件 -->
		<RollingFile name="appLog" fileName="${LOG_HOME}/app.log" filePattern="${LOG_HOME}/$${date:yyyy-MM-dd}/app.%d{yyyyMMdd}.%i.log.gz">
			<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] %p [%c{3}-%L] %m%n" />
			<Policies>
				<TimeBasedTriggeringPolicy />
				<SizeBasedTriggeringPolicy size="512 MB" />
			</Policies>
			<DefaultRolloverStrategy max="1000" />
		</RollingFile>

		<!-- 数据错误日志文件 -->
		<!--<RollingFile name="dataErrorLog" fileName="${LOG_HOME}/error.log" filePattern="${LOG_HOME}/$${date:yyyy-MM-dd}/error.%d{yyyyMMdd}.%i.log.gz">-->
			<!--<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss}] [%c-%L] %m%n" />-->
			<!--<Policies>-->
				<!--<TimeBasedTriggeringPolicy />-->
				<!--<SizeBasedTriggeringPolicy size="512 MB" />-->
			<!--</Policies>-->
			<!--<DefaultRolloverStrategy max="1000" />-->
		<!--</RollingFile>-->

	</Appenders>

	<Loggers>
		<!-- 正式环境打开appLog,关闭console -->
		<root level="debug">
				<AppenderRef ref="console" />
			<!--<AppenderRef ref="appLog" />-->
		</root>
		<!--<logger name="com.cnsugar" level="INFO" additivity="false">-->
			<!--<appender-ref ref="dataErrorLog" />-->
		<!--</logger>-->

		<!-- 下面配置一些第三方包的日志过滤级别,用于避免刷屏-->
		<Logger name="org.springframework" level="INFO" />
		<Logger name="org.springframework.beans.factory.aspectj" level="WARN" />
		<Logger name="org.springframework.transaction.interceptor" level="WARN" />
		<Logger name="org.springframework.beans.factory.support" level="WARN" />
		<Logger name="org.springframework.web.servlet" level="DEBUG" />
		<Logger name="org.springframework.transaction" level="WARN" />
		<Logger name="org.springframework.jdbc.datasource.DataSourceTransactionManager" level="WARN" />
		<Logger name="org.springframework.transaction.support.AbstractPlatformTransactionManager" level="WARN" />
		<Logger name="org.springframework.security" level="WARN" />
		<Logger name="org.apache.commons" level="WARN" />
		<Logger name="org.apache.kafka" level="WARN" />
		<Logger name="org.apache.http" level="WARN" />
		<Logger name="org.logicalcobwebs" level="WARN" />
		<Logger name="httpclient" level="ERROR" />
		<Logger name="net.sf.ehcache" level="WARN" />
		<Logger name="org.apache.zookeeper" level="WARN" />
		<Logger name="org.I0Itec" level="WARN" />
		<Logger name="com.meetup.memcached" level="WARN" />
		<Logger name="org.mongodb.driver" level="INFO" />
		<Logger name="org.quartz.core" level="INFO" />
		<Logger name="io.netty" level="INFO" />
		<Logger name="net.rubyeye.xmemcached" level="WARN" />
		<Logger name="com.google" level="WARN" />
	</Loggers>
</configuration>


================================================
FILE: src/main/resources/seetaface.properties
================================================
#依赖的lib名称,注意依赖关系顺序,用逗号隔开
#linux os
#libs=holiday,SeetaFaceDetector200,SeetaPointDetector200,SeetaFaceRecognizer200,SeetaFaceCropper200,SeetaFace2JNI
#windows os
libs=libgcc_s_sjlj-1,libeay32,libquadmath-0,ssleay32,libgfortran-3,libopenblas,holiday,SeetaFaceDetector200,SeetaPointDetector200,SeetaFaceRecognizer200,SeetaFaceCropper200,SeetaFace2JNI

#依赖的lib存放目录, 等同于-Djava.library.path=
#linux os
#libs.path=/usr/local/seetaface2/lib
#windows os
libs.path=F:\\ai-face\\SeetaFace2JNI\\x64\\Release;F:\\ai-face\\SeetaFaceEngine2\\win\\lib\\x64

#seetaface model目录
#linux os
#bindata.dir=/usr/local/seetaface2/bindata
#windows os
bindata.dir=F:\\ai-face\\SeetaFaceEngine2\\bindata

##sqlite config
sqlite.db.file=./faces-data.db
sqlite.conn.maxTotal=50
sqlite.conn.maxIdle=5
sqlite.conn.minIdle=0
sqlite.conn.maxWaitMillis=60000


================================================
FILE: src/test/java/Test.java
================================================
import com.cnsugar.ai.face.FaceHelper;
import com.cnsugar.ai.face.SeetafaceBuilder;
import com.cnsugar.ai.face.bean.Result;
import com.seetaface2.model.SeetaRect;
import org.apache.commons.io.FileUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Collection;

public class Test {

    private void init() {
        SeetafaceBuilder.build();//系统启动时先调用初始化方法

        //等待初始化完成
        while (SeetafaceBuilder.getFaceDbStatus() == SeetafaceBuilder.FacedbStatus.LOADING || SeetafaceBuilder.getFaceDbStatus() == SeetafaceBuilder.FacedbStatus.READY) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @org.junit.Test
    public void testCompare() throws Exception {
        init();

        String img1 = "F:\\ai\\demo-pic39.jpg";
        String img2 = "F:\\ai\\left_pic_one.jpg";
        System.out.println("result:" + FaceHelper.compare(new File(img1), new File(img2)));
    }

    @org.junit.Test
    public void testRegister() throws IOException {
        //将F:\ai\star目录下的jpg、png图片都注册到人脸库中,以文件名为key
        Collection<File> files = FileUtils.listFiles(new File("F:\\ai\\star"), new String[]{"jpg", "png"}, false);
        for (File file : files) {
            String key = file.getName();
            try {
                FaceHelper.register(key, FileUtils.readFileToByteArray(file));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println(1);
    }

    @org.junit.Test
    public void testSearch() throws IOException {
        init();

        long l = System.currentTimeMillis();
        Result result = FaceHelper.search(FileUtils.readFileToByteArray(new File("F:\\ai\\gtl.jpg")));
        System.out.println("搜索结果:" + result + ", 耗时:" + (System.currentTimeMillis() - l));
    }

    @org.junit.Test
    public void testDetect() throws IOException {
        SeetaRect[] rects = FaceHelper.detect(FileUtils.readFileToByteArray(new File("F:\\ai\\刘诗诗-bbbbbbbbbbbbbbbbbb.jpg")));
        if (rects != null) {
            for (SeetaRect rect : rects) {
                System.out.println("x="+rect.x+", y="+rect.y+", width="+rect.width+", height="+rect.height);
            }
        }
    }

    @org.junit.Test
    public void testCorp() throws IOException {
        BufferedImage image = FaceHelper.crop(FileUtils.readFileToByteArray(new File("F:\\ai\\刘诗诗-bbbbbbbbbbbbbbbbbb.jpg")));
        if (image != null) {
            ImageIO.write(image, "jpg", new File("F:\\ai\\corp-face1.jpg"));
        }
    }

    @org.junit.Test
    public void testDelete() {
        FaceHelper.removeRegister("Angelababy.jpg", "乔欣.jpg");
    }
}
Download .txt
gitextract_yuq3c2t4/

├── LICENSE
├── README.md
├── a.txt
├── build.pom
├── doc/
│   └── lib-linux-x64.tar.bz2
├── pom.xml
├── seetafaceJNI-1.1.jar
├── seetafaceJNI-2.0.jar
├── seetafaceJNI-3.0.jar
└── src/
    ├── main/
    │   ├── java/
    │   │   └── com/
    │   │       ├── cnsugar/
    │   │       │   ├── ai/
    │   │       │   │   └── face/
    │   │       │   │       ├── FaceHelper.java
    │   │       │   │       ├── SeetafaceBuilder.java
    │   │       │   │       ├── bean/
    │   │       │   │       │   ├── FaceIndex.java
    │   │       │   │       │   └── Result.java
    │   │       │   │       ├── dao/
    │   │       │   │       │   └── FaceDao.java
    │   │       │   │       └── utils/
    │   │       │   │           └── ImageUtils.java
    │   │       │   └── common/
    │   │       │       └── sqlite/
    │   │       │           ├── JdbcPool.java
    │   │       │           ├── JdbcPoolFactory.java
    │   │       │           ├── RowMapper.java
    │   │       │           └── SqliteUtils.java
    │   │       └── seetaface2/
    │   │           ├── SeetaFace2JNI.java
    │   │           └── model/
    │   │               ├── FaceLandmark.java
    │   │               ├── RecognizeResult.java
    │   │               ├── RegisterData.java
    │   │               ├── SeetaImageData.java
    │   │               ├── SeetaPointF.java
    │   │               └── SeetaRect.java
    │   └── resources/
    │       ├── log4j2.xml
    │       └── seetaface.properties
    └── test/
        └── java/
            └── Test.java
Download .txt
SYMBOL INDEX (104 symbols across 18 files)

FILE: src/main/java/com/cnsugar/ai/face/FaceHelper.java
  class FaceHelper (line 22) | public class FaceHelper {
    method compare (line 37) | public static float compare(File img1, File img2) throws IOException {
    method compare (line 50) | public static float compare(byte[] img1, byte[] img2) throws IOExcepti...
    method compare (line 63) | public static float compare(BufferedImage image1, BufferedImage image2) {
    method register (line 84) | public static boolean register(String key, byte[] img) throws IOExcept...
    method register (line 122) | public static boolean register(String key, BufferedImage image) throws...
    method search (line 149) | public static Result search(byte[] img) throws IOException {
    method search (line 161) | public static Result search(BufferedImage image) {
    method crop (line 183) | public static BufferedImage crop(byte[] img) throws IOException {
    method crop (line 194) | public static BufferedImage crop(BufferedImage image) {
    method detect (line 213) | public static SeetaRect[] detect(byte[] img) throws IOException {
    method detect (line 224) | public static SeetaRect[] detect(BufferedImage image) {
    method detectLandmark (line 239) | public static FaceLandmark detectLandmark(BufferedImage image) {
    method removeRegister (line 259) | public static void removeRegister(String... keys) {
    method clear (line 267) | public static void clear() {

FILE: src/main/java/com/cnsugar/ai/face/SeetafaceBuilder.java
  class SeetafaceBuilder (line 22) | public class SeetafaceBuilder {
    type FacedbStatus (line 26) | public enum FacedbStatus {
    method build (line 32) | public static SeetaFace2JNI build() {
    method buildIndex (line 47) | public static void buildIndex() {
    method getFaceDbStatus (line 70) | public static FacedbStatus getFaceDbStatus() {
    method init (line 74) | private static void init() {
    method loadFaceDb (line 119) | private static void loadFaceDb() {
    method register (line 162) | private static void register(String key, FaceIndex face) {
    method getConfig (line 177) | private static Properties getConfig() {

FILE: src/main/java/com/cnsugar/ai/face/bean/FaceIndex.java
  class FaceIndex (line 9) | public class FaceIndex implements Serializable {
    method getKey (line 19) | public String getKey() {
    method setKey (line 23) | public void setKey(String key) {
    method getIndex (line 27) | public int getIndex() {
    method setIndex (line 31) | public void setIndex(int index) {
    method getImgData (line 35) | public byte[] getImgData() {
    method setImgData (line 39) | public void setImgData(byte[] imgData) {
    method getWidth (line 43) | public int getWidth() {
    method setWidth (line 47) | public void setWidth(int width) {
    method getHeight (line 51) | public int getHeight() {
    method setHeight (line 55) | public void setHeight(int height) {
    method getChannel (line 59) | public int getChannel() {
    method setChannel (line 63) | public void setChannel(int channel) {

FILE: src/main/java/com/cnsugar/ai/face/bean/Result.java
  class Result (line 11) | public class Result implements Serializable {
    method Result (line 15) | public Result() {
    method Result (line 19) | public Result(RecognizeResult result) {
    method getKey (line 23) | public String getKey() {
    method setKey (line 27) | public void setKey(String key) {
    method getSimilar (line 31) | public float getSimilar() {
    method setSimilar (line 35) | public void setSimilar(float similar) {
    method toString (line 39) | @Override

FILE: src/main/java/com/cnsugar/ai/face/dao/FaceDao.java
  class FaceDao (line 15) | public class FaceDao {
    method findKeyByIndex (line 16) | public static String findKeyByIndex(int index) {
    method saveOrUpdate (line 25) | public static boolean saveOrUpdate(FaceIndex index) {
    method saveOrUpdateIndex (line 38) | public static boolean saveOrUpdateIndex(FaceIndex index) {
    method clearIndex (line 49) | public static boolean clearIndex() {
    method findFaceImgs (line 59) | public static List<FaceIndex> findFaceImgs(int pageNo, int pageSize) {
    method deleteFaceImg (line 81) | public static boolean deleteFaceImg(String... keys) {
    method clearImg (line 98) | public static boolean clearImg() {

FILE: src/main/java/com/cnsugar/ai/face/utils/ImageUtils.java
  class ImageUtils (line 13) | public class ImageUtils {
    method equalBandOffsetWith3Byte (line 19) | private static boolean equalBandOffsetWith3Byte(BufferedImage image, i...
    method isBGR3Byte (line 36) | public static boolean isBGR3Byte(BufferedImage image) {
    method getMatrixBGR (line 46) | public static byte[] getMatrixBGR(BufferedImage image) {
    method bgrToBufferedImage (line 92) | public static BufferedImage bgrToBufferedImage(byte[] data, int width,...

FILE: src/main/java/com/cnsugar/common/sqlite/JdbcPool.java
  class JdbcPool (line 15) | public class JdbcPool {
    method getInstance (line 22) | public static JdbcPool getInstance() {
    method JdbcPool (line 35) | private JdbcPool() {
    method getDefaultConfig (line 39) | private GenericObjectPoolConfig getDefaultConfig() {
    method getConnection (line 48) | public Connection getConnection() {
    method close (line 57) | public static void close(PreparedStatement ps, ResultSet rs) {
    method returnConnection (line 72) | public static void returnConnection(Connection conn) {
    method returnConnectionAndClose (line 76) | public static void returnConnectionAndClose(Connection conn, PreparedS...

FILE: src/main/java/com/cnsugar/common/sqlite/JdbcPoolFactory.java
  class JdbcPoolFactory (line 16) | public class JdbcPoolFactory extends BasePooledObjectFactory<Connection> {
    method create (line 39) | @Override
    method wrap (line 47) | @Override

FILE: src/main/java/com/cnsugar/common/sqlite/RowMapper.java
  type RowMapper (line 10) | public interface RowMapper<T> {
    method mapRow (line 22) | T mapRow(ResultSet rs) throws SQLException;

FILE: src/main/java/com/cnsugar/common/sqlite/SqliteUtils.java
  class SqliteUtils (line 15) | public class SqliteUtils {
    method queryForString (line 25) | public static String queryForString(String sql) throws SQLException {
    method queryForObject (line 45) | public static <T> T queryForObject(String sql, RowMapper<T> mapper) th...
    method queryForList (line 65) | public static <T> List<T> queryForList(String sql, RowMapper<T> mapper...
    method executeUpdate (line 86) | public static int executeUpdate(String sql) throws SQLException {
    method executeUpdate (line 97) | public static int executeUpdate(String sql, Object[] args) throws SQLE...
    method arrayToString (line 124) | protected static String arrayToString(Object[] objs) {

FILE: src/main/java/com/seetaface2/SeetaFace2JNI.java
  class SeetaFace2JNI (line 8) | public class SeetaFace2JNI {
    method initModel (line 19) | public native synchronized boolean initModel(String modelDir);
    method detect (line 27) | public native synchronized SeetaRect[] detect(SeetaImageData img);
    method detect (line 36) | public native synchronized SeetaPointF[] detect(SeetaImageData img, Se...
    method compare (line 45) | public native synchronized float compare(SeetaImageData img1, SeetaIma...
    method register (line 53) | public native synchronized int register(SeetaImageData img);
    method recognize (line 61) | public native synchronized RecognizeResult recognize(SeetaImageData img);
    method clear (line 67) | public native synchronized void clear();
    method crop (line 76) | public native synchronized byte[] crop(SeetaImageData img);

FILE: src/main/java/com/seetaface2/model/FaceLandmark.java
  class FaceLandmark (line 3) | public class FaceLandmark {

FILE: src/main/java/com/seetaface2/model/RecognizeResult.java
  class RecognizeResult (line 7) | public class RecognizeResult {

FILE: src/main/java/com/seetaface2/model/RegisterData.java
  class RegisterData (line 7) | public class RegisterData {

FILE: src/main/java/com/seetaface2/model/SeetaImageData.java
  class SeetaImageData (line 3) | public class SeetaImageData {
    method SeetaImageData (line 4) | public SeetaImageData() {
    method SeetaImageData (line 8) | public SeetaImageData(int width, int height, int channels) {
    method SeetaImageData (line 15) | public SeetaImageData(int width, int height) {

FILE: src/main/java/com/seetaface2/model/SeetaPointF.java
  class SeetaPointF (line 3) | public class SeetaPointF {

FILE: src/main/java/com/seetaface2/model/SeetaRect.java
  class SeetaRect (line 3) | public class SeetaRect {

FILE: src/test/java/Test.java
  class Test (line 13) | public class Test {
    method init (line 15) | private void init() {
    method testCompare (line 28) | @org.junit.Test
    method testRegister (line 37) | @org.junit.Test
    method testSearch (line 52) | @org.junit.Test
    method testDetect (line 61) | @org.junit.Test
    method testCorp (line 71) | @org.junit.Test
    method testDelete (line 79) | @org.junit.Test
Condensed preview — 29 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (60K chars).
[
  {
    "path": "LICENSE",
    "chars": 1312,
    "preview": "BSD 2-Clause License\n\nCopyright (c) 2019, cnsugar\nAll rights reserved.\n\nRedistribution and use in source and binary form"
  },
  {
    "path": "README.md",
    "chars": 5586,
    "preview": "# seetafaceJNI\n\n#### 项目介绍\n基于中科院seetaface2进行封装的JAVA人脸识别算法库,支持人脸识别、1:1比对、1:N比对。\nseetaface2:https://github.com/seetaface/Se"
  },
  {
    "path": "a.txt",
    "chars": 4,
    "preview": "111\n"
  },
  {
    "path": "build.pom",
    "chars": 1391,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "pom.xml",
    "chars": 3091,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "src/main/java/com/cnsugar/ai/face/FaceHelper.java",
    "chars": 7834,
    "preview": "package com.cnsugar.ai.face;\n\nimport com.cnsugar.ai.face.bean.FaceIndex;\nimport com.cnsugar.ai.face.bean.Result;\nimport "
  },
  {
    "path": "src/main/java/com/cnsugar/ai/face/SeetafaceBuilder.java",
    "chars": 6256,
    "preview": "package com.cnsugar.ai.face;\n\nimport com.cnsugar.ai.face.bean.FaceIndex;\nimport com.cnsugar.ai.face.dao.FaceDao;\nimport "
  },
  {
    "path": "src/main/java/com/cnsugar/ai/face/bean/FaceIndex.java",
    "chars": 1128,
    "preview": "package com.cnsugar.ai.face.bean;\n\nimport java.io.Serializable;\n\n/**\n * @Author Sugar\n * @Version 2019/4/22 17:14\n */\npu"
  },
  {
    "path": "src/main/java/com/cnsugar/ai/face/bean/Result.java",
    "chars": 807,
    "preview": "package com.cnsugar.ai.face.bean;\n\nimport com.seetaface2.model.RecognizeResult;\n\nimport java.io.Serializable;\n\n/**\n * @A"
  },
  {
    "path": "src/main/java/com/cnsugar/ai/face/dao/FaceDao.java",
    "chars": 3603,
    "preview": "package com.cnsugar.ai.face.dao;\n\nimport com.cnsugar.ai.face.bean.FaceIndex;\nimport com.cnsugar.common.sqlite.RowMapper;"
  },
  {
    "path": "src/main/java/com/cnsugar/ai/face/utils/ImageUtils.java",
    "chars": 4576,
    "preview": "package com.cnsugar.ai.face.utils;\n\n//import java.awt.color.ColorSpace;\nimport java.awt.image.BufferedImage;\n//import ja"
  },
  {
    "path": "src/main/java/com/cnsugar/common/sqlite/JdbcPool.java",
    "chars": 2427,
    "preview": "package com.cnsugar.common.sqlite;\n\nimport org.apache.commons.pool2.impl.GenericObjectPool;\nimport org.apache.commons.po"
  },
  {
    "path": "src/main/java/com/cnsugar/common/sqlite/JdbcPoolFactory.java",
    "chars": 1422,
    "preview": "package com.cnsugar.common.sqlite;\n\nimport org.apache.commons.pool2.BasePooledObjectFactory;\nimport org.apache.commons.p"
  },
  {
    "path": "src/main/java/com/cnsugar/common/sqlite/RowMapper.java",
    "chars": 768,
    "preview": "package com.cnsugar.common.sqlite;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\n/**\n * @Author Sugar\n * @V"
  },
  {
    "path": "src/main/java/com/cnsugar/common/sqlite/SqliteUtils.java",
    "chars": 3905,
    "preview": "package com.cnsugar.common.sqlite;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.sql.*;\nimport "
  },
  {
    "path": "src/main/java/com/seetaface2/SeetaFace2JNI.java",
    "chars": 1707,
    "preview": "package com.seetaface2;\n\nimport com.seetaface2.model.*;\n\n/**\n * SeetaFace2JNI接口\n */\npublic class SeetaFace2JNI {\n    /**"
  },
  {
    "path": "src/main/java/com/seetaface2/model/FaceLandmark.java",
    "chars": 124,
    "preview": "package com.seetaface2.model;\n\npublic class FaceLandmark {\n    public SeetaRect[] rects;\n    public SeetaPointF[] points"
  },
  {
    "path": "src/main/java/com/seetaface2/model/RecognizeResult.java",
    "chars": 164,
    "preview": "package com.seetaface2.model;\n\n/**\n * @Author Sugar\n * @Version 2019/4/4 18:07\n */\npublic class RecognizeResult {\n    pu"
  },
  {
    "path": "src/main/java/com/seetaface2/model/RegisterData.java",
    "chars": 160,
    "preview": "package com.seetaface2.model;\n\n/**\n * @Author Sugar\n * @Version 2019/4/23 11:03\n */\npublic class RegisterData {\n    publ"
  },
  {
    "path": "src/main/java/com/seetaface2/model/SeetaImageData.java",
    "chars": 506,
    "preview": "package com.seetaface2.model;\n\npublic class SeetaImageData {\n    public SeetaImageData() {\n\n    }\n\n    public SeetaImage"
  },
  {
    "path": "src/main/java/com/seetaface2/model/SeetaPointF.java",
    "chars": 102,
    "preview": "package com.seetaface2.model;\n\npublic class SeetaPointF {\n    public double x;\n    public double y;\n}\n"
  },
  {
    "path": "src/main/java/com/seetaface2/model/SeetaRect.java",
    "chars": 139,
    "preview": "package com.seetaface2.model;\n\npublic class SeetaRect {\n    public int x;\n    public int y;\n    public int width;\n    pu"
  },
  {
    "path": "src/main/resources/log4j2.xml",
    "chars": 2921,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration status=\"WARN\" monitorInterval=\"300\">\n\t<properties>\n\t\t<property nam"
  },
  {
    "path": "src/main/resources/seetaface.properties",
    "chars": 825,
    "preview": "#依赖的lib名称,注意依赖关系顺序,用逗号隔开\n#linux os\n#libs=holiday,SeetaFaceDetector200,SeetaPointDetector200,SeetaFaceRecognizer200,Seeta"
  },
  {
    "path": "src/test/java/Test.java",
    "chars": 2813,
    "preview": "import com.cnsugar.ai.face.FaceHelper;\nimport com.cnsugar.ai.face.SeetafaceBuilder;\nimport com.cnsugar.ai.face.bean.Resu"
  }
]

// ... and 4 more files (download for full content)

About this extraction

This page contains the full source code of the xikuqi/OpenCV GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 29 files (52.3 KB), approximately 14.8k tokens, and a symbol index with 104 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!