ffmpeg: преобразование RGB в YUV теряет цвет и масштаб

Я пытаюсь преобразовать кадры RGB в формат YUV420P в ffmpeg/libav. Ниже приведен код для преобразования, а также изображения до и после преобразования. Преобразованное изображение теряет всю информацию о цвете, а также значительно изменяется масштаб. Кто-нибудь знает, как с этим справиться? Я совершенно новичок в ffmpeg/libav!

// Did we get a video frame?
   if(frameFinished)
   {
       i++;
       sws_scale(img_convert_ctx, (const uint8_t * const *)pFrame->data,
                 pFrame->linesize, 0, pCodecCtx->height,
                 pFrameRGB->data, pFrameRGB->linesize);                   

       //==============================================================
       AVFrame *pFrameYUV = avcodec_alloc_frame();
       // Determine required buffer size and allocate buffer
       int numBytes2 = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,                                 
                                          pCodecCtx->height);
       uint8_t *buffer = (uint8_t *)av_malloc(numBytes2*sizeof(uint8_t));

       avpicture_fill((AVPicture *)pFrameYUV, buffer, PIX_FMT_RGB24,
                       pCodecCtx->width, pCodecCtx->height);


       rgb_to_yuv_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,  
                                       PIX_FMT_RGB24,
                                       pCodecCtx->width,pCodecCtx->height, 
                                       PIX_FMT_RGB24,
                                       SWS_BICUBIC, NULL,NULL,NULL);

       sws_scale(rgb_to_yuv_ctx, pFrameRGB->data, pFrameRGB->linesize, 0, 
                 pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);

       sws_freeContext(rgb_to_yuv_ctx);

       SaveFrame(pFrameYUV, pCodecCtx->width, pCodecCtx->height, i);

       av_free(buffer);
       av_free(pFrameYUV);
   }

исходный кадр RGB24

кадр после преобразования RGB24-›YUV420P


person learner    schedule 21.02.2014    source источник
comment
Эти изображения похожи на мои, я делаю rgb2yuv и обратно вручную, и черно-белое изображение - это именно то, что я вижу при открытии его в GIMP, но данные верны, это то же самое, что отправляет моя камера, так что, может быть, это просто проблема с предварительным просмотром? Можно ли проверить данные другим способом?   -  person tomasb    schedule 17.01.2016


Ответы (1)


Ну для начала предположу где у вас:

rgb_to_yuv_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,  
                                   PIX_FMT_RGB24,
                                   pCodecCtx->width,pCodecCtx->height, 
                                   PIX_FMT_RGB24,
                                   SWS_BICUBIC, NULL,NULL,NULL);

Вы действительно намеревались:

rgb_to_yuv_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,  
                                   PIX_FMT_RGB24,
                                   pCodecCtx->width,pCodecCtx->height, 
                                   PIX_FMT_YUV420P,
                                   SWS_BICUBIC, NULL,NULL,NULL);

Я также не уверен, почему вы дважды вызываете swscale!

YUV — это планарный формат. Это означает, что все три канала сохраняются независимо. Где RGB хранится как: RGBRGBRGB

YUV420P находится в таких магазинах, как: YYYYYYYYYYYYYYYY..UUUUUUUUUU..VVVVVVVV

Поэтому swscale требовал, чтобы вы дали ему три указателя.

Затем вы хотите, чтобы ваш шаг линии был кратен 16 или 32, чтобы можно было использовать векторные единицы процессора. И, наконец, размеры плоскости Y должны делиться на два (поскольку плоскости U и V составляют четверть размера плоскости Y).

Итак, давайте перепишем это:

#define RNDTO2(X) ( ( (X) & 0xFFFFFFFE )
#define RNDTO32(X) ( ( (X) % 32 ) ? ( ( (X) + 32 ) & 0xFFFFFFE0 ) : (X) )




if(frameFinished)
{
    static SwsContext *swsCtx = NULL;
    int width    = RNDTO2 ( pCodecCtx->width );
    int height   = RNDTO2 ( pCodecCtx->height );
    int ystride  = RNDTO32 ( width );
    int uvstride = RNDTO32 ( width / 2 );
    int ysize    = ystride * height;
    int vusize   = uvstride * ( height / 2 );
    int size     = ysize + ( 2 * vusize )

    void * pFrameYUV = malloc( size );
    void *plane[] = { pFrameYUV, pFrameYUV + ysize, pFrameYUV + ysize + vusize, 0 };
    int *stride[] = { ystride, vustride, vustride, 0 };

    swsCtx = sws_getCachedContext ( swsCtx, pCodecCtx->width, pCodecCtx->height,
    pCodecCtx->pixfmt, width, height, AV_PIX_FMT_YUV420P, 
    SWS_LANCZOS | SWS_ACCURATE_RND , NULL, NULL, NULL );
    sws_scale ( swsCtx, pFrameRGB->data, pFrameRGB->linesize, 0, 
    pFrameRGB->height, plane, stride );
}    

Я также переключил ваш алгоритм на использование SWS_LANCZOS | SWS_ACCURATE_RND. Это даст вам более красивые изображения. Измените его обратно, если он будет медленным. Я также использовал формат пикселей из исходного кадра вместо того, чтобы все время предполагать, что это RGB.

person szatmary    schedule 21.02.2014
comment
Спасибо за ваш ответ. Я пробовал... но как теперь увидеть преобразованные кадры? Ранее я сохранял преобразованные кадры с соответствующим номером кадра: SaveFrame(pFrameYUV, pCodecCtx->width, pCodecCtx->height, i);, как показано в опубликованном коде. Функция SaveFrame() точно так же, как показано здесь в руководстве по dranger. - person learner; 26.02.2014
comment
Я попытался сохранить «плоскость», которая, как я думаю, содержит преобразованный кадр. Выдает ошибку: [mpeg4 @ 0x874020] Invalid and inefficient vfw-avi packed B frames detected есть идеи, что там не так? Спасибо! - person learner; 27.02.2014
comment
Если у вас есть новый вопрос. Пожалуйста, откройте новый вопрос. - person szatmary; 27.02.2014
comment
ну, поскольку ошибка связана с кодом, предложенным выше, я думаю, что более уместно задать ее только здесь. - person learner; 27.02.2014
comment
учащийся: Я думаю, что @szatmary ожидал, что вы зададите вопрос как сохранить кадр YUV420p и как я могу увидеть результат? - person Alex Cohn; 25.03.2014
comment
@AlexCohn это так? Ну, я не мог понять, что имелось в виду под новым вопросом здесь. Я написал ошибку здесь сам... - person learner; 27.03.2014