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

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;
}

运行结果:

相关推荐
L-影1 天前
多线程:让你的 App 从“单行道”变成“立体交通枢纽”
开发语言·多线程
szxinmai主板定制专家5 天前
基于ZYNQ UltraScale+ MPSOC的超高清红外-可见光融合成像相机设计
数码相机·相机
Brookty14 天前
【JavaEE】线程安全(一).4:写块串行保安全、CAS
java·开发语言·java-ee·多线程·线程安全
张槊哲16 天前
什么是相机标定
相机
小bo波1 个月前
使用Thread子类创建线程 VS 使用Runnable接口创建线程的区别
java·多线程·thread·并发编程·runnable
明飞19871 个月前
高德红外镜头 配置电脑ip
相机
保定中博输送带输送机1 个月前
口碑好的输送机厂家
相机
颗粒机Cathy1 个月前
口碑不错的羊饲料颗粒机
相机
埃科光电1 个月前
应用分享丨告别测量盲区!锂电极片刻槽检测新方案
图像处理·计算机视觉·视觉检测·相机
程序喵大人1 个月前
【C++并发系列】第一章:多线程读写同一个变量为什么会出错
开发语言·c++·多线程·并发