Я получаю сообщение об ошибке утверждения в этом простом примере 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.
@wohlstad Presumablty cv::dnn::blobFromImage. Было бы гораздо очевиднее, если бы ОП потрудился правильно определить идентификаторы в пространстве имен.
Обязательно нужно разобраться со случаем, когда не удается прочитать следующий кадр из видео. В настоящее время вы предполагаете, что файл бесконечен. IIRC cap.isOpened()
все равно вернется true
даже после того, как вы достигнете конца файла, так что это не поможет — вы можете также вывести его из цикла и протестировать его один раз. И вам действительно следует избавиться от всей этой using namespace
ерунды и правильно квалифицировать вещи - это облегчит понимание кода и не позволит вам выстрелить себе в ногу.
@DanMašek Теперь это имеет больше смысла. Я не знаком с этими cv::dnn
функциями. И я присоединяюсь к вашему призыву перестать употреблять using namespace
.
@wohlstad Я считаю, что эта связь носит лишь косвенный характер. Согласно документации, он создает «четырехмерный объект». Вызов blobFromImg.size()
вызывает MatSize::operator()()
, который возвращает cv::Size
. И этот класс может представлять только два измерения — ширину и высоту. Этого явно недостаточно для представления размеров 4D-массива, отсюда и утверждение. ОП должен использовать другой подход для распечатки размера вывода Mat
. (Думаю, используя operator[]
объекта MatSize
).
Кстати, ваш минимальный воспроизводимый пример примерно на 68 строк слишком длинный;) В любом случае, это утверждение существовало в кодовой базе OpenCV гораздо дольше, чем статья, поэтому я думаю, что автор никогда не запускал его в режиме отладки. Среди других вопросов, я бы сказал, подходить с осторожностью.
Это можно воспроизвести в режиме отладки, используя следующий короткий 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), если матрица имеет более двух измерений
Что за функция
blobFromImage(...)
? Пожалуйста, опубликуйте минимально воспроизводимый пример. В любом случае, похоже, что он не инициализирует cv::MatblobFromImg
. Вам нужно отладить функциюblobFromImage(...)
, чтобы выяснить, почему. Может быть,img
, который вы получили отcap
, пуст?