首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

OpenCV 即時偵測 USB 攝影機斷線重連機制:isOpened() 與讀取失敗雙重檢查

     編輯:程式設計綜合問答

核心問題

VideoCapture::isOpened() 僅在開啟瞬間檢查裝置是否可存取。攝影機在運行中被拔除,isOpened() 仍回傳 true,需透過幀讀取失敗來偵測斷線。

健壯的偵測與重連架構

#include 
#include 
#include 
#include 

class RobustCamera {
public:
    RobustCamera(int deviceId = 0) : deviceId_(deviceId), running_(false) {}
    
    void start() {
        running_ = true;
        captureThread_ = std::thread(&RobustCamera::captureLoop, this);
    }
    
    void stop() {
        running_ = false;
        if (captureThread_.joinable()) captureThread_.join();
        if (cap_.isOpened()) cap_.release();
    }
    
    // 取得最新幀 (線程安全)
    bool getLatestFrame(cv::Mat& frame) {
        std::lock_guard lock(frameMutex_);
        if (latestFrame_.empty()) return false;
        frame = latestFrame_.clone();
        return true;
    }

private:
    void captureLoop() {
        cv::Mat frame;
        int consecutiveFailures = 0;
        const int MAX_FAILURES = 5; // 連續 5 幀失敗判定斷線
        
        while (running_) {
            // 1. 確保裝置已開啟
            if (!cap_.isOpened()) {
                if (!reconnect()) {
                    std::this_thread::sleep_for(std::chrono::seconds(2));
                    continue;
                }
                consecutiveFailures = 0;
            }
            
            // 2. 嘗試讀取幀
            bool success = cap_.read(frame);
            
            if (!success || frame.empty()) {
                consecutiveFailures++;
                if (consecutiveFailures >= MAX_FAILURES) {
                    std::cerr << "Camera disconnected, attempting reconnect..." << std::endl;
                    cap_.release(); // 釋放句柄觸發重連
                    consecutiveFailures = 0;
                }
                std::this_thread::sleep_for(std::chrono::milliseconds(30));
                continue;
            }
            
            // 3. 讀取成功:重置計數、更新快取
            consecutiveFailures = 0;
            {
                std::lock_guard lock(frameMutex_);
                latestFrame_ = frame.clone();
            }
        }
    }
    
    bool reconnect() {
        std::cerr << "Reconnecting to camera " << deviceId_ << "..." << std::endl;
        for (int i = 0; i < 10 && running_; ++i) {
            cap_.open(deviceId_);
            if (cap_.isOpened()) {
                // 可選:重新套用解析度、FPS 等參數
                cap_.set(cv::CAP_PROP_FRAME_WIDTH, 1920);
                cap_.set(cv::CAP_PROP_FRAME_HEIGHT, 1080);
                std::cerr << "Reconnected successfully." << std::endl;
                return true;
            }
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
        return false;
    }

    int deviceId_;
    cv::VideoCapture cap_;
    cv::Mat latestFrame_;
    std::mutex frameMutex_;
    std::thread captureThread_;
    std::atomic running_;
};

關鍵設計要點

  1. 雙重檢查:isOpened() + read() 回傳值 + frame.empty()。
  2. 連續失敗計數:避免單幀抖動誤判,設定門檻 (如 5 幀) 才觸發重連。
  3. 釋放再開啟:cap_.release() 釋放 OS 層面句柄,cap_.open() 重新枚舉裝置。
  4. 背景執行緒:不阻塞主執行緒 (UI/推理),透過鎖取得最新幀。
  5. 參數重套用:重連後必須重新 set() 解析度、編碼格式等參數。

平台差異提醒

  • Linux (V4L2):拔除裝置後 /dev/videoX 消失,重插可能分配新號碼,建議掃描 /dev/video* 找回同一裝置 (可用 v4l2-ctl 查詢序號)。
  • Windows (DirectShow/MSMF):裝置索引通常固定,重連較穩定。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved