月別アーカイブ: 2018年9月

OpenCVでCAP_PROP_FRAME_WIDTHが効かないカメラをEWCLIBの併用で何とかする

OpenCV (C++) でWebカメラからのキャプチャ解像度を変更したい場合,本来は以下のようにすれば良いはずです。

cv::VideoCapture vc;
vc.set(cv::CAP_PROP_FRAME_WIDTH, 1920);
vc.set(cv::CAP_PROP_FRAME_HEIGHT, 1080);

しかし,このAPIはカメラによっては効果がないことが良くあります。特にLogicoolのカメラでは16:9のキャプチャをしようと思っても無視されてしまう傾向にある気がします。長年未解決な問題です。
(Windowsでの問題。LinuxやMacでは違うかも知れません)

この問題については,EWCLIB を併用するのが良いようです。キャプチャ部分を他のライブラリに頼る方法は EWCLIB 以外にもいくつかあるようですが,EWCLIB には以下のようなメリットがあります。

  • ヘッダのみなので導入が簡単。
  • Visual Studioのバージョンを殆ど選ばない1
  • x64向けにも簡単にビルドできる。

デメリットもありますが2それほど根本的なものでもありません。導入は以下のようにします。

Windows 10 + Visual Studio Professional 2015 で動作確認しています。

qedit.hを用意する

EWCLIBには別途Windows SDKの「qedit.h」が必要です。これは以下のように用意できます3

  1. Microsoft Windows Software Development Kit Update for Windows Vistaのページで [DOWNLOAD] をクリックして 6.1.6000.16384.10.WindowsSDK_Vista_Feb2007Update_rtm.DVD.Rel.iso をダウンロードします。
  2. 上記isoファイルをマウントします。例えばWindows10の場合はダブルクリックすればドライブレターが割り振られてマウントされます。
  3. マウントされたドライブの Setup フォルダ内の WinSDKBuild-SDK_DirectShow_BLD_Common-common.0.cab を探します。qedit.h はこの cab ファイル内にあります。
  4. cab ファイルを伸長できるツールで上記 cab ファイルを伸長します。Windows 10 の場合はダブルクリックすればフォルダのように扱えます。
  5. qedit_h.99023124_2CFC_4698_80A9_F84FC02DCB6C を探して,任意のフォルダにコピーし,qedit.h にリネームします。
Visual Studioから使えるようにする

ewclib.h と qedit.h を任意のフォルダにおいて,自作プロジェクト等から「追加のインクルードディレクトリ」等を設定します。
私はプロジェクト内に置いて相対パスで指定するようにしています。

なお .lib は無いので「追加のライブラリディレクトリ」等を設定する必要はありません。

OpenCV と併用するので OpenCV の設定も必要です。EWCLIB 2.5 と OpenCV 3.4.2 の併用で特に問題はありませんでした。

サンプルコードその1

640×360 という,16:9 の解像度でキャプチャする例です。
CAP_PROP_FRAME_WIDTH での設定ではこの解像度はうまくいかないのですが,このコードだときちんと 640×360 でのキャプチャになります。

#include <opencv2/opencv.hpp>
#include "ewclib.h"

#ifdef _DEBUG
#pragma comment(lib, "opencv_world342d.lib")
#else
#pragma comment(lib, "opencv_world342.lib")
#endif

int main(void)
{
    const int cameraID = 0;
    const int width = 640;
    const int height = 360;

    int ret = EWC_Open(cameraID, width, height, 30, cameraID, MEDIASUBTYPE_RGB24);
    if (ret != 0) {
        fprintf(stderr, "EWC_Open failed.(%d: %d x %d)\n", cameraID, width, height);
        std::exit(1);
    }
    EWC_SetValue(cameraID, EWC_FOCUS, 0.0);
    cv::Mat_<cv::Vec3b> camera = cv::Mat_<cv::Vec3b>(height, width);

    cv::namedWindow("camera", CV_WINDOW_AUTOSIZE);
    for (;;) {
        EWC_GetImage(0, camera.data);
        cv::imshow("camera", camera);
        if (cv::waitKey(1) == 0x1b) { // ESC
            break;
        }
    }

    EWC_Close(cameraID);

    return 0;
}
サンプルコードその2

Logicool の C615 の詳細ページによると,このカメラは以下の解像度でビデオキャプチャできるとあります。

[4:3 SD] 320×240,640×480
[16:9 W] 360P,480P,720P,1080P

以下のコードは,これらの解像度で順にキャプチャをしていく例です。
なお,16:9の方の480Pというのは良くわかりませんでした(以下のコードに854×480を追加してみてもEWC_Open()が失敗します)。

#include <opencv2/opencv.hpp>
#include <vector>
#include <string>
#include "ewclib.h"

#ifdef _DEBUG
#pragma comment(lib, "opencv_world342d.lib")
#else
#pragma comment(lib, "opencv_world342.lib")
#endif

int main(void)
{
    const int cameraID = 0;

    const std::vector<cv::Size> capsizes = {
        // 4:3
        cv::Size(320, 240),
        cv::Size(640, 480),
        // 16:9
        cv::Size(640, 360),
        cv::Size(1280, 720),
        cv::Size(1920, 1080)
        // logicool c615 は上記全部OK
    };

    for (auto size : capsizes) {
        int ret = EWC_Open(cameraID, size.width, size.height, 30, cameraID, MEDIASUBTYPE_RGB24);
        if (ret != 0){
            fprintf(stderr, "EWC_Open failed.(%d: %d x %d)\n", cameraID, size.width, size.height);
            std::exit(1);
        }
        EWC_SetValue(cameraID, EWC_FOCUS, 0.0);
        cv::Mat_<cv::Vec3b> camera = cv::Mat_<cv::Vec3b>(size.height, size.width);

        std::string sizestr = std::to_string(size.width) + " x " + std::to_string(size.height);

        cv::namedWindow("camera", CV_WINDOW_AUTOSIZE);
        for (;;) {
            EWC_GetImage(0, camera.data);
            cv::putText(camera, sizestr.c_str(), cv::Point(40, 40), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 0, 255), 2, CV_AA);
            cv::imshow("camera", camera);
            if (cv::waitKey(1) == 0x1b) { // ESCで次の解像度へ
                break;
            }
        }

        EWC_Close(cameraID);
    }

    return 0;
}