Kithara与Dlib (二) - 人脸实时检测

news2024/11/24 8:50:35

Kithara和Dlib进行人脸实时检测


目录

    • Kithara和Dlib进行人脸实时检测
      • ResNet (残差网络)
      • 流程介绍
      • 核心代码
      • 性能测试
      • 开源源码


ResNet (残差网络)

ResNet,全称为Residual Network(残差网络),是由何凯明(Kaiming He)、张祥雨(Xiangyu Zhang)、任少卿(Shaoqing Ren)和孙剑(Jian Sun)在2015年提出的深度学习架构。这项工作在当年的计算机视觉领域顶级会议CVPR上获得了最佳论文奖,并且ResNet架构因其在ImageNet图像分类挑战中的卓越表现而迅速成为深度学习领域的里程碑之一。

ResNet不仅在图像分类任务中表现出色,还被广泛应用于目标检测、语义分割、人脸识别等多个计算机视觉领域,以及自然语言处理和其他机器学习任务中。

本次人像识别就是通过Dlib已经训练好的ResNet模型进行人脸识别。

流程介绍

Kithara RealTime Suite、OpenCV、Dlib与Qt结合使用ResNet进行实时人脸识别的流程可以概括如下:

  • Kithara 实时图像采集:
    Kithara RealTime Suite 在操作系统内核层处理图像采集,确保低延迟和高精度的时间响应,这对于实时应用至关重要。
    它直接从摄像头或图像传感器获取图像数据,由于其运行在内核级别,可以避免用户空间程序可能引入的额外延迟。

  • OpenCV 图像转换:
    OpenCV 是一个强大的计算机视觉库,用于处理图像和视频流。
    它将从Kithara接收到的原始图像数据转换成适合进一步处理的格式,例如转换色彩空间或调整尺寸。

  • OpenCV 到 Dlib 图像适配:
    OpenCV 处理后的图像被转换成 Dlib 可以处理的格式。Dlib 是一个用于机器学习和数据分析的C++库,特别擅长处理计算机视觉任务。
    使用Dlib的深度残差网络(ResNet)进行人脸识别,这包括人脸检测和特征提取。

  • 共享内存传输:
    处理好的图像数据通过共享内存机制从内核层传输到用户空间的应用层。共享内存是一种进程间通信方式,允许不同进程之间高效地交换大量数据,而无需复制数据。
    这种机制保证了图像数据可以在不同层级之间快速传递,减少了数据传输的开销。

  • Qt 渲染与交互:
    Qt 是一个跨平台的图形用户界面工具包,用于创建高性能的桌面和移动应用程序。
    Qt 接收处理好的图像数据,并负责将其渲染在屏幕上,同时提供用户界面元素,如按钮、滑块等,以便用户与系统进行交互。
    用户可以通过Qt界面查看识别结果,控制摄像头设置,或者执行其他操作。

整个流程从硬件层面的图像采集开始,经过多级软件处理,最终在用户界面上呈现结果,形成了一个完整的实时人脸识别系统。

核心代码

  • 图像采集部分
// 这是实时任务将运行的函数,并对接收到的图像执行 Dlib 操作。只有实时任务才应调用 Dlib 函数。
KSError __stdcall Dlibcallback(void * /*pArgs*/, void * /*pContext*/)
{
    // 在内核层模式下自动并行化 OpenCV 可能会与您的实时应用程序冲突。 建议关闭自动并行化,除非真的需要
    // 禁用并行化
    cv::setNumThreads(0);

    // 表示已准备好处理图像。
    krenel_data_ptr_->ready = 1;

    DlibHandle dlib_handle;

    // 图形抖动性测试
    int64 last_diff_time {0};
    int is_valid_time = 0;  // 时间是否有效 0 无效 1 时间有效
    int count = 0;  // 计数器
    int64 jitter_time_sum {0};  // 抖动总时间

    // 处理循环,此循环仅在发出中止信号时停止。
    for (;;)
    {
        // 等待图像接收或停止的通知。
        KSError error = KS_waitForEvent(krenel_data_ptr_->image_received_event_handle, KSF_NO_FLAGS, 0);
        if (error != KS_OK) { KS_printK("KS_waitForEvent failed! \n"); }
        if (krenel_data_ptr_->abort != 0) { break; }

        // 计数器
        count++;
        // 获取当前时间,用于计算图像处理的抖动时间
        int64 last_time {0};
        error = KS_getClock(&last_time, KS_CLOCK_MEASURE_HIGHEST);
        if (error != KS_OK) { return error; }

        // 获取接收到的图像数据的缓冲区
        KSCameraBlock *camera_block;
        void *image_data;
        error = KS_recvCameraImage(krenel_data_ptr_->stream_handle, &image_data, &camera_block,KSF_NO_FLAGS);
        if (error != KS_OK)
        {
            krenel_data_ptr_->ready = 1;
            continue;
        }

        //如果接收到的块类型不是图像,我们跳过。在任何情况下,如果 KS_recvCameraImage() 成功接收到的缓冲区必须使用 KS_releaseCameraImage() 释放。
        if (camera_block->blockType != KS_CAMERA_BLOCKTYPE_IMAGE)
        {
            KS_releaseCameraImage(krenel_data_ptr_->stream_handle, image_data, KSF_NO_FLAGS);
            break;
        }

        // 在构建 OpenCV cv::Mat 之前,请检查接收到的图像是否具有正确的像素格式。
        const auto *image_block = reinterpret_cast<KSCameraImage *>(camera_block);
        // 图像转换
        cv::Mat image = dlib_handle.CreateMat(image_block->height, image_block->width, image_block->pixelFormat, image_data, image_block->linePadding);

        // 获取人脸名称
        for (char & chr : krenel_data_ptr_->image_info.person_name)
        {
            chr = '\0';
        }

        std::string name{};
        // 面部识别检测
        image = dlib_handle.FaceDetect(image,name);

        KS_printK("name:%s\n",name.c_str());
        if (const size_t ret_size = KSRTL_strlen(name.c_str()); ret_size < NAME_SIZE - 1)
        {
            KSRTL_strncpy(krenel_data_ptr_->image_info.person_name, name.c_str(), NAME_SIZE);
        }
        else
        {
            KSRTL_strncpy(krenel_data_ptr_->image_info.person_name, nullptr, NAME_SIZE);
        }

        error = KS_releaseCameraImage(krenel_data_ptr_->stream_handle, image_data, KSF_NO_FLAGS);
        if (error != KS_OK) { KS_printK("KS_releaseCameraImage failed! \n"); }

        // 填充图像信息到共享内存中
        krenel_data_ptr_->image_info.image_height = image.rows;
        krenel_data_ptr_->image_info.image_width = image.cols;
        krenel_data_ptr_->image_info.pixel_format = image_block->pixelFormat;

        if (image_block->pixelFormat == KS_CAMERA_PIXEL_MONO_8)
        {
            KS_memCpy(pixel_buffer_, image.data, (int) image.cols * image.rows, KSF_NO_FLAGS);
        }
        else if (image_block->pixelFormat == KS_CAMERA_PIXEL_BGR_8)
        {
            KS_memCpy(pixel_buffer_, image.data, (int) image.cols * image.rows * 3, KSF_NO_FLAGS);
        }

        //  图形处理完成后,减去上次处理完成的时间
        int64 time {0};
        error = KS_getClock(&time, KS_CLOCK_MEASURE_HIGHEST);
        if (error != KS_OK) { return error; }

        // 检测圆处理时间
        const int64 diff_time = time - last_time;

        int64 time_cyc = diff_time;
        KS_convertClock(&time_cyc, KS_CLOCK_MEASURE_HIGHEST, KS_CLOCK_MACHINE_TIME, KSF_NO_FLAGS);
        krenel_data_ptr_->jitter_value.time_cyc = time_cyc;

        if (is_valid_time == 0)
        {
            last_diff_time = diff_time;
            is_valid_time = 1;
        }
        else
        {
            // 处理时间的抖动
            const int64 jitter_time = diff_time - last_diff_time;
            int64 single_time = jitter_time;
            KS_convertClock(&single_time, KS_CLOCK_MEASURE_HIGHEST, KS_CLOCK_MACHINE_TIME, KSF_NO_FLAGS); // 100 ns 为单位
            last_diff_time = diff_time;
            jitter_time_sum += single_time;

            if (krenel_data_ptr_->jitter_value.lat_min > single_time)
            {
                krenel_data_ptr_->jitter_value.lat_min = single_time;
            }

            if (krenel_data_ptr_->jitter_value.lat_max < single_time)
            {
                krenel_data_ptr_->jitter_value.lat_max = single_time;
            }

            krenel_data_ptr_->jitter_value.lat_avg = jitter_time_sum / count;
            krenel_data_ptr_->jitter_value.cur_val = single_time;
        }

        krenel_data_ptr_->ready = 1;

        // 启动相机拍摄获取下一帧图像 KS_CAMERA_SINGLE_FRAME 单帧获取
        KS_startCameraAcquisition(krenel_data_ptr_->camera_handle, KS_CAMERA_SINGLE_FRAME, KSF_NO_FLAGS);
    }
    return KS_OK;
}
  • 人脸检测部分
cv::Mat DlibHandle::FaceDetect(const cv::Mat &mat, std::string &name)
{
    try
    {
        if (mat.empty())
        {
            return {};
        }

        constexpr int zoom = 2;
        cv::Mat src = mat;

        cv::Mat gray;
        // 降低采样
        cv::pyrDown(mat, gray, cv::Size(mat.cols / zoom, mat.rows / zoom));
        // 转换为灰度图
        if (mat.type() == CV_8UC3)
        {
            cv::cvtColor(gray, gray, cv::COLOR_BGR2GRAY);
        }

        // 将OpenCV图像转换成Dlib的matrix
        dlib::array2d<dlib::bgr_pixel> dlib_img;
        dlib::assign_image(dlib_img, dlib::cv_image<uchar>(gray));

        // 提取人脸
        const std::vector<dlib::rectangle> dets = detector_(dlib_img);
        std::vector<dlib::matrix<dlib::rgb_pixel> > faces;
        //std::vector<dlib::full_object_detection> shapes;
        for (auto det: dets)
        {
            //画出人脸所在区域
            cv::Rect r;
            r.x = det.left() * zoom;
            r.y = det.top() * zoom;
            r.width = det.width() * zoom;
            r.height = det.height() * zoom;
            cv::rectangle(src, r, cv::Scalar(0, 255, 0), 3, cv::LINE_8, 0);

            // 提取面部特征
            auto shape = predictor_(dlib_img, det);
            //shapes.push_back(shape);

            // 根据面部轮廓裁剪面部图像
            dlib::matrix<dlib::rgb_pixel> face_chip;
            // 人脸对齐
            extract_image_chip(dlib_img, get_face_chip_details(shape, 150, 0.25), face_chip);
            faces.push_back(std::move(face_chip));
        }

        // 绘制特征点
        //DrawFaceFeaturePoints(src, shapes);

        // 人像追踪
        bool is_check {false};
        if (last_rectangles_.size() == dets.size())
        {
            for (int i = 0; i < dets.size(); ++i)
            {
                int x = dets[i].left() - last_rectangles_[i].left() > 0
                            ? dets[i].left() - last_rectangles_[i].left()
                            : last_rectangles_[i].left() - dets[i].left();
                int y = dets[i].top() - last_rectangles_[i].top() > 0
                            ? dets[i].top() - last_rectangles_[i].top()
                            : last_rectangles_[i].top() - dets[i].top();

                if (x < 100 && y < 100)
                {
                    cv::Point origin;
                    origin.x = dets[i].left() * zoom;
                    origin.y = dets[i].top() * zoom - 15;
                    cv::putText(src, last_name_[i], origin, cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 0, 0), 2, 2, false);
                }
                else
                {
                    is_check = true;
                }
            }
        }
        else
        {
            is_check = true;
        }

        if (is_check)
        {
            last_name_.clear();
            // 提取这一组RseNet人脸识别数据
            const std::vector<dlib::matrix<float, 0, 1> > face_descriptors = res_net_face_recognition_(faces);
            for (int i = 0; i < face_descriptors.size(); ++i)
            {
                for (const auto &[key,value]: name_face_map_)
                {
                    // 计算欧几里得距离,获取相似度
                    vec_error_ = dlib::length(face_descriptors[i] - value);
                    // 人像已被识别
                    if (vec_error_ < error_min_)
                    {
                        // 识别成功后,去除_后面的数字编号
                        name = key.substr(0, key.find("_"));
                        break;
                    }
                    else
                    {
                        if (unknow_check_count_ >10)
                        {
                            name = "UnKnown";
                            unknow_check_count_ = 0;
                        }
                        unknow_check_count_++;
                        last_rectangles_.clear();
                    }
                }

                //将文本框居中绘制
                cv::Point origin;
                origin.x = dets[i].left() * zoom;
                origin.y = dets[i].top() * zoom - 15;
                cv::putText(src, name, origin, cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 0, 0), 2, 2, false);
                last_name_[i] = name + " " + std::to_string(vec_error_);
            }
        }

        last_rectangles_ = dets;
        return src;
    }
    catch (...)
    {
        return {};
    }
}

软件效果图:
在这里插入图片描述

性能测试

简单优化
Kithara Windows实时套件通过其独特的CPU核心独占特性,能够在Windows环境下即便CPU处于高负载状态时,依然保持检测任务的稳定执行和结果的及时输出。然而,考虑到人像识别算法对计算资源(尤其是CPU和GPU)的高需求,实际性能在不同硬件配置的机器上会呈现出显著差异。

进一步优化
实际测试表明,人像识别的效率和可靠性高度依赖于图像处理的优化水平。越是精细的程序优化,比如针对特定硬件的代码调优、算法的高效实现以及内存管理的优化,都能显著减少图像处理所需时间,同时提升系统的整体稳定性。

总的来说,Kithara的实时处理能力确保了在复杂环境下的可靠运行,但为了最大化人像识别的性能,深入的图像解析优化和适应不同硬件的定制化调整是必不可少的。这种优化不仅能加快处理速度,还能增强系统的鲁棒性和一致性。

开源源码

测试源码现已开源,项目Demo均是测试代码,请勿用于实际项目!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1973503.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

飞瓜数据没有视频号爆款视频你就错过了涨粉的最新方法!

为了帮助用户更快的了解用户需求并切入视频号营销中&#xff0c;也为大家提供了更多的垂直类目。 飞瓜数据 飞瓜数据为很多视频平台提供了视频相关的数据服务&#xff0c;但我们发现并飞瓜并没有参与视频号相关的内容仅在视频号直播做了相关的扩展。视频号相关的数据并未提供。…

Java中等题-多数元素2(力扣)【摩尔投票升级版】

给定一个大小为 n 的整数数组&#xff0c;找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。 示例 1&#xff1a; 输入&#xff1a;nums [3,2,3] 输出&#xff1a;[3] 示例 2&#xff1a; 输入&#xff1a;nums [1] 输出&#xff1a;[1]示例 3&#xff1a; 输入&#xff1a;num…

队列的基本运算(顺序,环形,链式)

以下分别介绍了顺序队列&#xff0c;环形队列&#xff0c;链式队列的基本运算。主要有五种基本运算&#xff1a;1.初始化队列&#xff0c;2.销毁队列&#xff0c;3.判断队列是否为空&#xff0c;4.进队列&#xff0c;5.出队。 目录 顺序队列 环形队列 链式队列 顺序队列与环…

《LeetCode热题100》---<5.③普通数组篇五道>

本篇博客讲解LeetCode热题100道普通数组篇中的五道题 第五道&#xff1a;缺失的第一个正数&#xff08;困难&#xff09; 第五道&#xff1a;缺失的第一个正数&#xff08;困难&#xff09; 方法一&#xff1a;将数组视为哈希表 class Solution {public int firstMissingPosi…

频率的工程测量01 - Rif算法的构造

1.原始文档 《用于正弦波频率估计的修正I-Rife算法》&#xff0c;王哲文&#xff0c;2024 DOI&#xff1a; 10. 16337/j. 1004‑9037. 2024. 02. 019 1.1 这篇论文所属的自科基金U21A20500&#xff1a;近5年所承担的重要科研项目表-智能感知系统与安全教育部重点实验室&#…

位运算(更新中)

一、基础知识 1.基础位运算 << 左移操作符 &按位与 有0就是0 >>右移操作符 | 按位或 有1就是1 ~按位取反 ^异或 相同为1&#xff0c;相异为0 /…

大模型检索X一键成片,巴黎奥运的AI新演绎

媒体智能与巴黎奥运的一场邂逅。 随着巴黎奥运会开幕式为全世界掀起一场文艺浪潮&#xff0c;塞纳河畔也从浪漫艺术的盛宴&#xff0c;转向体育竞技的击攘。让全世界不可错过巴黎前方的每刻高光&#xff0c;更需要一场多媒体技术的迎赶革新。 于是&#xff0c;我们看到另一场媒…

现代前端架构介绍(第三部分):深入了解状态管理层及其对前端App的影响

远离JavaScript疲劳和框架大战&#xff0c;了解真正重要的东西 在第二部分中&#xff0c;我们讨论了功能架构的三个层次。其中一个就是状态管理层&#xff0c;今天我们将对其进行更深入的探讨。下面是现代前端架构系列的第三部分和最后一部分介绍。 状态管理&#xff0c;你可能…

抖音视频素材一般都从哪里找?抖音视频素材库分享

在浏览抖音时&#xff0c;你是否曾被那些内容丰富、制作精良的视频所吸引&#xff1f;这些视频背后的秘密其实非常简单——高质量的视频素材。优质素材能够让你的视频更加出彩。然而&#xff0c;许多抖音内容创作者在初期可能会困惑&#xff1a;这些视频素材究竟从哪里获取呢&a…

实现生成二维码,在table显示,图片预览可下载

utils/images/downLoadRemoteFile.js /*** 获取 blob 实现不跳转下载* param {String} url 目标文件地址* return {Promise}*/ const getBlob (url) > {return new Promise(resolve > {const xhr new XMLHttpRequest();xhr.open(GET, url, true);xhr.responseType b…

怎么恢复U盘里被隐藏的数据?三招助找回“消失”数据

随着信息技术的飞速发展&#xff0c;U盘已成为我们日常生活中不可或缺的数据存储设备。然而&#xff0c;有时我们会遇到一些棘手的问题&#xff0c;比如U盘中的数据莫名其妙地被隐藏了&#xff0c;这不仅影响了我们的工作效率&#xff0c;还可能导致重要信息的丢失。那么&#…

Linux 照片图像编辑器

前言 照片图像编辑器是一种软件程序,它允许用户对数字照片或图像进行各种编辑和修改。以下是一些常见的功能及其解释: 裁剪与旋转 : 裁剪:移除图像的某些部分,以改善构图或符合特定尺寸要求。旋转:改变图像的方向,可以校正歪斜的照片或者为了艺术效果而旋转。调整亮度…

CC++:贪吃蛇小游戏教程

❀创作不易&#xff0c;关注作者不迷路❀&#x1f600;&#x1f600; 目录 &#x1f600;贪吃蛇简介 &#x1f603;贪吃蛇的实现 &#x1f40d;生成地图 &#x1f40d;生成蛇模块 ❀定义蛇的结构体 ❀初始化蛇的相关信息 ❀初始化食物的相关信息 &#x1f40d;光标定位和…

【zabbix6自定义监控带参数】

目录 一、环境准备二、选择监控的数据三、在zabbix_server主机测试四、在web界面上配置监控项五、在web界面上添加触发器 一、环境准备 注意&#xff1a;避免一些问题&#xff0c;可以把防火墙&#xff0c;selinux都关闭 安装zabbix-sever&#xff1a;https://blog.csdn.net/q…

等保测评练习卷25

等级保护初级测评师试题25 姓名&#xff1a; 成绩&#xff1a; 一、判断题&#xff08;10110分&#xff09; 1.安全区域边界对象主要根据系统中网络访问控制设备的部署情况来确定&#xff08;&#xff09;不是网络访问控制设备而…

zero - hackmyvm

简介 靶机名称&#xff1a;Zero 难度&#xff1a;简单 靶场地址&#xff1a;https://hackmyvm.eu/machines/machine.php?vmZero 本地环境 虚拟机&#xff1a;vitual box 靶场IP&#xff08;Zero&#xff09;&#xff1a;未知 windows_IP&#xff1a;192.168.130.158 k…

Vulnhub靶场DC-9练习

目录 0x00 准备0x01 主机信息收集0x02 站点信息收集0x03 漏洞查找与利用1. 发现SQL注入点2. Sqlmap跑数据3. 文件包含4. SSH爆破端口敲门服务5. 提权&#xff08;写入/etc/passwd&#xff09; 0x04 总结 0x00 准备 下载链接&#xff1a;https://download.vulnhub.com/dc/DC-9.z…

数据化项目中如何优化数据分析报表的响应速度

引言&#xff1a;在数据化项目中&#xff0c;优化数据分析报表的响应速度是一个关键任务&#xff0c;它直接影响到用户的体验和决策效率。以下是一些有效的策略和方法来优化数据分析报表的响应速度&#xff1a; 一.从IAAS层优化&#xff1a; 硬件与网络资源优化&#xff1a;提…

无法读取配置节“dataConfiguration”

无法读取配置节“dataConfiguration”&#xff0c;因为它缺少节声明 问题 在web.config中加了<dataConfiguration defaultDatabase"DefaultDB" />&#xff0c;服务器运行报无法读取配置节“dataConfiguration” 分析检查配置文件&#xff1a; Web.config…

前端常用的【设计模式】和使用场景

设计原则 最重要的&#xff1a;开放封闭原则 对扩展开放对修改封闭 工厂模式 用一个工厂函数&#xff0c;来创建实例&#xff0c;隐藏 new 如 jQuery 的 $ 函数&#xff0c;React 的 createElement 函数 单例模式 全局唯一的实例(无法生成第二个) 如 Vuex 和 Redux 的 store…