У меня есть цветные изображения 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
формат вызывает это?
Проблема в двух словах: 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, очевидно), но я думаю, вы понимаете, о чем я.
Постскриптум Кстати, ваше видео не на самом деле черное и белое. Просто каждая последующая строка смещается на два байта, поэтому цвета поворачиваются: красный становится зеленым, зеленый становится синим и так далее. В результате получается оттенки серого, но если вы внимательно посмотрите, отдельные строки будут окрашены.
Рассматривали ли вы использование Особенности OpenCV создать видео для вас? Это гораздо проще, так как ваши данные уже хранятся в cv::Mat
,
Если вы хотите сохранить свой подход, вы можете просто повернуть cv::Mat
.
О проблеме цвета в ОБНОВЛЕНИИ оригинального поста. Это вызвано,
OpenCV Mat is (BGR) -> FFmpeg AVFrame есть (RGB)?
Если это так, попробуйте,
cvtColor( cvFrame , cvFrame , CV_BGR2RGB ) ;
перед строкой 1.