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

如何用Rasterio生成可在系统查看器正常显示的NDVI伪彩色GeoTIFF

NDVI GeoTIFF伪彩色显示问题解决

问题描述

计算NDVI数据集并将其从[-1,1]缩放至[0,255],希望保存为可在普通图像查看器(如Gwenview)中显示伪彩色的GeoTIFF,但生成的文件始终显示为灰度图。因目标应用不支持带地理参考的PNG,必须使用TIFF格式。

现有代码

sds = rasterio.open(ds.GetSubDatasets()[c.DS_10m][0])
bands = sds.read([c.BAND_NIR, c.BAND_RED])
ndvi = np.zeros(bands[0].shape, dtype=rasterio.float32)

ndvi = scaleOnes(
    (bands[0].astype(float) - bands[1].astype(float)) / (bands[0] + bands[1])
)

kwargs = sds.meta

kwargs.update(driver="GTIFF", dtype=rasterio.uint8, count=1, compress="lzw")

with rasterio.open("ndvitest2.tif", "w", **kwargs) as dst:
    dst.write(ndvi, indexes=1)
    dst.write_colormap(1, c.COLORMAP_NDVI)

颜色映射表

COLORMAP_NDVI = {
    102: (191, 191, 191, 255),
    114: (173, 173, 173, 255),
    127: (255, 255, 224, 255),
    130: (255, 249, 204, 255),
    133: (237, 232, 181, 255),
    137: (222, 217, 156, 255),
    140: (204, 199, 130, 255),
    143: (189, 184, 107, 255),
    146: (176, 194, 97, 255),
    149: (163, 204, 89, 255),
    153: (145, 191, 82, 255),
    159: (128, 179, 71, 255),
    165: (112, 163, 64, 255),
    172: (97, 150, 54, 255),
    178: (79, 138, 46, 255),
    184: (64, 125, 36, 255),
    191: (48, 110, 28, 255),
    197: (33, 97, 18, 255),
    204: (15, 84, 10, 255),
}

问题根源与解决步骤

1. 数据类型与缩放范围不匹配

现有代码中scaleOnes生成的NDVI为float32类型,缩放范围未对齐颜色映射表的键区间(102-204),且未显式转换为uint8,导致查看器无法正确关联调色板。

修正代码:

# 替换原NDVI计算与缩放逻辑
ndvi_raw = (bands[0].astype(float) - bands[1].astype(float)) / (bands[0] + bands[1])
ndvi_raw = np.nan_to_num(ndvi_raw, nan=-1)  # 处理除以0产生的NaN
ndvi_raw = np.clip(ndvi_raw, -1, 1)  # 限制值在标准NDVI范围内
# 将[-1,1]映射到颜色表对应的102-204区间,转为uint8
ndvi_scaled = ((ndvi_raw + 1) * (204 - 102) / 2 + 102).astype(np.uint8)

2. 颜色映射表不完整

当前颜色表仅包含离散值,普通图像查看器大多不支持自动插值缺失颜色,会默认显示为灰度。需补全0-255全范围的颜色映射。

补全代码:

from scipy.interpolate import interp1d

# 提取现有颜色表的键和颜色值
keys = sorted(c.COLORMAP_NDVI.keys())
colors = np.array([c.COLORMAP_NDVI[k] for k in keys])

# 创建各颜色通道的线性插值函数
r_interp = interp1d(keys, colors[:,0], kind='linear')
g_interp = interp1d(keys, colors[:,1], kind='linear')
b_interp = interp1d(keys, colors[:,2], kind='linear')
a_interp = interp1d(keys, colors[:,3], kind='linear')

# 生成完整的0-255颜色映射
full_colormap = {}
for i in range(256):
    if i in c.COLORMAP_NDVI:
        full_colormap[i] = c.COLORMAP_NDVI[i]
    else:
        # 插值后确保颜色值在0-255范围内
        r = int(np.clip(r_interp(i), 0, 255))
        g = int(np.clip(g_interp(i), 0, 255))
        b = int(np.clip(b_interp(i), 0, 255))
        a = int(np.clip(a_interp(i), 0, 255))
        full_colormap[i] = (r, g, b, a)

3. 元数据缺失 photometric 标记

GeoTIFF需明确设置photometric='PALETTE',告知查看器这是调色板图像,而非灰度图。

更新元数据:

kwargs.update(driver="GTIFF", dtype=rasterio.uint8, count=1, compress="lzw", photometric='PALETTE')

修正后的完整代码

import numpy as np
import rasterio
from scipy.interpolate import interp1d

# 读取遥感数据
sds = rasterio.open(ds.GetSubDatasets()[c.DS_10m][0])
bands = sds.read([c.BAND_NIR, c.BAND_RED])

# 计算原始NDVI并处理异常值
ndvi_raw = (bands[0].astype(float) - bands[1].astype(float)) / (bands[0] + bands[1])
ndvi_raw = np.nan_to_num(ndvi_raw, nan=-1)
ndvi_raw = np.clip(ndvi_raw, -1, 1)

# 映射到颜色表对应的数值区间并转换类型
ndvi_scaled = ((ndvi_raw + 1) * (204 - 102) / 2 + 102).astype(np.uint8)

# 补全颜色映射表
keys = sorted(c.COLORMAP_NDVI.keys())
colors = np.array([c.COLORMAP_NDVI[k] for k in keys])
r_interp = interp1d(keys, colors[:,0], kind='linear')
g_interp = interp1d(keys, colors[:,1], kind='linear')
b_interp = interp1d(keys, colors[:,2], kind='linear')
a_interp = interp1d(keys, colors[:,3], kind='linear')

full_colormap = {}
for i in range(256):
    if i in c.COLORMAP_NDVI:
        full_colormap[i] = c.COLORMAP_NDVI[i]
    else:
        r = int(np.clip(r_interp(i), 0, 255))
        g = int(np.clip(g_interp(i), 0, 255))
        b = int(np.clip(b_interp(i), 0, 255))
        a = int(np.clip(a_interp(i), 0, 255))
        full_colormap[i] = (r, g, b, a)

# 更新输出元数据
kwargs = sds.meta
kwargs.update(driver="GTIFF", dtype=rasterio.uint8, count=1, compress="lzw", photometric='PALETTE')

# 写入伪彩色GeoTIFF
with rasterio.open("ndvitest2.tif", "w", **kwargs) as dst:
    dst.write(ndvi_scaled, indexes=1)
    dst.write_colormap(1, full_colormap)

内容的提问来源于stack exchange,提问作者Stefan Gofferje

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 07:45:54