【问题标题】:OpenCV Video Capture nonfunctional as class member (C++)OpenCV Video Capture 不能作为类成员(C++)
【发布时间】:2020-11-10 06:49:30
【问题描述】:

我的应用程序要求将 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;
}

只为 N = [0,9] 生成 test_N.tiff,即,生成的唯一图像来自 main( ) 中的 cv::VideoCapture 对象,不是来自类 @987654327 @。

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

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

如何解释这种行为?是否有一些要求cv::VideoCapture 可能在不同的范围内?如果图片没有保存或者视频流没有正确打开,会不会像上面的代码那样产生错误信息?

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


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

};

编辑 20201110_2: OpenCV 版本是 4.2.0

我尝试从 (a) 函数中捕获图像帧,并 (b) 将在 main( ) 中实例化的 OpenCV 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 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;
}        

该程序的输出如下所示: 请注意,在 FooBar 的类成员函数中不会保存任何图像!

【问题讨论】:

    标签: c++ opencv video-capture


    【解决方案1】:

    编辑1:

    真正的问题出在这一行

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

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

    应该是

      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() 方法会生成带有抓取帧的图像文件。

    【讨论】:

    • 感谢您的回答!我编辑了原始问题,因为我仍然观察到相同的行为。
    • 我已经编辑了答案。这个问题实际上更简单,可能不需要以前的更改。经过测试并在我身边工作。不是 Tegra 系统,简单的 Ubuntu Linux 桌面,但应该是有效的。
    • 谢谢 :) 故事的寓意是不要忽视简单的错误!!
    猜你喜欢
    • 2015-09-14
    • 1970-01-01
    • 1970-01-01
    • 2023-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多