【C++并发入门】opencv摄像头帧率计算和多线程相机读取(下):完整代码实现-1 多线程读取相机

时间:2024-10-02 12:51:50
1-1 代码实现
  • 结合上一节我们学习到的内容,我们使用面向对象的思路,简单写出以下的代码
#include<iostream>
#include <opencv2/opencv.hpp>
#include <thread>
#include<chrono>
#define _CRT_SECURE_NO_WARNINGS 1

class ThreadCam
{
private:
	cv::Mat  frame;
	cv::VideoCapture cap;
	std::thread cameraCaptureThread;
	std::thread cameraProcessingThread;
	std::mutex mtx;
	std::chrono::time_point<std::chrono::steady_clock> startTime = std::chrono::steady_clock::now();
	std::chrono::time_point<std::chrono::steady_clock> endTime;

	void cameraCaptureThreadFunc() 
	{
		while (true) 
		{
			{
				std::lock_guard<std::mutex> guard(mtx);
				bool ret = cap.read(frame);
			}
		}
	}
	void cameraProcessingThreadFunc()
	{
		double frame_count = 0;
		double fps = 0;
		while (true)
		{
			if (frame.empty())
				continue;
			frame_count++;
			endTime = std::chrono::steady_clock::now();
	        double timeTaken = std::chrono::duration<double, std::milli>(endTime - startTime).count();
	        if (timeTaken >= 1000)
	        {
	            fps = frame_count;
	            startTime = std::chrono::steady_clock::now();
	            frame_count = 0;
	        }
			

			cv::putText(frame, std::to_string(int(fps)) + " FPS", cv::Point(frame.cols / 4, frame.rows / 3), cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(255, 0, 0), 2);
		
			
			cv::imshow("Frame", frame);
			
			if (cv::waitKey(1) == 'q')
			    break;
			
		}
	}
public:
	ThreadCam() :cap(0)
	{
		if (!cap.isOpened())
		{
			std::cerr << "open camera failed!" << std::endl;
			std::abort();
		}
		cap.set(cv::CAP_PROP_FRAME_WIDTH, 640);
		cap.set(cv::CAP_PROP_FRAME_HEIGHT, 400);
		cap.set(cv::CAP_PROP_FOURCC, cv::VideoWriter::fourcc('M', 'J', 'P', 'G'));
		cameraCaptureThread = std::thread(&ThreadCam::cameraCaptureThreadFunc, this);
		cameraProcessingThread = std::thread(&ThreadCam::cameraProcessingThreadFunc, this);

		cameraCaptureThread.join();
		cameraProcessingThread.join();
	}
};
int main()
{
	try {
		ThreadCam thread_cam;
	
	}
	catch (const std::exception& e) {
		std::cerr << "Exception caught: " << e.what() << std::endl;
		return -1;
	}

	return 0;
}
  • cameraCaptureThreadFunc:这个线程不断地从摄像头捕获帧,并将捕获到的帧存储在frame变量中。它使用std::lock_guard来保证在读取和写入frame时不会发生竞争条件。
  • cameraProcessingThreadFunc:这个线程捕获摄像头画面计算并显示视频的帧率。它首先检查frame是否为空,然后计算自startTime以来的时间。如果时间超过1000毫秒,则计算帧率,重置startTime,并将帧计数器frame_count重置为0。然后,它在每一帧上显示当前的帧率,并在按下’q’键时退出循环。
  • 下面代码创建了一个std::lock_guard对象guard,它自动锁定互斥量mtxstd::lock_guard的作用域是紧随其声明之后的代码块,即大括号{}内的区域。当std::lock_guard对象超出这个作用域时,其析构函数会被调用,这将导致互斥锁被自动释放。
{
	std::lock_guard<std::mutex> guard(mtx);
	bool ret = cap.read(frame);
}
1-2 效果展示
  • 运行效果如下,一看帧率???甚至超出了摄像头的最高帧率,这是怎么回事呢请添加图片描述

  • 由于摄像头捕获的线程和摄像头处理(FPS计算的线程)是异步,那也就意味着摄像头处理的线程甚至可能快于摄像头捕获线程的运行速率,导致捕获到的frame会出现连续相同的画面,以导致画面帧数计算错误。那解决这个问题也很简单,如果我们需要计算真正的FPS,我们只需要剔除重复的画面即可。

  • 但是在这样多线程的读取捕获处理下,即使会出现相同的画面,也就不会出现像上一节那样由于耗时操作导致捕获到的画面不及时,一定程度上解决了这个问题。