OpenCV Video Capture无法作为类成员C ++

问题描述

我的应用程序要求将OpenCV VideoCapture对象用作成员变量。无法解决这个要求。

使用cv::VideoCapture作为用户定义的类的成员时,我遇到奇怪的行为。我运行了以下代码

#define int64 opencv_broken_int
#include <opencv2/opencv.hpp>
#undef int64


class Foo {
public:
    Foo() = default;
    void run(void) {
        cv::VideoCapture* cap = new cv::VideoCapture();
        cv::VideoCapture g_cap = *cap;
        if (not g_cap.open(0)) {
            std::cerr << "Cannot open camera 0" << std::endl;
            exit(-1);
        }
        for (int i = 0; i < 10; i++) {
            if (not g_cap.isOpened())
                std::cerr << "Video capture is not open here!" << std::endl;
            cv::Mat f;
            g_cap >> f;
            if (f.empty()) std::cerr << "Frame was empty" << std::endl; break;
            std::string filename = "Foo_test_" + std::to_string(i) + ".tiff";
            bool b = cv::imwrite(filename,f);
            if (not b) std::cerr << "Error in writing image" << std::endl;
            //cv::waitKey(25);
        }
    };

};

int main(void) {
    cv::VideoCapture cap;

    if (not cap.open(0)) {
        std::cerr << "Cannot open camera 0" << std::endl;
        return -1;
    }

    for (int i =0; i < 10; i++) {
        cv::Mat frame; 
        cap >> frame;
        if (frame.empty()) {
            std::cerr << "frame is empty" << std::endl;
            break;
        }
        // Else write the image to a file
        std::string filename = "test_" + std::to_string(i) + ".tiff";
        bool res = cv::imwrite(filename,frame);
        if (not res) 
            std::cerr << "Error in writing image" << std::endl;

    }

    cap.release();

    Foo f;
    f.run();

    return 0;
}

仅产生test = N.tiff,其中N = [0,9],即,产生的唯一图像来自cv::VideoCapture中的main( )对象,而来自 not 在课程Foo中。

我试图实例化类型为g_cap全局变量cv::VideoCapture,但仍然只能从main函数中的对象读取/写入图像。如上面的代码所示,我还尝试将VideoCapture对象实例化为指针(如果愿意的话,请给一个冰雹玛丽),但这也不起作用。但是请注意,将cap中声明的main( )设置为对g_cap的引用(显然,g_cap在全局范围内时)会产生相同的输出-从{{ 1}},但不是根据需要从main( )内部进行。

请注意,另一个奇怪的行为是控制台中没有错误消息出现。这表明Foo::run( )类型为Foo的成员实际上是打开的,并且将图像帧加载到类型为VideoCapture的{​​{1}}中并没有返回空帧。类似地,f函数不会返回false,表示操作成功。但是,如前所述,没有产生名称为Foo_test_N.tiff的文件

如何解释这种行为?是否有一些要求cv::Mat处于不同范围内的要求?如果未保存图像或视频流未正确打开,那么在编写上述代码时会不会产生错误消息?

编辑20201110_1: 针对以下Viktor Latypov的评论: 我已经实施了建议的更改,并且仍在观察相同的行为。编辑:

imwrite

编辑20201110_2: OpenCV版本是4.2.0

我试图从(a)函数中捕获图像帧,并且(b)将在cv::VideoCapture中实例化的OpenCV VideoCapture对象作为副本传递给参数化构造函数,并同时传递指针,结果仍然相同。完整的代码如下所示:


class Foo {
public:
        Foo() = default;
        void run(void) {
                cv::VideoCapture* cap = new cv::VideoCapture();
                //cv::VideoCapture g_cap = *cap;
                if (not cap -> open(0)) {
                        std::cerr << "Cannot open camera 0" << std::endl;
                        exit(-1);
                }
                for (int i = 0; i < 10; i++) {
                        if (not cap -> isOpened())
                                std::cerr << "Video capture is not open here!" << std::endl;
                        cv::Mat f;
                        *cap >> f;
                        if (f.empty()) std::cerr << "Frame was empty" << std::endl; break;
                        std::string filename = "Foo_test_" + std::to_string(i) + ".tiff";
                        bool b = cv::imwrite(filename,f);
                       if (not b) std::cerr << "Error in writing image" << std::endl;
                       //cv::waitKey(25);
                }
                cap -> release();
        };

};

该程序的输出如下所示:

Output from OpenCVTest Executable

请注意,main( )#define int64 opencv_broken_int #include <opencv2/opencv.hpp> #undef int64 class Foo { public: Foo() = default; void run(void) { cv::VideoCapture* cap = new cv::VideoCapture(); //cv::VideoCapture g_cap = *cap; if (not cap -> open(0)) { std::cerr << "Cannot open camera 0" << std::endl; exit(-1); } for (int i = 0; i < 10; i++) { if (not cap -> isOpened()) std::cerr << "Video capture is not open here!" << std::endl; cv::Mat f; *cap >> f; if (f.empty()) std::cerr << "Frame was empty" << std::endl; break; std::string filename = "Foo_test_" + std::to_string(i) + ".tiff"; bool b = cv::imwrite(filename,f); if (not b) std::cerr << "Error in writing image" << std::endl; //cv::waitKey(25); } cap -> release(); }; }; class Bar { public: Bar(cv::VideoCapture* c) : cap(c) {}; void run(void) { //cv::VideoCapture* cap = new cv::VideoCapture(); //cv::VideoCapture g_cap = *cap; if (not cap -> open(0)) { std::cerr << "Cannot open camera 0" << std::endl; exit(-1); } for (int i = 0; i < 10; i++) { if (not cap -> isOpened()) std::cerr << "Video capture is not open here!" << std::endl; cv::Mat f; *cap >> f; if (f.empty()) std::cerr << "Frame was empty" << std::endl; break; std::string filename = "Bar_test_" + std::to_string(i) + ".tiff"; bool b = cv::imwrite(filename,f); if (not b) std::cerr << "Error in writing image" << std::endl; //cv::waitKey(25); } cap -> release(); }; cv::VideoCapture* cap; }; void test(void) { cv::VideoCapture cap(0); if (not cap.open(0)) { std::cerr << "Cannot open camera 0" << std::endl; exit(-1); } for (int i =0; i < 10; i++) { cv::Mat frame; cap >> frame; if (frame.empty()) { std::cerr << "frame is empty" << std::endl; break; } // Else write the image to a file std::string filename = "testFunc_test_" + std::to_string(i) + ".tiff"; bool res = cv::imwrite(filename,frame); if (not res) std::cerr << "Error in writing image" << std::endl; } cap.release(); } int main(void) { cv::VideoCapture cap; if (not cap.open(0)) { std::cerr << "Cannot open camera 0" << std::endl; return -1; } for (int i =0; i < 10; i++) { cv::Mat frame; cap >> frame; if (frame.empty()) { std::cerr << "frame is empty" << std::endl; break; } // Else write the image to a file std::string filename = "main_test_" + std::to_string(i) + ".tiff"; bool res = cv::imwrite(filename,frame); if (not res) std::cerr << "Error in writing image" << std::endl; } cap.release(); Bar b(&cap); b.run(); Foo f; f.run(); test(); return 0; } 类成员函数中没有保存图像!

解决方法

EDIT1:

真正的问题在这一行

  if (f.empty()) std::cerr << "Frame was empty" << std::endl; break;

我忽略了这个明显的问题。无论break支票返回什么,f.empty()语句都会执行。

应该是

  if (f.empty())
  {
       std::cerr << "Frame was empty" << std::endl;
       break;
  }

==================

以前的答案(问题N2):

分配cap对象之后

    cv::VideoCapture* cap = new cv::VideoCapture();

您正在呼叫赋值运算符

    cv::VideoCapture g_cap = *cap;

执行的动作可能会超出您的实际需求。

删除g_cap变量,然后直接使用cap

例如,行

    if (not g_cap.open(0)) {

应该是

    if (not cap->open(0)) {

和行

        g_cap >> f;

将成为

        *cap >> f;   // or  cap->read(f);

在进行了这些简单的修改之后,run()方法将生成带有抓取帧的图像文件。