LINUX.ORG.RU

YUV -> RGB преобразование кадров

 ,


1

1

Всем привет.

Делаю получение кадров из видеофайлов в виде RGB24 битмапов. Использую для этого ffmpeg, отрисовываю полученые изображения на виджете, пронаследованном от QGLWidget. Получаю такую картинку: http://joxi.ru/0PagUtg5CbA4Ihe_rNQ , то есть вместо одного полноценного изображения - 3 маленьких с разным цветовым оттенком. Работаю с кодеками первый раз, поэтому слабо представляю куда копать. Если надо еще что-то показать из кода, скажите. Посмотрел, те файлы на которых я тестировал - имеют формат картинки YUV.

Контекст конвертирования создаю так:

this->img_convert_context = sws_getCachedContext(NULL,
                                                    this->codec_context->width, this->codec_context->height,
                                                    this->codec_context->pix_fmt,
                                                    this->codec_context->width, this->codec_context->height,
                                                    AV_PIX_FMT_RGB24, SWS_BICUBIC,
                                                    NULL, NULL, NULL);

И собственно преобразование кадра:

sws_scale(this->img_convert_context,
                                            (uint8_t const * const *)this->pFrame->data, this->pFrame->linesize,
                                            0, this->codec_context->height,
                                            this->pFrameRGB->data, this->pFrameRGB->linesize);


Уточни, какой функцией ты выводишь изображение, и какой формат нужно подавать на вход этой функции.

Krieger_Od ★★
()
Ответ на: комментарий от Krieger_Od

Делаю из this->pFrameRGB->data[0] QImage:

QImage((uchar*)this->pFrameRGB->data[0], 352, 240, QImage::Format_RGB888);

отправляю его своему виджету, и вызываю у него paintEvent:

QPainter painter(this);
if (!this->image->isNull()) {
        QPoint *point = new QPoint(0,0);
        painter.drawImage(*point, *this->image);
    }

this->image - это полученный на предидущем шаге QImage

upd: если что, задача не показать изображение, а получить RGB24 битмапы для кадров. А отображение - это демонстрация работы.

ALeo
() автор топика
Последнее исправление: ALeo (всего исправлений: 1)

Очень похоже на неправильное определение входного формата. YUV - это что за формат? YCbCr? ffmpeg при разборе видеофайлов как определяет формат изображения?

Dead ★★★★
()
Ответ на: комментарий от Krieger_Od

Просто, судя по картинке, мне кажется я получил не то что надо.

ALeo
() автор топика
Ответ на: комментарий от Dead

Я так понял что YUV это семейство форматов (прочитал тут http://ru.wikipedia.org/wiki/YUV). Лично я видел на некоторых файлах YUV420, вероятно и в данном случае также (сейчас ide с программой не под рукой, еще раз точно проверить не могу).

ALeo
() автор топика

this->pFrameRGB->data, this->pFrameRGB->linesize

Вот эти две штуки — это массивы. Убедись, что в linesize[0] на самом деле находится нужное число. Вроде это число байт между началами строк.

i-rinat ★★★★★
()

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

namezys ★★★★
()
Ответ на: комментарий от ALeo

Верно, там целое семейство. Я не увидел место, где вы задаете конкретный формат из этого семейства. В ffmpeg, кстати, есть функции конвертации в rgb24 из разных форматов.

Dead ★★★★
()
Ответ на: комментарий от ALeo
QImage((uchar*)this->pFrameRGB->data[0], 352, 240, QImage::Format_RGB888);

вот в этой строке попробуй поменять QImage::Format_RGB888 на другие из списка возможных, может помочь. Мне и самому бы не помешал алгоритм перевода из yuv в rgb без потери качества, то что откопал где-то урезает разрешение в 4 раза. Пользоваться сторонними библиотеками только для этого желания нет, из уже используемых есть только gstreamer и qt4.

Вот и сам алгоритм, собственно, функция из гстримеровского буфера переводит в qimage:


QImage SomeClass::convertBufferYuv420ToRgb(QGst::BufferPtr buffer) const
{
    QGst::CapsPtr caps_ptr = buffer->caps();
    QGst::StructurePtr struct_ptr = caps_ptr->internalStructure(0);

    int width, height;
    width = struct_ptr->value("width").get<int>();
    height = struct_ptr->value("height").get<int>();

    QImage image(width/2, height/2, QImage::Format_RGB32);

    const uchar *data = (const uchar *)buffer->data();

    for (int y=0; y<height; y+=2) {
        const uchar *yLine = data + y*width;
        const uchar *uLine = data + width*height + y*width/4;
        const uchar *vLine = data + width*height*5/4 + y*width/4;

        for (int x=0; x<width; x+=2) {
            const qreal Y = 1.164*(yLine[x]-16);
            const int U = uLine[x/2]-128;
            const int V = vLine[x/2]-128;

            int b = qBound(0, int(Y + 2.018*U), 255);
            int g = qBound(0, int(Y - 0.813*V - 0.391*U), 255);
            int r = qBound(0, int(Y + 1.596*V), 255);

            image.setPixel(x/2,y/2,qRgb(r,g,b));
        }
    }
    return image;
}

Blastbit
()
Ответ на: комментарий от Dead

Пересмотрел API FFMpeg, понял ваш код :) Вроде бы все правильно, но я бы убедился, что srcFormat для sws_getCachedContext установлен верно.

Dead ★★★★
()
Ответ на: комментарий от Dead

Не понял кому адресовано второе предложение, но если мне то, я не совсем понял о чем) sws_getCachedContext в гстример буфере это я так понимаю тоже самое что и QGst::StructurePtr struct_ptr = caps_ptr->internalStructure(0) строка. В алгоритме в самом в принципе видно что и высота и ширина делится на 2, я пробовал методом тыка, можно сказать, поменять разные параметры, но нормальной полноразмерной картинки так и не получил, либо были гдето серые полосы, либо ещё какие-то артефакты. А если к ТС, то наверное да, может быть формат не верный выставляется, я бы провирил на всякий по всем, когда сам подбирал, то тоже при не верном формате картинка распадалась на несколько цветовых, ну и прочие забавности.

Blastbit
()
Ответ на: комментарий от Blastbit

Мне и самому бы не помешал алгоритм перевода из yuv в rgb без потери качества, то что откопал где-то урезает разрешение в 4 раза

width/2, height/2, x/2, y/2 - деление пополам убрать, кроме (uv)Line[x/2]

x+=2, y+=2 - заменить на ++x, ++y

Elyas ★★★★★
()
Ответ на: комментарий от Elyas

Ок, так проверил.

for (int y=0; y<height; ++y) {
        const uchar *yLine = data + y*width;
        const uchar *uLine = data + width*height + y*width/4;
        const uchar *vLine = data + width*height*5/4 + y*width/4;

        for (int x=0; x<width; ++x) {
            const qreal Y = 1.164*(yLine[x]-16);
            const int U = uLine[x/2]-128;
            const int V = vLine[x/2]-128;

            int b = qBound(0, int(Y + 2.018*U), 255);
            int g = qBound(0, int(Y - 0.813*V - 0.391*U), 255);
            int r = qBound(0, int(Y + 1.596*V), 255);

            image.setPixel(x,y,qRgb(r,g,b));
        }
    }

На выхлопе: http://hostingkartinok.com/show-image.php?id=9c2a7c149ff22056d6358ba371e328e0

в моём исходном кастрированном варианте: http://hostingkartinok.com/show-image.php?id=740d19d24dafa1a48543f42aa33c3e12

Blastbit
()
Ответ на: комментарий от Elyas

Сделал так

for (int y=0; y<height; ++y) {
        const uchar *yLine = data + y*width;
        const uchar *uLine = data + width*height + (y/2)*width/2;
        const uchar *vLine = data + width*height*5/4 + (y/2)*width/2;

        for (int x=0; x<width; ++x) {
            const qreal Y = 1.164*(yLine[x]-16);
            const int U = uLine[x/2]-128;
            const int V = vLine[x/2]-128;

            int b = qBound(0, int(Y + 2.018*U), 255);
            int g = qBound(0, int(Y - 0.813*V - 0.391*U), 255);
            int r = qBound(0, int(Y + 1.596*V), 255);

            image.setPixel(x,y,qRgb(r,g,b));
        }
}

Похоже на правду, по крайней мере этак картинка соответствует. Спасибо!)

Blastbit
()
Ответ на: комментарий от Blastbit

Мне и самому бы не помешал алгоритм перевода из yuv в rgb без потери качества

Пользоваться сторонними библиотеками только для этого желания нет, из уже используемых есть только gstreamer и qt4.

Может вот это поможет: http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-base-plu... ?

i-rinat ★★★★★
()

На картинке видно, что изображение с planar pixel format отображен, как packed pixel format без преобразования. В уже показанном тобой коде ошибок с форматом нет: и AV_PIX_FMT_RGB24, и QImage::Format_RGB888 это «packed RGB 8:8:8, 24bpp, RGBRGB...» Ошибка возникает до sws_getCachedContext.

Вероятные причины порчи картинки:

  • ты сам выставил this->codec_context->pix_fmt неверно.
  • В исходном файле нет информации о формате пикселя и ffmpeg неверно его угадывает.
  • В исходном файле ошибочная информация о формате.

Для дальнейшего анализа проблемы нужна информация:

  • устанавливаешь ли ты сам this->codec_context->pix_fmt? При декодировании в обычных условиях этот параметр устанавливает ffmpeg и менять его не стоит.
  • покажи, как открываешь файл (avformat_open*) и декодер (avcodec_open*) и инициализируешь передаваемые этим функциям параметры.
  • покажи информацию о входном видео-файле, например, так - «ffprobe <source_file>»
  • какая версия ffmpeg?
fopen ★★
()
Ответ на: комментарий от i-rinat

Внезапно проблема решилась после того как увеличил длину linesize[0] в 3 раза. На моем скриншоте отображались 3 раза только первые трети кадров. Теперь из первой части формируется полноразмерный rgb24 битмап, а остальное, судя по всему, отбрасывается.

ALeo
() автор топика
Ответ на: комментарий от i-rinat

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

Blastbit
()
Ответ на: комментарий от ALeo

а остальное, судя по всему, отбрасывается.

Скорее всего, у тебя в linesize[0] была ширина картинки в пикселях, тогда как нужно в байтах, что в три раза больше. Тогда у тебя отрисовка происходила так: первая линия src рисовалась на первой линии dst, потом указатель в dst смещался на треть линии, и вторая линия src рисовалась на оставшихся двух третях первой линии dst и первой трети второй линии dst. Третья линия src рисовалась на последней трети первой линии dst и первых двух третях второй линии dst. Последние две трети от линий src всегда перезаписывались следующими линиями, поэтому у тебя должно было получаться три «копии» левой трети кадра. Ну как копии, они разные, это же разные линии. Поэтому по высоте они в три раза меньше.

Теперь преобразование происходит один в один, там нет лишних данных, которые отбрасываются.

Внезапно проблема решилась после того как увеличил длину linesize[0] в 3 раза

Вообще-то это не моё дело, но если решать проблемы простым перебором параметров, без понимания их значения, подобные проблемы будут появляться с завидным постоянством.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

Спасибо за разъяснения. Согласен что надо глубже разбираться, просто сейчас это сильно новая область для меня.

ALeo
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.