You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NV12转RGB24格式转换异常:边框畸变与颜色错位问题求助

问题排查:FFmpeg GPU解码后NV12转BGR24 Bitmap的畸变与颜色问题

问题现象

  • 使用FFmpeg的sw_scale实现NV12转BGR24:颜色显示正常,但左侧边框出现畸变
  • 使用PixelFormatConverter的自定义NV12转BGR24逻辑:不仅左侧边框畸变,还出现颜色错位

关键排查点

1. Bitmap行对齐校验

BMP格式要求每行像素数据的字节数必须是4的整数倍。你用av_image_get_buffer_size(AV_PIX_FMT_BGR24, ..., 32)获取的缓冲区大小已经包含了对齐处理,但要确保转换函数(不管是sw_scale还是自定义逻辑)使用的目标行步长是对齐后的值,而非直接用frame->width * 3:

  • 用sw_scale时,要把dstStride设为av_image_get_buffer_size计算出的每行字节数,而非硬编码width*3
  • 自定义转换函数中,不能直接按width*3逐行写入,要使用对齐后的行宽作为步长,否则会因数据偏移导致左侧边框畸变

2. NV12数据的行步长(linesize)问题

FFmpeg GPU解码输出的AVFrame中,Y平面(data[0])和UV平面(data[1])的linesize不一定等于图像宽度(可能因硬件对齐要求大于宽度)。如果转换时直接用frame->width作为行宽,而非frame->linesize[0]和frame->linesize[1],会读取到错误的UV数据,引发颜色错位或边框异常。

3. Bitmap的存储方向

BMP的像素数据是从下到上存储的,而AVFrame的像素是从上到下排列。如果转换后未反转图像行顺序,可能导致显示异常(虽当前问题未提,但需确认是否在显示环节处理了此问题)。

4. 自定义转换的UV采样逻辑

NV12是YUV420格式,每个UV像素对应4个Y像素。自定义转换时需检查:

  • UV平面的行步长是否使用frame->linesize[1]而非frame->width
  • UV采样的坐标映射是否正确(比如行偏移、列偏移的计算是否符合420采样规则),错误的采样会直接导致颜色错位

转换后生成Bitmap的代码

// frame 是FFmpeg GPU解码得到的NV12格式AVFrame
int image_size = av_image_get_buffer_size(AV_PIX_FMT_BGR24, frame->width, frame->height, 32);

size_t sizeof_file_header = sizeof(BITMAPFILEHEADER);
size_t sizeof_info_header = sizeof(BITMAPINFOHEADER);

BITMAPFILEHEADER bmpheader = {0};
bmpheader.bfType = 0x4d42;
bmpheader.bfReserved1 = 0;
bmpheader.bfReserved2 = 0;
bmpheader.bfOffBits = sizeof_file_header + sizeof_info_header;
bmpheader.bfSize = bmpheader.bfOffBits + image_size;

BITMAPINFOHEADER bmpinfo = {0};
bmpinfo.biSize = sizeof_info_header;
bmpinfo.biWidth = frame->width;
bmpinfo.biHeight = frame->height;
bmpinfo.biPlanes = 1;
bmpinfo.biBitCount = 24;
bmpinfo.biCompression = BI_RGB;
bmpinfo.biSizeImage = 0;
bmpinfo.biXPelsPerMeter = 0;
bmpinfo.biYPelsPerMeter = 0;
bmpinfo.biClrUsed = 0;
bmpinfo.biClrImportant = 0;

uint8_t* buffer = new uint8_t[bmpheader.bfSize];

memcpy(buffer, &bmpheader, sizeof_file_header);
memcpy(buffer + sizeof_file_header, &bmpinfo, sizeof_info_header);

// 调用转换函数(sw_scale或自定义NV12_TO_BGR24)
NV12_TO_BGR24(frame->data[0], (buffer + bmpheader.bfOffBits), frame->width, frame->height);

内容的提问来源于stack exchange,提问作者Kostas Kourkounis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 11:38:15