Как создать видео, используя avcodec из изображений JPEG типа OpenCV :: Mat?

У меня есть цветные изображения JPEG OpenCV::Mat типа и я создаю из них видео с помощью avcodec, Видео, которое я получаю, перевёрнуто, чёрное & белый и каждый ряд каждого кадра сдвинут, и я получил диагональную линию. В чем может быть причина такого выхода?
следить этот ссылку, чтобы посмотреть видео, которое я получаю с помощью avcodec.
я использую acpicture_fill функция для создания avFrame от cv::Mat Рамка!

Постскриптум
Каждый cv :: Mat cvFrame имеет ширину = 810, высоту = 610, шаг = 2432
Я заметил, что avFrame (который заполняется acpicture_fill) имеет linesize[0]=2430
Я пытался вручную установить avFrame->linesizep0]=2432 и не 2430, но это все равно не помогло.

======== КОД =============================================== ================

AVCodec *encoder = avcodec_find_encoder(AV_CODEC_ID_H264);
AVStream *outStream = avformat_new_stream(outContainer, encoder);
avcodec_get_context_defaults3(outStream->codec, encoder);

outStream->codec->pix_fmt = AV_PIX_FMT_YUV420P;
outStream->codec->width = 810;
outStream->codec->height = 610;
//...

SwsContext *swsCtx = sws_getContext(outStream->codec->width, outStream->codec->height, PIX_FMT_RGB24,
outStream->codec->width, outStream->codec->height,  outStream->codec->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL);

for (uint i=0; i < frameNums; i++)
{
// get frame at location I using OpenCV
cv::Mat cvFrame;
myReader.getFrame(cvFrame, i);
cv::Size frameSize = cvFrame.size();
//Each cv::Mat cvFrame has  width=810, height=610, step=24321.  // create AVPicture from cv::Mat frame
2.  avpicture_fill((AVPicture*)avFrame, cvFrame.data, PIX_FMT_RGB24, outStream->codec->width, outStream->codec->height);
3avFrame->width = frameSize.width;
4.  avFrame->height = frameSize.height;

// rescale to outStream format
sws_scale(swsCtx, avFrame->data, avFrame->linesize, 0, outStream->codec->height, avFrameRescaledFrame->data, avFrameRescaledFrame ->linesize);
encoderRescaledFrame->pts=i;
avFrameRescaledFrame->width = frameSize.width;
avFrameRescaledFrame->height = frameSize.height;

av_init_packet(&avEncodedPacket);
avEncodedPacket.data = NULL;
avEncodedPacket.size = 0;

// encode rescaled frame
if(avcodec_encode_video2(outStream->codec, &avEncodedPacket, avFrameRescaledFrame, &got_frame) < 0) exit(1);
if(got_frame)
{
if (avEncodedPacket.pts != AV_NOPTS_VALUE)
avEncodedPacket.pts =  av_rescale_q(avEncodedPacket.pts, outStream->codec->time_base, outStream->time_base);
if (avEncodedPacket.dts != AV_NOPTS_VALUE)
avEncodedPacket.dts = av_rescale_q(avEncodedPacket.dts, outStream->codec->time_base, outStream->time_base);

// outContainer is "mp4"av_write_frame(outContainer, & avEncodedPacket);

av_free_packet(&encodedPacket);
}
}

ОБНОВЛЕНО

Как @Alex предложил, я изменил строки 1-4 с кодом ниже

int width = frameSize.width, height = frameSize.height;
avpicture_alloc((AVPicture*)avFrame, AV_PIX_FMT_RGB24, outStream->codec->width, outStream->codec->height);
for (int h = 0; h < height; h++)
{
memcpy(&(avFrame->data[0][h*avFrame->linesize[0]]), &(cvFrame.data[h*cvFrame.step]), width*3);
}

Видео (Вот) Получаю сейчас почти идеально. Это НЕ в обратном порядке, НЕ черный & белый, НО, кажется, что один из компонентов RGB отсутствует. Каждые коричневые / красные цвета стали синими (в оригинальных изображениях это должно быть наоборот).
В чем может быть проблема? Может быть масштабирование (sws_scale) чтобы AV_PIX_FMT_YUV420P формат вызывает это?

2

Решение

Проблема в двух словах: avpicture_fill() не ожидает заполнения между рядами, т.е. шаг (шаг) будет равен width*sizeof(pixel)т. е. 810 * 3 = 2430. Фактический шаг данных в шаге cv :: Mat, как вы говорите, — 2432, который отличается, так что простая передача данных напрямую не будет работать. Там нет никакого способа сказать avpicture_fill() использовать другой шаг для ввода данных; он не является частью API (можно сказать, что так и должно быть 🙂

Есть два возможных решения:

Создайте массив, в котором входные данные будут смежными, без заполнения между строками. Вам нужно будет скопировать каждую строку из cv :: Mat в этот массив. Затем передайте это avpicture_fill(),

int width, height; // get from mat
uint8_t* buf = malloc(width * height * 3); // 3 bytes per pixel
for (int i = 0; i < height; i++)
{
memcpy( &( buf[ i*width*3 ] ), &( mat->data[ i*mat->step ] ), width*3 );
}
avpicture_fill(..., buf, ...)

Кстати, чтобы перевернуть видео по вертикали, вы можете сделать это, чтобы скопировать последний ряд в первый и так далее:

...
memcpy( &( buf[ i*width*3 ] ), &( mat->data[ (height - i - 1)*mat->step ] ), width*3 );
...

Или заполните AVPicture самостоятельно:

AVPicture* pic = malloc(sizeof(AVPicture));
avpicture_alloc(pic, PIX_FMT_BGR24, width, height);
for (int i = 0; i < height; i++)
{
memcpy( &( pic->data[0][ i*pic->linesize[0] ] ),  &( mat->data[ i*mat->step ] ), width*3);
}

Нет необходимости выделять pic-> data [0] или устанавливать pic-> linesize [0], avpicture_alloc () должна это делать. Также нет необходимости заполнять данные [1] или данные [2], они должны быть нулевыми.

РЕДАКТИРОВАТЬ: Удален старый код, который показывал копирование R, G, B в отдельные плоскости. PIX_FMT_BGR24 не является плоским форматом.

Я недостаточно знаком с OpenCV C ++ API, чтобы понять, как получить ширину и высоту (это не mat-> width, очевидно), но я думаю, вы понимаете, о чем я.

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

2

Другие решения

Рассматривали ли вы использование Особенности OpenCV создать видео для вас? Это гораздо проще, так как ваши данные уже хранятся в cv::Mat,

Если вы хотите сохранить свой подход, вы можете просто повернуть cv::Mat.

0

О проблеме цвета в ОБНОВЛЕНИИ оригинального поста. Это вызвано,

OpenCV Mat is (BGR) -> FFmpeg AVFrame есть (RGB)?

Если это так, попробуйте,

cvtColor( cvFrame , cvFrame , CV_BGR2RGB ) ;

перед строкой 1.

0
По вопросам рекламы [email protected]