Question is cross-posted from https://stackoverflow.com/questions/64764106/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], i.e., 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:
Note that no images are saved from within the class member functions of either Foo
or Bar
!