一、前言

在人脸识别到以后,需要在实时视频上将所有人脸框绘制出来,一把来说识别人脸会有多种选择,一个是识别最大人脸,这种场景主要用于刷脸门禁,还有一种是识别所有人脸,这种场景主要用于人脸识别摄像机,就是将画面中的所有人脸识别出来发给服务器,人脸框的数据主要是四个参数,左上角和右下角的位置,也可以说是x、y、width、height,可能有些做的比较好的还有倾斜角度,这个意义不是很大,人脸识别的速度一般都是飞快的,就算你用学习上用的opencv做识别也是非常快的,基本上都是毫秒级的响应,主要的耗时操作在特征值的提取,所以一般要求能够响应每个通道每秒钟25帧-30帧的画面绘制+人脸框的绘制,当然人脸框的数据可能会有多个。

用Qt来绘制人脸框,核心就是一个函数,调用QPainter的drawRect方法,传入区域即可,如果花哨点的话还可以设置边框的粗细和颜色、圆角角度等,注意圆角角度使用的是drawRoundedRect而不是drawRoundRect,很多人这里会搞错哦。近期接触的项目对人脸框的要求越来越多,之前是让用户自己拿到图片来绘制,近期索性直接将这个功能内置到视频控件中(视频控件封装了多种内核版本,有ffmpeg、vlc、mpv、海康sdk等),提供了可设置边框粗细、颜色,传入人脸框区域集合的接口,用户只要自己的算法分析拿到人脸的区域集合(用户是上帝,用户的需求就是我的需求),通过setFaceRects函数设置即可,如果要清空人脸,只要设置人脸框区域集合为空即可。总体测试下来速度非常快,可以忽略,采用的QOPenGLWidget绘制的实时图像,也支持人脸框的绘制。

二、功能特点

  1. 支持的功能包括人脸识别、人脸比对、人脸搜索、活体检测等。
  2. 在线版还支持身份证、驾驶证、行驶证、银行卡等识别。
  3. 在线版的协议支持百度、旷视,离线版的支持百度,可定制。
  4. 除了支持X86架构,还支持嵌入式linux比如contex-A9、树莓派等。
  5. 每个功能的执行除了返回结果还返回执行用时时间。
  6. 多线程处理,通过type控制当前处理类型。
  7. 支持单张图片检索相似度最高的图片。
  8. 支持指定目录图片用来生成人脸特征值文件。
  9. 可设置等待处理图片队列中的数量。
  10. 每次执行都有成功或者失败的信号返回。
  11. 人脸搜索的返回结果包含了原图+最大相似度图+相似度等。
  12. 人脸比对同时支持两张图片和两个特征值比对。
  13. 相关功能自定义一套协议用于客户端和服务端,可以通过TCP通信进行交互。
  14. 自定义人脸识别协议非常适用于中心一台服务器,现场若干设备请求的场景。
  15. 每个模块全部是独立的一个类,代码整洁、注释完善。

三、效果图

四、相关站点

  1. 国内站点:https://gitee.com/feiyangqingyun/QWidgetDemo
  2. 国际站点:https://github.com/feiyangqingyun/QWidgetDemo
  3. 个人主页:https://blog.csdn.net/feiyangqingyun
  4. 知乎主页:https://www.zhihu.com/people/feiyangqingyun/
  5. 体验地址:https://blog.csdn.net/feiyangqingyun/article/details/97565652

五、核心代码

bool FFmpegWidget::eventFilter(QObject *watched, QEvent *event)
{
if (watched == osdWidget && event->type() == QEvent::Paint) {
if (drawImage) {
QPainter painter;
painter.begin(osdWidget);
painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); //绘制边框
drawBorder(&painter);
if (thread->getIsInit()) {
//绘制背景图片
drawImg(&painter, image);
//绘制人脸框
drawFace(&painter);
//绘制标签
drawOSD(&painter, osd1Visible, osd1FontSize, osd1Text, osd1Color, osd1Image, osd1Format, osd1Position);
drawOSD(&painter, osd2Visible, osd2FontSize, osd2Text, osd2Color, osd2Image, osd2Format, osd2Position);
} else {
//绘制背景
if (!isDrag) {
drawBg(&painter);
}
} painter.end();
}
} return QWidget::eventFilter(watched, event);
} void FFmpegWidget::drawBorder(QPainter *painter)
{
if (borderWidth == 0) {
return;
} painter->save();
QPen pen;
pen.setWidth(borderWidth);
pen.setColor(hasFocus() ? focusColor : borderColor);
painter->setPen(pen);
painter->drawRect(rect());
painter->restore();
} void FFmpegWidget::drawBg(QPainter *painter)
{
painter->save(); //背景图片为空则绘制文字,否则绘制背景图片
if (bgImage.isNull()) {
painter->setFont(this->font());
painter->setPen(palette().foreground().color());
painter->drawText(rect(), Qt::AlignCenter, bgText);
} else {
//居中绘制
int x = rect().center().x() - bgImage.width() / 2;
int y = rect().center().y() - bgImage.height() / 2;
QPoint point(x, y);
painter->drawImage(point, bgImage);
} painter->restore();
} void FFmpegWidget::drawImg(QPainter *painter, QImage img)
{
if (img.isNull()) {
return;
} painter->save(); int offset = borderWidth * 1 + 0;
img = img.scaled(width() - offset, height() - offset, Qt::KeepAspectRatio, Qt::SmoothTransformation); if (fillImage) {
QRect rect(offset / 2, offset / 2, width() - offset, height() - offset);
painter->drawImage(rect, img);
} else {
//按照比例自动居中绘制
int x = rect().center().x() - img.width() / 2;
int y = rect().center().y() - img.height() / 2;
QPoint point(x, y);
painter->drawImage(point, img);
} painter->restore();
} void FFmpegWidget::drawFace(QPainter *painter)
{
if (faceRects.count() == 0) {
return;
} painter->save(); //人脸边框的颜色
QPen pen;
pen.setWidth(faceBorder);
pen.setColor(faceColor);
painter->setPen(pen); //逐个取出人脸框区域进行绘制
foreach (QRect rect, faceRects) {
painter->drawRect(rect);
} painter->restore();
} void FFmpegWidget::drawOSD(QPainter *painter,
bool osdVisible,
int osdFontSize,
const QString &osdText,
const QColor &osdColor,
const QImage &osdImage,
const FFmpegWidget::OSDFormat &osdFormat,
const FFmpegWidget::OSDPosition &osdPosition)
{
if (!osdVisible) {
return;
} painter->save(); //标签位置尽量偏移多一点避免遮挡
QRect osdRect(rect().x() + (borderWidth * 2), rect().y() + (borderWidth * 2), width() - (borderWidth * 5), height() - (borderWidth * 5));
int flag = Qt::AlignLeft | Qt::AlignTop;
QPoint point = QPoint(osdRect.x(), osdRect.y()); if (osdPosition == OSDPosition_Left_Top) {
flag = Qt::AlignLeft | Qt::AlignTop;
point = QPoint(osdRect.x(), osdRect.y());
} else if (osdPosition == OSDPosition_Left_Bottom) {
flag = Qt::AlignLeft | Qt::AlignBottom;
point = QPoint(osdRect.x(), osdRect.height() - osdImage.height());
} else if (osdPosition == OSDPosition_Right_Top) {
flag = Qt::AlignRight | Qt::AlignTop;
point = QPoint(osdRect.width() - osdImage.width(), osdRect.y());
} else if (osdPosition == OSDPosition_Right_Bottom) {
flag = Qt::AlignRight | Qt::AlignBottom;
point = QPoint(osdRect.width() - osdImage.width(), osdRect.height() - osdImage.height());
} if (osdFormat == OSDFormat_Image) {
painter->drawImage(point, osdImage);
} else {
QDateTime now = QDateTime::currentDateTime();
QString text = osdText;
if (osdFormat == OSDFormat_Date) {
text = now.toString("yyyy-MM-dd");
} else if (osdFormat == OSDFormat_Time) {
text = now.toString("HH:mm:ss");
} else if (osdFormat == OSDFormat_DateTime) {
text = now.toString("yyyy-MM-dd HH:mm:ss");
} //设置颜色及字号
QFont font;
font.setPixelSize(osdFontSize);
painter->setPen(osdColor);
painter->setFont(font); painter->drawText(osdRect, flag, text);
} painter->restore();
}

Qt音视频开发44-实时人脸框的更多相关文章

  1. WebRTC 音视频开发

    WebRTC 音视频开发 webrtc   Android IOS WebRTC 音视频开发总结(七八)-- 为什么WebRTC端到端监控很关键? 摘要: 本文主要介绍WebRTC端到端监控(我们翻译 ...

  2. 转:Android IOS WebRTC 音视频开发总结 (系列文章集合)

    随笔分类 - webrtc   Android IOS WebRTC 音视频开发总结(七八)-- 为什么WebRTC端到端监控很关键? 摘要: 本文主要介绍WebRTC端到端监控(我们翻译和整理的,译 ...

  3. Python音视频开发:消除抖音短视频Logo的图形化工具实现

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 在<Python音视频开发:消除抖音短视频Logo和去电视台标的实现详解>节介绍了怎么通过Python+Moviepy+OpenCV实现 ...

  4. Android IOS WebRTC 音视频开发总结(八十三)-- 使用WebRTC广播网络摄像头视频(上)

    本文主要介绍WebRTC (我们翻译和整理的,译者:weizhenwei,校验:blacker),最早发表在[编风网] 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID:bl ...

  5. Android开发 音视频开发需要了解的专业术语知识

    前言 在摸索一段时间的音视频开发后,越来越发现这个坑的深度真是特别的深. 除了了解Android自带的音视频处理API以外,还得了解一些视频与音频方面的知识.这篇博客就是主要讲解这方面的专业术语.内容 ...

  6. Python音视频开发:消除抖音短视频Logo和去电视台标

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 对于带Logo(如抖音Logo.电视台标)的视频,有三种方案进行Logo消除: 直接将对应区域用对应图像替换: 直接将对应区域模糊化: 通过变换将要 ...

  7. Moviepy音视频开发:视频转gif动画或jpg图片exe图形化工具开发案例

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 老猿之所以学习和研究Moviepy的使用,是因为需要一个将视频转成动画的工具,当时在网上到处搜索查找免费使用工具,结果找了很多自称免费的工具,但转完 ...

  8. moviepy音视频开发:音频剪辑基类AudioClip

    ☞ ░ 前往老猿Python博文目录 ░ 一.背景知识介绍 1.1.声音三要素: 音调:人耳对声音高低的感觉称为音调(也叫音频).音调主要与声波的频率有关.声波的频率高,则音调也高. 音量:也就是响度 ...

  9. Android 音视频开发(一):PCM 格式音频的播放与采集

    什么是 PCM 格式 声音从模拟信号转化为数字信号的技术,经过采样.量化.编码三个过程将模拟信号数字化. 采样 顾名思义,对模拟信号采集样本,该过程是从时间上对信号进行数字化,例如每秒采集 44100 ...

  10. 【秒懂音视频开发】02_Windows开发环境搭建

    音视频开发库的选择 每个主流平台基本都有自己的音视频开发库(API),用以处理音视频数据,比如: iOS:AVFoundation.AudioUnit等 Android:MediaPlayer.Med ...

随机推荐

  1. Linux+PXE+DHCP+TFTP+NFS实现无人值守安装

    一.实验环境:OS:Redhat6.4软件:DHCP服务.TFTP服务.NFS服务硬件:Dell R630服务器两台物理连接图: em1 em1 二.工作原理:服务器通过PXE网卡启动,从dhcp服务 ...

  2. 经典排序算法(C语言、Java版)

    排序 比较 分类 比较排序的时间复杂度的下界O(nlogn) 对于n个待排序元素,在未比较时,可能的正确结果有n!种.在经过一次比较后,其中两个元素的顺序被确定,所以可能的正确结果剩余n!/2种(确定 ...

  3. 2.14 Linux文件目录结构一览表

    学习 Linux,不仅限于学习各种命令,了解整个 Linux 文件系统的目录结构以及各个目录的功能同样至关重要. 使用 Linux 时,通过命令行输入 ls -l / 可以看到,在 Linux 根目录 ...

  4. 共享存储ISCSI

    建立共享iscsi磁盘组 资源环境 服务端:192.168.2.131 客户端:192.168.2.[110,169] 服务端磁盘: [root@centos ~]# lsblk NAME MAJ:M ...

  5. Java核心知识体系10-线程管理

    Java系列 Java核心知识体系1:泛型机制详解 Java核心知识体系2:注解机制详解 Java核心知识体系3:异常机制详解 Java核心知识体系4:AOP原理和切面应用 Java核心知识体系5:反 ...

  6. linux history 想显示历史命令的时间和作者

    vi ~/.profile 增加 HISTTIMEFORMAT="%F %T `whoami` `who am i|awk '{print $1,$5}'|sed 's/ (/@/'|sed ...

  7. 读书笔记-C#8.0本质论-06

    18.4 并行迭代 如果一个对CPU资源占用较大的计算可以很容易被分割为多个彼此完全独立的部分以任意顺序执行,则要使用并行循环.示例如下: using System; using System.Col ...

  8. Java 编程的动态性,第 1 部分: 类和类装入

    研究类以及 JVM 装入类时所发生的情况 这一有关 Java 编程动态方面的新的系列文章研究了执行 Java 应用程序时幕后所发生的事情.企业 Java专家 Dennis Sosnoski 提供了 J ...

  9. 想学习建个网站?WAMP Server助你在Windows上快速搭建PHP集成环境

    我想只要爬过几天网的同学都会知道PHP吧,异次元的新版本就是基于PHP的WordPress程序制造出来的,还有国内绝大部分论坛都是PHP的哦.据我所知很多同学都想要试着学习一下PHP,无奈要在Wind ...

  10. python之gevent

    gevent 是一个基于协程的 Python 网络库,它使用 Greenlet 库提供了一种高效的协程实现. 协程是一种轻量级的线程,允许并发执行,但没有真正的并行性.协程可 以在遇到 I/O 操作时 ...