Ошибка: утверждение не выполнено (dims() <= 2) в cv::MatSize::operator (), файл D:\vcpkg\installed\x64-windows\include\opencv2\core\mat.inl.hpp, строка 1198

Я получаю сообщение об ошибке утверждения в этом простом примере C++, который я нашел здесь

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/video.hpp>
#include <opencv2/dnn.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/imgproc.hpp>
using namespace cv;
using namespace std;
using namespace dnn;

int main()
{
    VideoCapture cap("D:/video1.mp4");
    std::string model = "./Models/yolov4_tiny_train2_best.weights";  
    std::string config = "./Models/yolov4_tiny_train2.cfg";

    Net network = readNet(model, config, "Darknet");
    network.setPreferableBackend(DNN_BACKEND_DEFAULT);
    network.setPreferableTarget(DNN_TARGET_OPENCL);

    for (;;)
    {
        if (!cap.isOpened()) {
            cout << "Video Capture Fail" << endl;
            break;
        }
        Mat img;
        cap >> img;
        static Mat blobFromImg;
        bool swapRB = true;
        blobFromImage(img, blobFromImg, 1, Size(416, 416), Scalar(), swapRB, false);
        cout << blobFromImg.size() << endl; #exception here
        float scale = 1.0 / 255.0;
        Scalar mean = 0;
        network.setInput(blobFromImg, "", scale, mean);
        Mat outMat;
        network.forward(outMat);
        int rowsNoOfDetection = outMat.rows;
        int colsCoordinatesPlusClassScore = outMat.cols;
        for (int j = 0; j < rowsNoOfDetection; ++j)
        {
            Mat scores = outMat.row(j).colRange(5, colsCoordinatesPlusClassScore);

            Point PositionOfMax;
            double confidence;
            minMaxLoc(scores, 0, &confidence, 0, &PositionOfMax);

            if (confidence > 0.5)
            {
                int centerX = (int)(outMat.at<float>(j, 0) * img.cols);
                int centerY = (int)(outMat.at<float>(j, 1) * img.rows);
                int width = (int)(outMat.at<float>(j, 2) * img.cols + 20);
                int height = (int)(outMat.at<float>(j, 3) * img.rows + 100);

                int left = centerX - width / 2;
                int top = centerY - height / 2;


                stringstream ss;
                ss << PositionOfMax.x;
                string clas = ss.str();
                int color = PositionOfMax.x * 10;
                putText(img, clas, Point(left, top), 1, 2, Scalar(color, 255, 255), 2, false);
                stringstream ss2;
                ss << confidence;
                string conf = ss.str();

                rectangle(img, Rect(left, top, width, height), Scalar(color, 0, 0), 2, 8, 0);
            }
        }

        namedWindow("Display window", WINDOW_AUTOSIZE);
        imshow("Display window", img);
        waitKey(25);
    }
    return 0;
}

Я не понимаю, почему это не работает, а сообщение об ошибке вообще не помогает. Когда я отлаживал, я обнаружил, что исключение происходит в строке cout << blobFromImg.size() << endl;. Кроме того, rows / cols из blobFromImg — это -1, и это объясняет.

Примечание. Я загружаю видео с разрешением 1280x720, а конфигурация даркнета — yolov4-tiny.

Что за функция blobFromImage(...)? Пожалуйста, опубликуйте минимально воспроизводимый пример. В любом случае, похоже, что он не инициализирует cv::Mat blobFromImg. Вам нужно отладить функцию blobFromImage(...), чтобы выяснить, почему. Может быть, img, который вы получили от cap, пуст?

wohlstad 16.06.2024 13:49

@wohlstad Presumablty cv::dnn::blobFromImage. Было бы гораздо очевиднее, если бы ОП потрудился правильно определить идентификаторы в пространстве имен.

Dan Mašek 16.06.2024 14:13

Обязательно нужно разобраться со случаем, когда не удается прочитать следующий кадр из видео. В настоящее время вы предполагаете, что файл бесконечен. IIRC cap.isOpened() все равно вернется true даже после того, как вы достигнете конца файла, так что это не поможет — вы можете также вывести его из цикла и протестировать его один раз. И вам действительно следует избавиться от всей этой using namespace ерунды и правильно квалифицировать вещи - это облегчит понимание кода и не позволит вам выстрелить себе в ногу.

Dan Mašek 16.06.2024 14:19

@DanMašek Теперь это имеет больше смысла. Я не знаком с этими cv::dnn функциями. И я присоединяюсь к вашему призыву перестать употреблять using namespace.

wohlstad 16.06.2024 14:19

@wohlstad Я считаю, что эта связь носит лишь косвенный характер. Согласно документации, он создает «четырехмерный объект». Вызов blobFromImg.size() вызывает MatSize::operator()(), который возвращает cv::Size. И этот класс может представлять только два измерения — ширину и высоту. Этого явно недостаточно для представления размеров 4D-массива, отсюда и утверждение. ОП должен использовать другой подход для распечатки размера вывода Mat. (Думаю, используя operator[] объекта MatSize).

Dan Mašek 16.06.2024 14:26

Кстати, ваш минимальный воспроизводимый пример примерно на 68 строк слишком длинный;) В любом случае, это утверждение существовало в кодовой базе OpenCV гораздо дольше, чем статья, поэтому я думаю, что автор никогда не запускал его в режиме отладки. Среди других вопросов, я бы сказал, подходить с осторожностью.

Dan Mašek 16.06.2024 15:03
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
68
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это можно воспроизвести в режиме отладки, используя следующий короткий MCVE:

#include <iostream>
#include <opencv2/dnn.hpp>

int main()
{
    cv::Mat img(cv::Mat::zeros(720, 1280, CV_8UC3));
    cv::Mat blobFromImg;
    cv::dnn::blobFromImage(img, blobFromImg, 1, cv::Size(416, 416), cv::Scalar(), true, false);
    std::cout << blobFromImg.size() << std::endl;
}

получить

OpenCV(4.6.0) Error: Assertion failed (dims() <= 2) in cv::MatSize::operator (), file .../opencv2/core/mat.inl.hpp, line 1198

Поскольку проверка присутствовала в базе кода OpenCV, по крайней мере, начиная с версии 4.0, я готов поспорить, что код был сломан с момента его публикации.


По умолчанию объекты cv::Mat, используемые большей частью OpenCV, считаются двухмерными. Хотя цветовые плоскости/каналы изображений можно считать третьим измерением, в OpenCV это не так, где каналы обрабатываются отдельно. Например, наше входное изображение BGR представляет собой 2D Mat с 720 строками и 1280 столбцами (и 3 каналами).

Однако обратите внимание на документацию cv::dnn::blobFromImage:

Создает четырехмерный объект из изображения.

с "каплей"

4-мерный Mat с порядком размеров NCHW.

Обратите внимание, что это Mat не 2D, как обычно, а 4D. Почему это имеет значение? Давайте посмотрим, что на самом деле включает в себя выражение blobFromImg.size().

Сначала мы получаем доступ к переменной-члену cv::Mat::size из blobFromImg. Это экземпляр cv::MatSize . Пока все хорошо, это может представлять произвольное количество измерений. Однако затем мы вызываем cv::MatSize::operator() , чтобы преобразовать его в cv::Size. К сожалению, Size может представлять только два измерения (ширину и высоту), чего достаточно для изображений и других 2D-изображений Mats, но у нас 4D.

Следовательно, blobFromImg.size() не является допустимым подходом в этом случае, если вы хотите напечатать полные размеры Mat. Поскольку проверка представляет собой всего лишь CV_DbgAssert, она не будет жаловаться в режиме Release, а будет возвращать только первые два измерения без уведомления.

Чтобы он работал правильно и выводил все размеры, вы можете написать простую удобную функцию для преобразования MatSize в строку и распечатать ее:

#include <iostream>
#include <opencv2/dnn.hpp>

std::string to_string(cv::MatSize const& sz)
{
    std::ostringstream s;
    s << "[";
    for (int i(0); i < sz.dims(); ++i) {
        if (i != 0) {
            s << " x ";
        }
        s << sz[i];
    }
    s << "]";
    return s.str();
}

int main()
{
    cv::Mat img(cv::Mat::zeros(720, 1280, CV_8UC3));
    cv::Mat blobFromImg;
    cv::dnn::blobFromImage(img, blobFromImg, 1, cv::Size(416, 416), cv::Scalar(), true, false);
    std::cout << to_string(blobFromImg.size) << std::endl;
}

Какие выходы:

[1 x 3 x 416 x 416]

Конечно, есть и другие способы реализации этой функциональности, но это выходит за рамки данной проблемы.


Кроме того, rows / cols из blobFromImg — это -1, и это объясняет.

Это документированное поведениеMat. Что касается cv::Mat::cols и cv::Mat::rows:

количество строк и столбцов или (-1, -1), если матрица имеет более двух измерений

Другие вопросы по теме