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

      下面是一个简单的计时器任务实现,可持续地每秒保存一幅图像,也可持续地每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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣19题——删除链表的倒数第 N 个结点

#题目 #代码 //定义虚拟头结点ListNode curnew ListNode(0,head); //定义两个指针指向虚拟头结点ListNode lcur;ListNode rcur;for(int i0;i<n;i){rr.next;}while(r!null){rr.next;ll.next;} //l.next就是我们要删除的那个元素if(l.next!null){l.nextl.next.next;}return c…

网络工程师 (42)IP地址

一、定义与功能 IP地址是IP协议提供的一种统一的地址格式&#xff0c;它为互联网上的每一个网络和每一台主机分配一个逻辑地址&#xff0c;以此来屏蔽物理地址的差异。这种地址分配方式确保了用户在连网的计算机上操作时&#xff0c;能够高效且方便地从众多计算机中选出自己所需…

记忆力训练day19

万能字母组合编码法 所有的文字和字母的背后都有画面 练的不是记单词&#xff0c;练的是注意力给到单词&#xff0c;出什么画面&#xff0c;然后画面与画面之间进行连接 拆的过程就是找熟词的过程 要关注自己的回忆路径是什么&#xff1f;也就是你是怎么回忆起来的&#xff0c…

flutter image_cropper插件安装后 打包apk 报错命名空间问题

本篇文章主要讲解&#xff0c;Flutter安装完新依赖打包apk报错 A problem occurred configuring project ‘:image_cropper’. 命名空间问题的解决办法及原因说明。 日期&#xff1a;2025年2月15日 作者&#xff1a;任聪聪 一、报错现象&#xff1a; 报文信息&#xff1a; FAI…

八、SPI读写XT25数据

8.1 SPI 简介 SPI&#xff08;Serial Peripheral Interface&#xff0c;串行外设接口&#xff09;是一种同步串行通信协议&#xff0c;广泛用于嵌入式系统中连接微控制器与外围设备&#xff0c;如传感器、存储器、显示屏等。 主要特点 1. 全双工通信&#xff1a;支持同时发送…

kibana es 语法记录 elaticsearch

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

三、Unity基础(主要框架)

一、Unity场景概念 如果把游戏运行过程理解成表演&#xff0c;那么场景就是舞台&#xff1b; 场景本质上是一个配置文件&#xff0c;这个配置文件决定了场景中有哪些东西&#xff1b; 二、Scene和Game窗口 1、Scene 滚轮缩放、拖动 单独选中也可以 最下面这个是全能工具…

pdf文档提取信息

目录 一、前言二、核心代码说明1、PyPDF2提取文本2、pdfplumber提取文本和表格3、fitz提取文本和图片4、fitz按页提取图片一、前言 本博客文章介绍pdf的文本、图片、表格等信息提取的技术方案对比。目前比较熟知的是pdfplumber 、PyPDF2 、fitz(PyMuPDF)。 它们之间对比如下 …

Git指南-从入门到精通

代码提交和同步命令 流程图如下&#xff1a; 第零步: 工作区与仓库保持一致第一步: 文件增删改&#xff0c;变为已修改状态第二步: git add &#xff0c;变为已暂存状态 bash $ git status $ git add --all # 当前项目下的所有更改 $ git add . # 当前目录下的所有更改 $ g…

我们来学HTTP/TCP -- 三次握手?

三次握手 题记三次呼叫结语 题记 来&#xff0c;我们来演示下川普王和普京帝会面了 哎呦&#xff01;你好你好&#xff0c;握手…哎嗨&#xff01;侬好侬好&#xff0c;握手…欧嘿呦玛斯&#xff0c;握手… 抓狂啊&#xff01;作孽啊!!! 不说人话啊! 关键的是&#xff0c;“三…

kubectl top输出与Linux free命令不一致原因?

当你在 Kubernetes 集群中使用 kubectl top 命令查看资源使用情况时&#xff0c;可能会发现与在节点上直接运行 Linux free 命令得到的结果不一致。这种不一致可能源于多个原因&#xff0c;以下是一些关键因素&#xff1a; MobaXterm中文版下载&#xff1a; https://pan.quark…

【设计模式】【行为型模式】迭代器模式(Iterator)

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f44d; 欢迎点赞、收藏、关注&#xff0c;跟上我的更新节奏 &#x1f3b5; 当你的天空突…

论文解读之DeepSeek R1

今天带来DeepSeek R1的解读 一、介绍 deepseek主打复杂推理任务&#xff0c;如数学、代码任务。 R1以预训练过的V1-base初始化&#xff0c;主要发挥了RL在长思维链上的优势&#xff0c;R1-Zero直接RL而在前置步骤中不进行SFT&#xff0c;即缺少了有监督的指令微调阶段&#…

Linux:用 clang 编译带 sched_ext 功能内核

文章目录 1. 前言2. 编译过程2.1 准备内核源代码2.2 安装编译工具2.3 配置、编译、运行2.3.1 配置2.3.2 编译2.3.3 运行 3. 参考资料 1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&#xff0c;因此而给读者带来的损失&#xff0c;作者不做任何承诺。 2. 编译过程 …

FPGA之​​​​​​​​​​​​​​HRBANK与HOBANK有什么区别?

在FPGA设计中&#xff0c;HP Bank&#xff08;High-Performance Bank&#xff09;与HR Bank&#xff08;High-Range Bank&#xff09;是针对I/O电气特性划分的不同区域&#xff0c;二者的主要区别在于支持的电压范围、信号速率以及应用场景。以下是具体对比&#xff1a; 核心区…

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_ssl_init 函数

#if (NGX_OPENSSL)ngx_ssl_init(log); #endif objs/ngx_auto_config.h 中 #ifndef NGX_OPENSSL #define NGX_OPENSSL 1 #endif 所以这个条件编译成立 NGX_OPENSSL 是一个宏定义&#xff0c;用于控制与 OpenSSL 相关的功能是否被启用 若用户通过./configure参数&#xff08;如-…

pandas(13 Caveats Gotchas和SQL比较)

前面内容&#xff1a;pandas(12 IO工具和稀松数据) 目录 一、Caveats警告 & Gotchas预见 1.1 在Pandas中使用if/Truth语句 1.2 位运算布尔 1.3 isin操作 1.4 重新索引reindex和 loc&iloc 使用注意事项 1.5 loc和iloc 二、Python Pandas 与SQL的比较 2.1 数…

MongoDB 7 分片副本集升级方案详解(下)

#作者&#xff1a;任少近 文章目录 1.4 分片升级1.5 升级shard11.6 升级shard2,shard31.7 升级mongos1.8重新启用负载均衡器1.9 推荐MongoDB Compass来验证数据 2 注意事项&#xff1a; 1.4 分片升级 使用“滚动”升级从 MongoDB 7.0 升级到 8.0&#xff0c;即在其他成员可用…

洛谷 P2894 USACO08FEB Hotel 题解

题意 第一行输入 n , m n,m n,m&#xff0c; n n n 代表有 n n n 个房间 ( 1 ≤ n ≤ 50 , 000 ) (1\leq n \leq 50,000) (1≤n≤50,000)&#xff0c;编号为 1 ∼ n 1 \sim n 1∼n&#xff0c;开始都为空房&#xff0c; m m m 表示以下有 m m m 行操作 ( 1 ≤ m < 50…

VS2022中.Net Api + Vue 从创建到发布到IIS

VS2022中.Net Api Vue 从创建到发布到IIS 前言一、先决条件二、创建项目三、运行项目四、增加API五、发布到IIS六、设置Vue的发布 前言 最近从VS2019 升级到了VS2022,终于可以使用官方的.Net Vue 组合了,但是使用过程中还是有很多问题,这里记录一下. 一、先决条件 Visual …