一.本章节介绍
本章节将讲解如何使用rockx提取人脸图像特征值,并将其存储到sqlite3数据库中。在实际开发中,人脸特征值通常都会存入数据库,常见的选择包括sqlite3、MySQL等。(注:本项目不会深入讲解数据库知识,而是带大家完成基础的增删改查操作)。

二.rockx读取人脸特征值保存数据库大体框图

上图展示了将人脸特征和图片数据写入数据库的处理流程:
- 连接并读取人脸SQLite3数据库
- 初始化三个Rockx句柄:
- 人脸检测句柄(face_det_handle)
- 人脸识别句柄(face_recognize_handle)
- 人脸关键点句柄(face_5landmarks_handle)
- 使用rockx_image_read读取人脸图片
- 通过FILE API获取图片长度和二进制数据
- 使用rockx_face_align进行人脸图片对齐
- 调用rockx_face_recognize提取人脸特征值
- 将人脸特征值和图片二进制数据存入SQLite3数据库
三.rockx读取人脸特征值保存数据库的代码
3.1.Connection_sqlite3DataBase连接数据库
cpp
printf("Start Connection sqlite3......................\n");
Connection_sqlite3DataBase();
printf("End Connection_sqlite3DataBase......................\n");
这里封装了一个了SQLITE3连接数据库的函数Connection_sqlite3Database,这个函数的实现如下
cpp
int Connection_sqlite3DataBase()
{
rc = sqlite3_open("/userdata/face.db", &db);
if (rc != SQLITE_OK)
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
else
{
printf("You have opened a sqlite3 database named bind.db successfully!\nCongratulation! Have fun!\n");
}
return 0;
}
这个函数直接调用了sqlite3的api sqlite3_open来初始化人脸数据库,若返回值不等于SQLITE_OK则初始化数据库失败,否则就初始化成功。
3.2.创建三个rockx句柄
cpp
// 1. 创建人脸检测句柄
ret = rockx_create(&face_det_handle, ROCKX_MODULE_FACE_DETECTION_V2, config, 0);
if (ret != ROCKX_RET_SUCCESS)
{
printf("init rockx module ROCKX_MODULE_FACE_DETECTION error %d\n", ret);
return -1;
}
// 2. 创建人脸5关键点句柄
ret = rockx_create(&face_5landmarks_handle, ROCKX_MODULE_FACE_LANDMARK_5,
config, 0);
if (ret != ROCKX_RET_SUCCESS)
{
printf("init rockx module ROCKX_MODULE_FACE_LANDMARK_68 error %d\n", ret);
return -1;
}
// 3. 创建人脸特征提取句柄
ret = rockx_create(&face_recognize_handle, ROCKX_MODULE_FACE_RECOGNIZE,
config, 0);
if (ret != ROCKX_RET_SUCCESS)
{
printf("init rockx module ROCKX_MODULE_FACE_LANDMARK_68 error %d\n", ret);
return -1;
}
我们需要创建三个人脸处理句柄来实现完整功能:
- 人脸检测句柄(ROCKX_MODULE_FACE_DETECTION_V2)
- 人脸识别句柄(ROCKX_MODULE_FACE_RECOGNIZE)
- 人脸关键点检测句柄(ROCKX_MODULE_FACE_LANDMARK_5)
这些句柄都通过rockx_create函数进行初始化。
3.3.读取人脸图片
cpp
rockx_image_t input_image1;
rockx_image_read(image_path, &input_image1, 1); //读取图片文件到rockx_image_t(第二个参数1表示按RGB格式读取)
上图是读取人脸的图片,这里使用的是rockx_image_read来读取人脸图片,其中input_image是需要从命令行输入的。
3.4.使用FILE读取图片的二进制数据和长度
cpp
FILE *imageFile = fopen(image_path, "rb"); // 打开名为image.jpg的图片文件(二进制模式)
if (!imageFile)
{
printf("无法打开图片文件.\n");
return -1;
}
fseek(imageFile, 0L, SEEK_END); // 定位到文件尾部
long fileSize = ftell(imageFile); // 获取文件大小
rewind(imageFile); // 重新定位到文件起始位置
printf("fileSize = %ld\n", fileSize);
unsigned char *buffer = new unsigned char[fileSize]; // 分配足够大小的缓冲区
size_t bytesRead = fread(buffer, sizeof(unsigned char), fileSize, imageFile); // 从文件中读取图像数据到缓冲区
3.5.使用 run_face_recognize检测人脸数据并找到最精确的人脸
cpp
int run_face_recognize(const char *name, rockx_image_t *in_image, rockx_face_feature_t *out_feature, unsigned char * buffer, int buffer_size)
{
rockx_ret_t ret;
/*************** FACE Detect ***************/
// create rockx_face_array_t for store result
rockx_object_array_t face_array;
memset(&face_array, 0, sizeof(rockx_object_array_t));
// detect face
ret = rockx_face_detect(face_det_handle, in_image, &face_array, NULL);
if (ret != ROCKX_RET_SUCCESS)
{
printf("rockx_face_detect error %d\n", ret);
return -1;
}
// process result
for (int i = 0; i < face_array.count; i++)
{
int left = face_array.object[i].box.left;
int top = face_array.object[i].box.top;
int right = face_array.object[i].box.right;
int bottom = face_array.object[i].box.bottom;
float score = face_array.object[i].score;
printf("%d box=(%d %d %d %d) score=%f\n", i, left, top, right, bottom,score);
}
rockx_object_t *max_face = get_max_face(&face_array);
if (max_face == NULL)
{
printf("error no face detected\n");
return -1;
}
// Face Align
rockx_image_t out_img;
memset(&out_img, 0, sizeof(rockx_image_t));
/*************** 2. 人脸对齐 ***************/
ret = rockx_face_align(face_5landmarks_handle, in_image, &(max_face->box),NULL, &out_img);
if (ret != ROCKX_RET_SUCCESS)
{
return -1;
}
/*************** 3. 人脸特征提取 ***************/
rockx_face_recognize(face_recognize_handle, &out_img, out_feature);
/*************** 4. 特征+图片数据入库 ***************/
insert_face_data_toDataBase(name, out_feature->feature, FEATURE_SIZE, buffer, buffer_size);
// Release Aligned Image
rockx_image_release(&out_img);
return 0;
}
run_face_recognize(name, &input_image1, &out_feature1, buffer, bytesRead);
cpp
rockx_object_t *get_max_face(rockx_object_array_t *face_array)
{
if (face_array->count == 0)
{
return NULL;
}
rockx_object_t *max_face = NULL;//存储人脸的最大指针
int i;
for (i = 0; i < face_array->count; i++)
{
rockx_object_t *cur_face = &(face_array->object[i]);
if (max_face == NULL)
{
max_face = cur_face;
continue;
}
int cur_face_box_area = (cur_face->box.right - cur_face->box.left) *
(cur_face->box.bottom - cur_face->box.top);
int max_face_box_area = (max_face->box.right - max_face->box.left) *
(max_face->box.bottom - max_face->box.top);
if (cur_face_box_area > max_face_box_area)
{
max_face = cur_face;
}
}
printf("get_max_face %d\n", i - 1);
return max_face;
}
get_max_face 方法通过遍历所有人脸数据实现功能:计算每个人脸区域面积(cur_face_box_area )并与当前最大面积(max_face_box_area)比较。当检测到更大的人脸区域时,会更新最大面积值,最终确定max_face为最精确的人脸数据。
3.6. 把人脸特征值和图片数据保存到sqlite3数据库
cpp
void insert_face_data_toDataBase(const char *name, float feature[512], int featureSize, uint8_t *image_data, int image_length)
{
printf("face_size = %d\n", image_length);
// 1. 预编译SQL并检查返回值
int ret = sqlite3_prepare(db, "insert into face_data_table(name,face_feature,feature_size,image_data,image_size) values (?,?,?,?,?);", -1, &stmt, NULL);
if (ret != SQLITE_OK) {
printf("sqlite3_prepare error: %s\n", sqlite3_errmsg(db));
return;
}
// 2. 绑定参数(修正特征字节数)
ret = sqlite3_bind_text(stmt, 1, name, strlen(name), SQLITE_STATIC); // SQLITE_STATIC 等价于NULL,表示不释放
if (ret != SQLITE_OK) goto err;
ret = sqlite3_bind_blob(stmt, 2, feature, featureSize * sizeof(float), SQLITE_STATIC);
if (ret != SQLITE_OK) goto err;
ret = sqlite3_bind_int(stmt, 3, featureSize);
if (ret != SQLITE_OK) goto err;
ret = sqlite3_bind_blob(stmt, 4, image_data, image_length, SQLITE_STATIC);
if (ret != SQLITE_OK) goto err;
ret = sqlite3_bind_int(stmt, 5, image_length);
if (ret != SQLITE_OK) goto err;
// 3. 打印调试
printf("insert face feature: ");
for (int i = 0; i < 50; i++) {
printf("%f ", feature[i]);
}
printf("\n");
// 4. 执行插入并检查返回值
ret = sqlite3_step(stmt);
if (ret != SQLITE_DONE) {
printf("sqlite3_step error: %s\n", sqlite3_errmsg(db));
}
err:
// 5. 释放语句对象
sqlite3_finalize(stmt);
}
3.7.测试结果

(图二)
最后一步是将人脸特征值和图片数据存入SQLite3数据库。我们使用以下SQL插入语句:
sql
INSERT INTO face_data_table(name, face_feature, feature_size, image_data, image_size)
VALUES (?, ?, ?, ?, ?);
其中face_data_table是存储人脸数据的数据表,包含以下字段:
name:人脸名称face_feature:人脸特征值feature_size:特征值长度image_data:图片数据image_size:图片长度
通过sqlite3_prepare函数将SQL语句编译为可执行字节码,支持查询、插入、更新和删除等操作。具体参数绑定如下:
sqlite3_bind_text(stmt, 1, name, strlen(name), NULL):绑定名称sqlite3_bind_blob(stmt, 2, feature, featureSize, NULL):绑定人脸特征值sqlite3_bind_int(stmt, 3, featureSize):绑定特征值长度sqlite3_bind_blob(stmt, 4, image_data, image_length, NULL):绑定图片数据sqlite3_bind_int(stmt, 5, image_length):绑定图片长度
3.8.完整代码
cpp
/*
* ./sqlite3_operation_test name image_path
*/
#include "rockx.h"
#include "sqlite3_operation.h"
#define FEATURE_SIZE 512
rockx_handle_t face_det_handle;
rockx_handle_t face_5landmarks_handle;
rockx_handle_t face_recognize_handle;
void insert_face_data_toDataBase(const char *name, float feature[512], int featureSize, uint8_t *image_data, int image_length)
{
printf("face_size = %d\n", image_length);
// 1. 预编译SQL并检查返回值
int ret = sqlite3_prepare(db, "insert into face_data_table(name,face_feature,feature_size,image_data,image_size) values (?,?,?,?,?);", -1, &stmt, NULL);
if (ret != SQLITE_OK) {
printf("sqlite3_prepare error: %s\n", sqlite3_errmsg(db));
return;
}
// 2. 绑定参数(修正特征字节数)
ret = sqlite3_bind_text(stmt, 1, name, strlen(name), SQLITE_STATIC); // SQLITE_STATIC 等价于NULL,表示不释放
if (ret != SQLITE_OK) goto err;
ret = sqlite3_bind_blob(stmt, 2, feature, featureSize * sizeof(float), SQLITE_STATIC);
if (ret != SQLITE_OK) goto err;
ret = sqlite3_bind_int(stmt, 3, featureSize);
if (ret != SQLITE_OK) goto err;
ret = sqlite3_bind_blob(stmt, 4, image_data, image_length, SQLITE_STATIC);
if (ret != SQLITE_OK) goto err;
ret = sqlite3_bind_int(stmt, 5, image_length);
if (ret != SQLITE_OK) goto err;
// 3. 打印调试
printf("insert face feature: ");
for (int i = 0; i < 50; i++) {
printf("%f ", feature[i]);
}
printf("\n");
// 4. 执行插入并检查返回值
ret = sqlite3_step(stmt);
if (ret != SQLITE_DONE) {
printf("sqlite3_step error: %s\n", sqlite3_errmsg(db));
}
err:
// 5. 释放语句对象
sqlite3_finalize(stmt);
}
int Connection_sqlite3DataBase()
{
rc = sqlite3_open("/userdata/face.db", &db);
if (rc != SQLITE_OK)
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
else
{
printf("You have opened a sqlite3 database named bind.db successfully!\nCongratulation! Have fun!\n");
}
return 0;
}
rockx_object_t *get_max_face(rockx_object_array_t *face_array)
{
if (face_array->count == 0)
{
return NULL;
}
rockx_object_t *max_face = NULL;//存储人脸的最大指针
int i;
for (i = 0; i < face_array->count; i++)
{
rockx_object_t *cur_face = &(face_array->object[i]);
if (max_face == NULL)
{
max_face = cur_face;
continue;
}
int cur_face_box_area = (cur_face->box.right - cur_face->box.left) *
(cur_face->box.bottom - cur_face->box.top);
int max_face_box_area = (max_face->box.right - max_face->box.left) *
(max_face->box.bottom - max_face->box.top);
if (cur_face_box_area > max_face_box_area)
{
max_face = cur_face;
}
}
printf("get_max_face %d\n", i - 1);
return max_face;
}
int run_face_recognize(const char *name, rockx_image_t *in_image, rockx_face_feature_t *out_feature, unsigned char * buffer, int buffer_size)
{
rockx_ret_t ret;
/*************** FACE Detect ***************/
// create rockx_face_array_t for store result
rockx_object_array_t face_array;
memset(&face_array, 0, sizeof(rockx_object_array_t));
// detect face
ret = rockx_face_detect(face_det_handle, in_image, &face_array, NULL);
if (ret != ROCKX_RET_SUCCESS)
{
printf("rockx_face_detect error %d\n", ret);
return -1;
}
// process result
for (int i = 0; i < face_array.count; i++)
{
int left = face_array.object[i].box.left;
int top = face_array.object[i].box.top;
int right = face_array.object[i].box.right;
int bottom = face_array.object[i].box.bottom;
float score = face_array.object[i].score;
printf("%d box=(%d %d %d %d) score=%f\n", i, left, top, right, bottom,score);
}
rockx_object_t *max_face = get_max_face(&face_array);
if (max_face == NULL)
{
printf("error no face detected\n");
return -1;
}
// Face Align
rockx_image_t out_img;
memset(&out_img, 0, sizeof(rockx_image_t));
/*************** 2. 人脸对齐 ***************/
ret = rockx_face_align(face_5landmarks_handle, in_image, &(max_face->box),NULL, &out_img);
if (ret != ROCKX_RET_SUCCESS)
{
return -1;
}
/*************** 3. 人脸特征提取 ***************/
rockx_face_recognize(face_recognize_handle, &out_img, out_feature);
/*************** 4. 特征+图片数据入库 ***************/
insert_face_data_toDataBase(name, out_feature->feature, FEATURE_SIZE, buffer, buffer_size);
// Release Aligned Image
rockx_image_release(&out_img);
return 0;
}
int main(int argc, char *argv[])
{
rockx_ret_t ret;
printf("----------------- main init ---------------------\n");
#if 1
if (argc != 3)
{
printf("Usage: Insert DataBase ./sqlite3_operation_test name image_path\n");
return -1;
}
printf("Start Connection sqlite3......................\n");
Connection_sqlite3DataBase();
printf("End Connection_sqlite3DataBase......................\n");
// 创建rockx配置对象
rockx_config_t *config = rockx_create_config();
// 设置rockx模型数据路径(需提前放置模型文件)
rockx_add_config(config, ROCKX_CONFIG_DATA_PATH, "/userdata/rockx_data/");
/*************** Creat Handle ***************/
// create a face detection handle
// 1. 创建人脸检测句柄
ret = rockx_create(&face_det_handle, ROCKX_MODULE_FACE_DETECTION_V2, config, 0);
if (ret != ROCKX_RET_SUCCESS)
{
printf("init rockx module ROCKX_MODULE_FACE_DETECTION error %d\n", ret);
return -1;
}
// 2. 创建人脸5关键点句柄
ret = rockx_create(&face_5landmarks_handle, ROCKX_MODULE_FACE_LANDMARK_5,
config, 0);
if (ret != ROCKX_RET_SUCCESS)
{
printf("init rockx module ROCKX_MODULE_FACE_LANDMARK_68 error %d\n", ret);
return -1;
}
// 3. 创建人脸特征提取句柄
ret = rockx_create(&face_recognize_handle, ROCKX_MODULE_FACE_RECOGNIZE,
config, 0);
if (ret != ROCKX_RET_SUCCESS)
{
printf("init rockx module ROCKX_MODULE_FACE_LANDMARK_68 error %d\n", ret);
return -1;
}
const char *name = argv[1];
const char *image_path = argv[2];
rockx_face_feature_t out_feature1;
rockx_image_t input_image1;
rockx_image_read(image_path, &input_image1, 1); //读取图片文件到rockx_image_t(第二个参数1表示按RGB格式读取)
FILE *imageFile = fopen(image_path, "rb"); // 打开名为image.jpg的图片文件(二进制模式)
if (!imageFile)
{
printf("无法打开图片文件.\n");
return -1;
}
fseek(imageFile, 0L, SEEK_END); // 定位到文件尾部
long fileSize = ftell(imageFile); // 获取文件大小
rewind(imageFile); // 重新定位到文件起始位置
printf("fileSize = %ld\n", fileSize);
unsigned char *buffer = new unsigned char[fileSize]; // 分配足够大小的缓冲区
size_t bytesRead = fread(buffer, sizeof(unsigned char), fileSize, imageFile); // 从文件中读取图像数据到缓冲区
/*************** 执行人脸处理+入库 ***************/
run_face_recognize(name, &input_image1, &out_feature1, buffer, bytesRead);
#endif
return 0;
}
四.在板子上查询数据库的人脸数据
4.1. 打开sqlite3数据库

使用sqlite3 face.db打开人脸数据库
4.2. 查询人脸的数据

通过SQL查询语句检索人脸识别数据,执行命令为:
sql
SELECT * FROM face_data_table
查询结果如上方截图所示。