使用PyAV生成的MP4视频存在颜色偏暗偏差,如何解决?
解决PyAV编码MP4时颜色偏暗的问题
问题原因
你遇到的颜色偏暗问题核心是色彩范围与空间不匹配:
imageio.imread读取的RGB图像属于全范围(PC Range),像素值覆盖0-255区间;- PyAV默认将RGB转YUV时采用有限范围(TV Range),会把Y分量压缩到16-235区间,导致原图像的暗部(0-15)被强制提亮、亮部(236-255)被压缩,最终整体画面偏暗;
- 输入图像是sRGB格式(imageio默认读取格式),但PyAV默认转换矩阵不匹配该色彩空间,进一步加剧颜色偏差。
解决方案
通过显式指定流的色彩参数,让PyAV按照正确规则完成RGB到YUV的转换:
修改后的完整代码
import av import numpy as np from imageio.v2 import imread class MP4: def __init__(self, fname, width, height, fps): self.output = av.open(fname, 'w', format='mp4') self.stream = self.output.add_stream('h264', str(fps)) self.stream.width = width self.stream.height = height self.stream.pix_fmt = 'yuv420p' self.stream.options = {'crf': '17'} # 关键设置:匹配输入图像的色彩空间与范围 self.stream.colorspace = 'srgb' self.stream.color_range = 'pc' def write_frame(self, pixels): # 确保输入为uint8格式的RGB数组 if pixels.dtype != np.uint8: pixels = np.clip(pixels, 0, 255).astype(np.uint8) frame = av.VideoFrame.from_ndarray(pixels, format='rgb24') # 同步帧与流的色彩参数 frame.colorspace = self.stream.colorspace frame.color_range = self.stream.color_range packet = self.stream.encode(frame) self.output.mux(packet) def close(self): # 确保编码器剩余数据包全部写入 packet = self.stream.encode(None) while packet: self.output.mux(packet) packet = self.stream.encode(None) self.output.close() # 测试示例 if __name__ == '__main__': test_img = imread('solid_color_test.png') w, h = test_img.shape[1], test_img.shape[0] writer = MP4('corrected_output.mp4', w, h, 24) writer.write_frame(test_img) writer.close()
补充说明
- 色彩范围设置:
color_range='pc'强制编码器使用全范围转换规则,避免亮度被压缩; - 色彩空间设置:
colorspace='srgb'匹配imageio读取的图像格式,保证RGB到YUV的转换矩阵准确; - 编码器收尾优化:修改
close方法循环处理剩余数据包,避免视频文件损坏。
若修改后仍有偏差,可检查:
- 播放器的色彩范围设置,确保使用**全范围(PC)**解析视频;
- PyAV依赖的FFmpeg版本不低于4.0,旧版本对色彩参数支持不完善。
内容的提问来源于stack exchange,提问作者Yossi Kreinin
相关产品推荐
相关产品推荐

