人脸识别(二)

news/2023/9/27 7:00:59

源代码路径:https://github.com/comhaqs/face_find.git   分支 develop_step1

第一阶段得使用ffmpeg解码视频流并在qt上显示,这里使用的是一段电视剧视频。Qt上是使用QLabel控件显示QImage对象,但QImage对象只识别AV_PIX_FMT_RGBA图像数据,我手上的视频解码后是YUV420P数据,需要使用sws_scale函数转换图像格式,这个流程后面也会用到,因为opencv的矩阵对象接收AV_PIX_FMT_BGR24。

void media_decoder::work(std::string url, fun_type fun, int width_out, int height_out, std::shared_ptr<std::atomic_bool> p_stop){AVFormatContext *p_context = nullptr;// 打开视频auto ret = avformat_open_input(&p_context, url.c_str(), nullptr, nullptr);if (0 != ret){LOG_ERROR("打开url失败;  错误代码:" << ret << "; 路径:" << url);return ;}// 获取视频信息ret = avformat_find_stream_info(p_context, nullptr);if (0 > ret){LOG_ERROR("获取流信息失败; 错误代码:" << ret);return;}// 查找视频流AVCodec *pi_code = nullptr;auto index_video = av_find_best_stream(p_context, AVMEDIA_TYPE_VIDEO, -1, -1, &(pi_code), 0);if (0 > index_video){LOG_ERROR("获取视频索引失败:" << index_video);return;}auto p_video_stream = p_context->streams[index_video];auto p_video_code_ctx = avcodec_alloc_context3(pi_code);// 复制视频流中相关参数到视频上下文,不然p_video_code_ctx某些参数会丢失ret = avcodec_parameters_to_context(p_video_code_ctx, p_video_stream->codecpar);if(0 > ret){return;}ret = avcodec_open2(p_video_code_ctx, pi_code, nullptr);if(0 > ret){return;}// 申请了两个Frame,一个用来存储解码后的视频帧,一个用来存储转换后的帧AVPixelFormat pix_fmt_out = AV_PIX_FMT_RGBA;AVFrame *p_frame_yuv = av_frame_alloc();int num_yuv = av_image_get_buffer_size(p_video_code_ctx->pix_fmt, p_video_code_ctx->width, p_video_code_ctx->height, 1);uint8_t* p_data_yuv = static_cast<uint8_t *>(av_malloc(static_cast<std::size_t>(num_yuv)*sizeof(uint8_t)));av_image_fill_arrays(p_frame_yuv->data, p_frame_yuv->linesize, p_data_yuv, p_video_code_ctx->pix_fmt, p_video_code_ctx->width, p_video_code_ctx->height, 1);AVFrame *p_frame_rgb = av_frame_alloc();int num_rgb = av_image_get_buffer_size(pix_fmt_out, width_out, height_out, 1);uint8_t* p_data_rgb = static_cast<uint8_t *>(av_malloc(static_cast<std::size_t>(num_rgb)*sizeof(uint8_t)));av_image_fill_arrays(p_frame_rgb->data, p_frame_rgb->linesize, p_data_rgb, pix_fmt_out, width_out, height_out, 1);// 获取图像转换相关对象struct SwsContext *p_sws_context = nullptr;p_sws_context = sws_getCachedContext(p_sws_context, p_video_code_ctx->width, p_video_code_ctx->height, p_video_code_ctx->pix_fmt,width_out, height_out, pix_fmt_out, SWS_BICUBIC, nullptr, nullptr, nullptr);if(nullptr == p_sws_context){return;}while(!*p_stop){AVPacket pkt;ret = av_read_frame(p_context, &pkt);if (0 > ret){break;}if (pkt.stream_index != index_video){continue;}// 这里使用了新版的解码函数,avcodec_send_packet后不一定能avcodec_receive_frame到帧,涉及到I、P、B帧的解码流程int re = avcodec_send_packet(p_video_code_ctx ,&pkt);if (0 > ret){av_packet_unref(&pkt);continue;}while(!*p_stop){// 要反复调用avcodec_receive_frame,直到无法获取到帧re = avcodec_receive_frame(p_video_code_ctx, p_frame_yuv);if (re != 0){break;}if(nullptr != p_sws_context){auto h = sws_scale(p_sws_context, p_frame_yuv->data, p_frame_yuv->linesize, 0, p_video_code_ctx->height, p_frame_rgb->data, p_frame_rgb->linesize);if(0 < h && fun){auto p_info = std::make_shared<info_data>();p_info->p_data = p_data_rgb;p_info->width = width_out;p_info->height = height_out;p_info->data_max = static_cast<std::size_t>(num_rgb);fun(p_info);}// 如果不延迟,因为解码的速度很快,显示又是依赖于解码,会导致播放速度非常快,这里不应该写死,应该按照视频流的time_base来计算延迟std::this_thread::sleep_for(std::chrono::milliseconds(40));}else{LOG_ERROR("获取缩放上下文失败; 宽度:"<<p_frame_yuv->width<<"; 高度:"<<p_frame_yuv->height<<"; 帧格式:"<<static_cast<AVPixelFormat>(p_frame_yuv->format));break;}}// 释放数据包av_packet_unref(&pkt);}// 清理相关对象if(nullptr != p_sws_context){sws_freeContext(p_sws_context);p_sws_context = nullptr;}if(nullptr != p_frame_rgb){av_frame_free(&p_frame_rgb);p_frame_rgb = nullptr;}if(nullptr != p_data_rgb){av_free(p_data_rgb);p_data_rgb = nullptr;}if(nullptr != p_frame_yuv){av_frame_free(&p_frame_yuv);p_frame_yuv = nullptr;}if(nullptr != p_data_yuv){av_free(p_data_yuv);p_data_yuv = nullptr;}if(nullptr != p_context){avformat_close_input(&p_context);p_context = nullptr;}
}

 

整个解码类定义如下:

#include "media_decoder.h"
#include "utility_tool.h"
#include <vector>media_decoder::media_decoder(const std::string& url, fun_type fun):m_url(url), m_fun(fun)
{}media_decoder::~media_decoder(){}void media_decoder::start(){mp_flag_stop = std::make_shared<std::atomic_bool>(false);m_thread = std::thread(std::bind(media_decoder::handle_media, m_url, m_fun, mp_flag_stop));
}void media_decoder::stop(){mp_flag_stop && (*mp_flag_stop = true);m_thread.join();
}void media_decoder::handle_media(std::string url, fun_type fun, std::shared_ptr<std::atomic_bool> p_stop){try {work(url, fun, 960, 540, p_stop);} catch (const std::exception& e) {LOG_ERROR("发生错误:"<<e.what());}
}void media_decoder::work(std::string url, fun_type fun, int width_out, int height_out, std::shared_ptr<std::atomic_bool> p_stop){AVFormatContext *p_context = nullptr;// 打开视频auto ret = avformat_open_input(&p_context, url.c_str(), nullptr, nullptr);if (0 != ret){LOG_ERROR("打开url失败;  错误代码:" << ret << "; 路径:" << url);return ;}// 获取视频信息ret = avformat_find_stream_info(p_context, nullptr);if (0 > ret){LOG_ERROR("获取流信息失败; 错误代码:" << ret);return;}// 查找视频流AVCodec *pi_code = nullptr;auto index_video = av_find_best_stream(p_context, AVMEDIA_TYPE_VIDEO, -1, -1, &(pi_code), 0);if (0 > index_video){LOG_ERROR("获取视频索引失败:" << index_video);return;}auto p_video_stream = p_context->streams[index_video];auto p_video_code_ctx = avcodec_alloc_context3(pi_code);// 复制视频流中相关参数到视频上下文,不然p_video_code_ctx某些参数会丢失ret = avcodec_parameters_to_context(p_video_code_ctx, p_video_stream->codecpar);if(0 > ret){return;}ret = avcodec_open2(p_video_code_ctx, pi_code, nullptr);if(0 > ret){return;}// 申请了两个Frame,一个用来存储解码后的视频帧,一个用来存储转换后的帧AVPixelFormat pix_fmt_out = AV_PIX_FMT_RGBA;AVFrame *p_frame_yuv = av_frame_alloc();int num_yuv = av_image_get_buffer_size(p_video_code_ctx->pix_fmt, p_video_code_ctx->width, p_video_code_ctx->height, 1);uint8_t* p_data_yuv = static_cast<uint8_t *>(av_malloc(static_cast<std::size_t>(num_yuv)*sizeof(uint8_t)));av_image_fill_arrays(p_frame_yuv->data, p_frame_yuv->linesize, p_data_yuv, p_video_code_ctx->pix_fmt, p_video_code_ctx->width, p_video_code_ctx->height, 1);AVFrame *p_frame_rgb = av_frame_alloc();int num_rgb = av_image_get_buffer_size(pix_fmt_out, width_out, height_out, 1);uint8_t* p_data_rgb = static_cast<uint8_t *>(av_malloc(static_cast<std::size_t>(num_rgb)*sizeof(uint8_t)));av_image_fill_arrays(p_frame_rgb->data, p_frame_rgb->linesize, p_data_rgb, pix_fmt_out, width_out, height_out, 1);// 获取图像转换相关对象struct SwsContext *p_sws_context = nullptr;p_sws_context = sws_getCachedContext(p_sws_context, p_video_code_ctx->width, p_video_code_ctx->height, p_video_code_ctx->pix_fmt,width_out, height_out, pix_fmt_out, SWS_BICUBIC, nullptr, nullptr, nullptr);if(nullptr == p_sws_context){return;}while(!*p_stop){AVPacket pkt;ret = av_read_frame(p_context, &pkt);if (0 > ret){break;}if (pkt.stream_index != index_video){continue;}// 这里使用了新版的解码函数,avcodec_send_packet后不一定能avcodec_receive_frame到帧,涉及到I、P、B帧的解码流程int re = avcodec_send_packet(p_video_code_ctx ,&pkt);if (0 > ret){av_packet_unref(&pkt);continue;}while(!*p_stop){// 要反复调用avcodec_receive_frame,直到无法获取到帧re = avcodec_receive_frame(p_video_code_ctx, p_frame_yuv);if (re != 0){break;}if(nullptr != p_sws_context){auto h = sws_scale(p_sws_context, p_frame_yuv->data, p_frame_yuv->linesize, 0, p_video_code_ctx->height, p_frame_rgb->data, p_frame_rgb->linesize);if(0 < h && fun){auto p_info = std::make_shared<info_data>();p_info->p_data = p_data_rgb;p_info->width = width_out;p_info->height = height_out;p_info->data_max = static_cast<std::size_t>(num_rgb);fun(p_info);}// 如果不延迟,因为解码的速度很快,显示又是依赖于解码,会导致播放速度非常快,这里不应该写死,应该按照视频流的time_base来计算延迟std::this_thread::sleep_for(std::chrono::milliseconds(40));}else{LOG_ERROR("获取缩放上下文失败; 宽度:"<<p_frame_yuv->width<<"; 高度:"<<p_frame_yuv->height<<"; 帧格式:"<<static_cast<AVPixelFormat>(p_frame_yuv->format));break;}}// 释放数据包av_packet_unref(&pkt);}// 清理相关对象if(nullptr != p_sws_context){sws_freeContext(p_sws_context);p_sws_context = nullptr;}if(nullptr != p_frame_rgb){av_frame_free(&p_frame_rgb);p_frame_rgb = nullptr;}if(nullptr != p_data_rgb){av_free(p_data_rgb);p_data_rgb = nullptr;}if(nullptr != p_frame_yuv){av_frame_free(&p_frame_yuv);p_frame_yuv = nullptr;}if(nullptr != p_data_yuv){av_free(p_data_yuv);p_data_yuv = nullptr;}if(nullptr != p_context){avformat_close_input(&p_context);p_context = nullptr;}
}

 


https://dhexx.cn/news/show-25402.html

相关文章

vs12 vs2013下open3.0配置扩展模块

在使用人脸识别face.hpp的时候&#xff0c;如果直接在opencv官网下载的已编译好的.exe安装的话将没有扩展库的功能&#xff0c;如果要使用扩展库&#xff0c;必须要进行扩展库的编译。 1、准备资源 opencv未编译版&#xff1a; https://github.com/Itseez/opencv opencv扩展…

基于Python的图片隐写算法设计与实现

目 录 摘 要 1 第一章 绪论 2 1.1 研究背景 2 1.1.1 信息隐藏技术 2 1.1.2 图像格式 2 1.1.3 信息隐写的国内外发展 3 1.2 研究意义 3 1.3 本文研究的主要内容 3 第二章 理论研究与分析 4 2.1 图像隐写术 4 2.1.1 图像隐写术 4 2.1.2 图像隐写的流程 4 2.2图像隐写算法 5 2.2.1…

Linux三剑客grep、awk和sed

grep&#xff0c;sed 和 awk是Linux/Unix 系统中常用的三个文本处理的命令行工具&#xff0c;称为文本处理三剑客。本文将简要介绍这三个命令的基本用法以及它们在Windows系统中的使用方法。 目录管道grep定义选项参数实例1&#xff1a;查找文件内容&#xff0c;显示行号实例2&…

基于html的旅游网站的设计与实现

目 录 第一章 绪论 1 1.1. 选题背景及意义 1 1.2. 国内外研究现状 1 1.3. 研究主要内容 2 第二章 开发工具的选用及介绍 3 2.1. 网页开发技术 3 2.1.1. HTML简介 3 2.1.2. DIVCSS简介 3 2.1.3. bootstrap 3 2.2. 网页制作工具 4 2.2.1. Photoshop 4 2.2.2. Dreamweaver 4 2.2.3…

Appium控件交互

与Web元素操作一样&#xff08;参考Selenium Web元素操作&#xff09;&#xff0c;定位到APP控件元素后&#xff0c;可以对控件进行一系列的操作&#xff0c;实现与APP交互&#xff0c;比如点击、文本输入、元素属性获取等。 目录元素操作元素常用操作方法Python测试实例元素属…

基于PLC的棒料自动上料车削加工装置设计

目 录 1 绪论 1 1.1自动上下料机现状 1 1.2设计目的 1 1.3 国内外研究方向 2 1.4 设计原则 2 2 棒料自动上料车削加工装置方案设计 3 2.1自动上料车削加工装置的总体设计 3 2.1.1 车削加工装置总体结构的类型 3 2.1.2 设计具体采用方案 4 2.2 车削加工装置腰座结构的设计 4 2.…

Android WebView测试

混合应用中包含 Web 视图的应用&#xff0c;在 Appium 介绍及环境安装 中介绍了appium支持WebView测试&#xff0c;本文将分享Android 混合页面的测试方法。 目录WebView测试环境准备手机端PC端查看手机浏览器版本查看手机webview版本客户端代码WebView元素定位Android混合页面…

人脸识别(四)

源码位置&#xff1a;https://github.com/comhaqs/face_find.git 分支&#xff1a; develop_face_recognition opencv的人脸识别模块现在是放在另外一个库opencv_contrib里&#xff0c;需要编译到opencv里才可以使用&#xff0c;故这里将opencv和opencv_contrib的源码都下下来…

基于RFID的学生一卡通管理系统的设计与实现

目 录 摘 要 I ABSTRACT II 第1章 绪论 1 1.1选题背景及意义 1 1.2国内外研究现状 1 1.3研究主要内容 2 第2章 系统设计 4 2.1需求分析 4 2.1.1可行性分析 4 2.1.2功能需求概述 5 2.1.3 UML用例图 5 2.2学生一卡通的基本组成结构 7 2.2.1天线 7 2.2.2阅读器 7 2.2.3电子标签 11…

自动遍历测试之Monkey工具

某些移动APP业务线多&#xff0c;流程复杂&#xff0c;且产品迭代速度快&#xff0c;在回归测试中涉及到大量用例&#xff0c;工作量大&#xff0c;解决这个问题的一个可行方法是使用自动遍历测试&#xff0c;可以降低用例维护成本&#xff0c;尽可能的自动化覆盖回归业务。 目…