- 2024-12-28
-
发表了主题帖:
AI挑战营(进阶) 五:实现多张人脸的注册和识别
本帖最后由 zhuxirui 于 2024-12-29 15:51 编辑
本次节主要就是分享一下基于官方例程修改的代码,可以实现同时检测多张人脸,并且将结果输出到控制台,接下来还是简单地讲讲修改思路吧。
首先是facenet和arcface模型的输出和输入大小有区别,facenet的输入是160x160的图像输出是大小为128的特征向量而arcface则是112x112输入,512的输出,所以我们需要修改定义的模型输入尺寸和输出向量的数组长度。
接着是添加注册多张人脸,主要就是输入选定人脸文件夹下的路径并推理一次得到每一张人脸的特征向量储存下来,同时以他们的文件名作为人名存入人名集。通过摄像头采集图像逐帧输入到retinaface并得到可能的的人脸框后送入arcface检测,并且遍历人脸特征库,计算欧氏距离最小值,如果最小值小于阈值就记录下来,直到检测完一帧中的每一张人脸后输出结果。精度得分的话就是score = 1/(1+distance(x0,xmin)),比较简单粗暴,测试下来一般的distance<1.2就可以认为是同一个人了,所以score>0.45就可以认为识别到了人脸库的对应人脸,
修改后的main.cc代码如下:
// Copyright (c) 2023 by Rockchip Electronics Co., Ltd. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*-------------------------------------------
Includes
-------------------------------------------*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <iomanip>
#include <cstring>
#include <dirent.h>
#include <libgen.h>
#include <stdlib.h>
#include <vector>
#include <utility>
#include <algorithm>
#include <iostream>
#include <unordered_set>
#include "retinaface_facenet.h"
#include <time.h>
#include <sys/time.h>
#include "dma_alloc.cpp"
#define USE_DMA 0
/*-------------------------------------------
Main Function
-------------------------------------------*/
int main(int argc,char **argv)
{
//if (argc != 4)
//{
// printf("%s <retinaface model_path> <facenet model_path> <reference pic_path> \n", argv[0]);
// return -1;
//}
system("RkLunch-stop.sh");
//const char *model_path = argv[1];
//const char *model_path2 = argv[2];
//const char *image_path = argv[3];
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " <folder_path>\n";
return -1;
}
float set_dis = atof(argv[2]);
// 获取命令行参数中的文件夹路径
char* folder_path = argv[1];
struct stat path_stat;
if (stat(folder_path, &path_stat) != 0 || !S_ISDIR(path_stat.st_mode)) {
std::cerr << "The provided path is not a valid directory." << std::endl;
return -1;
}
// 打开目录
DIR* dir = opendir(folder_path);
if (dir == nullptr) {
std::cerr << "Failed to open directory." << std::endl;
return -1;
}
// 遍历文件夹并获取文件路径和文件名(不含后缀)
std::vector<std::string> file_paths;
std::vector<std::string> file_names_without_extension;
struct dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
if (entry->d_type == DT_REG) { // 普通文件
std::string file_path = std::string(folder_path) + "/" + entry->d_name;
file_paths.push_back(file_path);
// 获取不含后缀的文件名
char* base_name = basename(entry->d_name);
std::string name_without_extension = base_name;
size_t last_dot_pos = name_without_extension.find_last_of('.');
if (last_dot_pos != std::string::npos) {
name_without_extension = name_without_extension.substr(0, last_dot_pos);
}
file_names_without_extension.push_back(name_without_extension);
}
}
closedir(dir);
// 文件数量
size_t face_target = file_paths.size();
// 创建一个char**数组来存储文件路径
char** image_path_collection = new char*[face_target];
for (size_t i = 0; i < face_target; ++i) {
image_path_collection[i] = new char[file_paths[i].size() + 1];
std::strcpy(image_path_collection[i], file_paths[i].c_str());
}
// 创建一个char**数组来存储文件名(不含后缀)
char** name_collection = new char*[face_target];
for (size_t i = 0; i < face_target; ++i) {
name_collection[i] = new char[file_names_without_extension[i].size() + 1];
std::strcpy(name_collection[i], file_names_without_extension[i].c_str());
}
char *model_path = "./model/retinaface.rknn";
char *model_path2 = "./model/arcface.rknn";
char *image_path = "./model/test.jpg";
float* out_fp32 = (float*)malloc(sizeof(float) * 512); // 每个 out_fp32 存储 512 个 float
float** out_fp32_collection = (float**)malloc(sizeof(float*) * face_target); // out_fp32_collection
clock_t start_time;
clock_t end_time;
//Model Input
//Retinaface
int retina_width = 640;
int retina_height = 640;
//Facenet
int facenet_width = 112;
int facenet_height = 112;
int channels = 3;
int ret;
rknn_app_context_t app_retinaface_ctx;
rknn_app_context_t app_facenet_ctx;
object_detect_result_list od_results;
memset(&app_retinaface_ctx, 0, sizeof(rknn_app_context_t));
memset(&app_facenet_ctx, 0, sizeof(rknn_app_context_t));
//Init Model
init_retinaface_facenet_model(model_path, model_path2, &app_retinaface_ctx, &app_facenet_ctx);
//Init fb
int disp_flag = 0;
int pixel_size = 0;
size_t screensize = 0;
int disp_width = 0;
int disp_height = 0;
void* framebuffer = NULL;
struct fb_fix_screeninfo fb_fix;
struct fb_var_screeninfo fb_var;
int framebuffer_fd = 0; //for DMA
cv::Mat disp;
int fb = open("/dev/fb0", O_RDWR);
if(fb == -1)
printf("Screen OFF!\n");
else
disp_flag = 1;
if(disp_flag){
ioctl(fb, FBIOGET_VSCREENINFO, &fb_var);
ioctl(fb, FBIOGET_FSCREENINFO, &fb_fix);
disp_width = fb_var.xres;
disp_height = fb_var.yres;
pixel_size = fb_var.bits_per_pixel / 8;
printf("Screen width = %d, Screen height = %d, Pixel_size = %d\n",disp_width, disp_height, pixel_size);
screensize = disp_width * disp_height * pixel_size;
framebuffer = (uint8_t*)mmap(NULL, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
if( pixel_size == 4 )//ARGB8888
disp = cv::Mat(disp_height, disp_width, CV_8UC3);
else if ( pixel_size == 2 ) //RGB565
disp = cv::Mat(disp_height, disp_width, CV_16UC1);
#if USE_DMA
dma_buf_alloc(RV1106_CMA_HEAP_PATH,
disp_width * disp_height * pixel_size,
&framebuffer_fd,
(void **) & (disp.data));
#endif
}
else{
disp_height = 240;
disp_width = 240;
}
//Init Opencv-mobile
cv::VideoCapture cap;
cv::Mat bgr(disp_height, disp_width, CV_8UC3);
cv::Mat retina_input(retina_height, retina_width, CV_8UC3, app_retinaface_ctx.input_mems[0]->virt_addr);
cap.set(cv::CAP_PROP_FRAME_WIDTH, disp_width);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, disp_height);
cap.open(0);
//Get referencve img feature
cv::Mat image = cv::imread(image_path);
cv::Mat facenet_input(facenet_height, facenet_width, CV_8UC3, app_facenet_ctx.input_mems[0]->virt_addr);
letterbox(image,facenet_input);
ret = rknn_run(app_facenet_ctx.rknn_ctx, nullptr);
if (ret < 0) {
printf("rknn_run fail! ret=%d\n", ret);
return -1;
}
uint8_t *output = (uint8_t *)(app_facenet_ctx.output_mems[0]->virt_addr);
float* reference_out_fp32 = (float*)malloc(sizeof(float) * 512);
//output_normalization(&app_facenet_ctx,output,reference_out_fp32);
//memset(facenet_input.data, 0, facenet_width * facenet_height * channels);
//float* out_fp32 = (float*)malloc(sizeof(float) * 128);
for(int i = 0; i < face_target; i++){
cv::Mat image = cv::imread(image_path_collection[i]);
cv::Mat facenet_input(facenet_height, facenet_width, CV_8UC3, app_facenet_ctx.input_mems[0]->virt_addr);
letterbox(image,facenet_input);
ret = rknn_run(app_facenet_ctx.rknn_ctx, nullptr);
if (ret < 0) {
printf("rknn_run fail! ret=%d\n", ret);
return -1;
}
uint8_t *output = (uint8_t *)(app_facenet_ctx.output_mems[0]->virt_addr);
float* reference_out_fp32 = (float*)malloc(sizeof(float) * 512);
output_normalization(&app_facenet_ctx,output,reference_out_fp32);
out_fp32_collection[i] = reference_out_fp32;
}
//
char show_text[12];
char fps_text[32];
float fps = 0;
while(1)
{
start_time = clock();
//opencv get photo
cap >> bgr;
cv::resize(bgr, retina_input, cv::Size(retina_width,retina_height), 0, 0, cv::INTER_LINEAR);
ret = inference_retinaface_model(&app_retinaface_ctx, &od_results);
if (ret != 0)
{
printf("init_retinaface_model fail! ret=%d\n", ret);
return -1;
}
//printf("running------------------\n");
for (int i = 0; i < od_results.count; i++)
{
//Get det
object_detect_result *det_result = &(od_results.results[i]);
mapCoordinates(bgr, retina_input, &det_result->box.left , &det_result->box.top);
mapCoordinates(bgr, retina_input, &det_result->box.right, &det_result->box.bottom);
cv::rectangle(bgr,cv::Point(det_result->box.left ,det_result->box.top),
cv::Point(det_result->box.right,det_result->box.bottom),cv::Scalar(0,255,0),3);
//Face capture
cv::Rect roi(det_result->box.left,det_result->box.top,
(det_result->box.right - det_result->box.left),
(det_result->box.bottom - det_result->box.top));
cv::Mat face_img = bgr(roi);
//Give five key points
// for(int j = 0; j < 5;j ++)
// {
// //printf("point_x = %d point_y = %d\n",det_result->point[j].x,
// // det_result->point[j].y);
// cv::circle(bgr,cv::Point(det_result->point[j].x,det_result->point[j].y),10,cv::Scalar(0,255,0),3);
// }
letterbox(face_img,facenet_input);
ret = rknn_run(app_facenet_ctx.rknn_ctx, nullptr);
if (ret < 0) {
printf("rknn_run fail! ret=%d\n", ret);
return -1;
}
output = (uint8_t *)(app_facenet_ctx.output_mems[0]->virt_addr);
float *norm_list = (float*)malloc(sizeof(float) * face_target);
output_normalization(&app_facenet_ctx, output, out_fp32);
std::vector<std::pair<float, int>> distances; // 存储距离和对应的标签
std::vector<std::pair<std::string, float>> detected_names_with_scores; // 存储人名和精度得分
std::unordered_set<int> matched_indices; // 用于记录已匹配的标签索引
// 计算所有距离
for (int i = 0; i < face_target; i++) {
float distance = get_duclidean_distance(out_fp32, out_fp32_collection[i]);
distances.push_back(std::make_pair(distance, i));
}
// 根据距离对标签进行排序,最近的标签在最前面
std::sort(distances.begin(), distances.end());
// 遍历排序后的距离列表,找到所有小于阈值的匹配标签
for (const auto& pair : distances) {
if (pair.first < set_dis) {
// 确保每个标签只匹配一次
if (matched_indices.find(pair.second) == matched_indices.end()) {
// 计算精度得分,距离越小,得分越高
float score = 1.0f / (1.0f + pair.first); // 精度得分公式
detected_names_with_scores.emplace_back(name_collection[pair.second], score);
matched_indices.insert(pair.second); // 记录已经匹配的标签
}
}
}
// 构建最终的检测到的人名和精度得分字符串
std::ostringstream name_detected_stream;
for (size_t i = 0; i < detected_names_with_scores.size(); i++) {
if (i > 0) {
name_detected_stream << ", ";
}
// 使用 std::fixed 和 std::setprecision 来确保精度得分有两位小数
name_detected_stream << detected_names_with_scores[i].first
<< " (" << std::fixed << std::setprecision(2) << detected_names_with_scores[i].second << ")";
}
std::string name_detected = name_detected_stream.str();
// 打印检测到的人名和精度得分
if (!name_detected.empty()) {
printf("detected: %s\n", name_detected.c_str());
}
// 释放内存
free(norm_list);
//sprintf(show_text,"norm=%f",norm);
//cv::putText(bgr, show_text, cv::Point(det_result->box.left, det_result->box.top - 8),
// cv::FONT_HERSHEY_SIMPLEX,0.5,
// cv::Scalar(0,255,0),
// 1);
}
if(disp_flag){
//Fps Show
sprintf(fps_text,"fps=%.1f",fps);
cv::putText(bgr,fps_text,cv::Point(0, 20),
cv::FONT_HERSHEY_SIMPLEX,0.5,
cv::Scalar(0,255,0),1);
//LCD Show
if( pixel_size == 4 )
cv::cvtColor(bgr, disp, cv::COLOR_BGR2BGRA);
else if( pixel_size == 2 )
cv::cvtColor(bgr, disp, cv::COLOR_BGR2BGR565);
memcpy(framebuffer, disp.data, disp_width * disp_height * pixel_size);
#if USE_DMA
dma_sync_cpu_to_device(framebuffer_fd);
#endif
}
//Update Fps
end_time = clock();
fps = ((float)CLOCKS_PER_SEC / (end_time - start_time)) ;
}
free(reference_out_fp32);
free(out_fp32);
if(disp_flag){
close(fb);
munmap(framebuffer, screensize);
#if USE_DMA
dma_buf_free(disp_width*disp_height*2,
&framebuffer_fd,
bgr.data);
#endif
}
release_facenet_model(&app_facenet_ctx);
release_retinaface_model(&app_retinaface_ctx);
return 0;
}
编译后将install/luckfox_pico_retinaface_facenet_demo的内容上传到开发板
chmod 777 ./luckfox_pico_retinaface_facenet
./luckfox_pico_retinaface_facenet ./test/person 1.2
其中 ./test/person路径下存放要注册的人脸, 1.2为设置的阈值
结果展示
[localvideo]c52c1c740df5df0876079cb7204ba097[/localvideo]
按照官方rkmpi的例程,应该还能实现rtsp推理查看推理结果,并且画框标注,但是略复杂一些,看看后面有没有时间完善一下
就结果来看感觉效果还有提升空间,之前自己在python基于onnx的测试的时候正确的distance基本都在0.8左右也就是得分基本在0.5以上,大概率是因为int8量化带来的精度损失。 在一些复杂场景下人脸识别结果可能不太理想,但单人的检测还是能胜任的。
下一节我们将结合LuckFox pico的引脚I/O资源实现我们的智能门禁抓拍系统
- 2024-12-25
-
发表了主题帖:
AI挑战营(进阶) 四:onnx2rknn流程简析
上节我们已经得到了retinaface和arcface的onnx模型本节,接下来终于进入到本次挑战最重要的环节。我们将onnx转换为可以在rv1106的npu上运行的rknn模型
首先我们可以在luckfox官方的仓库下找到luckfox官方提供的环境包:LuckfoxTECH/luckfox_pico_rknn_example。
部署rknn环境先前也有了很多教程,但是建议还是按照luckfox教程里的来。建议使用官方的这个库,里面的rknpu版本为1.6.2,而官方的为2.3.0。至少我在使用官方的rknpu转换到板端运行的时候会报版本不兼容的错误。使用官方的版本和转换脚本就不会。
按照官方教程指引找到转换脚本,官方有提供retinaface的转换脚本及模型,retinaface直接按照官方的教程转换即可。
得到模型后我们也可以使用第一节提到的rkmpi例程测试一下我们的模型。
至于arcface模型,官方只提供了facenet模型的例程,所以我们需要修改一下covert.py
#convert.py
import sys
from rknn.api import RKNN
if __name__ == '__main__':
model_path = "../model/w600k_mbf.onnx"
dataset_path = "../dataset/dataset.txt"
output_path = "./arcface.rknn"
model_type = "arcface"
# Create RKNN object
rknn = RKNN(verbose=False)
# Pre-process config
print('--> Config model')
if model_type == 'Retinaface':
rknn.config(mean_values=[[104, 117, 123]], std_values=[[1, 1, 1]], target_platform='rv1103',
quantized_algorithm="normal", quant_img_RGB2BGR=True,optimization_level=0)
print("Use retinaface mode")
elif model_type == 'arcface':
rknn.config(mean_values=[[127.5, 127.5, 127.5]], std_values=[[127.5, 127.5, 127.5]], target_platform='rv1103')
print('done')
# Load model
print('--> Loading model')
ret = rknn.load_onnx(model=model_path)
if ret != 0:
print('Load model failed!')
exit(ret)
print('done')
# Build model
print('--> Building model')
ret = rknn.build(do_quantization=True, dataset=dataset_path)
if ret != 0:
print('Build model failed!')
exit(ret)
print('done')
# Export rknn model
print('--> Export rknn model')
ret = rknn.export_rknn(output_path)
if ret != 0:
print('Export rknn model failed!')
exit(ret)
print('done')
# Release
rknn.release()
后期再看看需不需要加入板端推理和精度分析的内容。
至此我们就已经在luckfox 上成功的部署了自己的转换的模型,下一节我们会基于官方的例程修改得到一个可以注册并识别多张人脸的例程。
-
发表了主题帖:
AI挑战营(进阶) 三:获取onnx模型
紧跟上节我们介绍了InsightFace中各个流程使用到的模型并选择了retinaface和arcface来实现我们人脸识别的任务。
根据LuckFox官方和RKNPU2的文档指引,我们需要先获取onnx(最常用)的模型格式,但这在InsightFace官方程序中并未提供,我们可以自己找一下已有的insighface,onnx化部署工作。
一般我会先在onnx_model_zoo项目下寻找,这个项目由ONNX官方收录了许多完成了预训练的ONNX模型,可以降低我们的转换工作的难度。
但是很不幸,这里只提供了retinanet的网络模型,并没有retinaface的模型,而且里面的arcface模型采用的骨干网络为ResNet100过于复杂庞大,虽然也可以转换为rknn而且精度更高,但不适合在RV1106这样计算资源十分紧张的端侧设备上部署,我们希望能找到一个使用mobilenetssd作为骨干网络的arcface模型。
最后我在这两个项目里面找到了我们需要的onnx模型。
RetinaFace: https://github.com/bubbliiiing/retinaface-pytorch
ArcFace: https://github.com/yakhyo/face-reidentification/tree/main
其中的retinaface模型仍需要我们从pth转换到onnx但这个过程比较简单,前面也已经有网友给出了转换步骤和脚本
git clone https://github.com/bubbliiiing/retinaface-pytorch
cd retinaface-pytorch
touch 2onnx.py
#2onnx.py
from nets.retinaface import RetinaFace
from utils.config import cfg_mnet
import torch
model_path='model_data/Retinaface_mobilenet0.25.pth' #模型路径
model=RetinaFace(cfg=cfg_mnet,pretrained = False) #模型初始化
device = torch.device('cpu')
model.load_state_dict(torch.load(model_path,map_location=device),strict=False) #模型加载
net=model.eval()
example=torch.rand(1,3,640,640) #给定输入
torch.onnx.export(model,(example),'model_data/retinaface.onnx',verbose=True,opset_version=9) #导出
接着我们可以通过https://netron.app/,将模型拖入观察一下模型结构,主要是确认一下输入和输出的大小
Retianface:
Arcface:
我们发现arcface模型的batch没有固定,rv1106似乎也不支持批量推理和动态输入,所以需要我们手动调整一下
touch crop_model.py
#Crop_model:
import onnx
def change_input_size(model_path, new_size1):
model = onnx.load(model_path)
for input in model.graph.input:
input.type.tensor_type.shape.dim[0].dim_value = new_size1
onnx.save(model, model_path)
change_input_size("w600k_mbf.onnx", 1)
调整完成后得到的arcface模型结构如下:
至此我们就得到了retinaface和arcface两个关键的onnx模型,下一节我们将完成onnx2rknn的工作。
这里也给出了onnx模型供大家参考
- 2024-12-15
-
发表了主题帖:
AI挑战营(进阶) 二:InsightFace项目讲解
InsightFace是一个开源的2D和3D深度人脸分析工具箱,它提供了学术领域多种SOTA算法实现,包括人脸检测、人脸识别和人脸对齐,这些算法在学术研究和工业应用中都有广泛的应用。InsightFace主要技术流程如下:
一、Face Detection(人脸检测)。这一流程主要是找到图像中的人脸,为后面的识别提供更加准确的人脸信息。
- RetinaFace:InsightFace支持RetinaFace算法,这是由InsightFace团队提出的一种实时的单级面部检测器。
- SCRFD:SCRFD是一种高效的高精度人脸检测方法,在Arxiv中进行了初步描述。InsightFace同样提供易于使用的管道来训练支持NAS的高效人脸检测器。
-MCNN (Multi-task Cascaded Convolutional Networks):MCNN是一种级联卷积神经网络,用于人脸检测。它通过级联多个子网络来逐步精细化人脸的检测过程。
二、Face Alignment(人脸对齐)。这一流程可以增强人脸信息的鲁棒性,优化人脸识别效果
- SDUNets:SDUNets是一种基于热图的方法,被BMVC接受。这种方法通过找到人脸的若干个关键点(基准点,如眼角,鼻尖,嘴角等),然后利用这些对应的关键点通过相似变换(旋转、缩放和平移)将人脸尽可能变换到标准人脸。
- SimpleRegression:SimpleRegression提供非常轻量级的面部特征点模型,具有快速坐标回归功能。这些模型的输入是松散裁剪的人脸图像,而输出是各关键点的坐标。
三、Face Recognition(人脸识别)。这一过程会将人脸图片进行特征提取并交由模型推理得到一组向量用于相似度计算。
- ArcFace:InsightFace主要使用一种加性角裕量损失(ArcFace)的模型来获得人脸识别的判别特征。这种模型通过在特征向量和分类边界之间引入角度间隔,增强了模型的判别能力,从而提高了人脸识别的准确性。
由于人脸对齐需要额外的计算资源且对一般场景下的人脸识别准确率提升有限,所以在接下来的工作中我们只完成人脸检测和人脸识别两个模型也就是retinaface和arcface的部署和转换工作。
-
发表了主题帖:
AI挑战营(进阶) 一:LuckFox Pico Max Pro(RV1106)首次开箱使用以及官方例程测试
本帖最后由 zhuxirui 于 2024-12-15 12:16 编辑
有幸收到了这个来自luckfox的板卡,它搭载了瑞芯微的RV1106芯片,int8推理能力达到了1tops,这意味着我们可以在这个平台上部署一些微型的神经网络,例如目标检测模型yolov5,图像分类模型mobilenetssd等,那么本贴先来说说这个板卡的基本使用和例程效果展示。
因为Max Pro版本板载了256M的FLASH芯片,所以我们无需准备内存卡。首先我们安装官网的教程下载FLASH专用的镜像包,接着安装官方提供的烧录程序烧录即可完成系统安装(具体请查看官方教程)。
我这边就直接插了网线接路由器使用,若前面烧录无误,上电后板卡会闪红灯,连上网线后便可在路由器后台找到名称luckfox的设备(或者观察租期剩余时间最多的设备),记下ip使用ssh工具登录即可。
我们现在进到了ssh命令行,实际上luckfox官方还提供很多其他技术的例程,不过我们的任务是在上面部署AI模型复现人脸识别,所以就直接跳转到RKNN模型推理部分(细节可查看官网教程)。
按照教程我们可以在官方的github仓库找到install下的五个例程,为了演示更直观这里选择了rkmpi的测试例程
git clone https://github.com/LuckfoxTECH/luckfox_pico_rkmpi_example.git
cd luckfox_pico_rkmpi_example/install/luckfox_pico_rtsp_yolov5_demo
chmod 777 luckfox_pico_rtsp_yolov5
./luckfox_pico_rtsp_yolov5
这里需要注意先使用uname -a查询一下你所使用板卡的内核版本,我使用官网提供的buildroot是5.10.110,但是官方仓库的默认分支为5.10.160,两个分支的代码会存在不兼容的情况,注意分辨。
接着打开VLC软件(我这里使用了potplayer)右键主界面-->打开链接-->输入rtsp://{luckfox的IP} /live/0,点击确定即可看到RTSP推流画面
软件可以正常显示rtsp推流的画面,功能正常,实际帧数在一两帧左右,为了推流稳定,需要设置较大的延迟缓冲时间(potplayer似乎会自动调节)。
下一个帖子我们讲来讲一讲InsightFace的项目结构和推流流程。
- 2024-11-21
-
回复了主题帖:
入围名单公布:嵌入式工程师AI挑战营(进阶)的挑战者们,领取板卡啦
个人信息已确认,领取板卡,可继续完成任务。
- 2024-11-14
-
回复了主题帖:
嵌入式工程师AI挑战营(进阶):在RV1106部署InsightFace算法的多人实时人脸识别实战
本人在RK3588的开发板上跑通过官方的RKNN例程和RKLLM大模型,也部署过自己训练的yolov5模型,了解RKNN的api调用,但是考虑到rk3588的成本较高且电源要求较高,一直找不到实际的嵌入式应用场景,这次申请也是想在全流程的嵌入式ai部署中再多多学习,多多理解技术细节,同时找到一个边缘ai的落脚点。
InsightFace框架提供了丰富的人脸识别、人脸检测和人脸对齐算法,并且针对训练和部署进行了优化,个人认为其最大的优势就是实现了毫秒级的实时检测,这对嵌入式设备十分友好,借助瑞芯微的RKNN工具链,还可以进一步提高推理效能。在实际部署中,可以将预训练好的模型通过pytorch框架导出为pt模型再转为onnx模型(也可直接转为rknn模型)再通过工具链转换为rknn模型,在PC上进行算法验证后期便可上传到开发板进行运行测试,相应程序功能预计可以采用python和opencv包实现。
可以利用InsightFace的人脸识别算法实现一套智能门铃系统当录入人脸被识别后,亮灯开门,当陌生人脸靠近后蜂鸣器发出警报并抓拍人脸。