虹软人脸识别 - 采用数据库存取人脸特征数据

前几天有个朋友遇到了个问题,他在使用虹软的人脸识别引擎时,想更换一下人脸识别的存储方式,原本demo中使用的是文件的方式进行存储,而他想要通过数据库的方式进行存储。由于他刚接触Android不久,对数据库这块不甚了解,再加之听上去要存特征数据,听上去就很难的样子,可愁坏了他。其实虹软的人脸特征数据就是一个byte[],存储起来还是相当方便的。我为他写了一个demo,顺便与大家分享一下,希望能帮到有需要的人,本文将分为以下几点讲述。

两种数据库存储方式

封装数据库操作接口

ArcFaceDemo接入

一、两种数据库操作方式

方案一:使用原生数据库存储人脸特征数据

1. 建表

首先我们需要建一张表来存储特征数据,但是光存储特征数据还是不够的,不足以满足人脸识别的需要,因此我们还需要人脸姓名、人脸图片等数据。下面我创建了一个User表,以id为主键,并自增长,faceName用TEXT类型存储,把人脸图片与特征都用Blob存储。

注意:对于人脸库中人脸较多的场景,请将注册图存储至本地,数据库中仅保留文件路径,否则会占用很多内存。

public class FaceDatabaseHelper extends SQLiteOpenHelper {

private static final String TAG = "FaceDatabaseHelper";

public FaceDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {

super(context, name, factory, version);

}

@Override

public void onCreate(SQLiteDatabase db) {

//创建Face表 ID 名字 照片 特征

db.execSQL("CREATE TABLE IF NOT EXISTS Face" +

"(id INTEGER PRIMARY KEY AUTOINCREMENT, faceName TEXT, facePic Blob , faceFeature Blob)");

//建库成功后给出提示

Log.i(TAG, "dataBase Create Success");

}

@Override

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

}

}

2. 新增人脸数据

表已经建好了,接下来就是往表中插入数据,需要注意的是,这里的最后一个参数需要传入jpeg格式的数据。

public long addFace(String faceName, byte[] faceFeature, byte[] facePic) {

//存人脸

ContentValues values = new ContentValues();

long index = -1;

//写入表

try {

values.put("faceName", faceName);

values.put("faceFeature", faceFeature);

values.put("facePic", facePic);

index = db.insert("Face", null, values);

} catch (Exception e) {

e.printStackTrace();

}

return index;

}

3. 加载人脸数据

人脸数据已经成功的插入到了表中,接下来我们就需要将表内所有的数据加载到内存。

public ArrayList selectAllFaces() {

ArrayList dataList = new ArrayList<>();

Cursor cursor = db.query("Face", null, null, null, null, null, null);

while (cursor.moveToNext()) {

FaceEntity data = new FaceEntity();

int id = cursor.getInt(cursor.getColumnIndex("id"));

String faceName = cursor.getString(cursor.getColumnIndex("faceName"));

byte[] facePic = cursor.getBlob(cursor.getColumnIndex("facePic"));

byte[] faceFeature = cursor.getBlob(cursor.getColumnIndex("faceFeature"));

data.setId(id);

data.setFaceName(faceName);

data.setFacePic(facePic);

data.setFaceFeature(faceFeature);

dataList.add(data);

}

cursor.close();

return dataList;

}

4. 清空人脸数据

支持删除所有人脸或单张人脸。

public void deleteAllFace() {

db.execSQL("delete from Face");

}

public void deleteFaceById(int id) {

db.delete("Face", "id=?", new String[]{"" + id});

}

方案二:使用Room框架存储人脸特征数据

上面使用的是原生数据库操作接口,这边还提供一种使用Android官方推荐的Room框架操作数据库的示例。

1. 添加依赖

首先我们要在app下的build.gradle文件内添加room的依赖。

implementation 'android.arch.persistence.room:runtime:1.1.1'

annotationProcessor 'android.arch.persistence.room:compiler:1.1.1'

2. 建表

Room的数据库操作相比于原生的方式来说简单的多,只需要对实体类进行注解即可。

注意:对于人脸库中人脸较多的场景,请将注册图存储至本地,数据库中仅保留文件路径,否则会占用很多内存。

// entity声明定义,并且指定了映射数据表明

@Entity(tableName = "Face")

public class FaceEntity {

// 设置主键,并且定义自增增

@PrimaryKey(autoGenerate = true)

public int id;

// 字段映射具体的数据表字段名

@ColumnInfo(name = "faceName")

private String faceName;

@ColumnInfo(name = "faceFeature")

private byte[] faceFeature;

@ColumnInfo(name = "facePic")

private byte[] facePic;

// getter/setter就不贴了

...

}

3. 建库

每次打开数据库文件都会产生比较大的开销,所以将FaceRoomDatabase设计成单例。

//注解指定了database的表映射实体数据以及版本等信息

@Database(entities = {FaceEntity.class}, version = 1)

public abstract class FaceRoomDatabase extends RoomDatabase {

//RoomDatabase提供直接访问底层数据库实现,我们通过定义抽象方法返回具体Dao

//然后进行数据库增删该查的实现。

public abstract FaceDao userDao();

private static volatile FaceRoomDatabase instance;

public static FaceRoomDatabase getInstance(Context context) {

if (instance == null) {

synchronized (FaceRoomDatabase.class) {

if (instance == null) {

instance = Room.databaseBuilder(context.getApplicationContext(), FaceRoomDatabase.class, "RoomFaceDB.db")

.build();

}

}

}

return instance;

}

}

4. 数据库操作

基于Room框架的数据库操作较为简单,就不一一例举,统一放在一起进行说明。

@Dao

public interface FaceDao {

//返回Long数据表示,插入条目的主键值(uid)

@Insert

Long addFace(FaceEntity user);

//获取所有人脸数据

@Query("SELECT * FROM Face")

List getAllFace();

//删除所有人脸数据

@Query("DELETE FROM Face")

int deleteAll();

//删除指定人脸

@Delete

int delete(FaceEntity user);

}

二、封装数据库操作接口

2.1 接口声明

为了代码切换更便捷,能够在代码中快速切换使用Room或直接使用SQLite进行数据库操作:

// ROOM方式

private static FaceDatabaseAccessObject dao = new RoomFaceDao();

// 直接使用SQLite方式

// private static FaceDatabaseAccessObject dao = new SQLiteFaceDao();

我们可以预先定义一套接口FaceDatabaseAccessObject:

public interface FaceDatabaseAccessObject {

/**

* 初始化

*/

void init();

/**

* 插入一个人脸

*

* @param userEntity 人脸信息

* @return index

*/

long insert(FaceEntity userEntity);

/**

* 获取所有人脸

*

* @return 所有人脸

*/

List getAll();

/**

* 清空所有人脸

*/

void clearAll();

/**

* 回收资源操作

*/

void release();

}

2.2 SQLite实现FaceDatabaseAccessObject接口

public class SQLiteFaceDao implements FaceDatabaseAccessObject {

private static FaceDatabaseManager faceDataBaseManager;

@Override

public void init() {

faceDataBaseManager = new FaceDatabaseManager(ArcFaceApp.getApplication());

}

@Override

public long insert(FaceEntity userEntity) {

if (faceDataBaseManager == null) {

return -1;

}

return faceDataBaseManager.addFace(userEntity.getFaceName(), userEntity.getFaceFeature(), userEntity.getFacePic());

}

@Override

public List getAll() {

if (faceDataBaseManager == null) {

return null;

}

return faceDataBaseManager.selectAllFaces();

}

@Override

public void clearAll() {

if (faceDataBaseManager == null) {

return;

}

faceDataBaseManager.deleteAllFace();

}

@Override

public void release() {

if (faceDataBaseManager == null) {

return;

}

faceDataBaseManager.release();

}

}

2.3 ROOM实现FaceDatabaseAccessObject接口

public class RoomFaceDao implements FaceDatabaseAccessObject {

private static FaceDao dao;

private FaceRoomDatabase appDatabase;

@Override

public void init() {

appDatabase = FaceRoomDatabase.getInstance(ArcFaceApp.getApplication());

dao = appDatabase.userDao();

}

@Override

public long insert(FaceEntity userEntity) {

return dao == null ? -1 : dao.addFace(userEntity);

}

@Override

public List getAll() {

return dao == null ? null : dao.getAllFace();

}

@Override

public void clearAll() {

if (dao == null) {

return;

}

dao.deleteAll();

}

@Override

public void release() {

if (appDatabase != null) {

appDatabase.close();

}

}

}

三、ArcFaceDemo接入

数据库操作已经实现,接下来就要实际运用到项目中,首先是人脸注册,我们直接修改Demo中FaceServer类的register方法。

3.1 注册

// 创建一个头像的Bitmap,存放旋转结果图

Bitmap headBmp = getHeadImage(bgr24, width, height, faceInfoList.get(0).getOrient(), cropRect, ArcSoftImageFormat.BGR24);

// 录入人脸

ByteArrayOutputStream baos = new ByteArrayOutputStream();

headBmp.compress(Bitmap.CompressFormat.JPEG, 50, baos);

byte[] facePic = baos.toByteArray();

FaceEntity user = new FaceEntity();

user.setFaceName(userName);

user.setFaceFeature(faceFeature.getFeatureData());

user.setFacePic(facePic);

dao.insert(user);

3.2 查询

上面我们已经完成了注册,接下来就是查询,由于Room不推荐在主线程中进行UI操作,我们新创建一个线程,将原本Demo中的initFaceList替换为initFaceListByDataBase。

private void initFaceListByDataBase() {

new Thread(new Runnable() {

@Override

public void run() {

faceRegisterInfoList = new ArrayList<>();

List userTableList = dao.getAll();

for (FaceEntity userInfo : userTableList) {

faceRegisterInfoList.add(new FaceRegisterInfo(userInfo.getFaceFeature(), userInfo.getFaceName(), userInfo.getFacePic()));

}

}

}).start();

}

3.3 删除

删除人脸同样属于IO操作,推荐放在子线程中处理。

public int clearAllFaces(Context context) {

synchronized (this) {

if (context == null) {

return 0;

}

int number = 0;

if (faceRegisterInfoList != null) {

number = faceRegisterInfoList.size();

faceRegisterInfoList.clear();

}

new Thread(new Runnable() {

@Override

public void run() {

dao.clearAll();

}

}).start();

return number;

}

}

3.4 显示

由于原本图片的加载模式是加载本地文件,而现在图片是直接存储在数据库的,因此需要对CompareResult进行修改,同时要对FaceSearchResultAdapter的图片加载做一些修改。

public class CompareResult {

private String userName;

private float similar;

private int trackId;

private byte[] facePic;

public CompareResult(String userName, float similar) {

this.userName = userName;

this.similar = similar;

}

public CompareResult(String userName, float similar, byte[] facePic) {

this.userName = userName;

this.similar = similar;

this.facePic = facePic;

}

}

public CompareResult getTopOfFaceLib(FaceFeature faceFeature) {

//......

//.....

if (maxSimilarIndex != -1) {

//return new CompareResult(faceRegisterInfoList.get(maxSimilarIndex).getName(), maxSimilar);

return new CompareResult(faceRegisterInfoList.get(maxSimilarIndex).getName(), maxSimilar, faceRegisterInfoList.get(maxSimilarIndex).getFacePic());

}

return null;

}

FaceSearchResultAdapter内修改注册照显示的图像加载。

@Override

public void onBindViewHolder(@NonNull CompareResultHolder holder, int position) {

if (compareResultList == null) {

return;

}

// File imgFile = new File(FaceServer.ROOT_PATH + File.separator + FaceServer.SAVE_IMG_DIR + File.separator + compareResultList.get(position).getUserName() + FaceServer.IMG_SUFFIX);

// Glide.with(holder.imageView)

// .load(imgFile)

// .into(holder.imageView);

Glide.with(holder.imageView)

.load(compareResultList.get(position).getFacePic())

.into(holder.imageView);

holder.textView.setText(compareResultList.get(position).getUserName());

}

3.5 效果

至此为止我们已经搭建好数据库,并且完成了识别界面相关的代码修改,下面是实际运行的效果。

四、附录

本文通过两种数据库操作方式介绍了人脸特征数据的存取,供大家参考,若有不对的地方,请大家指正!

如果对您有所帮助,可以为我的demo点个star!

提示:本文示例代码在使用前需先修改com.arcsoft.arcfacedemo.common.Constants.java中的APP_ID与SDK_KEY。

相关产品在虹软人脸识别开放平台进一步了解喔

vc中人脸识别数据导入mysql_虹软人脸识别 - 采用数据库存取人脸特征数据相关推荐

  1. 虹软人脸识别 - 采用数据库存取人脸特征数据

    虹软人脸识别 - 采用数据库存取人脸特征数据 前几天有个朋友遇到了个问题,他在使用虹软的人脸识别引擎时,想更换一下人脸识别的存储方式,原本demo中使用的是文件的方式进行存储,而他想要通过数据库的方式 ...

  2. yspider爬取数据导入mysql_爬虫实战四、PyCharm+Scrapy爬取数据并存入MySQL

    一.创建爬虫项目 注意:不能直接使用PyCharm创建Scrapy项目,所以需要在爬虫实战三.PyCharm搭建Scrapy开发调试环境的基础(PyCharm配置完Scrapy)之上,选中mySpid ...

  3. python 数据导入mysql_用python批量向数据库(MySQL)中导入数据

    用python批量向数据库(MySQL)中导入数据 现有数十万条数据,如下的经过打乱处理过的数据进行导入 数据库内部的表格的数据格式如下与下面的表格结构相同Current database: pyth ...

  4. sql compact 转mysql_如何将数据导入到 SQL Server Compact Edition 数据库中(三)

    系列文章导航: 如何将数据导入到 SQL Server Compact Edition 数据库中(一) 如何将数据导入到 SQL Server Compact Edition 数据库中(二) 摘要:时 ...

  5. sql compact 转mysql_如何将数据导入到 SQL Server Compact Edition 数据库中(五)

    系列文章导航: 如何将数据导入到 SQL Server Compact Edition 数据库中(一) 如何将数据导入到 SQL Server Compact Edition 数据库中(二) 如何将数 ...

  6. sql compact 转mysql_如何将数据导入到 SQL Server Compact Edition 数据库中(四)

    系列文章导航: 如何将数据导入到 SQL Server Compact Edition 数据库中(一) 如何将数据导入到 SQL Server Compact Edition 数据库中(二) 如何将数 ...

  7. 如何将数据导入到 SQL Server Compact Edition 数据库中(四)

    系列文章导航: 如何将数据导入到 SQL Server Compact Edition 数据库中(一) 如何将数据导入到 SQL Server Compact Edition 数据库中(二) 如何将数 ...

  8. rds 数据导入mysql_将数据导入到 Amazon RDS 数据库实例

    常规数据导入性能准则 以下性能准则适用于所有 Amazon RDS 数据导入/导出操作: 使用压缩和多线程并行加载和卸载数据.如果您正在将大量数据并行加载到客户端计算机,请确保在数据加载过程中具有足够 ...

  9. python批量导入mysql_用python批量向数据库(MySQL)中导入数据

    用python批量向数据库(MySQL)中导入数据 现有数十万条数据,如下的经过打乱处理过的数据进行导入 数据库内部的表格的数据格式如下与下面的表格结构相同 Current database: pyt ...

最新文章

  1. 从Qcheck 1.3 不能在不同操作系统上运行问题(chro124、chro342)说开来------
  2. (SpringMVC)RestFul和Controller
  3. Socket.IO介绍:支持WebSocket、用于WEB端的即时通讯的框架
  4. SQLServer权限
  5. 查找-------(HashCode)哈希表的原理
  6. 在 Nvidia 显卡下设置装备铺排双浮现器
  7. python怎么通过手机号定位_基于Python的免费手机号码归属地查询
  8. NSR | 国科大王艳芬组中国草地多维资源梯度上的单峰生产力与生物多样性关系...
  9. P4556 [Vani有约会] 树上差分 + 线段树合并
  10. 大专什么专业学c语言,大专专业计算机网络技术学不学编程
  11. Life with qmail -- 中文版(英文版本2 Jan 2006)
  12. html5 css 渐变背景,css渐变,css渐变色背景
  13. Linux平台设备框架驱动
  14. dcloud 5+ 监听安卓前后台切换状态 并后台运行程序
  15. python爬虫-爬妹子图_Python 爬虫入门之爬取妹子图
  16. 电路习题解答 第四章 4-25
  17. 克隆系统分区或者硬盘
  18. 【电路】电路与电子技术基础 课堂笔记 第4章 动态电路的分析方法
  19. 阅读笔记3:基于深度学习的运动想象脑电信号分类算法研究
  20. Autofac Circular component dependency detected 错误解决

热门文章

  1. 使用Jmeter进行压力测试结果不准的原因分析
  2. 【IoT】 产品设计之结构设计:如何选择产品硅胶硬度
  3. 硬件设计7---什么是纹波、噪声、过冲、回沟?
  4. MySql配置参数详解
  5. 深度学习与神经网络(三)——多层感知机 反向传播 优化实战
  6. Python中使用jpype调用Jar包中的方法
  7. 二分查找(上界,下界)
  8. 【Verilog基础】Verilog语法之标识符(命名规范)
  9. 从0到1优雅实现沉浸式状态栏
  10. 关于home 的英语