Видеозахват OpenCV нефункциональный как член класса (C )

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

Вывод из этой программы показан:
Вывод из исполняемого файла OpenCVTest
Обратите внимание, что никакие изображения не сохраняются из функций-членов класса либо 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. Спасибо 🙂 мораль этой истории такова: не упускайте из виду простые ошибки!!