ffmpeg:RGB到YUV的转换会丢失颜色和比例

ffmpeg:RGB到YUV的转换会丢失颜色和比例,ffmpeg,video-encoding,libavcodec,libav,libavformat,Ffmpeg,Video Encoding,Libavcodec,Libav,Libavformat,我正在尝试将RGB帧转换为ffmpeg/libav中的YUV420P格式。以下是转换代码以及转换前后的图像。转换后的图像会丢失所有颜色信息,并且比例也会发生显著变化。有人知道怎么处理吗?我对ffmpeg/libav完全陌生 // Did we get a video frame? if(frameFinished) { i++; sws_scale(img_convert_ctx, (const uint8_t * const *)pFrame->d

我正在尝试将RGB帧转换为ffmpeg/libav中的YUV420P格式。以下是转换代码以及转换前后的图像。转换后的图像会丢失所有颜色信息,并且比例也会发生显著变化。有人知道怎么处理吗?我对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);
   }


YUV420P转换“>

对于初学者,我假设您有:

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是一种类似于: YYYYYYYYYYYYYYYYYY..uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

所以你需要给它三个指针

接下来,您希望线跨距为16或32的倍数,以便可以使用处理器的向量单位。最后,Y平面的尺寸需要可以被2整除(因为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_Accurance|RND。这将为您提供更好看的图像。如果速度较慢,请将其更改回。我还使用了源帧的像素格式,而不是一直使用RGB。首先,我将假设您有:

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是一种类似于: YYYYYYYYYYYYYYYYYY..uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

所以你需要给它三个指针

接下来,您希望线跨距为16或32的倍数,以便可以使用处理器的向量单位。最后,Y平面的尺寸需要可以被2整除(因为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。

感谢您的回复。我尝试过了…但现在如何查看转换后的帧?之前我正在保存co具有相应帧编号的已转换帧:
SaveFrame(pFrameYUV,pCodecCtx->width,pCodecCtx->height,i);
如我发布的代码所示。
SaveFrame()
函数如图所示,我试图保存保存已转换帧的“平面”。它给出错误:
[mpeg4@0x874020]检测到无效且低效的vfw avi压缩B帧
知道哪里出了问题吗?谢谢!如果你有新问题,请打开一个新问题。好吧,由于错误与上面建议的代码有关,我认为只在这里提问更相关。学习者:我认为@szatmary希望你打开一个问题”如何保存YUV420p帧以及如何查看结果?“谢谢你的回复。我尝试过了……但是现在我如何查看转换的帧呢?之前我用相应的帧号保存转换的帧:
SaveFrame(pFrameYUV,pCodecCtx->width,pCodecCtx->height,I);
如我发布的代码所示。
SaveFrame()
函数如图所示,我试图保存保存保存转换帧的“平面”。它给出错误:
[mpeg4@0x874020]检测到无效且低效的vfw avi压缩B帧
知道哪里出了问题吗?谢谢!如果你有新问题,请打开一个新问题。好吧,由于错误与上面建议的代码有关,我认为只在这里提问更相关。学习者:我认为@szatmary希望你打开一个问题”如何保存YUV420p帧以及如何查看结果?“这些图像与我的类似,我正在手动执行rgb2yuv和返回,b&w图像正是我在GIMP中打开它时看到的图像,但数据是正确的,它与我的相机发出的图像相同,所以这可能只是预览问题吗?”?你能用另一种方法测试数据吗?这些图像与我的类似,我正在手动执行rgb2yuv并返回,b&w图像正是我在GIMP中打开它时看到的,但数据是正确的,它与我的相机发出的相同,所以这可能只是预览问题吗?你能用另一种方法测试数据吗?