基于Python的卫星云图识别:从HDF5数据到UNet分割实战

发布时间:2026/10/12 12:24:10
基于Python的卫星云图识别:从HDF5数据到UNet分割实战 简介基于Python的卫星云层图像识别系统设计与实现是一套面向课程实践与期末项目的完整工程包适合计算机视觉、遥感处理方向的初学者与高校学生系统覆盖图像预处理、特征提取与分类识别全流程配有友好图形界面代码注释详尽兼具实用性与教学参考价值。压缩包共168个文件体积62.33MB以70个Python源码、44个pyc文件为主另含yaml配置、csv数据集、PDF实验报告、PPT课件及sh脚本、zbak备份等目录分层清晰便于按模块查阅。包内附有完整分析文档与实验报告可支撑从图像处理到模型训练的全过程复现。目前已有43人学习浏览用户可直接运行样例数据观察不同算法下的分类效果并结合注释修改参数进行扩展实验对理解机器学习在遥感图像识别中的应用很有帮助。1. 基于Python的卫星云层图像识别为什么说它不是普通图像分类一提到“图像识别”多数人脑子里是猫狗分类、车牌识别、人脸检测。而卫星云层图像识别完全不是这么回事——它面对的是黑背景上的白斑、模糊的纹理边缘、昼夜交替的灰度漂移以及极不平衡的正负样本。做传统CV出身的人第一次拿到葵花8或FY-4的L1级数据经常会被文件名里的HDF5、经纬度网格和定标系数直接劝退。这个方向能解决的实际问题却很明确把气象卫星的原始辐射数据自动转换成“哪块是云、哪块是地面/海面、云顶大概多高”的判读结果服务于云量估算、降水辅助判识、光伏功率预测和航空航线天气辅助决策。本文不是把你往“调一个现成分类模型跑通demo”的路上带而是给出一个能从数据预处理一直走到模型训练和推理评估的完整方案。你会看到为什么卫星云图不能用普通RGB图片的思路硬套也会拿到可直接改的Python代码和参数建议。这套系统适合有Python基础、想入遥感图像识别方向的人也适合已经在做气象业务系统、需要把人工判云换成自动化流程的工程师。下面直接进入正题先从最关键的数据层拆起。2. 数据是第一道坎卫星云图的三个来源与读取方式2.1 HDF5/NetCDF不是图片先搞懂波段和定标卫星云图的原始数据通常以HDF5或NetCDF格式分发常见来源是葵花8/9号Himawari的AHI传感器、FY-4A/4B的AGRI传感器以及MODIS的L1/L2产品。这些文件里存的不是已经渲染好的JPEG而是每个像元的辐射计数值通常叫DN值Digital Number。DN值需要通过定标系数转换成反射率或亮温然后才能用于后续的模型输入。很多人在这第一步就翻车原因是直接用cv2.imread去读.h5文件得到的自然是报错。正确做法是用h5py或xarray读取。以葵花8的“真彩色”合成和红外通道为例你要先搞清楚自己需要哪些波段可见光通道Band 3约0.64微米看白天云系纹理最直观红外通道Band 13约10.4微米反映云顶亮温夜间只能靠红外。做云检测最少需要“可见光红外”的组合如果做云顶高度反演还要再多加几个水汽通道。import h5py import numpy as np # 以Himawari-8 AHI L1数据为例 with h5py.File(HS_H08_20240701_0200_B13.h5, r) as f: # 常见的HDF5内部路径/Data/波段名不同来源可能不同 dn f[/Data/IR] # 红外波段计数值 dn np.array(dn) # 读取定标系数通常在属性中 slope f[/Data/IR].attrs[Slope] intercept f[/Data/IR].attrs[Intercept] # 读取经纬度网格 lon np.array(f[/Geolocation/Longitude]) lat np.array(f[/Geolocation/Latitude]) # 定标DN - 辐射值 - 亮温此处仅示意真实公式以官方文档为准 radiance dn * slope intercept代码说明slope和intercept是逐像素或全局定标系数不同卫星产品差异很大务必先打印attrs里的全部属性确认。经纬度网格一般是一次性读取、后续重复使用不需要每次训练都读一遍。参数说明DN数组一般是uint16类型做减法或除法后要转成float32否则会出现整型溢出或截断。另外HDF5的压缩块读取性能对你的预处理速度影响极大建议第一次读取时把所需波段一次性读入内存避免训练时反复I/O。常见的坑是“内存占用爆炸”——一个全圆盘观测的波段是5500×5500像素两个波段就能占掉几百MB加上经纬度网格就更多。解决办法是裁剪区域而不是全盘加载后面会讲到。2.2 制作云图数据集区域裁剪、云掩膜与样本均衡原始L1数据不能直接送进网络训练因为它的几何范围覆盖整个圆盘而我们真正关注的地面区域可能只是某个省份或某个风电场周边。另一个问题是标签从哪里来。如果你做的是云检测标签可以来自官方云掩膜产品比如Himawari的CLP产品也可以用人工标注的粗标签替代。这里有一个非常重要的经验在起步阶段别太纠结标签的“像素级完美”先用能拿到的粗糙标签跑通全流程比花两周精标一个完美数据集更有价值。import numpy as np from PIL import Image def crop_to_region(band_data, lon, lat, lon_min, lon_max, lat_min, lat_max): # 将经纬度范围内的像素裁剪出来 mask (lon lon_min) (lon lon_max) (lat lat_min) (lat lat_max) rows np.any(mask, axis1) cols np.any(mask, axis0) return band_data[np.ix_(rows, cols)], rows, cols # 以华中地区为例 region_band, row_idx, col_idx crop_to_region( radiance, lon, lat, 108, 116, 28, 34 ) # 转成8位图用于可视化或作为模型输入 region_8bit ((region_band - region_band.min()) / (region_band.max() - region_band.min()) * 255).astype(np.uint8) Image.fromarray(region_8bit).save(region_vis.png)逻辑说明先用经纬度布尔掩码找出目标区域内所有像素的行列索引再做一次性切片。np.ix_的用法是初学者最容易忽略的细节——如果直接用布尔数组去索引二维数组得到的结果可能是一维展平的而不是矩形切片。参数说明lon_min/lon_max等经纬度范围要根据业务需求设置。区域越小模型越好训但泛化到其他区域时掉点越明显。建议一开始用相对大的区域例如整个中国东部后面再逐步收窄。如果你做的是全球尺度任务可以直接用等经纬度投影网格重采样但这里不展开。样本均衡问题在云检测中非常突出晴空像素总是占多数云像素在不同季节、不同下垫面的占比差异极大。为了解决这个问题我通常会在训练集构建时做两种处理一是计算数据集的云/晴空像素比例超过1:5就做负样本下采样二是用加权损失函数而不是简单换数据集。下面代码中的一行是关键——随机裁剪到256×256它既降低了内存压力又相当于做了数据增强。def make_training_tiles(vis_band, ir_band, label_mask, tile_size256, stride128): tiles [] h, w vis_band.shape for y in range(0, h - tile_size, stride): for x in range(0, w - tile_size, stride): vis_tile vis_band[y:ytile_size, x:xtile_size] ir_tile ir_band[y:ytile_size, x:xtile_size] lbl_tile label_mask[y:ytile_size, x:xtile_size] # 过滤掉标签过于单一的块比如全是云或全是晴空 if np.mean(lbl_tile) 0.05 or np.mean(lbl_tile) 0.95: continue tiles.append((vis_tile, ir_tile, lbl_tile)) return tiles这里的stride设置为128意味着相邻图块有50%重叠这是一种滑窗裁剪的标准做法能显著增加样本量。过滤“标签极纯”的块是为了防止模型学到“整块全云”或“整块全晴”的捷径逼它学边缘细节。3. 模型选型与训练为什么UNet是云层分割的主流骨架3.1 从FCN到UNet的选型理由云层识别的本质是像素级分类也就是语义分割而不是整图分类。你可以用ResNet或EfficientNet做云图分类判断“这片区域多云还是少云”但业务上更常见的是要知道云聚集在哪个经纬度范围、边界在哪。所以模型骨架的选择主要围绕分割网络展开。FCN是最早的端到端分割模型但它的上采样过于粗糙对云边界预测不够精细U-Net通过跳层连接把深层语义信息和浅层细节融合在一起在医学图像和遥感图像上都表现稳定是目前做云检测最常用的基线模型。此外DeepLabV3的ASPP模块对多尺度云块从积云到锋面云系会有更好的适应能力但实现复杂度更高。一个重要的选型思路是先跑通UNet再换DeepLab。UNet收敛快、显存占用中等、调试方便适合快速验证数据预处理和训练管线的正确性。DeepLabV3虽然精度上限更高但它的空洞卷积在边缘处理上反而容易出现过平滑在云检测这种“边界即价值”的任务上未必是稳赢。3.2 构建网络与损失函数加入注意力机制的UNet实现下面给一个可以直接跑的UNet变体——在经典UNet基础上在最深层加入一个简单的通道注意力模块。通道注意力的作用是让模型自动增强“红外亮温差异”这个通道在云检测中的贡献因为有些云在可见光下不明显但红外亮温极低。import torch import torch.nn as nn import torch.nn.functional as F class ChannelAttention(nn.Module): def __init__(self, channels, reduction8): super().__init__() self.fc nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Conv2d(channels, channels // reduction, 1, biasFalse), nn.ReLU(inplaceTrue), nn.Conv2d(channels // reduction, channels, 1, biasFalse), nn.Sigmoid() ) def forward(self, x): return x * self.fc(x) class DoubleConv(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.conv nn.Sequential( nn.Conv2d(in_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue), nn.Conv2d(out_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue) ) def forward(self, x): return self.conv(x) class UNetWithAttention(nn.Module): def __init__(self, in_channels3, out_channels1): super().__init__() self.enc1 DoubleConv(in_channels, 32) self.enc2 DoubleConv(32, 64) self.enc3 DoubleConv(64, 128) self.enc4 DoubleConv(128, 256) self.pool nn.MaxPool2d(2) self.attn ChannelAttention(256) self.dec4 DoubleConv(256 128, 128) self.dec3 DoubleConv(128 64, 64) self.dec2 DoubleConv(64 32, 32) self.out nn.Conv2d(32, out_channels, 1) def forward(self, x): e1 self.enc1(x) e2 self.enc2(self.pool(e1)) e3 self.enc3(self.pool(e2)) e4 self.enc4(self.pool(e3)) e4 self.attn(e4) # 通道注意力加在编码器最深层 d4 F.interpolate(e4, scale_factor2, modebilinear, align_cornersFalse) d4 self.dec4(torch.cat([d4, e3], dim1)) d3 F.interpolate(d4, scale_factor2, modebilinear, align_cornersFalse) d3 self.dec3(torch.cat([d3, e2], dim1)) d2 F.interpolate(d3, scale_factor2, modebilinear, align_cornersFalse) d2 self.dec2(torch.cat([d2, e1], dim1)) return torch.sigmoid(self.out(d2))逻辑说明输入是batch, 3, 256, 256三个通道可以设计为“可见光反射率、红外亮温、亮温差”这样模型能学到不同通道的组合特征。输出经过sigmoid变成0到1的云概率图。代码里最应该在意的细节是torch.cat时的维度对齐——跳层连接要求编码器特征图与解码器特征图的空间尺寸一致使用F.interpolate而不是转置卷积做上采样是为了减少棋盘格伪影。参数说明初始通道数设为32如果你的GPU显存只有6GB建议改成16out_channels1表示只做云/非云二分类如果你要做“云/晴空/水体/陆地”多分类改成4即可但损失函数也要换成多类交叉熵。损失函数方面推荐“BCE Dice”组合原因有两个BCE能让模型快速收敛对每个像素独立优化Dice损失则直接优化IoU缓解类别不平衡问题。实践中把两个损失按7:3混合比较常见。def combined_loss(pred, target, bce_weight0.7, dice_weight0.3): bce F.binary_cross_entropy(pred, target, reductionmean) smooth 1.0 pred_flat pred.view(-1) target_flat target.view(-1) dice 1.0 - (2.0 * (pred_flat * target_flat).sum() smooth) / ( pred_flat.sum() target_flat.sum() smooth ) return bce_weight * bce dice_weight * dice参数说明smooth参数是为了防止分母为0一般取1或1e-5。当数据集中云像素占比极小比如低于5%时把dice_weight提高到0.5否则模型会出现“全预测为晴空”的假阴性灾难。3.3 训练参数与验证策略不要用默认学习率硬冲云图图像识别任务里一个几乎必然踩坑的地方是学习率设置。很多人直接用Adam的默认学习率1e-3然后发现Loss震荡到飞起。卫星云图的像素分布极其不均匀——大量暗背景太空或深水的梯度贡献几乎为零而小块亮云会产生剧烈梯度。这种梯度差异下1e-3的Adam很容易把BatchNorm统计量打乱。我一般会把初始学习率设在2e-4到5e-4之间并配合ReduceLROnPlateau调度器。from torch.optim.lr_scheduler import ReduceLROnPlateau optimizer torch.optim.AdamW(model.parameters(), lr2e-4, weight_decay1e-4) scheduler ReduceLROnPlateau( optimizer, modemax, factor0.5, patience5, min_lr1e-6 ) # 训练循环中每个epoch结束后用验证集的IoU更新学习率 # scheduler.step(val_iou)验证指标不能只看Accuracy。因为云像素占比常常只有20%40%一个“全预测为晴空”的模型也能拿到70%以上的Accuracy这在业务上是完全不能用的。你应该同时监控IoU、Recall和Precision业务上更关注Recall——云被漏掉是致命的云被多报最多是标注成“可能有云”危害小得多。训练时的另一个技巧是数据增强。对卫星云图做垂直翻转、水平翻转和随机旋转90度都是安全的因为云的物理形态不具备“上下”语义。但要注意不要做随机裁剪缩放Resize那会让细小的卷云被拉伸成块状改变云的物理纹理。亮度抖动也不宜过大因为可见光反射率和红外亮温本身已经过定标大尺度扰动会破坏辐射一致性。4. 训练环境与工程坑Python依赖和显存管理4.1 环境搭建与依赖版本建议图像识别系统的Python环境搭建没有太多玄学关键是版本一致。PyTorch的API变化不算剧烈但CUDA版本和cuDNN版本不同可能会导致莫名其妙的显存报错。这里给一个我常用的环境配置流程在Linux服务器上操作。conda create -n satellite python3.10 -y conda activate satellite pip install torch2.2.0 torchvision --index-url https://download.pytorch.org/whl/cu121 pip install h5py numpy pillow matplotlib scikit-learn tqdm这段命令的要点Python版本选3.10比3.12更稳妥因为一些老的HDF5底层库在3.12下编译容易出警告或兼容性问题。PyTorch版本要跟你的显卡驱动匹配如果是RTX 30系/40系卡CUDA 12.1的预编译包可以直接用如果是老卡如GTX 10系要改用cu118版本。h5py和netCDF4是目前读取卫星数据的两个必装库如果文件是NetCDF格式还需要额外装netCDF4包。一个容易被忽视的问题是内存管理。5500×5500像素的波段数据在NumPy里是float32数组的话单个波段占用约121MB看起来不大但要同时载入10个波段加上经纬度网格峰值内存很容易突破2GB。如果你在本地Windows机器上跑这可能是导致系统假死的原因。解决方法是提高预处理阶段的数据读取效率优先读取你需要的波段子集而不是整文件全量读取。4.2 显存不足梯度累积和混合精度的取舍图像分割模型的显存消耗大头是特征图而不全是模型参数。一个256×256输入、初始通道32的UNet在batch_size16时显存占用约68GB取决于PyTorch是否开启cudnn.benchmark。如果你的GPU只有8GB首选方案不是换小模型而是把batch_size降到4并开启梯度累积。accum_steps 4 # 等效batch_size 4 * 4 16 scaler torch.cuda.amp.GradScaler() for i, (imgs, labels) in enumerate(trainloader): imgs, labels imgs.cuda(), labels.cuda() with torch.cuda.amp.autocast(): preds model(imgs) loss combined_loss(preds, labels) scaler.scale(loss).backward() if (i 1) % accum_steps 0: scaler.step(optimizer) scaler.update() optimizer.zero_grad()这个代码里最重要的是第i1取模accum_steps的判断。混合精度AMP在UNet这类模型上收益非常明显尤其是在Ampere架构及更新的GPU上速度提升接近一倍而且因为使用了GradScaler不会出现梯度下溢。要注意的一点是BatchNorm在混合精度下偶尔会有数值波动如果你发现训练到中后期验证指标上下跳动剧烈可以先关掉AMP试试。另外一个易被忽略的参数是num_workers。很多人图省事设成0结果GPU利用率只有30%。合理做法是设成CPU核心数的一半。但如果数据读取环节本身存在瓶颈比如从机械硬盘读取大HDF5文件调大num_workers并不会缓解瓶颈反而会让内存吃紧。这时候更好的做法是把数据预处理成离线npy或内存映射文件训练时直接读npy不再做HDF5解析。5. 云图识别系统的常见问题与排查从图像质量到坐标对齐5.1 问题一模型预测图出现“棋盘格”状伪影现象模型输出的云概率图有明显的方格感尤其是云的边缘处看起来像马赛克。原因最常见的是转置卷积叠加导致的“Deconvolution checkerboard”。有些实现会在解码器用ConvTranspose2d上采样当卷积核大小与步长不匹配时会天然产生重叠不均匀区域。另一个次要原因是输入图像本身被压缩成有损JPEG但一般来说影响不致命。解决把解码器的上采样全部换成F.interpolatebilinear上采样再接卷积层。这种组合在遥感图像分割里是更稳的套路代价是理论上限略低但实际效果通常更平滑。如果你必须用转置卷积建议把kernel_size设为步长的2倍如stride2, kernel4来减小重叠问题。5.2 问题二训练Loss在下降但IoU一直为0现象训练Loss从0.8降到0.3看起来正常但验证集IoU始终在0.1以下甚至为0。原因这种诡异情况通常源于标签与输入图像没有对齐。卫星云图经过地理定位后不同波段间可能存在像素偏移尤其是可见光和红外波段因为扫描时间差会有一两个像素的空间错位。你拿可见光图做输入、拿红外波段生成的云掩膜做标签如果不做配准模型永远学不到结构性映射。解决在数据预处理中加入波段间的配准步骤。最简单的做法是读取官方地理定位文件如Himawari的LAT/LON网格用scipy.ndimage.shift做亚像素平移对齐。更省事的做法是干脆只用同一波段的亮温阈值生成标签避免跨波段错位。检测方法也简单把可见光图和标签图叠加后人工看云边界是否有系统性偏移如果偏移超过2个像素就得修数据。5.3 问题三夜间数据全部预测为“无云”现象模型在白天数据上表现很好IoU能到0.7以上但输入夜间红外图像时输出概率图几乎全为0。原因训练集分布中白天样本占绝大多数而且模型学到的云特征高度依赖可见光反射率。夜间没有可见光通道模型失去最强特征后直接“摆烂”。这是跨域泛化问题不是模型bug。解决不能只在训练集里加几个夜间样本完事必须做“模态丢弃增强”。具体做法是在训练时随机把可见光通道置为0强制模型学习红外通道的判云能力。这个技巧在业务系统中非常有效因为它人工模拟了“夜间观测模态”。# 数据增强训练时随机丢弃可见光通道 def random_drop_vis(batch, drop_prob0.3): if torch.rand(1).item() drop_prob: batch[:, 0, :, :] 0.0 # 假设第0通道是可见光 return batch5.4 问题四GPU显存明明足够但报“CUDA out of memory”现象PyTorch报显存不足但nvidia-smi显示显存还有空余。原因PyTorch的缓存分配器会预占部分显存并且不会在每次张量释放时立刻归还给系统。同时HDF5读取或NumPy数组如果不小心转移到CUDA会产生隐式显存占用。还有一个典型场景是DataLoader的pin_memoryTrue设置与多进程读取的组合会让每个worker各留一份缓存。解决在训练循环开头加入torch.cuda.empty_cache()是治标不治本的手段真正有效的是减小batch_size、关闭pin_memory、并在加载数据时将数组显式用float32。另外检查代码中是否有不小心把经纬度网格也送到GPU的写法那种大矩阵会白白吃掉几百MB。5.5 问题五小云团被完全漏检但大云团识别良好现象锋面大云系的识别效果不错但零散的积云或破碎层云几乎全部漏报。原因模型的感受野决定了大目标占优。UNet的深层特征擅长捕捉整体云系形态但浅层特征对小目标响应不足再加上损失函数中Dice部分对“小目标在全图中的占比极低”不敏感导致小云团被当成背景噪声。解决一个立竿见影的手段是在损失函数中做“尺度加权”——计算每个连通域的像素数量给面积小于阈值的云团区域的损失权重乘以2到3。另一个可选手段是在数据增强中加入随机遮挡Random Erasing强迫模型不依赖大面积连续云团的特征。6. 一个可落地的进阶技巧用云图时间序列做运动矢量外推让系统从“看图识字”变成“预判未来”最后用一个能直接提升系统价值的技巧收尾。纯粹做单帧云图分割业务价值局限于“当前时刻的云量统计和云类型标注”。但如果把连续多帧云图的分割结果做配准和光流估计就能得到云的移动方向和速度进一步预测未来1到2小时的云覆盖变化。这对光伏功率预测、无人机航线规划和赛事气象保障都有直接作用。实现思路是用Farneback稠密光流算法对两帧云概率图计算运动场然后把当前云概率图按光流场进行偏移外推。Farneback在OpenCV里是现成的不需要训练光流网络对256×256的输入CPU上处理速度也够实时。import cv2 import numpy as np def extrapolate_cloud(prob_map, future_frame_count3, flow_scale0.3): # prob_map: 当前帧云概率图, 0~1 float32 img1 (prob_map * 255).astype(np.uint8) # 模拟“下一帧”实际业务中取上一帧或再前一帧 img2 img1.copy() # 光流计算从当前帧到下一帧 flow cv2.calcOpticalFlowFarneback( img1, img2, None, pyr_scale0.5, levels3, winsize21, iterations3, poly_n7, poly_sigma1.5, flags0 ) # 按光流方向外推 h, w prob_map.shape grid_x, grid_y np.meshgrid(np.arange(w), np.arange(h)) # 外推N帧每帧按光流大小的一定比例平移 for _ in range(future_frame_count): map_x (grid_x flow[..., 0] * flow_scale).astype(np.float32) map_y (grid_y flow[..., 1] * flow_scale).astype(np.float32) prob_map cv2.remap(prob_map, map_x, map_y, cv2.INTER_LINEAR) return prob_map参数说明winsize21是一个比较折中的取值窗口太小噪声大窗口太大慢且细节丢失。flow_scale的取值取决于帧间隔时间如果两帧间隔10分钟外推1小时则scale取0.3左右具体要按云速统计来标定。这个方法不是万能的强对流云的快速发展它是预测不了的但对层云、积云这类以平流运动为主的云系效果足够实用。我自己的习惯是在外推后叠加一个“云量变化缓冲带”——对预测出来的云边界往外膨胀35个像素宁可报多不可报漏。这个习惯让我在多次实际应用中避免了“预测无云但实际被云盖住”的尴尬。希望这套从数据处理、模型搭建到系统外推的完整流程能帮你在卫星云图识别这个方向上少走弯路。去把你的第一张HDF5读出来切成512×512的图块跑一个20轮训练的UNet你很快会发现这不是一个“能不能做”的问题而是一个“做到多好”的问题。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询