计时器任务实现(保存视频和图像)

news2025/4/3 4:29:49

      下面是一个简单的计时器任务实现,可持续地每秒保存一幅图像,也可持续地每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 images
	bool save_video_{false}; // video or image
	unsigned 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));
	}
}

} // namespace

float 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);
	else
		return 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 }; // GB
	constexpr unsigned int video_seconds{ 60 };
	constexpr unsigned int minimum_remaining_space{ 50 }; // GB
	constexpr 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) { // GB
		std::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) { // video
		task.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 exit
				break;
		}

		cap.release();
		if (write_video.isOpened())
			write_video.release();
	} else { // image: save one image per second
		task.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 exit
				break;
		}

		cap.release();
	}

	cv::destroyAllWindows();
	task.release();
	return 0;
}

      执行结果如下图所示:

      GitHub:https://github.com/fengbingchun/OpenCV_Test

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

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

相关文章

Django 美化使用ModelForm的输入框

在初次使用ModelForm时&#xff0c;我的html文件代码如下&#xff0c;主要内容是显示一个卡片式表单&#xff0c;通过循环遍历 form 对象动态生成表单字段 {% extends layout.html %}{% block content %} <div class"container"><div class"c1"&g…

应用层优秀的共享民宿物联网框架该怎么选?

有一说一&#xff0c;应用层优秀的物联网框架通常能帮助提升用户体验、提高运营效率、节能减排等等优势&#xff0c;很多老板也很注重这个层面的设计和打磨&#xff0c;那么对于选择应用层优秀的共享民宿物联网框架时&#xff0c;大家可以从哪几个关键因素进行考量呢&#xff1…

【kafka系列】生产者

目录 发送流程 1. 流程逻辑分析 阶段一&#xff1a;主线程处理 阶段二&#xff1a;Sender 线程异步发送 核心设计思想 2. 流程 关键点总结 重要参数 一、核心必填参数 二、可靠性相关参数 三、性能优化参数 四、高级配置 五、安全性配置&#xff08;可选&#xff0…

Unity 获取独立显卡数量

获取独立显卡数量 导入插件包打开Demo 运行看控制台日志 public class GetGraphicCountDemo : MonoBehaviour{public int count;// Start is called before the first frame updatevoid Start(){count this.GetIndependentGraphicsDeviceCount();}}

Deepseek R1模型本地化部署+API接口调用详细教程:释放AI生产力

文章目录 前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装ollama2部署DeepSeek R1模型删除已存在模型&#xff0c;以7b模型为例 三、DeepSeek API接口调用Cline配置 前言 随着最近人工智能 DeepSeek 的爆火&#xff0c;越来越多的技术大佬们开始关注如…

Mac ARM 架构的命令行(终端)中,删除整行的快捷键是:Ctrl + U

在 Mac ARM 架构的命令行&#xff08;终端&#xff09;中&#xff0c;删除整行的快捷键是&#xff1a; Ctrl U这个快捷键会删除光标所在位置到行首之间的所有内容。如果你想删除光标后面的所有内容&#xff0c;可以使用&#xff1a; Ctrl K这两个快捷键可以帮助你快速清除当…

用pytorch实现一个简单的图片预测类别

前言&#xff1a; 在阅读本文之前&#xff0c;你需要了解Python&#xff0c;Pytorch&#xff0c;神经网络的一些基础知识&#xff0c;比如什么是数据集&#xff0c;什么是张量&#xff0c;什么是神经网络&#xff0c;如何简单使用tensorboard,DataLoader。 本次模型训练使用的是…

深度学习框架探秘|TensorFlow:AI 世界的万能钥匙

在人工智能&#xff08;AI&#xff09;蓬勃发展的时代&#xff0c;各种强大的工具和框架如雨后春笋般涌现&#xff0c;而 TensorFlow 无疑是其中最耀眼的明星之一。它不仅被广泛应用于学术界的前沿研究&#xff0c;更是工业界实现 AI 落地的关键技术。今天&#xff0c;就让我们…

Linux 服务器部署deepseek

把手教你在linux服务器部署deepseek&#xff0c;打造专属自己的数据库知识库 正文开始 第一步&#xff1a;安装Ollama 打开官方网址&#xff1a;https://ollama.com/download/linux 下载Ollama linux版本 复制命令到linux操作系统执行 [rootpostgresql ~]# curl -fsSL http…

DeepSeek、Kimi、文心一言、通义千问:AI 大语言模型的对比分析

在人工智能领域&#xff0c;DeepSeek、Kimi、文心一言和通义千问作为国内领先的 AI 大语言模型&#xff0c;各自展现出了独特的特点和优势。本文将从技术基础、应用场景、用户体验和价格与性价比等方面对这四个模型进行对比分析&#xff0c;帮助您更好地了解它们的特点和优势。…

CSDN、markdown环境下如何插入各种图(流程图,时序图,甘特图)

流程图 横向流程图 mermaid graph LRA[方形] --> B{条件a}B -->|满足| C(圆角)B -->|不满足| D(圆角)C --> E[输出结果1]D --> E效果图&#xff1a; 竖向流程图 mermaid graph TDC{条件a} --> |a1| A[方形]C --> |a2| F[竖向流程图]A --> B(圆角)B …

unity学习40:导入模型的 Animations文件夹内容,动画属性和修改动画文件

目录 1 Animations文件夹内容 2 每个模型文件的4个标签 3 model 4 rig 动画类型 5 Animation 5.1 新增动画和修改动画 5.2 限制动画某个轴的变化&#xff0c;烘焙 6 material 材料 1 Animations文件夹内容 下面有很多文件夹每个文件夹都是不同的动作模型每个文件夹下…

web第三次作业

弹窗案例 1.首页代码 <!DOCTYPE html><html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>综合案例</title><st…

GMSL 实例1:当 MAX96717 遇上 MAX96724,打通 Camera 视频数据传输

新年伊始&#xff0c;继 Deepseek 在 AI 圈掀起风波之后。比亚迪在2月10日发布会上重磅官宣&#xff1a;全系车型将搭载自研的高阶智驾系统“天神之眼”&#xff0c;覆盖从10万元级入门车型到高端豪华车型的所有范围。此举如一颗重磅炸弹投向当前一卷再卷的新能源汽车赛道&…

DeepSeek 助力 Vue 开发:打造丝滑的侧边栏(Sidebar)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

基于opencv的 24色卡IQA评测算法源码-可完全替代Imatest

1.概要 利用24色卡可以很快的分析到曝光误差&#xff0c;白平衡误差&#xff0c;噪声&#xff0c;色差&#xff0c;饱和度&#xff0c;gamma值。IQA或tuning工程一般用Imatest来手动计算&#xff0c;不便于产测部署&#xff0c;现利用opencv实现了imatest的全部功能&#xff0c…

数据结构与算法之排序算法-(计数,桶,基数排序)

排序算法是数据结构与算法中最基本的算法之一&#xff0c;其作用就是将一些可以比较大小的数据进行有规律的排序&#xff0c;而想要实现这种排序就拥有很多种方法~ &#x1f4da; 非线性时间比较类&#xff1a; 那么我将通过几篇文章&#xff0c;将排序算法中各种算法细化的&a…

MATLAB图像处理:图像特征概念及提取方法HOG、SIFT

图像特征是计算机视觉中用于描述图像内容的关键信息&#xff0c;其提取质量直接影响后续的目标检测、分类和匹配等任务性能。本文将系统解析 全局与局部特征的核心概念&#xff0c;深入讲解 HOG&#xff08;方向梯度直方图&#xff09;与SIFT&#xff08;尺度不变特征变换&…

kibana es 语法记录 elaticsearch

目录 一、认识elaticsearch 1、什么是正向索引 2、什么是倒排索引 二、概念 1、说明 2、mysql和es的对比 三、mapping属性 1、定义 四、CRUD 1、查看es中有哪些索引库 2、创建索引库 3、修改索引库 4、删除索引库 5、新增文档 6、删除文档 5、条件查询 一、认识…

手写一个Java Android Binder服务及源码分析

手写一个Java Android Binder服务及源码分析 前言一、Java语言编写自己的Binder服务Demo1. binder服务demo功能介绍2. binder服务demo代码结构图3. binder服务demo代码实现3.1 IHelloService.aidl3.2 IHelloService.java&#xff08;自动生成&#xff09;3.3 HelloService.java…