用 GDAL 将多波段 TIF 转为单张 PNG 图片

遥感影像通常以多波段 GeoTIFF 格式存储,每个波段对应一个光谱范围。在可视化、网页展示或报告制作时,我们需要将其转换为 RGB 或单波段 PNG。

1. 命令行快速转换

1.1 选择波段合成 RGB

最常见的需求:将 红/绿/蓝 三个波段合成为一张彩色 PNG。假设你的 TIF 文件包含 4 个波段(B/G/R/NIR),选择第 3、2、1 波段作为 R、G、B:

gdal_translate -of PNG \ -b 1 -b 2 -b 3 \ -scale \ input.tif output.png

-b 1 -b 2 -b 3 表示输出 PNG 的第一、二、三通道分别取 TIF 的第 1、2、3 波段。

-scale 自动将数据拉伸到 0-255 范围(PNG 每个通道为 8 位)。

1.2 百分位拉伸(推荐)

简单拉伸容易受极端值影响。建议先统计 2%-98% 百分位的值:

# 先用 Python/GDAL 获取百分位值,然后: gdal_translate -of PNG \ -b 1 -b 2 -b 3 \ -scale_1 50 800 -scale_2 80 900 -scale_3 60 850 \ input.tif output.png

1.3 单波段灰度图

对于 DEM、NDVI 等单波段数据:

gdal_translate -of PNG -b 1 -scale -ot Byte input.tif gray_output.png

-ot Byte 确保输出为 8 位格式。

2. Python + GDAL 编程转换

2.1 基础转换

from osgeo import gdal import numpy as np def tif_to_png(input_tif, output_png, bands=(1,2,3)): """将多波段 TIF 转为 RGB PNG""" ds = gdal.Open(input_tif) if ds is None: raise ValueError(f"无法打开文件: {input_tif}") cols = ds.RasterXSize rows = ds.RasterYSize # 读取指定波段 band_arrays = [] for b in bands: band = ds.GetRasterBand(b) arr = band.ReadAsArray().astype(np.float32) band_arrays.append(arr) # 堆叠为 (rows, cols, channels) img = np.stack(band_arrays, axis=-1) # 百分位拉伸 for c in range(img.shape[-1]): channel = img[:, :, c] vmin = np.percentile(channel[channel > 0], 2) if np.any(channel > 0) else 0 vmax = np.percentile(channel[channel > 0], 98) if np.any(channel > 0) else 255 channel = np.clip((channel - vmin) / (vmax - vmin) * 255, 0, 255) img[:, :, c] = channel img = img.astype(np.uint8) # 写入 PNG driver = gdal.GetDriverByName('PNG') out_ds = driver.Create(output_png, cols, rows, len(bands), gdal.GDT_Byte) for i in range(len(bands)): out_ds.GetRasterBand(i + 1).WriteArray(img[:, :, i]) # 复制地理参考信息(可选) out_ds.SetGeoTransform(ds.GetGeoTransform()) out_ds.SetProjection(ds.GetProjection()) ds = None out_ds = None print(f"已生成: {output_png}") if __name__ == '__main__': tif_to_png('input.tif', 'output.png')

2.2 添加透明度(Alpha 通道)

将无效值(NoData)区域设为透明:

# 创建 RGBA 图像(4 通道) img_rgba = np.zeros((rows, cols, 4), dtype=np.uint8) img_rgba[:, :, :3] = img_rgb img_rgba[:, :, 3] = 255 # Alpha 默认为不透明 # 无效值区域透明 nodata_mask = (band_arrays[0] == nodata_value) img_rgba[nodata_mask, 3] = 0 # 用 GDAL 写出(4 波段 PNG = RGBA) driver = gdal.GetDriverByName('PNG') out_ds = driver.Create(output_png, cols, rows, 4, gdal.GDT_Byte) for i in range(4): out_ds.GetRasterBand(i + 1).WriteArray(img_rgba[:, :, i])

3. 常见问题

问题原因解决方法
PNG 全黑或全白 数据范围超出 0-255 -scale 参数或手动拉伸
色彩异常(植被变蓝) 波段顺序错误 确认源文件波段顺序(B/G/R/NIR → 选 3,2,1)
内存不足 影像过大 使用 -outsize 缩小分辨率或分块读取
坐标信息丢失 PNG 不存储地理参考 另存 World File (.pgw) 或保留原始 TIF
提示: 如果只是需要预览,使用 QGIS 导出比命令行更直观;批量处理则推荐 Python + GDAL 脚本方式。
← 返回问题手记