- 积分
- 26275
- 贡献
-
- 精华
- 在线时间
- 小时
- 注册时间
- 2017-9-4
- 最后登录
- 1970-1-1
|
登录后查看更多精彩内容~
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
本帖最后由 灭火器 于 2022-4-18 11:44 编辑
Aqua 卫星的 MYD021KM 产品 36 个波段的 1 km 分辨率的地表反射率数据,将其中波段 1(0.65 μm)的数值作为红色分量,波段 4(0.56 μm)的数值作为绿色分量,波段 3(0.47 μm)的数值作为蓝色分量,即可合成与人眼感受类似的彩色云图。具体来说需要以下步骤:
(1)从 MYD021KM 配套的 MYD03 产品中读取 1 km 分辨率的地理经纬度数据。
(2)根据反射率变量的元数据将 short 类型的反射率缩放为浮点型。
(3)将三个波段的反射率合并为 RGB 数组,处理缺测和超出 [0, 1] 范围的元素。
(4)将 RGB 数组用 pcolormesh 方法画在地图上,每个四边形像元的颜色通过 color 参数给出。
(5)增强图像的亮度,比较增强前后的效果。
MYD021KM 数据的变量说明和缩放方法请见官方文档:
https://mcst.gsfc.nasa.gov/l1b-documents
https://ftp.ssec.wisc.edu/pub/willemm/Creating_Reprojected_True_Color_MODIS_Images_A_Tutorial_process.pdf
增强图像亮度采用了两种方法:
(1)伽马矫正(http://unidata.github.io/python-gallery/examples/mapping_GOES16_TrueColor.html)
(2)分段映射(http://www.idlcoyote.com/ip_tips/brightmodis.html)
pcolormesh 方法画 RGB 数组的颜色参考了
https://stackoverflow.com/questions/41389335/how-to-plot-geolocated-rgb-data-faster-using-python-basemap
https://github.com/matplotlib/matplotlib/issues/4277
读取文件使用 pyhdf 包,画图使用 cartopy 包。代码如下:
- '''
- @author : 灭火器
- @time : 2022-04-15
- @use : 利用MYD02产品画出真彩色图, 并比较不同图像处理方法的效果.
- '''
- from pathlib import Path
- from datetime import datetime
- from pyhdf.SD import SD, SDC
- import numpy as np
- from scipy.interpolate import interp1d
- import matplotlib.pyplot as plt
- import matplotlib.ticker as mticker
- import cartopy.crs as ccrs
- import cartopy.feature as cfeature
- from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
- def to_reflectance(sds):
- '''将反射率的SDS变为浮点型数组.'''
- ref = sds[:]
- attrs = sds.attributes()
- scales = np.array(attrs['reflectance_scales'])
- offsets = np.array(attrs['reflectance_offsets'])
- index = np.s_[:, np.newaxis, np.newaxis]
- ref = np.where(
- ref > 32767, np.nan,
- (ref - offsets[index]) * scales[index]
- )
- return ref
- def scale_modis(rgb):
- '''增亮MODIS的彩色图.'''
- x = np.array([0, 30, 60, 120, 190, 255]) / 255
- y = np.array([0, 110, 160, 210, 240, 255]) / 255
- f = interp1d(x, y, bounds_error=False, fill_value=1)
- return f(rgb)
- if __name__ == '__main__':
- # 输入和输出目录.
- dirpath_data = Path('./data')
- dirpath_pics = Path('./pics')
- if not dirpath_pics.exists():
- dirpath_pics.mkdir()
- # 输入文件.
- filepath_MYD02 = dirpath_data / 'MYD021KM.A2018120.0500.061.2018120164637.hdf'
- filepath_MYD03 = dirpath_data / 'MYD03.A2018120.0500.061.2018120162824.hdf'
- # 提取文件名中的时间.
- parts = filepath_MYD02.name.split('.')
- date = datetime.strptime(parts[1] + parts[2], 'A%Y%j%H%M')
- # 读取1km经纬度.
- sd = SD(str(filepath_MYD03), SDC.READ)
- lon = sd.select('Longitude')[:]
- lat = sd.select('Latitude')[:]
- sd.end()
- # 地图范围由granule的范围决定.
- extents = [lon.min(), lon.max(), lat.min(), lat.max()]
- # 读取反射率.
- sd = SD(str(filepath_MYD02), SDC.READ)
- ref250 = to_reflectance(sd.select('EV_250_Aggr1km_RefSB'))
- ref500 = to_reflectance(sd.select('EV_500_Aggr1km_RefSB'))
- sd.end()
- ref1 = ref250[0, :, :]
- ref3 = ref500[0, :, :]
- ref4 = ref500[1, :, :]
- # 合并RGB数组, 处理缺测部分.
- rgb = np.dstack((ref1, ref4, ref3))
- rgb[np.any(np.isnan(rgb), axis=-1)] = 1
- rgb = np.clip(rgb, 0, 1)
- # 三种处理的图像.
- rgb1 = rgb
- rgb2 = np.power(rgb, 1 / 2.2)
- rgb3 = scale_modis(rgb)
- # 组图形状为(1, 3).
- proj = ccrs.PlateCarree()
- subplot_kw = {'projection': proj}
- fig, axes = plt.subplots(1, 3, figsize=(15, 5), subplot_kw=subplot_kw)
- fig.subplots_adjust(wspace=0.2)
- # 画出地图.
- for ax in axes:
- ax.coastlines(resolution='50m', lw=0.5)
- ax.add_feature(cfeature.BORDERS, lw=0.5)
- ax.set_xticks(np.arange(-180, 181, 10), crs=proj)
- ax.set_yticks(np.arange(-90, 91, 10), crs=proj)
- ax.xaxis.set_minor_locator(mticker.AutoMinorLocator(2))
- ax.yaxis.set_minor_locator(mticker.AutoMinorLocator(2))
- ax.xaxis.set_major_formatter(LongitudeFormatter())
- ax.yaxis.set_major_formatter(LatitudeFormatter())
- ax.set_extent(extents, crs=proj)
- ax.tick_params(labelsize='small')
- # 画出三种图像.
- rgbs = [rgb1, rgb2, rgb3]
- titles = ['Original', 'Gamma Correction', 'Enhancement']
- for i, ax in enumerate(axes):
- colors = rgbs[i][:-1, :-1].reshape(-1, 3)
- ax.pcolormesh(
- lon, lat, lon[:-1, :-1], color=colors,
- shading='flat', transform=proj
- )
- ax.set_title(titles[i], fontsize='medium')
- # 设置图片标题.
- t = date.strftime('%Y-%m-%d %H:%M')
- fig.suptitle(t, y=0.9, fontsize='large')
- # 保存图片.
- filepath_output = dirpath_pics / 'true_color.png'
- fig.savefig(str(filepath_output), dpi=300, bbox_inches='tight')
- plt.close(fig)
复制代码 效果如下图,可以看到如果不增强亮度的话,无云的陆面和洋面会显得黑糊糊的,难以识别;而增强后云又会显得过亮,损失了一些纹理细节。
下图是 https://worldview.earthdata.nasa.gov/ 网站的在线图片,与上图的差别是陆面植被的绿色更加醒目,可能是官方对反射率进行了其它订正操作。但总的来说效果还是比较接近的。
代码中使用 pcolormesh 画图是为了保留 MODIS granule 非规则的网格结构。本来 pcolor 和 pcolormesh 只能画伪彩色图,默认使用 viridis 色标填色,这里通过 color 参数用 RGB 数组的颜色覆盖了默认的填色。其实 Axes 的 pcolorfast 方法能直接画 RGB(A) 数组,但因为这个方法仍处于实验状态,且 GeoAxes 并没有对其进行改写,所以不保证能在别的地图投影下正常工作。相比之下 Meteoinfo 直接就有 imshowm 函数……
对于大小为 (2030, 1354) 的数组来说,这个程序每张图大概要画上十几秒,效率堪忧。这里给出另一种处理思路:利用 pyresample 包的 resample_nearest 函数对 swath 数据进行最邻近插值,得到网格化的 RGB 数组,然后通过 imshow 方法直接画在地图上。计算时间会稍微延长,但画图时间会快十倍。不过 pyresample 包对区域的定义我还看不懂,所以这里就只提供简单的思路。
我在网上搜到的教程大多是画同步卫星的真彩色图(使用 Satpy 包),而关于 MODIS 产品的教程则非常少,所以这里的程序也只是一种尝试,也可能存在错误或需要改进的地方,还请熟悉的朋友多多交流。
|
|