简体   繁体   中英

OpenCV Video Capture nonfunctional as class member (C++)

My application requires that the OpenCV VideoCapture object be used as a member variable. There is no way around this requirement.

I am experiencing strange behavior when using cv::VideoCapture as a member of a user-defined class. I've run the following code:

#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;
}

which only produced test_N.tiff for N = [0,9], ie, the only images produced are coming from the cv::VideoCapture object in main( ) and not from within class Foo .

I tried to instantiate a global variable g_cap of type cv::VideoCapture and still I only can read/write images from the object within the main function. As shown in the above code I also tried to instantiate the VideoCapture object as a pointer (a hail mary, if you will) and that does not work either. Note however that setting cap declared in main( ) to be a reference to g_cap (obviously when g_cap was in global scope) gave the same output - getting images from within main( ) but not from within Foo::run( ) as needed.

Note another strange behavior is that no error messages appear in the console. That would indicate that Foo 's member of type VideoCapture is in fact open and that loading an image frame into f of type cv::Mat did not return an empty frame. Similarly the imwrite function does not return false indicating that the operation was successful. However, as previously stated, no files of name Foo_test_N.tiff were produced.

How could this behavior be explained? Is there some requirement that cv::VideoCapture be in some different scope perhaps? If the images are not saved or the video stream is not opened correctly would this not produce an error message as the above code is written?

Edit 20201110_1: In response to Viktor Latypov's comment below: I've implemented the proposed changes and am still observing the same behavior. Edits:


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

};

Edit 20201110_2: OpenCV Version is 4.2.0

I tried to capture image frames from (a) within a function and (b) passing the OpenCV VideoCapture object instantiated in main( ) to a parameterized constructor both as a copy and passing the pointer with still the same results. Complete code is shown below:

#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;
}        

The output from this program is shown: OpenCVTest 可执行文件的输出 Note that no images are saved from within the class member functions of either Foo or Bar !

EDIT1:

The real problem is in this line

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

I have overlooked this obvious problem. The break statement executes no matter what the f.empty() check returns.

It should be

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

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

Former answer (problem N2) :

After allocating the cap object

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

you are calling the assignment operator

    cv::VideoCapture g_cap = *cap;

which may perform more actions than you actually want.

Remove the g_cap variable and use cap directly.

Eg, the line

    if (not g_cap.open(0)) {

should be

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

and the line

        g_cap >> f;

would become

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

After these simple modifications the run() method produces image files with grabbed frames.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM