计时器任务实现(保存视频和图像)
下面是一个简单的计时器任务实现,可持续地每秒保存一幅图像,也可持续地每60秒保存一个视频,图像和视频均以当前时间命名:
TimerTask类的实现如下:
class TimerTask {
public:TimerTask(const std::string& path):path_(path) {}~TimerTask() { release(); }void release(){running_ = false;if (monitor_thread_.joinable())monitor_thread_.join();}std::tuple<bool, float> set_minimum_available_space(unsigned int gb);bool set_save_directory_name(const std::string& dir_name);std::string get_local_time();std::tuple<bool, std::string> get_current_directory_name();void save_video(unsigned int seconds) { save_video_ = true; seconds_ = seconds; }void save_image() { save_video_ = false; }void monitor_disk_space(unsigned int gb);private:float get_available_space();std::string path_;unsigned int gb_{0};std::string dir_name_{}; // relative path,used to store videos or imagesbool save_video_{false}; // video or imageunsigned int seconds_{ 0 };std::atomic<bool> running_{ true };std::thread monitor_thread_;
}; // class TimerTask
namespace {
float get_disk_space(std::string_view path)
{namespace fs = std::filesystem;constexpr float GB{ 1024.0 * 1024 * 1024 };try {auto space_info = fs::space(path);return (space_info.available / GB);} catch (const fs::filesystem_error& e) {std::cerr << "Error: " << e.what() << std::endl;return 0.f;}
}void monitor_space(unsigned int gb, std::string_view path, std::atomic<bool>& running)
{namespace fs = std::filesystem;std::mutex mtx;if (!fs::exists(path) || !fs::is_directory(path)) {std::lock_guard<std::mutex> lock(mtx);std::cerr << "Error: " << path << "is not a directory" << std::endl;}while (running) {try {float space = get_disk_space(path);//std::cout << "space: " << space << ", path: " << path << std::endl;if (space < gb) {std::vector<fs::path> names;for (const auto& entry : fs::directory_iterator(path)) {if (fs::is_directory(entry)) {names.push_back(entry.path());}}if (names.size() <= 1) {//{// std::lock_guard<std::mutex> lock(mtx);// std::cerr << "Error: requires at least 2 directories to exist: " << names.size() << std::endl;//}continue;}std::sort(names.begin(), names.end());fs::remove_all(names[0]);{std::lock_guard<std::mutex> lock(mtx);std::cout << "delete dir: " << names[0] << std::endl;}}} catch (const fs::filesystem_error& e) {std::lock_guard<std::mutex> lock(mtx);std::cerr << "Error: " << e.what() << std::endl;}std::this_thread::sleep_for(std::chrono::seconds(1));}
}} // namespacefloat TimerTask::get_available_space()
{return get_disk_space(path_);
}std::tuple<bool, float> TimerTask::set_minimum_available_space(unsigned int gb)
{gb_ = gb;auto space = get_available_space();if (gb_ > space)return std::make_tuple(false, space);elsereturn std::make_tuple(true, space);
}std::string TimerTask::get_local_time()
{using std::chrono::system_clock;auto time = system_clock::to_time_t(system_clock::now());std::tm* tm = std::localtime(&time);std::stringstream buffer;buffer << std::put_time(tm, "%Y%m%d%H%M%S");return buffer.str();
}bool TimerTask::set_save_directory_name(const std::string& dir_name)
{namespace fs = std::filesystem;dir_name_ = dir_name;fs::path path(path_ + "/" + dir_name_);if (fs::exists(path))return true;else {try {return fs::create_directories(path);} catch (const fs::filesystem_error& e) {std::cerr << "Error: " << e.what() << std::endl;return false;}}
}std::tuple<bool, std::string> TimerTask::get_current_directory_name()
{namespace fs = std::filesystem;auto local_time = get_local_time();std::string month(local_time.cbegin(), local_time.cbegin() + 6);std::string day(local_time.cbegin(), local_time.cbegin() + 8);auto curr_dir_name = path_ + "/" + dir_name_ + "/" + month + "/" + day;fs::path path(curr_dir_name);if (fs::exists(path))return std::make_tuple(true, curr_dir_name);else {try {return std::make_tuple(fs::create_directories(path), curr_dir_name);} catch (const fs::filesystem_error& e) {std::cerr << "Error: " << e.what() << std::endl;return std::make_tuple(false, curr_dir_name);}}
}void TimerTask::monitor_disk_space(unsigned int gb)
{monitor_thread_ = std::thread(monitor_space, gb, path_ + "/" + dir_name_, std::ref(running_));
}
类主要函数说明:
(1).set_minimum_available_space函数:用于指定当前可执行文件所在的磁盘剩余空间不低于指定值时才执行;
(2).set_save_directory_name函数:用于指定生成的图像或视频存在的路径。目录结构形式为:指定目录名/月(202502)/日(20250215)。
(3).monitor_disk_space函数:线程函数,用于持续监测磁盘剩余空间,低于指定值时则会删除之前已经存储的文件,每次删除一个月份目录。
(4).get_current_directory_name函数:用于获取当前图像或视频存放的路径名,会按时间自动创建。
(5).get_local_time函数:用于获取当前时间,格式为如为20250215125015。
测试代码如下:
int test_write_video()
{constexpr unsigned int minimum_available_space{ 100 }; // GBconstexpr unsigned int video_seconds{ 60 };constexpr unsigned int minimum_remaining_space{ 50 }; // GBconstexpr char dir_name[]{"record"};constexpr bool save_video{ true };auto current_path = std::filesystem::current_path();TimerTask task(current_path.string());if (auto [ret, space] = task.set_minimum_available_space(minimum_available_space); !ret) { // GBstd::cerr << "Error: insufficient remaining space: " << space << "GB" << std::endl;return -1;}if (auto ret = task.set_save_directory_name(dir_name); !ret) {std::cerr << "Error: failed to create directory: " << dir_name << std::endl;return -1;}task.monitor_disk_space(minimum_remaining_space);cv::VideoCapture cap(0);if (!cap.isOpened()) {std::cerr << "Error: failed to open capture" << std::endl;return -1;}cv::Mat frame;constexpr char win_name[]{"Show"};cv::namedWindow(win_name, cv::WINDOW_NORMAL);if (save_video) { // videotask.save_video(video_seconds);auto frame_width = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH));auto frame_height = static_cast<int>(cap.get(cv::CAP_PROP_FRAME_HEIGHT));if (frame_width == 0 || frame_height == 0) {std::cerr << "Error: failed to get frame width or height: " << frame_width << "," << frame_height << std::endl;return -1;}auto fps = cap.get(cv::CAP_PROP_FPS);if (fps <= 0)fps = 30.0;auto codec = cv::VideoWriter::fourcc('D', 'I', 'V', 'X');cv::VideoWriter write_video;auto start_time = std::chrono::high_resolution_clock::now();while (true) {cap >> frame;if (frame.empty()) {std::cerr << "Error: frame is empty" << std::endl;return -1;}auto current_time = std::chrono::high_resolution_clock::now();std::chrono::duration<double> elapsed = current_time - start_time;if (elapsed.count() >= video_seconds || !write_video.isOpened()) {if (write_video.isOpened())write_video.release();auto [ret, curr_dir_name] = task.get_current_directory_name();if (!ret) {std::cerr << "Error: failed to get current directory name: " << curr_dir_name << std::endl;return -1;}auto file_name{ curr_dir_name + "/" + task.get_local_time() + ".avi" };write_video.open(file_name, codec, fps, cv::Size(frame_width, frame_height));if (!write_video.isOpened()) {std::cerr << "Error: failed to open video write: " << file_name << std::endl;return -1;}start_time = std::chrono::high_resolution_clock::now();}write_video.write(frame);cv::imshow(win_name, frame);if (cv::waitKey(1) == 27) // Esc exitbreak;}cap.release();if (write_video.isOpened())write_video.release();} else { // image: save one image per secondtask.save_image();auto start_time = std::chrono::high_resolution_clock::now();while (true) {cap >> frame;if (frame.empty()) {std::cerr << "Error: frame is empty" << std::endl;return -1;}auto current_time = std::chrono::high_resolution_clock::now();std::chrono::duration<double> elapsed = current_time - start_time;if (elapsed.count() >= 1.0) {auto [ret, curr_dir_name] = task.get_current_directory_name();if (!ret) {std::cerr << "Error: failed to get current directory name: " << curr_dir_name << std::endl;return -1;}cv::imwrite(curr_dir_name + "/" + task.get_local_time() + ".png", frame);start_time = current_time;}cv::imshow(win_name, frame);if (cv::waitKey(1) == 27) // Esc exitbreak;}cap.release();}cv::destroyAllWindows();task.release();return 0;
}
执行结果如下图所示:
GitHub:https://github.com/fengbingchun/OpenCV_Test
相关文章:

计时器任务实现(保存视频和图像)
下面是一个简单的计时器任务实现,可持续地每秒保存一幅图像,也可持续地每60秒保存一个视频,图像和视频均以当前时间命名: TimerTask类的实现如下: class TimerTask { public:TimerTask(const std::string& path):…...

树莓百度百科能否揭开成都树莓集团的神秘面纱?
树莓百度百科作为大众获取信息的重要渠道,在一定程度上为人们了解树莓集团提供了窗口,但要完全揭开其神秘面纱,仍存在一定局限性。 从树莓百度百科上,我们能获取到关于树莓集团的基本信息,如公司的成立时间、法定代表人…...

【如何看懂数据手册和原理图】
【如何看懂数据手册和原理图】 文章目录 【如何看懂数据手册和原理图】1.数据手册1.1去哪里看?1.2需要注意的 2.支路3.回路4.网孔5.电路定理:基尔霍夫定律**集总参数电路** 抽象理想化5.1基尔霍夫电流定律 (KCL)5.2基尔霍夫电压定律 (KVL)5.3总结 6.读懂…...

SQL 优化工具使用之 explain 详解
一、导读 对于大部分开发人员来说,平常接触的无非就是增删改查这些基本操作,创建存储过程,视图等等都是 DBA 该干的活,但是想要把这些基本操作写的近乎完美也是一件难事。 而 explain 显示了 MySQL 如何使用索引来处理 select 语…...
深度解析Unity3D渲染管线:网格、材质与GPU渲染的协同逻辑
在3D实时渲染领域,网格(Mesh)、材质(Material)和GPU渲染三者构成了虚拟世界的基石。它们如同乐高积木的零件,通过精确的协作,最终在屏幕上呈现出复杂的视觉场景。本文将从技术原理、协作机制到性…...

POI优化Excel录入
57000单词原始录入时间258S 核心代码: List<Word> wordBookList ExcelUtil.getReader(file.getInputStream()).readAll(Word.class);if (!CollectionUtil.isEmpty(wordBookList)) {for (Word word : wordBookList) {//逐条向数据库中插入单词wordMapper.insert(word);}…...

实时图像与视频超分辨率:高效子像素卷积网络(ESPCN)解析
文章目录 概要理论知识操作实操环境配置基础命令格式:效果示例 概要 超分辨率系列论文阅读卷1:Real-Time Single Image and Video Super-Resolution Using an Efficient Sub-Pixel Convolutional Neural Network PDF网址:https://arxiv.org/…...

QT--对话框的切换
文章目录 前言一、主窗口ui二、创建子窗口三、步骤1.主界面------>子页面2.子界面------>主页面 四、总结 前言 之前我们学了qt中最重要的东西–信号和槽 我们现在实现这样一个demo,程序启动后弹出主界面,点击主界面的按钮弹出子窗口,…...
深入浅出:CUDA是什么,如何利用它进行高效并行计算
在当今这个数据驱动的时代,计算能力的需求日益增加,特别是在深度学习、科学计算和图像处理等领域。为了满足这些需求,NVIDIA推出了CUDA(Compute Unified Device Architecture),这是一种并行计算平台和编程模…...

Zotero PDF Translate插件配置百度翻译api
Zotero PDF Translate插件可以使用几种翻译api,虽然谷歌最好用,但是由于众所周知的原因,不稳定。而cnki有字数限制,有道有时也不行。其他的翻译需要申请密钥。本文以百度为例,进行申请 官方有申请教程: Zot…...

利用acme.sh 申请 Google 免费证书
1.Google API权限准备 获取 EAB 密钥 ID 和 HMAC 登录你的 GCP 控制台面板,进入 Public Certificate Authority API 管理页面(https://console.cloud.google.com/apis/library/publicca.googleapis.com)点击启动: 或者直接在下一…...

腾讯云cloudstudio使用笔记(一)
0、计划及目标 1)、这个系列用于将cloudstudio快速入门将前端代码在cloudstudio中从git仓库拉下来并运行—本文档的目标已实现 2)、基于cloudstudio和腾讯的ai代码助手腾讯自己满血的deepseek写代码,减少前端工作量—待补充 3)、…...

python自动化制作常规的日报数据可视化
python自动化制作常规的日报数据可视化 作者:i阿极 作者简介:Python领域新星作者、多项比赛获奖者:博主个人首页 😊😊😊如果觉得文章不错或能帮助到你学习,可以点赞👍收藏Ǵ…...

C语言:在主函数中输入十个等长的字符串。用另一函数对它们排序,然后在主函数输出这10个已排好序的字符串。
(1)用字符型二维数组 #include <stdio.h> #include <string.h> int main() {void sort(char s[][6]);int i;char str[10][6];printf("input 10 strings:\n");for (i0;i<10;i)scanf("%s",str[i]);sort(str);printf(&…...

构建高效智能对话前端:基于Ant Design X 的deepseek对话应用
文章目录 实现的效果前言Ant Design X添加欢迎组件创建对话气泡存储对话历史渲染对话气泡 输入组件WebSocket 连接总结 实现的效果 待机页面: 等待页面: 完成页面: 前言 随着人工智能技术的飞速发展,大模型对话系统已成为…...

SQLMesh 系列教程5- 详解SQL模型
本文将详细介绍 SQLMesh 的 SQL 模型组成要素及其在实际项目中的应用。SQLMesh 是一个强大的数据工程工具,其 SQL 模型由 MODEL DDL、预处理语句、主查询、后处理语句以及可选的 ON VIRTUAL UPDATE 语句组成。我们将通过一个电商平台每日销售报告的实例,…...
本地DeepSeek模型GGUF文件转换为PyTorch格式
接前文,我们在本地Windows系统上,基于GGUF文件部署了DeepSeek模型(DeepSeek-R1-Distill-Qwen-1.5B.gguf版本),但是GGUF是已经量化的版本,我们除了对其进行微调之外,无法对其训练,那么还有没有其他办法对本地的GGUF部署的DeepSeek模型进行训练呢?今天我们就反其道而行之…...
Flutter:动态表单(在不确定字段的情况下,生成动态表单)
关于数据模型:模型就是一种规范约束,便于维护管理,在不确定表单内会出现什么数据时,就没有模型一说。 这时就要用到动态表单(根据接口返回的字段,生成动态表单) 1、观察数据格式,定义…...

【Python项目】文本相似度计算系统
【Python项目】文本相似度计算系统 技术简介:采用Python技术、Django技术、MYSQL数据库等实现。 系统简介:本系统基于Django进行开发,包含前端和后端两个部分。前端基于Bootstrap框架进行开发,主要包括系统首页,文本分…...
C# ref 和 out 的使用详解
总目录 前言 在 C# 编程中,ref 和 out 是两个非常重要的关键字,它们都用于方法参数的传递,但用途和行为却有所不同。今天,我们就来深入探讨一下这两个关键字的用法和区别,让你在编程中能够得心应手地使用它们。 一、什…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...

渗透实战PortSwigger靶场:lab13存储型DOM XSS详解
进来是需要留言的,先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码,输入的<>当成字符串处理回显到页面中,看来只是把用户输…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...

ZYNQ学习记录FPGA(一)ZYNQ简介
一、知识准备 1.一些术语,缩写和概念: 1)ZYNQ全称:ZYNQ7000 All Pgrammable SoC 2)SoC:system on chips(片上系统),对比集成电路的SoB(system on board) 3)ARM:处理器…...

海云安高敏捷信创白盒SCAP入选《中国网络安全细分领域产品名录》
近日,嘶吼安全产业研究院发布《中国网络安全细分领域产品名录》,海云安高敏捷信创白盒(SCAP)成功入选软件供应链安全领域产品名录。 在数字化转型加速的今天,网络安全已成为企业生存与发展的核心基石,为了解…...
WEB3全栈开发——面试专业技能点P4数据库
一、mysql2 原生驱动及其连接机制 概念介绍 mysql2 是 Node.js 环境中广泛使用的 MySQL 客户端库,基于 mysql 库改进而来,具有更好的性能、Promise 支持、流式查询、二进制数据处理能力等。 主要特点: 支持 Promise / async-await…...

STM32标准库-ADC数模转换器
文章目录 一、ADC1.1简介1. 2逐次逼近型ADC1.3ADC框图1.4ADC基本结构1.4.1 信号 “上车点”:输入模块(GPIO、温度、V_REFINT)1.4.2 信号 “调度站”:多路开关1.4.3 信号 “加工厂”:ADC 转换器(规则组 注入…...
Windows 下端口占用排查与释放全攻略
Windows 下端口占用排查与释放全攻略 在开发和运维过程中,经常会遇到端口被占用的问题(如 8080、3306 等常用端口)。本文将详细介绍如何通过命令行和图形化界面快速定位并释放被占用的端口,帮助你高效解决此类问题。 一、准…...