通用多相机多线程采图框架

python版本:

python 复制代码
import threading
import queue
import time
import abc
import cv2
import numpy as np
from typing import Dict, List, Optional, Any

# 全局常量配置
MAX_QUEUE_SIZE = 30  # 图像缓存队列最大长度(避免内存溢出)
DEFAULT_TIMEOUT = 0.1  # 队列操作超时时间(秒)


class BaseCamera(metaclass=abc.ABCMeta):
    """相机抽象基类:定义通用接口,所有具体相机需继承并实现抽象方法"""

    def __init__(self, camera_id: str or int, **kwargs):
        """
        初始化相机基类
        :param camera_id: 相机唯一标识(如USB相机的0/1,工业相机的序列号)
        :param kwargs: 相机额外参数(如分辨率、曝光时间、帧率等)
        """
        self.camera_id = camera_id
        self.kwargs = kwargs

        # 线程安全状态控制
        self._lock = threading.Lock()
        self._initialized = False  # 相机是否初始化完成
        self._capturing = False    # 相机是否正在采集

        # 图像缓存队列(线程安全)
        self._frame_queue = queue.Queue(maxsize=MAX_QUEUE_SIZE)

        # 采集线程
        self._capture_thread: Optional[threading.Thread] = None

    @abc.abstractmethod
    def init_camera(self) -> bool:
        """
        初始化相机硬件(具体实现由子类完成)
        :return: 初始化成功返回True,失败返回False
        """
        pass

    @abc.abstractmethod
    def _capture_single_frame(self) -> Optional[np.ndarray]:
        """
        采集单帧图像(具体实现由子类完成,内部调用)
        :return: 图像数组(HWC格式),采集失败返回None
        """
        pass

    @abc.abstractmethod
    def close_camera(self) -> None:
        """关闭相机,释放硬件资源(具体实现由子类完成)"""
        pass

    def start_capture(self) -> bool:
        """
        启动相机采集线程
        :return: 启动成功返回True,失败返回False
        """
        with self._lock:
            if not self._initialized:
                print(f"[ERROR] 相机{self.camera_id}未初始化,无法启动采集")
                return False
            if self._capturing:
                print(f"[WARNING] 相机{self.camera_id}已在采集状态")
                return True

            self._capturing = True
            # 创建并启动采集线程
            self._capture_thread = threading.Thread(
                target=self._capture_loop,
                name=f"Camera-{self.camera_id}-Capture",
                daemon=True  # 守护线程:主程序退出时自动终止
            )
            self._capture_thread.start()
            print(f"[INFO] 相机{self.camera_id}采集线程已启动")
            return True

    def stop_capture(self) -> None:
        """停止相机采集线程(优雅停止,避免强制终止)"""
        with self._lock:
            if not self._capturing:
                return
            self._capturing = False

        # 等待线程结束
        if self._capture_thread and self._capture_thread.is_alive():
            self._capture_thread.join(timeout=1.0)
            print(f"[INFO] 相机{self.camera_id}采集线程已停止")

    def _capture_loop(self) -> None:
        """采集循环(线程执行函数)"""
        while True:
            # 检查是否需要停止采集
            with self._lock:
                if not self._capturing:
                    break

            try:
                # 采集单帧图像
                frame = self._capture_single_frame()
                if frame is None:
                    time.sleep(0.001)
                    continue

                # 将图像放入队列(包含时间戳和相机ID,方便上层处理)
                frame_data = {
                    "camera_id": self.camera_id,
                    "timestamp": time.time(),
                    "frame": frame
                }

                # 队列满时丢弃最旧帧(也可选择阻塞或丢弃新帧)
                if self._frame_queue.full():
                    try:
                        self._frame_queue.get_nowait()
                    except queue.Empty:
                        pass
                self._frame_queue.put(frame_data, timeout=DEFAULT_TIMEOUT)

            except Exception as e:
                print(f"[ERROR] 相机{self.camera_id}采集异常: {str(e)}")
                time.sleep(0.01)

    def get_frame(self, timeout: float = DEFAULT_TIMEOUT) -> Optional[Dict[str, Any]]:
        """
        获取最新帧(从队列中取出)
        :param timeout: 队列等待超时时间
        :return: 帧数据字典(camera_id/timestamp/frame),无数据返回None
        """
        try:
            return self._frame_queue.get(timeout=timeout)
        except queue.Empty:
            return None

    def clear_queue(self) -> None:
        """清空图像队列"""
        while not self._frame_queue.empty():
            try:
                self._frame_queue.get_nowait()
            except queue.Empty:
                break

    def __del__(self):
        """析构函数:确保资源释放"""
        self.stop_capture()
        self.close_camera()


class USBCamera(BaseCamera):
    """USB相机实现类(基于OpenCV)"""

    def __init__(self, camera_id: int, resolution: tuple = (640, 480), fps: int = 30, **kwargs):
        """
        初始化USB相机
        :param camera_id: USB相机索引(0/1/2...)
        :param resolution: 分辨率 (width, height)
        :param fps: 帧率
        :param kwargs: 其他参数(如曝光时间:exposure=-1(自动))
        """
        super().__init__(camera_id, resolution=resolution, fps=fps, **kwargs)
        self._cap: Optional[cv2.VideoCapture] = None

    def init_camera(self) -> bool:
        """初始化USB相机硬件"""
        with self._lock:
            if self._initialized:
                return True

            # 打开相机
            self._cap = cv2.VideoCapture(self.camera_id)
            if not self._cap.isOpened():
                print(f"[ERROR] 无法打开USB相机{self.camera_id}")
                return False

            # 设置分辨率
            width, height = self.kwargs["resolution"]
            self._cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
            self._cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)

            # 设置帧率
            self._cap.set(cv2.CAP_PROP_FPS, self.kwargs["fps"])

            # 设置曝光时间(可选)
            if "exposure" in self.kwargs:
                self._cap.set(cv2.CAP_PROP_EXPOSURE, self.kwargs["exposure"])

            self._initialized = True
            print(f"[INFO] USB相机{self.camera_id}初始化成功")
            return True

    def _capture_single_frame(self) -> Optional[np.ndarray]:
        """采集单帧图像"""
        if not self._cap or not self._cap.isOpened():
            return None

        ret, frame = self._cap.read()
        return frame if ret else None

    def close_camera(self) -> None:
        """关闭USB相机"""
        with self._lock:
            if not self._initialized:
                return

            if self._cap:
                self._cap.release()
                self._cap = None
            self._initialized = False
            print(f"[INFO] USB相机{self.camera_id}已关闭")


class MockCamera(BaseCamera):
    """模拟相机实现类(生成随机图像,用于测试)"""

    def __init__(self, camera_id: str, resolution: tuple = (640, 480), **kwargs):
        super().__init__(camera_id, resolution=resolution, **kwargs)
        self._width, self._height = resolution

    def init_camera(self) -> bool:
        """模拟初始化(无硬件操作)"""
        with self._lock:
            if self._initialized:
                return True
            self._initialized = True
            print(f"[INFO] 模拟相机{self.camera_id}初始化成功")
            return True

    def _capture_single_frame(self) -> Optional[np.ndarray]:
        """生成随机图像"""
        frame = np.random.randint(0, 255, (self._height, self._width, 3), dtype=np.uint8)
        # 绘制相机ID标识(方便测试区分)
        cv2.putText(
            frame, f"Camera: {self.camera_id}",
            (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2
        )
        return frame

    def close_camera(self) -> None:
        """模拟关闭(无硬件操作)"""
        with self._lock:
            if not self._initialized:
                return
            self._initialized = False
            print(f"[INFO] 模拟相机{self.camera_id}已关闭")


class CameraManager:
    """相机管理器:统一管理多个相机的生命周期"""

    def __init__(self):
        self._cameras: Dict[str or int, BaseCamera] = {}  # 相机实例字典
        self._lock = threading.Lock()

    def add_camera(self, camera: BaseCamera) -> bool:
        """
        添加相机到管理器
        :param camera: 相机实例
        :return: 添加成功返回True,重复ID返回False
        """
        with self._lock:
            if camera.camera_id in self._cameras:
                print(f"[WARNING] 相机ID {camera.camera_id} 已存在,添加失败")
                return False
            self._cameras[camera.camera_id] = camera
            print(f"[INFO] 相机{camera.camera_id}已添加到管理器")
            return True

    def init_all_cameras(self) -> bool:
        """初始化所有相机"""
        with self._lock:
            success_count = 0
            for cam_id, camera in self._cameras.items():
                if camera.init_camera():
                    success_count += 1

            all_success = success_count == len(self._cameras)
            print(f"[INFO] 相机初始化完成:成功{success_count}/{len(self._cameras)}")
            return all_success

    def start_all_capture(self) -> bool:
        """启动所有相机的采集线程"""
        with self._lock:
            success_count = 0
            for cam_id, camera in self._cameras.items():
                if camera.start_capture():
                    success_count += 1

            all_success = success_count == len(self._cameras)
            print(f"[INFO] 相机采集启动完成:成功{success_count}/{len(self._cameras)}")
            return all_success

    def stop_all_capture(self) -> None:
        """停止所有相机的采集线程"""
        with self._lock:
            for cam_id, camera in self._cameras.items():
                camera.stop_capture()
            print(f"[INFO] 所有相机采集已停止")

    def close_all_cameras(self) -> None:
        """关闭所有相机,释放资源"""
        with self._lock:
            for cam_id, camera in self._cameras.items():
                camera.close_camera()
            self._cameras.clear()
            print(f"[INFO] 所有相机已关闭并释放资源")

    def get_camera(self, camera_id: str or int) -> Optional[BaseCamera]:
        """获取指定ID的相机实例"""
        with self._lock:
            return self._cameras.get(camera_id, None)

    def get_all_frames(self, timeout: float = DEFAULT_TIMEOUT) -> Dict[str or int, Optional[Dict[str, Any]]]:
        """
        获取所有相机的最新帧
        :param timeout: 队列等待超时时间
        :return: 键为相机ID,值为帧数据(无数据则为None)
        """
        frames = {}
        with self._lock:
            for cam_id, camera in self._cameras.items():
                frames[cam_id] = camera.get_frame(timeout=timeout)
        return frames

    def clear_all_queues(self) -> None:
        """清空所有相机的图像队列"""
        with self._lock:
            for cam_id, camera in self._cameras.items():
                camera.clear_queue()


# -------------------------- 测试示例 --------------------------
def test_multi_camera_capture():
    """测试多相机采集"""
    # 1. 创建相机管理器
    manager = CameraManager()

    # 2. 添加相机(可混合添加不同类型相机)
    # 添加2个USB相机(若没有硬件,可替换为MockCamera)
    # manager.add_camera(USBCamera(camera_id=0, resolution=(640, 480), fps=30))
    # manager.add_camera(USBCamera(camera_id=1, resolution=(640, 480), fps=30))

    # 添加2个模拟相机(用于测试)
    manager.add_camera(MockCamera(camera_id="mock_0", resolution=(640, 480)))
    manager.add_camera(MockCamera(camera_id="mock_1", resolution=(640, 480)))

    try:
        # 3. 初始化所有相机
        if not manager.init_all_cameras():
            print("[ERROR] 相机初始化失败,退出测试")
            return

        # 4. 启动所有相机采集
        if not manager.start_all_capture():
            print("[ERROR] 相机采集启动失败,退出测试")
            return

        # 5. 循环获取并显示图像
        print("[INFO] 开始采集图像,按'q'退出...")
        while True:
            # 获取所有相机的最新帧
            all_frames = manager.get_all_frames()

            # 显示每个相机的图像
            for cam_id, frame_data in all_frames.items():
                if frame_data is None:
                    continue

                frame = frame_data["frame"]
                # 绘制时间戳(可选)
                timestamp = time.strftime("%H:%M:%S", time.localtime(frame_data["timestamp"]))
                cv2.putText(
                    frame, f"Time: {timestamp}",
                    (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2
                )
                cv2.imshow(f"Camera {cam_id}", frame)

            # 按q退出
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break

    except KeyboardInterrupt:
        print("[INFO] 接收到中断信号,停止采集")
    except Exception as e:
        print(f"[ERROR] 测试过程异常: {str(e)}")
    finally:
        # 6. 停止采集并释放资源
        manager.stop_all_capture()
        manager.close_all_cameras()
        cv2.destroyAllWindows()
        print("[INFO] 测试结束,资源已释放")


if __name__ == "__main__":
    test_multi_camera_capture()

c++版本:

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
#include <atomic>
#include <queue>
#include <map>
#include <vector>
#include <chrono>
#include <memory>
#include <stdexcept>
#include <ctime>
#include <opencv2/opencv.hpp>

// 全局常量配置
constexpr size_t MAX_QUEUE_SIZE = 30;    // 队列最大长度
constexpr int QUEUE_TIMEOUT_MS = 100;    // 队列操作超时(毫秒)
constexpr int CAPTURE_ERROR_DELAY_MS = 10; // 采集异常后延迟(毫秒)

// 线程安全队列模板类
template <typename T>
class ThreadSafeQueue {
public:
	ThreadSafeQueue() = default;
	~ThreadSafeQueue() = default;

	// 禁止拷贝和移动(简化设计,也可按需实现)
	ThreadSafeQueue(const ThreadSafeQueue&) = delete;
	ThreadSafeQueue& operator=(const ThreadSafeQueue&) = delete;
	ThreadSafeQueue(ThreadSafeQueue&&) = delete;
	ThreadSafeQueue& operator=(ThreadSafeQueue&&) = delete;

	// 入队(超时返回false,队列满时丢弃最旧元素)
	bool push(const T& data, int timeout_ms = QUEUE_TIMEOUT_MS) {
		std::unique_lock<std::mutex> lock(mtx_);

		// 队列满时丢弃最旧元素
		if (queue_.size() >= MAX_QUEUE_SIZE) {
			if (!queue_.empty()) {
				queue_.pop();
			}
		}

		// 等待队列有空间(带超时)
		if (cv_.wait_for(lock, std::chrono::milliseconds(timeout_ms),
			[this]() { return queue_.size() < MAX_QUEUE_SIZE; })) {
			queue_.push(data);
			cv_.notify_one();
			return true;
		}
		return false;
	}

	// 出队(超时返回false)
	bool pop(T& data, int timeout_ms = QUEUE_TIMEOUT_MS) {
		std::unique_lock<std::mutex> lock(mtx_);
		if (cv_.wait_for(lock, std::chrono::milliseconds(timeout_ms),
			[this]() { return !queue_.empty(); })) {
			data = queue_.front();
			queue_.pop();
			return true;
		}
		return false;
	}

	// 清空队列
	void clear() {
		std::lock_guard<std::mutex> lock(mtx_);
		while (!queue_.empty()) {
			queue_.pop();
		}
	}

	// 获取队列大小
	size_t size() const {
		std::lock_guard<std::mutex> lock(mtx_);
		return queue_.size();
	}

	// 判断队列是否为空
	bool empty() const {
		std::lock_guard<std::mutex> lock(mtx_);
		return queue_.empty();
	}

private:
	std::queue<T> queue_;
	mutable std::mutex mtx_;
	std::condition_variable cv_;
};

// 帧数据结构体(包含相机ID、时间戳、图像)
struct FrameData {
	std::string camera_id;          // 相机唯一标识
	double timestamp;               // 采集时间戳(秒)
	cv::Mat frame;                  // 图像数据(深拷贝避免悬空引用)

	// 构造函数(深拷贝Mat)
	FrameData() {};

	FrameData(std::string id, double ts, const cv::Mat& img)
		: camera_id(std::move(id)), timestamp(ts), frame(img.clone()) {}
};

// 相机抽象基类
class BaseCamera {
public:
	explicit BaseCamera(std::string camera_id)
		: camera_id_(std::move(camera_id)),
		initialized_(false),
		capturing_(false) {}

	virtual ~BaseCamera() {
		stopCapture();
		closeCamera(); // 调用虚函数,基类有默认实现
	}

	// 禁止拷贝,允许移动
	BaseCamera(const BaseCamera&) = delete;
	BaseCamera& operator=(const BaseCamera&) = delete;
	BaseCamera(BaseCamera&&) = default;
	BaseCamera& operator=(BaseCamera&&) = default;

	// 纯虚函数:相机初始化
	virtual bool initCamera() = 0;

	// 纯虚函数:采集单帧图像
	virtual cv::Mat captureSingleFrame() = 0;

	// 虚函数:关闭相机(提供默认实现,子类可重写)
	virtual void closeCamera() {
		// 基类默认空实现,子类根据实际相机类型重写
		std::cout << "[INFO] BaseCamera " << camera_id_ << " closed (default impl)!" << std::endl;
	}

	// 启动采集线程
	bool startCapture() {
		if (!initialized_) {
			std::cerr << "[ERROR] Camera " << camera_id_ << " not initialized!" << std::endl;
			return false;
		}

		if (capturing_) {
			std::cerr << "[WARNING] Camera " << camera_id_ << " is already capturing!" << std::endl;
			return true;
		}

		capturing_ = true;
		// 创建采集线程(detach避免主线程join阻塞,通过capturing_控制退出)
		capture_thread_ = std::thread(&BaseCamera::captureLoop, this);
		capture_thread_.detach();
		std::cout << "[INFO] Camera " << camera_id_ << " capture thread started!" << std::endl;
		return true;
	}

	// 停止采集
	void stopCapture() {
		if (!capturing_) {
			return;
		}

		capturing_ = false;
		// 等待线程退出(给1秒时间)
		std::this_thread::sleep_for(std::chrono::milliseconds(1000));
		std::cout << "[INFO] Camera " << camera_id_ << " capture thread stopped!" << std::endl;
	}

	// 获取最新帧(超时返回空Mat)
	FrameData getFrame(int timeout_ms = QUEUE_TIMEOUT_MS) {
		FrameData empty_frame("", 0.0, cv::Mat());
		FrameData frame_data("", 0.0, cv::Mat());
		if (frame_queue_.pop(frame_data, timeout_ms)) {
			return frame_data;
		}
		return empty_frame;
	}

	// 清空帧队列
	void clearQueue() {
		frame_queue_.clear();
	}

	// 获取相机ID
	std::string getCameraId() const { return camera_id_; }

	// 判断是否初始化完成
	bool isInitialized() const { return initialized_; }

	// 判断是否正在采集
	bool isCapturing() const { return capturing_; }

protected:
	std::string camera_id_;                  // 相机ID
	std::atomic<bool> initialized_;          // 初始化状态(原子变量)
	std::atomic<bool> capturing_;            // 采集状态(原子变量)
	ThreadSafeQueue<FrameData> frame_queue_; // 帧队列
	std::thread capture_thread_;             // 采集线程

	// 采集循环(线程执行函数)
	void captureLoop() {
		while (capturing_) {
			try {
				// 采集单帧
				cv::Mat frame = captureSingleFrame();
				if (frame.empty()) {
					std::this_thread::sleep_for(std::chrono::microseconds(1000));
					continue;
				}

				// 构造帧数据(带时间戳)
				double timestamp = std::chrono::duration_cast<std::chrono::duration<double>>(
					std::chrono::system_clock::now().time_since_epoch()
					).count();
				FrameData frame_data(camera_id_, timestamp, frame);

				// 入队
				if (!frame_queue_.push(frame_data)) {
					std::cerr << "[WARNING] Camera " << camera_id_ << " queue push timeout!" << std::endl;
				}
			}
			catch (const std::exception & e) {
				std::cerr << "[ERROR] Camera " << camera_id_ << " capture error: " << e.what() << std::endl;
				std::this_thread::sleep_for(std::chrono::milliseconds(CAPTURE_ERROR_DELAY_MS));
			}
			catch (...) {
				std::cerr << "[ERROR] Camera " << camera_id_ << " unknown capture error!" << std::endl;
				std::this_thread::sleep_for(std::chrono::milliseconds(CAPTURE_ERROR_DELAY_MS));
			}
		}
	}

	// 设置初始化状态(子类调用)
	void setInitialized(bool status) {
		initialized_ = status;
	}
};

// USB相机实现类(基于OpenCV)
class USBCamera : public BaseCamera {
public:
	USBCamera(int camera_index, const cv::Size& resolution = cv::Size(640, 480), int fps = 30)
		: BaseCamera(std::to_string(camera_index)),
		camera_index_(camera_index),
		resolution_(resolution),
		fps_(fps),
		cap_(nullptr) {}

	~USBCamera() override {
		// 析构函数无需重复调用closeCamera,基类析构会自动调用
	}

	// 初始化USB相机
	bool initCamera() override {
		if (initialized_) {
			return true;
		}

		// 打开相机
		cap_ = std::make_unique<cv::VideoCapture>(camera_index_);
		if (!cap_->isOpened()) {
			std::cerr << "[ERROR] Failed to open USB camera " << camera_id_ << std::endl;
			cap_.reset();
			return false;
		}

		// 设置参数
		cap_->set(cv::CAP_PROP_FRAME_WIDTH, resolution_.width);
		cap_->set(cv::CAP_PROP_FRAME_HEIGHT, resolution_.height);
		cap_->set(cv::CAP_PROP_FPS, fps_);

		// 可选:设置曝光(-1为自动)
		if (exposure_ >= 0) {
			cap_->set(cv::CAP_PROP_EXPOSURE, exposure_);
		}

		setInitialized(true);
		std::cout << "[INFO] USB camera " << camera_id_ << " initialized!" << std::endl;
		return true;
	}

	// 采集单帧
	cv::Mat captureSingleFrame() override {
		if (!cap_ || !cap_->isOpened() || !initialized_) {
			return cv::Mat();
		}

		cv::Mat frame;
		*cap_ >> frame;
		return frame;
	}

	// 关闭相机(重写基类虚函数)
	void closeCamera() override {
		if (cap_) {
			cap_->release();
			cap_.reset();
		}
		setInitialized(false);
		std::cout << "[INFO] USB camera " << camera_id_ << " closed!" << std::endl;
	}

	// 设置曝光时间(可选)
	void setExposure(int exposure) {
		exposure_ = exposure;
		if (cap_ && initialized_) {
			cap_->set(cv::CAP_PROP_EXPOSURE, exposure_);
		}
	}

private:
	int camera_index_;                // USB相机索引(0/1/2...)
	cv::Size resolution_;             // 分辨率
	int fps_;                         // 帧率
	int exposure_ = -1;               // 曝光时间(-1自动)
	std::unique_ptr<cv::VideoCapture> cap_; // OpenCV视频捕获对象
};

// 模拟相机实现类(生成随机图像,用于测试)
class MockCamera : public BaseCamera {
public:
	MockCamera(const std::string& camera_id, const cv::Size& resolution = cv::Size(640, 480))
		: BaseCamera(camera_id),
		resolution_(resolution) {}

	~MockCamera() override {
		// 析构函数无需重复调用closeCamera,基类析构会自动调用
	}

	// 模拟初始化
	bool initCamera() override {
		if (initialized_) {
			return true;
		}

		setInitialized(true);
		std::cout << "[INFO] Mock camera " << camera_id_ << " initialized!" << std::endl;
		return true;
	}

	// 生成随机图像
	cv::Mat captureSingleFrame() override {
		if (!initialized_) {
			return cv::Mat();
		}

		// 生成随机图像
		cv::Mat frame(resolution_, CV_8UC3);
		cv::randu(frame, cv::Scalar(0, 0, 0), cv::Scalar(255, 255, 255));

		// 绘制相机ID标识
		cv::putText(frame, "Camera: " + camera_id_,
			cv::Point(10, 30), cv::FONT_HERSHEY_SIMPLEX,
			1.0, cv::Scalar(0, 255, 0), 2);

		return frame;
	}

	// 模拟关闭(重写基类虚函数)
	void closeCamera() override {
		setInitialized(false);
		std::cout << "[INFO] Mock camera " << camera_id_ << " closed!" << std::endl;
	}

private:
	cv::Size resolution_; // 分辨率
};

// 相机管理器(统一管理多相机)
class CameraManager {
public:
	CameraManager() = default;
	~CameraManager() {
		stopAllCapture();
		closeAllCameras();
	}

	// 禁止拷贝和移动
	CameraManager(const CameraManager&) = delete;
	CameraManager& operator=(const CameraManager&) = delete;
	CameraManager(CameraManager&&) = delete;
	CameraManager& operator=(CameraManager&&) = delete;

	// 添加相机(所有权转移)
	bool addCamera(std::unique_ptr<BaseCamera> camera) {
		if (!camera) {
			std::cerr << "[ERROR] Null camera pointer!" << std::endl;
			return false;
		}

		const std::string& cam_id = camera->getCameraId();
		std::lock_guard<std::mutex> lock(mtx_);
		if (cameras_.find(cam_id) != cameras_.end()) {
			std::cerr << "[WARNING] Camera " << cam_id << " already exists!" << std::endl;
			return false;
		}

		cameras_[cam_id] = std::move(camera);
		std::cout << "[INFO] Camera " << cam_id << " added to manager!" << std::endl;
		return true;
	}

	// 初始化所有相机
	bool initAllCameras() {
		std::lock_guard<std::mutex> lock(mtx_);
		int success_count = 0;
		for (auto& [cam_id, camera] : cameras_) {
			if (camera->initCamera()) {
				success_count++;
			}
		}

		bool all_success = (success_count == cameras_.size());
		std::cout << "[INFO] Camera init finished: " << success_count << "/" << cameras_.size() << " success!" << std::endl;
		return all_success;
	}

	// 启动所有相机采集
	bool startAllCapture() {
		std::lock_guard<std::mutex> lock(mtx_);
		int success_count = 0;
		for (auto& [cam_id, camera] : cameras_) {
			if (camera->startCapture()) {
				success_count++;
			}
		}

		bool all_success = (success_count == cameras_.size());
		std::cout << "[INFO] Capture start finished: " << success_count << "/" << cameras_.size() << " success!" << std::endl;
		return all_success;
	}

	// 停止所有相机采集
	void stopAllCapture() {
		std::lock_guard<std::mutex> lock(mtx_);
		for (auto& [cam_id, camera] : cameras_) {
			camera->stopCapture();
		}
		std::cout << "[INFO] All cameras capture stopped!" << std::endl;
	}

	// 关闭所有相机
	void closeAllCameras() {
		std::lock_guard<std::mutex> lock(mtx_);
		for (auto& [cam_id, camera] : cameras_) {
			camera->closeCamera();
		}
		cameras_.clear();
		std::cout << "[INFO] All cameras closed and released!" << std::endl;
	}

	// 获取指定相机
	BaseCamera* getCamera(const std::string & camera_id) {
		std::lock_guard<std::mutex> lock(mtx_);
		auto it = cameras_.find(camera_id);
		return (it != cameras_.end()) ? it->second.get() : nullptr;
	}

	// 获取所有相机的最新帧
	std::map<std::string, FrameData> getAllFrames(int timeout_ms = QUEUE_TIMEOUT_MS) {
		std::map<std::string, FrameData> all_frames;
		std::lock_guard<std::mutex> lock(mtx_);
		for (auto& [cam_id, camera] : cameras_) {
			FrameData frame = camera->getFrame(timeout_ms);
			all_frames[cam_id] = frame;
		}
		return all_frames;
	}

	// 清空所有相机的队列
	void clearAllQueues() {
		std::lock_guard<std::mutex> lock(mtx_);
		for (auto& [cam_id, camera] : cameras_) {
			camera->clearQueue();
		}
	}

private:
	std::map<std::string, std::unique_ptr<BaseCamera>> cameras_; // 相机集合
	mutable std::mutex mtx_;                                     // 管理器互斥锁
};

// 测试示例
int main() {
	try {
		// 1. 创建相机管理器
		CameraManager manager;

		// 2. 添加相机(模拟相机,替换为USBCamera(0)可测试真实USB相机)
		manager.addCamera(std::make_unique<MockCamera>("mock_0", cv::Size(640, 480)));
		manager.addCamera(std::make_unique<MockCamera>("mock_1", cv::Size(640, 480)));
		// manager.addCamera(std::make_unique<USBCamera>(0, cv::Size(640, 480), 30)); // 真实USB相机

		// 3. 初始化所有相机
		if (!manager.initAllCameras()) {
			std::cerr << "[ERROR] Camera init failed!" << std::endl;
			return -1;
		}

		// 4. 启动所有相机采集
		if (!manager.startAllCapture()) {
			std::cerr << "[ERROR] Capture start failed!" << std::endl;
			return -1;
		}

		// 5. 循环获取并显示图像
		std::cout << "[INFO] Start capturing... Press 'q' to quit!" << std::endl;
		while (true) {
			// 获取所有相机的最新帧
			auto all_frames = manager.getAllFrames();

			// 显示每个相机的图像
			for (auto& [cam_id, frame_data] : all_frames) {
				if (frame_data.frame.empty()) {
					continue;
				}

				// 绘制时间戳(格式化,避免换行)
				std::time_t ts = static_cast<std::time_t>(frame_data.timestamp);
				char time_buf[64];
				std::strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", std::localtime(&ts));
				std::string time_str(time_buf);
				
				cv::putText(frame_data.frame, "Time: " + time_str,
					cv::Point(10, 60), cv::FONT_HERSHEY_SIMPLEX,
					1.0, cv::Scalar(0, 0, 255), 2);

				// 显示窗口
				cv::imshow("Camera " + cam_id, frame_data.frame);
			}

			// 按'q'退出(注意waitKey的延迟,避免CPU占用过高)
			int key = cv::waitKey(1);
			if (key == 'q' || key == 27) { // 27是ESC键
				break;
			}
		}

		// 6. 停止采集并释放资源
		manager.stopAllCapture();
		manager.closeAllCameras();
		cv::destroyAllWindows();
		std::cout << "[INFO] Test finished!" << std::endl;
	}
	catch (const std::exception & e) {
		std::cerr << "[ERROR] Test exception: " << e.what() << std::endl;
		return -1;
	}

	return 0;
}

运行结果:

相关推荐
故事不长丨15 小时前
C#队列深度剖析:解锁高效编程的FIFO密码
visualstudio·c#·wpf·多线程·winfrom·队列·queue
enjoy编程1 天前
Spring boot 4 探究netty的关键知识点
spring boot·设计模式·reactor·netty·多线程
小毅&Nora4 天前
【Java线程安全实战】⑤ 原子类(Atomic)深度解析:无锁编程(Lock-Free)的终极奥义(增强版)
java·多线程·原子操作
萧曵 丶4 天前
ThreadLocal 原理及内存泄漏详解
java·多线程·threadlocal
敲上瘾5 天前
C++11线程库指南:线程、锁、原子操作与并发编程实战
开发语言·c++·多线程
Da Da 泓5 天前
多线程(七)【线程池】
java·开发语言·线程池·多线程
小毅&Nora6 天前
【Java线程安全实战】② ConcurrentHashMap 源码深度拆解:如何做到高性能并发?
java·安全·多线程
金牌归来发现妻女流落街头6 天前
【线程池 + Socket 服务器】
java·运维·服务器·多线程
萧曵 丶6 天前
Synchronized 详解及 JDK 版本优化
java·多线程·synchronized