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])