mengineer's blog

ニッチなネタばかりですが。

QtでOpenCV VideoCapture

下記ネタの続き、QtからOpenCVのVideoCaptureを使う時の忘備録あれこれ。

mengineer.hatenablog.com

CMake.txtにOpenCV関係の記述追加

CMake.txtより下記抜粋、先頭4行と最終行がOpenCV関係の設定です。

# # for OpenCV
set(OpenCV_DIR E:\\Software\\opencv_mingw)
find_package(OpenCV REQUIRED)
set(CMAKE_PREFIX_PATH ${OpenCV_DIR})
INCLUDE_DIRECTORIES( ${OpenCV_INCLUDE_DIRS} )

target_link_libraries(TegeViewer PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
target_link_libraries(TegeViewer PRIVATE ${OpenCV_LIBRARIES})

注意、TegeViewerはMinGWコンパイルしているので、
OpenCVMinGWでビルドしたものが必要です。

今回は手っ取り早く、下記から頂きました。(ビルド済みのもの)

github.com

VideoCaptureの検出

VideoCapture関係は、CvVideoCaptureクラスにまとめ、
それをカメラコントロールのダイアログから参照します。

InitOpenCvCaptureで、indexを変えてcapture.openを実行し、
戻り値がtrueのindexをVectorとして返します。
(indexが飛ぶ場合も有るそうなので、Vectorで返します)

MAX_CAPTUREは最大接続台数で、現状"5"にしています、
もっと増やせますが、検出時間は長くなるはず。

QVector<int> CvVideoCapture::InitOpenCVCaptute()
{
    QVector<int> vResult;

    if(capture.isOpened())
        capture.release();

    videoCaptureCount = 0;
    for (int i = 0; i < MAX_CAPTURE; ++i) {
        bool result = capture.open(i, cv::CAP_ANY);
        if (result) {
            videoCaptureCount++;
            vResult.append(i);
        }
    }
    return vResult;
}

VideoCapture Propertyの取得

ダイアログの"Get Property"ボタンでプロパティを取得します、
下記のCAP_PROP_POS_MSEC ~CAP_PROP_AUTOFOCUSまで、計40個

OpenCV: Flags for video I/O

他にもProperty有りますが、上記は0~39と連番なので抜粋し、
QStringのVectorで”CAP_PROPERTY”という40個の項目名を定義、
下記で順番に読み込み、文字列でダイアログに表示させてるだけです。

QString CvVideoCapture::GetAllProperty()
{
    QString propText = "";
    if (capture.isOpened()) {
        for (int i = 0; i < CAP_PROPERTY.size(); ++i)
            propText += CAP_PROPERTY[i] + " : " + QString::number(capture.get(i)) + "\n";
    }
    propText = propText.trimmed();
    return propText;
}

後で気付いたのですが、cv::VideoCapture::get(int propId) の戻り値が0の場合、
そのPropertyは未対応みたいです、ということは、設定値で"0"は無いってことかな?

VideoCapture Propertyの設定

cv::VideoCapture::set(int propId, double value)を呼んでるだけです。

CODEC(cv::CAP_PROP_FOURCC)だけ、4文字を分割します、
例えば"MJPG"だとcv::VideoWriter::fourcc('M', 'J', 'P', 'G')と設定します。

bool CvVideoCapture::SetCodec(QString format)
{
    std::string sVal = format.toStdString();
    const char* pCodec = sVal.c_str();
    bool result = capture.set(cv::CAP_PROP_FOURCC, cv::VideoWriter::fourcc(pCodec[0], pCodec[1] , pCodec[2], pCodec[3]));

    return result;
}

パラメータ設定ダイアログ

WIndows版のOpenCVでは、cv::CAP_PROP_SETTINGSに"1"を書くと、
下記のダイアログが表示され、パラメータ変更出来ます、これ便利。

例えば明るさ、コントラストは、BRIGHTENESS、CONTRASTに相当

画像取込

CvVideoCaptureクラスは、QtのQThreadクラスを継承させています、
親はMainWindowで、Start(▶)ボタンでThread開始(Thread→start())

CvVideoCaptureのrun()内で、画像を取込み毎にBGR→RGBに変換し、
updateCapture()のsignalを発生させます。

void CvVideoCapture::run()
{
    QMutex mutex;

    while(true) {
        mutex.lock();
        if (this->StopVideoCapture) {
            break;
        }
        mutex.unlock();
        capture >> frame;
        cv::cvtColor(frame, dst, cv::COLOR_BGR2RGB);
        emit updateCapture();
        g_pUtil->CalcFrameRate();
    }
    qDebug() << "FPS:" << capture.get(cv::CAP_PROP_FPS);
}

MainWindow側では、CvVideoCaptureクラスからMat形式の画像を取得し、
それをQtのQImageに変換してimageScene->SetQImage(qimg)で表示させます。

色々ごちゃごちゃやってるのは、前回とサイズが変わった場合の再設定とか。

    cv::Mat matimag;
    videoCapture->GetMat(&matimag);
    QImage qimg = QImage((const unsigned char*)(matimag.data), matimag.cols, matimag.rows, QImage::Format_RGB888);

    bool sizeChanged = imageScene->IsNullImage() || imageScene->GetImageHeight() != qimg.height()
    || imageScene->GetImageWidth() != qimg.width();
    if (sizeChanged)
        infoDock->ActivateDock(SELTYPE_RECT);

    imageScene->SetQImage(qimg);

    if (!g_pUtil->IsCameraStarted()) {
        this->setCursor(Qt::ArrowCursor);
        g_pUtil->SetCameraStatus(true);
        pixelFormat = "";
        showTitle();
        enabler(true);

        changeCursorColorAction->setEnabled(true);
        if (sizeChanged) {
            imageView->resetTransform();
            imageView->InitZoomLevel();
            imageView->setSceneRect(imageScene->GetImageRect());
            fitWindow();
        }
    }

With / Height /FrameRate

いまいち挙動が判りません、起動直後はFrameRateは常に0になっている、
WidthやHeightも、何回か設定しないと反映されない? or 設定順が有るのか?

TegeViewer内のFramaRate表示も、カメラ設定と合わない場合有り。

良く考えたら、TegeViewerで動画として取り込んでいる訳では無いので、
(Frameごとに単独のMat画像として読む、の繰り返し)
CAP_PROP_FPS は関係無いのかもしれません。

改めてプロパティ見ると、CAP_PROP_FORMAT というプロパティも有りましたね、
なんやかんやで、色々見直すべきところが有りそうです。