#c #opencv #video-capture
#c #opencv #видеозахват
Вопрос:
Мое приложение требует, чтобы объект видеозахвата OpenCV использовался в качестве переменной-члена. Обойти это требование невозможно.
Я испытываю странное поведение при использовании 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( )
а не из класса Foo
.
Я попытался создать глобальную переменную g_cap
типа cv::VideoCapture
, и все же я могу только читать / записывать изображения из объекта в основной функции. Как показано в приведенном выше коде, я также попытался создать экземпляр объекта VideoCapture в качестве указателя (приветствие Марии, если хотите), и это тоже не работает. Однако обратите внимание, что параметр cap
, объявленный в main( )
качестве ссылки на g_cap
(очевидно, когда g_cap
он находился в глобальной области видимости), давал тот же результат — получение изображений изнутри main( )
, но не изнутри Foo::run( )
по мере необходимости.
Обратите внимание, что другое странное поведение заключается в том, что в консоли не отображаются сообщения об ошибках. Это будет указывать на то, что Foo
член типа VideoCapture
на самом деле открыт и что загрузка кадра изображения в f
тип cv::Mat
не возвращает пустой кадр. Аналогично imwrite
функция не возвращает false, указывающее, что операция прошла успешно. Однако, как указывалось ранее, файлы с именем Foo_test_N.tiff не были созданы.
Как можно объяснить такое поведение? Есть ли какие-то требования, которые cv::VideoCapture
могут быть в какой-то другой области? Если изображения не сохраняются или видеопоток открывается неправильно, не приведет ли это к появлению сообщения об ошибке при написании приведенного выше кода?
Редактировать 20201110_1: В ответ на комментарий Виктора Латыпова ниже: я внедрил предложенные изменения и все еще наблюдаю то же поведение. Правки:
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
Я попытался захватить кадры изображения из (а) внутри функции и (б) передать созданный экземпляр объекта OpenCV VideoCapture в 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(amp;cap); b.run();
Foo f; f.run();
test();
return 0;
}
Вывод из этой программы показан:
Обратите внимание, что никакие изображения не сохраняются из функций-членов класса либо Foo
или Bar
!
Ответ №1:
ПРАВКА1:
Настоящая проблема заключается в этой строке
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()
метод создает файлы изображений с захваченными кадрами.
Комментарии:
1. Спасибо за ответ! Я отредактировал исходный вопрос, поскольку я все еще наблюдаю то же поведение.
2. Я отредактировал ответ. Проблема на самом деле намного проще и, вероятно, не требует предыдущих изменений. Протестировано и работает на моей стороне. Не система Tegra, простой рабочий стол Ubuntu Linux, но должен быть действительным.
3. Спасибо 🙂 мораль этой истории такова: не упускайте из виду простые ошибки!!