ffmpeg:RGB到YUV的转换会丢失颜色和比例
我正在尝试将RGB帧转换为ffmpeg/libav中的YUV420P格式。以下是转换代码以及转换前后的图像。转换后的图像会丢失所有颜色信息,并且比例也会发生显著变化。有人知道怎么处理吗?我对ffmpeg/libav完全陌生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
// 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中打开它时看到的,但数据是正确的,它与我的相机发出的相同,所以这可能只是预览问题吗?你能用另一种方法测试数据吗?