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

下面是一个简单的计时器任务实现,可持续地每秒保存一幅图像,也可持续地每60秒保存一个视频,图像和视频均以当前时间命名:

TimerTask类的实现如下:

cpp 复制代码
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
cpp 复制代码
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。

测试代码如下:

cpp 复制代码
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;
}

执行结果如下图所示:

GitHubhttps://github.com/fengbingchun/OpenCV_Test

相关推荐
高力士等十万人2 小时前
OpenCV二值化处理
python·opencv·计算机视觉
默凉2 小时前
Ubuntu 20.04源码安装opencv 4.5.0
opencv·ubuntu
时光旅人01号6 小时前
深度学习工厂的蓝图:拆解CUDA驱动、PyTorch与OpenCV的依赖关系
pytorch·深度学习·opencv
@Mr_LiuYang9 小时前
使用 Python 和 OpenCV 从一组图片生成 MP4 格式的视频
python·opencv·音视频
de之梦-御风12 小时前
【OpenCV】OpenCV 中各模块及其算子的详细分类
人工智能·opencv·分类
de之梦-御风13 小时前
【OpenCV】OpenCV算子功能分类
opencv·计算机视觉·分类
jndingxin13 小时前
OpenCV机器学习(10)训练数据的一个核心类cv::ml::TrainData
人工智能·opencv·机器学习
paterWang18 小时前
基于 Python 和 OpenCV 的酒店客房入侵检测系统设计与实现
开发语言·python·opencv
东方佑18 小时前
使用Python和OpenCV实现图像像素压缩与解压
开发语言·python·opencv
程序媛小果1 天前
基于 Python+OpenCV 的疲劳检测系统设计与实现(源码+文档)
开发语言·python·opencv