2014-02-21 2 views
1

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

// 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); 
    } 

original RGB24 frame

frame after RGB24->YUV420P conversion

+0

Эти изображения похожи на мои, я делаю rgb2yuv и обратно вручную, а изображение b & w - это именно то, что я вижу при открытии его в GIMP, но данные верны, это то же самое, что и моя камера, может быть, просто проблема предварительного просмотра? Можете ли вы проверить данные другим способом? – tomasb

ответ

10

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

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 - это плоский формат. Это означает, что все три канала хранятся независимо. Whre 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.

+0

Спасибо за ваш ответ. Я попробовал ... но как мне теперь увидеть преобразованные фреймы? Раньше я сохранял преобразованные кадры с соответствующим номером кадра: 'SaveFrame (pFrameYUV, pCodecCtx-> width, pCodecCtx-> height, i);' , как показано в коде, который я опубликовал. Функция 'SaveFrame()' такая же, как показано здесь, в [учебнике пользователя] (http://dranger.com/ffmpeg/tutorial01.c) – learner

+0

преобразовать его в формат, который вы можете сохранить и посмотреть – szatmary

+0

Я попытался сохранить 'самолет ', который, я думаю, содержит преобразованный фрейм. Он дает ошибку: '[mpeg4 @ 0x874020] Неверные и неэффективные vfw-avi упакованные B-кадры обнаружены' любая идея, что там не так? благодаря! – learner

Смежные вопросы