1 (20.04.2011 12:07:56 отредактировано Yana)

Тема: Qt и OpenCV

Я получаю картинку с камеры с помощью библиотеки OpenCV. Мне нужно сохранить ее на диск - желательно не в виде raw-файла, а в каком-то графическом формате. Это можно было бы сделать, если бы удалось из картинки в собственном формате OpenCV (IplImage img;) создать QImage - т.к. для последней уже существуют функции сохранения в разных jpeg'ах/png.

Формат хранения пикселов в IplImage известен - они хранятся массивом троек байтов: r0 g0 b0 r1 g1 b1 r2 g2 b2... Каким образом мне быстро конвертировать такие данные в QImage?

Как на ваш взгляд - удастся ли мне за 1/15 или 1/30 секунды успеть сделать следующее:
1) Перегнать IplImage в QImage (пусть размер будет 320*240).
2) Преобразовать QImage в JPEG.
3) Записать этот JPEG в файл.
Понятно, что операции 2 и 3 выполняются в рамках одной функции QImage.save() - но и преобразование графики, и запись на "диск" - не самые быстрые операции, полагаю?

Или проще не заморачиваться, а гнать на "диск" поток rgbrgb... - и написать для него отдельный конвертор?

Поделиться

2 (20.04.2011 13:02:25 отредактировано yoush)

Re: Qt и OpenCV

У QImage есть конструктор, принимающий поток байт, размер и формат. Один из форматов - RGB888 - в точности что вам нужно.

Но есть подозрение, что QImage тут лишний - это же лишнее копирование денных, а вы ведь в реальном времени работать хотите...  может разумнее использовать libjpeg напрямую.

Поделиться

3 (20.04.2011 16:15:56 отредактировано Yana)

Re: Qt и OpenCV

Если я правильно понимаю, код должен бы был выглядеть как-то так:

IplImage* imgSrc;
...
QImage imgDst;
imgDst.loadFromData(imgSrc->imageData /* это uchar * */, img->width * img->height * 3, "RGB888");

Но как функция определит ширину и высоту потока байтов? В массиве-то только цвет пикселов, ни ширины, ни высоты...

Поделиться

4 (20.04.2011 17:47:15 отредактировано Hassium)

Re: Qt и OpenCV

Yana пишет:

Если я правильно понимаю, код должен бы был выглядеть как-то так:

IplImage* imgSrc;
...
QImage imgDst;
imgDst.loadFromData(imgSrc->imageData /* это uchar * */, img->width * img->height * 3, "RGB888");

Но как функция определит ширину и высоту потока байтов? В массиве-то только цвет пикселов, ни ширины, ни высоты...

Я что-то не понимаю в чём у вас затуп О_о

Смотрим в документацию:

IplImage

IPL image header

    typedef struct _IplImage
    {
        int  nSize;
        int  ID;
        int  nChannels;
        int  alphaChannel;
        int  depth;
        char colorModel[4];
        char channelSeq[4];
        int  dataOrder;
        int  origin;
        int  align;
        int  width;
        int  height;
        struct _IplROI *roi;
        struct _IplImage *maskROI;
        void  *imageId;
        struct _IplTileInfo *tileInfo;
        int  imageSize;
        char *imageData;
        int  widthStep;
        int  BorderMode[4];
        int  BorderConst[4];
        char *imageDataOrigin;
    }
    IplImage;

Причём:

param imageSize:
     

Image data size in bytes. For interleaved data, this equals image->height * image->widthStep

kernel panic, core dumped, system halted, please reboot this world.

Поделиться

5

Re: Qt и OpenCV

Hassium пишет:

Я что-то не понимаю в чём у вас затуп О_о

Где что лежит внутри IplImage - знаю. Проблема вот в чем. Когда я передаю в функции imgDst.loadFromData() ссылку на массив пикселов - как QImage определит ширину/высоту массива пикселов? Пусть у меня картинка 3*4 пиксела, соответственно, imageData будет занимать 36 байт. Что может соответствовать картинкам 3*4, 4*3, 6*2, 2*6, 1*12, 12*1. И как QImage определит габариты? Я-то функции не передаю ширину с высотой, и в первых байтах блока данных этой инфы нет.

Или мне нужно перед loadFromData() создать картинку нужного размера?

P.S.: нашла вариант решения: http://www.qtforum.org/article/28481/cr … -data.html
Нужно использовать не loadFromData(), а сразу создавать в конструкторе - а потом, видимо, менять пикселы c gjvjom. loadFromData().

Поделиться

6 (20.04.2011 18:10:32 отредактировано Hassium)

Re: Qt и OpenCV

Yana пишет:

Нужно использовать не loadFromData(), а сразу создавать в конструкторе - а потом, видимо, менять пикселы c gjvjom. loadFromData().

Вот поэтому я и не понимаю затупа. Мне сразу в голову приходит использовать конструктор QImage ( int width, int height, Format format ) а потом вызывать loadFromData(), или вообще     QImage ( const uchar * data, int width, int height, Format format ). Тем более пока вы спрашиваете уже можно было 3 раза проверить smile

kernel panic, core dumped, system halted, please reboot this world.

Поделиться

7

Re: Qt и OpenCV

Hassium пишет:

пока вы спрашиваете уже можно было 3 раза проверить smile

Пока я на работе - я работаю по основной работе, а не по собственным проектам. Проверять буду в течение часа дороги от офиса до дома.

Поделиться

8 (20.04.2011 18:30:59 отредактировано Hassium)

Re: Qt и OpenCV

Yana пишет:
Hassium пишет:

пока вы спрашиваете уже можно было 3 раза проверить smile

Пока я на работе - я работаю по основной работе, а не по собственным проектам. Проверять буду в течение часа дороги от офиса до дома.

Я вам не в укор это сказал, просто я на самом деле не понял в чём у вас затруднение, когда вы пояснили, стало понятно что просто неуверенность, так вот проще как бы самому проверить, чем ждать ответов smile Я с OpenCV дело не имел, но при этом бегло глянув документацию на OpenCV и Qt появилось несколько мыслей, которые проще проверить, чем спрашивать это на форуме smile

kernel panic, core dumped, system halted, please reboot this world.

Поделиться

9 (23.04.2011 15:33:59 отредактировано Yana)

Re: Qt и OpenCV

Когда я сделала неправильный код, все прекрасно заработало:

IplImage* imgCam; // В область памяти uchar* imgCam->imageData библиотека OpenCV будет сбрасывать изображение с камеры
QImage* imgCam2; // Эту картинку я буду выводить на экран
...
void Widget::startCam() {
  //Инициализация переменных  запуск съемки
  imgCam = cvQueryFrame(capture);
  imgCam2 = new QImage
    ( (uchar*)(imgCam->imageData)
    , imgCam->width, imgCam->height
    , QImage::Format_RGB888
    );
  repaint();
  timerCam->singleShot(frameRate, this, SLOT(updateCam()));
}
...
void Widget::updateCam() {
  imgCam = cvQueryFrame(capture);
  repaint();
  timerCam->singleShot(frameRate, this, SLOT(updateCam()));
}

В чем неправильность этого кода - imgCam2 и capture используют для хранения пикселов одну и ту же область памяти. Но когда я попыталась переписать код более корректно, чтобы capture и imgCam2 использовали разные области памяти - но в результате картинка вообще перестала выводиться:

IplImage* imgCam; // В область памяти uchar* imgCam->imageData библиотека OpenCV будет сбрасывать изображение с камеры
QImage* imgCam2; // Эту картинку я буду выводить на экран
...
void Widget::startCam() {
  //Инициализация переменных  запуск съемки
  imgCam = cvQueryFrame(capture);
  imgCam2 = new QImage
    ( imgCam->width, imgCam->height
    , QImage::Format_RGB888
    );
  imgCam2->loadFromData
    ( (uchar*)(imgCam->imageData)
    , imgCam->imageSize
    , "BGR888"
    );
  repaint();
  timerCam->singleShot(frameRate, this, SLOT(updateCam()));
}
...
void Widget::updateCam() {
  imgCam = cvQueryFrame(capture);
  imgCam2->loadFromData
    ( (uchar*)(imgCam->imageData)
    , imgCam->imageSize
    , "BGR888"
    );
  repaint();
  timerCam->singleShot(frameRate, this, SLOT(updateCam()));
}

Второй день ломаю голову, что во втором, "исправленном" варианте не так. В imgCam->imageSize лежит правильное число байтов (ширина*высота*число каналов). QImage::loadFromData что ли не работает - впрочем, в такой баг Qt совсем не верится...

Поделиться

10 (23.04.2011 19:13:18 отредактировано Hassium)

Re: Qt и OpenCV

bool QImage::loadFromData ( const uchar * data, int len, const char * format = 0 )
Loads an image from the first len bytes of the given binary data. Returns true if the image was successfully loaded; otherwise returns false.
The loader attempts to read the image using the specified format, e.g., PNG or JPG. If format is not specified (which is the default), the loader probes the file for a header to guess the file format.

Оно ожидает получить

specified format, e.g., PNG or JPG.

или ещё какой другой. А у вас сырые данные.

А чем вам не нравится первый вариант? Вам же как раз сохранить надо в файл, заодно не тратим время на лишнее копирование.

QImage::QImage ( uchar * data, int width, int height, Format format )
Constructs an image with the given width, height and format, that uses an existing memory buffer, data. The width and height must be specified in pixels, data must be 32-bit aligned, and each scanline of data in the image must also be 32-bit aligned.
The buffer must remain valid throughout the life of the QImage. The image does not delete the buffer at destruction.

QImage::QImage ( const uchar * data, int width, int height, int bytesPerLine, Format format )
Constructs an image with the given width, height and format, that uses an existing memory buffer, data. The width and height must be specified in pixels. bytesPerLine specifies the number of bytes per line (stride).
The buffer must remain valid throughout the life of the QImage. The image does not delete the buffer at destruction.

kernel panic, core dumped, system halted, please reboot this world.

Поделиться

11

Re: Qt и OpenCV

Hassium,
> Оно ожидает получить specified format, e.g., PNG or JPG или ещё какой другой. А у вас сырые данные.

Действительно. Полагала, что раз конструктор "ест" raw-формат, то и loadImageFromData() логически должен бы. Ну и не последнюю роль играет организация документации - вполне логично было бы или на аргумент format повесить ссылку на список форматов, или дать ссылку на их перечень в описании функции. Не сразу найдешь...

> А чем вам не нравится первый вариант? Вам же как раз сохранить надо в файл, заодно не тратим время на лишнее копирование.

Я не знаю, как именно работает OpenCV с этой областью данных. А заниматься обработкой данных, куда в любой момент могут начать сливаться данные нового кадра - не лучшая идея. Кроме того, у меня по большому счету задача не просто писать видеопоток, а обрабатывать его (поиск границ, распознавание маркеров и т.п.) - и это лучше делать в отдельной области памяти.

Еще вопрос по Qt. Я беру с камеры картинку 320*240 или 176 * 144. Если выводить ее 1 к 1, это будет маленький фрагмент на экране. Можно отмасштабировать картинку 320*240 на экран таким образом:

QRect* camRect = new QRect(0, 0, 640, 480);
painter.drawImage(*camRect, *imgCam2);

Но при этом вывод видео на экран начинает заметно тормозить. Понятно, что функция "заточена" под масштабирование картинки на произвольный прямоугольник. Можно предположить, что при кратном масштабировании 320 -> 640 функция будет выбирать более простой и быстрый вариант просчета. Но совершенно не факт, что это действительно будет так - при современной производительности компов программеры могли просто "забить" на такие оптимизации. Есть ли в QT гарантированно быстрый способ вывода кратно (вдвое втрое и т.д.) отмасштабированной картинки?

Поделиться

12 (23.04.2011 22:40:34 отредактировано Hassium)

Re: Qt и OpenCV

Yana пишет:

Ну и не последнюю роль играет организация документации - вполне логично было бы или на аргумент format повесить ссылку на список форматов, или дать ссылку на их перечень в описании функции. Не сразу найдешь...

Ну ведь там const char*, а не обособленный тип enum QImage::Format, видимо поэтому не очень хорошо делать линком и поэтому в конце описания метода даётся ссылка на другой раздел, где и можно узнать про форматы smile

Я не знаю, как именно работает OpenCV с этой областью данных. А заниматься обработкой данных, куда в любой момент могут начать сливаться данные нового кадра - не лучшая идея.

Ну я думаю оно же не будет лить просто так, пока вы не попросите его об этом ну или по крайней мере это под вашим управлением. Ну либо просто будете копировать сами в другой буфер.

Еще вопрос по Qt. Я беру с камеры картинку 320*240 или 176 * 144. Если выводить ее 1 к 1, это будет маленький фрагмент на экране. Можно отмасштабировать картинку 320*240 на экран таким образом:

QRect* camRect = new QRect(0, 0, 640, 480);
painter.drawImage(*camRect, *imgCam2);

Но при этом вывод видео на экран начинает заметно тормозить. Понятно, что функция "заточена" под масштабирование картинки на произвольный прямоугольник. Можно предположить, что при кратном масштабировании 320 -> 640 функция будет выбирать более простой и быстрый вариант просчета. Но совершенно не факт, что это действительно будет так - при современной производительности компов программеры могли просто "забить" на такие оптимизации. Есть ли в QT гарантированно быстрый способ вывода кратно (вдвое втрое и т.д.) отмасштабированной картинки?

А мне вот попадает на глаза:

QImage QImage::scaled ( const QSize & size, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio, Qt::TransformationMode transformMode = Qt::FastTransformation ) const
Returns a copy of the image scaled to a rectangle defined by the given size according to the given aspectRatioMode and transformMode.

If aspectRatioMode is Qt::IgnoreAspectRatio, the image is scaled to size.
If aspectRatioMode is Qt::KeepAspectRatio, the image is scaled to a rectangle as large as possible inside size, preserving the aspect ratio.
If aspectRatioMode is Qt::KeepAspectRatioByExpanding, the image is scaled to a rectangle as small as possible outside size, preserving the aspect ratio.
If the given size is empty, this function returns a null image.
See also isNull() and Image Transformations.

Про скорость я вам ничего не подскажу, не изучал этот вопрос относительно Qt (но вы ведь всегда можете заглянуть за шторку smile т.е. в сурцы кьюта, что бы что либо для себя прояснить smile ). Но если вы так не доверяете Qt, то раз вы уже используете OpenCV, то почему бы не возложить на него эту функцию?

cv::resize
void resize(const Mat& src, Mat& dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR)

    Resizes an image

И что в вашем понимании тормозит? Вы пытаетесь 15/30 кадров в секунду ресайзить? Тогда наверно вам тем более надо смотреть на OpenCV и смотреть в сторону чего-нить типа:

INTER_NEAREST nearest-neighbor interpolation

kernel panic, core dumped, system halted, please reboot this world.

13

Re: Qt и OpenCV

Вы достаточно интересную тему затронули, я в свое время много возился с производительностью при проигрывании видео.

Основной тезис заключается в том, что, несмотря на 21ый век на дворе, необходимо в том или ином виде использовать аппаратное ускорение, иначе будет мрак.

У вас кадры с камеры в каком формате цветности захватываются? тут есть 2 варианта :

1.) YUV. Это достаточно древний формат, пришедший с телевидения, тем не менее, практически все кодеки и девайсы используют его, и 90% из них - YUV420. Общепринятое решение - использовать аппаратный YUV Overlay на видеокарте, в таком случае и цветопреобразованием и масштабированием занимается видеокарта. На низком уровне для этого в X11 есть расширение XV, через которое и работают большинство плееров. SDL имеет достаточно удобное высокоуровневое API для использования YUV Overlay'ев. В Qt, к неприятному удивлению, в чистом виде работа с видео-оверлеями невозможна, они позиционируют Phonon, но он крайне негибок для любой задачи, отличающейся от "проиграть вот этот видеофайл без всяких наворотов".
Есть еще один извращенный путь, к которому мне приходилось прибегать в определенных ситуациях - гнать кадры в RECTANGLE текстуры, писать GLSL шейдер, который конвертирует YUV в RGB, и кидать эти текстуры на экран. В принципе, производительность была вполне приемлемая, а в качестве бонуса достигалась отвязка от фреймворка и хорошая переносимость, ибо OpenGL. Но как с RECTANGLE-текстурами (без них-то, в принципе можно обойтись) и шейдерами (а вот без них - никак, иначе вся затея теряет смысл) обстоит дело в GLES, сказать не могу.

2.) RGB. Если это rgb добывается софтверной конвертацией из yuv'а, идущего с камеры - то отказываться от такого решения, ибо оно само по себе уже должно неслабо жрать процессор. Если же этот RGB идет с камеры аппаратно, то поступать, как в хвосте предыдущего пункта - гнать текстурами через OpenGL.

+ открыть спойлер

Кстати, а вы уже сделали voice recognition? Если сделали, то на базе чего-то (чего?) или с нуля писали?

Поделиться

14

Re: Qt и OpenCV

Long пишет:

необходимо в том или ином виде использовать аппаратное ускорение, иначе будет мрак. ... Если это rgb добывается софтверной конвертацией из yuv'а, идущего с камеры - то отказываться от такого решения, ибо оно само по себе уже должно неслабо жрать процессор.

Пока аппаратное ускорение не используется. Вся работа с видео весьма нетороплива - берем картинку через V4L2, работаем с ней средствами Qt/OpenCV. Работа с видео пока велась на уровне "добиться работающего на прототипе варианта", "проверить концепт". Реальная работа с видео и AR начинается только сейчас. До этого основной упор был сделан на решение практически полезных задач - навигатор для велосипедистов и мотоциклистов, тренажер для лыжников и т.п. Сейчас переходим к реализации-тестированию-экспериментам в области пользовательского AR-интерфейса.

Long пишет:

Кстати, а вы уже сделали voice recognition? Если сделали, то на базе чего-то (чего?) или с нуля писали?

До распознавания речи руки пока не дошли, хотя представление о том, как оно должно быть реализовано на байк-навигаторе, есть.

Поделиться