如何关闭sw_scale的planar YUV 32字节对齐转换优化?
使用sw_scale转换planar YUV时右边缘伪影问题及解决方案
问题说明
使用sw_scale转换为planar YUV像素格式时,缩放后的图像右边缘会出现伪影。推测这是sw_scale针对目标端行做了32字节对齐优化导致的(相关说明未在官方文档中找到)。
场景背景:用sw_scale做图像合成时,目标帧的行是32字节对齐的,但实际要输出的子图像并不满足对齐要求。比如完整输出帧是1280x720的YUV422P10LE格式(符合32字节对齐),但需要在左上角缩放一个宽度为1280/3=426的子图像。该格式下426的宽度不符合32字节对齐,但sw_scale识别到目标行是32字节对齐的,会将426宽度之外的22字节填充区域写入垃圾值,而这些区域属于可显示区域,最终导致伪影。
需求:关闭该对齐优化,或者让sw_scale不触发该优化,同时保留现有程序逻辑。曾尝试给目标行添加额外填充使其不再32字节对齐,但没有效果。
代码示例(省略渲染部分)
可通过注释行切换输出宽度是否32字节对齐:
#include "libswscale/swscale.h" #include "libavutil/imgutils.h" #include "libavutil/pixelutils.h" #include "libavutil/pixfmt.h" #include "libavutil/pixdesc.h" #include <stdint.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { /// Set up a 1280x720 window, and an item with 1/3 width and height of the window. int window_width, window_height, item_width, item_height; window_width = 1280; window_height = 720; item_width = (window_width / 3); item_height = (window_height / 3); int item_out_width = item_width; /// This line sets the item width to be 32 byte aligned uncomment to see uncorrupted results /// Note %16 because outformat is 2 bytes per component //item_out_width -= (item_width % 16); enum AVPixelFormat outformat = AV_PIX_FMT_YUV422P10LE; enum AVPixelFormat informat = AV_PIX_FMT_UYVY422; int window_lines[4] = {0}; av_image_fill_linesizes(window_lines, outformat, window_width); uint8_t *window_planes[4] = {0}; window_planes[0] = calloc(1, window_lines[0] * window_height); window_planes[1] = calloc(1, window_lines[1] * window_height); window_planes[2] = calloc(1, window_lines[2] * window_height); /// Fill the window with all 0s, this is green in yuv. int item_lines[4] = {0}; av_image_fill_linesizes(item_lines, informat, item_width); uint8_t *item_planes[4] = {0}; item_planes[0] = malloc(item_lines[0] * item_height); memset(item_planes[0], 100, item_lines[0] * item_height); struct SwsContext *ctx; ctx = sws_getContext(item_width, item_height, informat, item_out_width, item_height, outformat, SWS_FAST_BILINEAR, NULL, NULL, NULL); /// Check a block in the normal region printf("Pre scale normal region %d %d %d\n", (int)((uint16_t*)window_planes[0])[0], (int)((uint16_t*)window_planes[1])[0], (int)((uint16_t*)window_planes[2])[0]); /// Check a block in the corrupted region (should be all zeros) These values should be out of the converted region int corrupt_offset_y = (item_out_width + 3) * 2; ///(item_width + 3) * 2 bytes per component Y PLANE int corrupt_offset_uv = (item_out_width + 3); ///(item_width + 3) * (2 bytes per component rshift 1 for horiz scaling) U and V PLANES printf("Pre scale corrupted region %d %d %d\n", (int)(*((uint16_t*)(window_planes[0] + corrupt_offset_y))), (int)(*((uint16_t*)(window_planes[1] + corrupt_offset_uv))), (int)(*((uint16_t*)(window_planes[2] + corrupt_offset_uv)))); sws_scale(ctx, (const uint8_t**)item_planes, item_lines, 0, item_height,window_planes, window_lines); /// Preform same tests after scaling printf("Post scale normal region %d %d %d\n", (int)((uint16_t*)window_planes[0])[0], (int)((uint16_t*)window_planes[1])[0], (int)((uint16_t*)window_planes[2])[0]); printf("Post scale corrupted region %d %d %d\n", (int)(*((uint16_t*)(window_planes[0] + corrupt_offset_y))), (int)(*((uint16_t*)(window_planes[1] + corrupt_offset_uv))), (int)(*((uint16_t*)(window_planes[2] + corrupt_offset_uv)))); return 0; }
示例输出
//No alignment Pre scale normal region 0 0 0 Pre scale corrupted region 0 0 0 Post scale normal region 400 400 400 Post scale corrupted region 512 36865 36865 //With alignment Pre scale normal region 0 0 0 Pre scale corrupted region 0 0 0 Post scale normal region 400 400 400 Post scale corrupted region 0 0 0
内容的提问来源于stack exchange,提问作者flansel
相关产品推荐
相关产品推荐

