cv :: imshow в opencv отображает только части составного изображения, но работает отдельно.Зачем? - PullRequest
0 голосов
/ 18 января 2019

Цель и проблема

Я пытаюсь обработать видеофайл на лету, используя OpenCV 3.4.1, захватывая каждый кадр, преобразуя его в оттенки серого, а затем определяя края Canny. Чтобы отобразить изображения (также на лету), я создал класс Mat с 3 дополнительными заголовками, который в три раза шире исходного кадра. 3 дополнительных заголовка представляют изображения, которые я хотел бы отобразить в композите, и расположены на 1-м, 2-м и 3-м горизонтальных сегментах композита.

Однако после обработки изображения отображение составного изображения не соответствует ожидаемому: первый сегмент (где должен быть исходный кадр) полностью черный , тогда как остальные сегменты (обработанных изображений) отображается нормально. Если, с другой стороны, я отображаю области интереса по одному в отдельных окнах , все изображения выглядят нормально.

Вот то, что я пытался преодолеть эту проблему:

  1. Используйте .copyTo для фактического копирования данных в соответствующие сегменты изображения. Результат был таким же.
  2. Я поместил изображение Canny в ROI compOrigPart, и оно отображалось в первом сегменте, так что это не проблема с определением ROI.
    • Определение составного изображения как трехканального изображения
    • В цикле преобразовать его в оттенки серого
    • положить обработанные изображения в него
    • конвертировать обратно в BGR
    • вставьте оригинал.

На этот раз весь композит был черным, ничего не показывалось.

  1. В соответствии с предложением gameon67, я также пытался создать namedWindow, но это тоже не помогает.

Код:

int main() {

    cv::VideoCapture vid("./Vid.avi");
    if (!vid.isOpened()) return -1;

    int frameWidth = vid.get(cv::CAP_PROP_FRAME_WIDTH);
    int frameHeight = vid.get(cv::CAP_PROP_FRAME_HEIGHT);
    int frameFormat = vid.get(cv::CAP_PROP_FORMAT);

    cv::Scalar fontColor(250, 250, 250);
    cv::Point textPos(20, 20);

    cv::Mat frame;

    cv::Mat compositeFrame(frameHeight, frameWidth*3, frameFormat);
    cv::Mat compOrigPart(compositeFrame, cv::Range(0, frameHeight), cv::Range(0, frameWidth));
    cv::Mat compBwPart(compositeFrame, cv::Range(0, frameHeight), cv::Range(frameWidth, frameWidth*2));
    cv::Mat compEdgePart(compositeFrame, cv::Range(0, frameHeight), cv::Range(frameWidth*2, frameWidth*3));


    while (vid.read(frame)) {
        if (frame.empty()) break;

        cv::cvtColor(frame, compBwPart, cv::COLOR_BGR2GRAY);
        cv::Canny(compBwPart, compEdgePart, 100, 150);
        compOrigPart = frame;

        cv::putText(compOrigPart, "Original", textPos, cv::FONT_HERSHEY_PLAIN, 1, fontColor);
        cv::putText(compBwPart, "GrayScale", textPos, cv::FONT_HERSHEY_PLAIN, 1, fontColor);
        cv::putText(compEdgePart, "Canny edge detection", textPos, cv::FONT_HERSHEY_PLAIN, 1, fontColor);

        cv::imshow("Composite of Original, BW and Canny frames", compositeFrame);
        cv::imshow("Original", compOrigPart);
        cv::imshow("BW", compBwPart);
        cv::imshow("Canny", compEdgePart);
        cv::waitKey(33);
    }
}

Вопросы

  • Почему я не могу отобразить составное изображение целиком в одном окне, а отображать их отдельно - это нормально?
  • В чем разница между этими дисплеями? Данные явно есть, о чем свидетельствуют отдельные окна.
  • Почему только оригинальная рамка плохо себя ведет?

Ответы [ 2 ]

0 голосов
/ 19 января 2019

Это сочетание нескольких проблем.

Первая проблема заключается в том, что для типа compositeFrame установлено значение, возвращаемое vid.get(cv::CAP_PROP_FORMAT). К сожалению, это свойство не кажется полностью надежным - я только что вернул 0 (что означает CV_8UC1) после открытия цветного видео и получения 3-канальных (CV_8UC3) кадров. Поскольку вы хотите, чтобы compositeFrame был того же типа, что и входной кадр, это не сработает.

Чтобы обойти это, вместо использования этих свойств, я бы лениво инициализировал compositeFrame и 3 области интереса после получения первого кадра (в зависимости от его размеров и типа).


Следующая группа проблем заключается в этих двух утверждениях:

cv::cvtColor(frame, compBwPart, cv::COLOR_BGR2GRAY);
cv::Canny(compBwPart, compEdgePart, 100, 150);

В этом случае предполагается, что frame - это BGR (поскольку вы пытаетесь конвертировать), что означает compositeFrame, а его ROI также являются BGR. К сожалению, в обоих случаях вы записываете изображение в градациях серого в ROI. Это приведет к перераспределению, и цель Mat перестанет быть ROI.

Чтобы исправить это, используйте временные Mat s для данных в градациях серого и используйте cvtColor, чтобы вернуть их в BGR для записи в области ROI.


Аналогичная проблема заключается в следующем утверждении:

compOrigPart = frame;

Это мелкая копия, означающая, что она просто сделает compOrigPart еще одной ссылкой на frame (и, следовательно, она перестанет быть ROI compositeFrame).

Вам нужна глубокая копия с использованием copyTo (обратите внимание, что типы данных все еще должны совпадать, но это было исправлено ранее).


Наконец, даже если вы пытаетесь проявлять гибкость в отношении типа входного видео (судя по vid.get(cv::CAP_PROP_FORMAT)), остальная часть кода действительно предполагает, что вход является 3-канальным, и прервется, если он не будет .

По крайней мере, должно быть какое-то утверждение, чтобы оправдать это ожидание.


Собираем все это вместе:

#include <opencv2/opencv.hpp>

int main()
{
    cv::VideoCapture vid("./Vid.avi");
    if (!vid.isOpened()) return -1;

    cv::Scalar fontColor(250, 250, 250);
    cv::Point textPos(20, 20);

    cv::Mat frame, frame_gray, edges_gray;
    cv::Mat compositeFrame;
    cv::Mat compOrigPart, compBwPart, compEdgePart; // ROIs

    while (vid.read(frame)) {
        if (frame.empty()) break;

        if (compositeFrame.empty()) {
            // The rest of code assumes video to be BGR (i.e. 3 channel)
            CV_Assert(frame.type() == CV_8UC3);
            // Lazy initialize once we have the first frame
            compositeFrame = cv::Mat(frame.rows, frame.cols * 3, frame.type());
            compOrigPart = compositeFrame(cv::Range::all(), cv::Range(0, frame.cols));
            compBwPart = compositeFrame(cv::Range::all(), cv::Range(frame.cols, frame.cols * 2));
            compEdgePart = compositeFrame(cv::Range::all(), cv::Range(frame.cols * 2, frame.cols * 3));
        }

        cv::cvtColor(frame, frame_gray, cv::COLOR_BGR2GRAY);
        cv::Canny(frame_gray, edges_gray, 100, 150);

        // Deep copy data to the ROI
        frame.copyTo(compOrigPart);
        // The ROI is BGR, so we need to convert back
        cv::cvtColor(frame_gray, compBwPart, cv::COLOR_GRAY2BGR);
        cv::cvtColor(edges_gray, compEdgePart, cv::COLOR_GRAY2BGR);

        cv::putText(compOrigPart, "Original", textPos, cv::FONT_HERSHEY_PLAIN, 1, fontColor);
        cv::putText(compBwPart, "GrayScale", textPos, cv::FONT_HERSHEY_PLAIN, 1, fontColor);
        cv::putText(compEdgePart, "Canny edge detection", textPos, cv::FONT_HERSHEY_PLAIN, 1, fontColor);

        cv::imshow("Composite of Original, BW and Canny frames", compositeFrame);
        cv::imshow("Original", compOrigPart);
        cv::imshow("BW", compBwPart);
        cv::imshow("Canny", compEdgePart);
        cv::waitKey(33);
    }
}

Снимок экрана составного окна (с использованием случайного тестового видео из Интернета):

Example Composite frame

0 голосов
/ 18 января 2019

Ваши compBwPart и compEdgePart являются изображениями в градациях серого, поэтому тип мата - CV8UC1 - один канал, и поэтому ваш композитный кадр также находится в градациях серого.Если вы хотите объединить эти два изображения с цветным изображением, вы должны сначала преобразовать его в BGR, а затем заполнить compOrigPart.

while (vid.read(frame)) {
  if (frame.empty()) break;

  cv::cvtColor(frame, compBwPart, cv::COLOR_BGR2GRAY);
  cv::Canny(compBwPart, compEdgePart, 100, 150);
  cv::cvtColor(compositeFrame, compositeFrame, cv::COLOR_GRAY2BGR);
  frame.copyTo(compositeFrame(cv::Rect(0, 0, frameWidth, frameHeight)));

  cv::putText(compOrigPart, "Original", textPos, cv::FONT_HERSHEY_PLAIN, 1, fontColor); //the rest  of your code
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...