卫星云图识别实战:规则基线+UNet模型攻克计算机视觉大作业
2026/9/11 1:54:03 网站建设 项目流程

简介:针对卫星云层图像理解与识别任务的计算机视觉大作业完整交付包,包含可运行的Python源码、实验报告PDF、报告PPT及配套说明文档,适合计算机视觉、人工智能等专业学生用于课程设计、毕业设计或项目初期演示。压缩包共140个文件,约62.33MB,以70个py源码文件和44个pyc编译文件为主,辅以sh训练脚本、csv数据标注、yaml配置及md说明,层次清晰便于直接复现与二次开发。项目代码均经过运行测试,答辩平均分达96分,且提供远程教学支持;内容预览中可见train_700等数据文件与结果图片,说明从数据处理、模型训练到结果可视化均有覆盖。目前已有239人学习下载,对于希望快速上手卫星云图识别、理解完整项目流程的读者,是一份兼具参考与模板价值的实用资料。

1. 计算机视觉大作业:把卫星云层图像的理解与识别拆成检测与分类两个子任务

“计算机视觉大作业”这个标签很容易让人误以为难点全在模型一侧。实际情况相反,卫星云层图像的理解与识别在课程项目里真正磨人的是数据与任务拆解。“理解”意味着模型要能区分云和地表,也要能区分不同云相态;“识别”则落到像素级分类或云量估计。如果一开始就把任务定义成“对整张图做语义分割”,后续会不断被标签噪声和类别不平衡打断。下面按数据准备、规则基线、UNet 模型和文档交付的顺序推进,先说结论:先写一条物理规则基线,再用深度学习去打它,别拿 UNet 直接起步。

2. 卫星云图数据准备:通道、定标、标签与python读取的最小闭环

2.1 先分清影像类型:可见光、红外与水汽通道各解决什么问题

云层图像与自然图像最大的区别在于“通道即物理量”。可见光通道(0.5-0.9 μm)反映云反射太阳辐射的能力,云顶反射率高,但夜间不可用;红外窗区通道(10-12 μm)表征云顶亮温,云顶越高通常越冷,白天黑夜都能用;水汽通道(6.2-7.0 μm)反映中高层水汽分布,对卷云与深厚对流更敏感。大作业里常见的做法是取可见光、11 μm 红外和 6.9 μm 水汽三个通道叠成三通道影像,这组选择比随便拿 RGB 自然图更有物理含义,实验报告里写一句就能体现“理解”。

2.2 公开数据源定位与选型

自己接收卫星数据不现实,用公开产品就够。常见数据源见下表,下载时以“L1B 辐射率”或“云掩膜产品”作为关键词检索:

数据源通道特点常见空间分辨率更新频率适合做什么
Himawari-8/916 通道,含可见光/红外/水汽0.5-2 km10 分钟云检测、云分类单帧实验
GOES-16/1716 通道0.5-2 km5 分钟云图序列、运动特征扩展
FY-4A 成像仪14 通道0.5-4 km15 分钟静轨平台数据对照
MODIS36 通道250 m-1 km1 天制作高精度训练标签

MODIS 自带 MOD35 云掩膜产品,能直接当作二分类标签,这是最省事的标签获取路径。Himawari-8 的 HSD 数据虽然有分类产品,但解包复杂度高,大作业时间有限时优先级靠后。拿到手的原始格式通常是 netCDF 或 HDF,先统一转成多通道 tif 或 npy,后边的 OpenCV 与 PyTorch 才接得上。

2.3 标签类别怎么定:别照搬气象书上的十属云

云属分类在气象上有十种,直接搬到计算机视觉大作业里会出现两类问题:一是卫星投影下云属形态与地面观测不是一回事,二是样本量不够分到十类,容易把任务复杂化。我一般会把类别压缩成三类:晴空、水云、冰云。水云云顶温度较高,冰云云顶温度较低,这个区分在红外通道上有物理依据,可视化时也直观。如果只做云检测,则压缩成二分类。对带质量标记的四级产品(晴空/可能云/确信云/不确定),合并策略为:晴空归 0,确信云归 1,可能云与不确定丢弃,因为含混标签进入训练集通常比缺失更伤。

2.4 python读取与预处理:xarray打开、重采样、切块

L1B 数据读取选 xarray,它把 netCDF/HDF 的维度与坐标直接暴露出来,处理起来比 GDAL 直观。下面是一段最简读取逻辑:

import xarray as xr import numpy as np ds = xr.open_dataset("AHI_L1B_20240901_0230.nc") print(list(ds.variables)) # 先确认通道名,各数据源不统一 ir11 = ds["IR11"].data.astype("float32") # 11.2μm亮温,单位K vis06 = ds["VIS06"].data.astype("float32") # 0.64μm反射率,单位% # 夜间可见光通道无有效值,直接置0 vis06 = np.where(vis06 > 100, 0, vis06)

读取后的处理顺序是:时间维度压缩、通道裁剪、重投影、裁剪研究区、归一化、切块。需要注意三个问题:第一,netCDF 变量名每套数据源都不同,写代码前先打印 variables 确认;第二,亮温统一转成开尔文,反射率转成 0-1,不要直接除以 255;第三,整幅 5500x5500 的影像要按 512x512 窗口滑动切块,切块时标签与影像必须保证同一裁剪框,防止行列错位。

标签质量也应纳入数据准备阶段。MOD35 对大面积积雪区域会有系统性误判,拿到后抽 30 个块做人工质检:把掩膜叠加到红外亮温图上,看晴空区域亮温是否平滑、云边缘是否连续。质检中发现标签把积雪或地表热异常标成云,就移到人工重标集合里。

提示:最容易翻车的是坐标对齐。影像与标签来自不同产品时,先比较经纬度网格,用最近邻插值把标签重采样到影像网格,不要按行列号直接对齐。

3. 规则基线:阈值云检测、GLCM纹理与SVM分类的python源码

3.1 为什么先做一个规则基线

计算机视觉大作业的评分通常落在问题理解、方法对比、结果分析三块,先搭建规则基线是给深度模型提供比较下界。如果 UNet 在测试集上只比阈值法高 2 到 3 个点,说明网络没有学到比物理规则更多的东西,实验报告里反而能写清改进点;如果高出一大截,深度方案的价值也更好论证。规则基线还有一个额外作用,它对积雪、沙地、明亮云底这些特殊像元会产生可解释的误判,这些误判样本可以直接补进深度学习训练集。

3.2 双通道阈值云检测:参数与物理前提

把云检测当作像素级二分类时,常用两条经验规则:可见光反射率高于 0.40 判为云,或红外亮温低于 260 K 判为云。白天两个通道用或逻辑合成,夜间只有红外可用。实现如下:

import numpy as np def threshold_cloud(vis, ir11, night=False): # vis : 可见光反射率,已归一化到 [0, 1] # ir11 : 11.2 μm 亮温,单位 K vis_cloud = vis > 0.40 # 云顶反射率高 ir_cloud = ir11 < 260.0 # 云顶比地表冷 return (ir_cloud if night else (vis_cloud | ir_cloud)).astype("uint8")

阈值有物理前提。地表在可见光波段的反射率很少超过 0.30,云顶反射率普遍高于 0.40,因此 0.40 能割开多数晴空像元,但积雪和沙漠会被连带判成云;中纬度地表亮温夏季多在 285 K 以上,云顶亮温常在 220-250 K,260 K 能圈出主要云区,但夏季低云和地表温差小,会留下漏检。260 K 和 0.40 都是经验值,不是固定常数。我建议在实验过程中对红外阈值做扫描,比如从 250 K 到 270 K 每 5 K 计算一次 F1,把阈值-精度曲线放进实验报告,这比只报一个最终数字更经得起答辩追问。

规则基线的评估一般用 sklearn 的 classification_report。经验结论通常是阈值法召回率高、精确率低,也就是多标云、少漏云,在气象业务上这种偏向是安全的,但在课程项目里会让误检集中在雪和云底。这个误检分布会成为“深度学习要解决什么问题”的直接论据。

3.3 从检测到分类:GLCM纹理特征加SVM

如果任务要求“识别”而不是单纯“检测”,需要把像元分成晴空、水云、冰云三类。单点阈值无法支撑多分类,替代做法是提取局部纹理特征再喂给 SVM。灰度共生矩阵(GLCM)统计的是特定方向和距离下灰度的共现关系,能刻画云图表面粗糙程度:晴空海洋纹理平坦,冰云边缘锐利高频多,水云处于中间。下面是一个面向像素分类的纹理特征提取实现:

import numpy as np from skimage.feature import graycomatrix, graycoprops def glcm_features(chip, levels=64, dist=1): # chip: 单波段灰度块,先压到 [0, levels-1] lo, hi = chip.min(), chip.max() gray = ((chip - lo) / (hi - lo + 1e-6) * (levels - 1)).astype("uint8") angles = [0, np.pi / 4, np.pi / 2, np.pi * 3 / 4] glcm = graycomatrix(gray, distances=[dist], angles=angles, levels=levels, symmetric=True, normed=True) feats = [] for i in range(4): feats.append(graycoprops(glcm, "contrast")[0, i]) feats.append(graycoprops(glcm, "energy")[0, i]) feats.append(graycoprops(glcm, "homogeneity")[0, i]) return np.hstack(feats) # 4方向 x 3统计量 = 12维

提取后的特征向量可拼上原始亮温与反射率,组成 14 维向量。窗口取 7x7 或 9x9。GLCM 的 levels 参数取 64 一般够用,levels 过大会牺牲统计稳定性,过小会丢失灰度差异。特征标准化用 StandardScaler,亮温和反射率的量纲差异会把纹理项压缩到不可见。分类器选带径向基核的 SVM,C 取 1.0,gamma 取 scale,在训练集上做一次交叉验证微调即可。相关参数可按下表起步:

参数建议值调整方向
levels(量化级)64噪声大时降到 32
窗口大小7x7纹理细密时增到 9x9
SVM C1.0样本少时减小到 0.1 避免过拟合
SVM gammascale特征维度高时保持 default

最终的三分类准确率通常在 85% 上下,水云和冰云会混得比较多。这两个类在 GLCM 空间高度重叠时,正好说明后续网络需要更长的感受野或更强的上下文建模。

4. UNet云识别主力模型:数据读取、损失函数与训练参数

4.1 模型选型:课程作业场景下UNet比HRNet可控

深度方案里我倾向 UNet 而不是 HRNet 这类更复杂的主干。卫星云图样本往往只有几百张 512x512 切片,HRNet 的多分辨率并行结构参数规模大,小数据上容易过拟合;UNet 编码-解码对称,参数适中,空间细节保留好,在单卡上能快速迭代。由于输入通道是自选的可见光与红外组合,预训练权重价值有限,直接用随机初始化训练,在多通道输入和中等数据量下收敛也会很快。这一条在实验报告的方法部分写清楚,能节省大量无意义的调参时间。

4.2 数据加载、增强与切分策略

训练集和验证集按时间切,不按样本块随机切。相邻时刻云场在空间上高度相关,随机切分会让验证集的 mIoU 虚高,答辩时经不起细问。数据增强以几何和亮度变化为主:云场方向不决定类别,翻转旋转可以安全使用;亮度乘性系数控制在 [0.8, 1.2],区间太大会破坏红外亮温与标签的物理关系。增强时图像和标签要使用同一组随机种子,标签采用最近邻插值。

import torch, random from torch.utils.data import Dataset import numpy as np class CloudDataset(Dataset): def __init__(self, img_ids, data_dir): self.img_ids = img_ids self.data_dir = data_dir def __getitem__(self, idx): img_id = self.img_ids[idx] img = np.load(f"{self.data_dir}/{img_id}_img.npy") # C,H,W 多通道 label = np.load(f"{self.data_dir}/{img_id}_label.npy") # H,W # 图像与标签必须用同一个随机种子,保证变换对齐 seed = random.randint(0, 2**32 - 1) random.seed(seed); img = self.geometric(img) random.seed(seed); label = self.geometric(label) return torch.from_numpy(img).float(), torch.from_numpy(label).long()

读到内存里的数组要保持 float32,省显存也避免 CPU-GPU 传输被大量内存拷贝拖累。标签必须 long 类型,CrossEntropy 系列损失不接受 float 形式的类别索引。

4.3 训练代码与损失函数:Dice与Focal按0.5对0.5叠加

多数卫星云图里晴空像素占 70% 以上,直接用交叉熵会让模型倾向把不确定像元全判成晴空。我一般把 Dice 损失和 Focal 损失按 0.5 对 0.5 叠加:Dice 从集合相似度角度抗类别不平衡,Focal 负责纠难分像素。单独使用任一损失都会出现“召回率与精确率失衡”的问题,组合后明显更稳。

import torch.nn.functional as F def dice_loss(logits, mask, eps=1e-6): probs = torch.softmax(logits, dim=1) true = F.one_hot(mask, num_classes=probs.shape[1]).permute(0, 3, 1, 2).float() inter = (probs * true).sum(dim=(2, 3)) union = probs.sum(dim=(2, 3)) + true.sum(dim=(2, 3)) return 1 - (2 * inter + eps) / (union + eps) def focal_loss(logits, mask, gamma=1.5, alpha=0.75): ce = F.cross_entropy(logits, mask, reduction="none") pt = torch.exp(-ce) return (alpha * (1 - pt) ** gamma * ce).mean()

gamma 控制对难样本的关注程度:类别极其不平衡时调到 2.0,否则 1.5 即可;alpha 偏向少数类,二分类云检测场景取 0.75。损失函数内部不用再对 logits 做 softmax,两个函数都按原始 logits 计算。训练主循环 40 行左右就能写完整:每 5 轮在验证集上算一次云类 F1,把训练 loss、验证 loss 和 val-F1 全部记录到一个 csv,方便报告里画曲线。

4.4 训练参数表与收敛判断

下面这组参数在单张消费级显卡上可直接起步,输入 512x512,显存占用约 6-8 GB:

参数取值说明
输入尺寸512 x 512与数据切块保持一致
batch size8显存不足先降 batch,再降输入分辨率
优化器AdamWweight_decay 取 1e-4
学习率1e-4第 40 轮后降到 1e-5
轮数80早停 patience 设为 15 轮
损失权重Dice:Focal = 0.5:0.5不平衡严重时 Focal 提到 0.6
评估指标云类 F1、mIoUOA 在云占比低时没有区分度

训练时看两个迹象:loss 下降但 F1 停滞,先检查标签边界噪声而不是调学习率;Dice 损失震荡剧烈,优先把 batch size 翻倍或降低学习率。推理阶段对输出概率图做一次小后处理:概率阈值取 0.5 得到掩膜后,用连通域分析去掉面积小于 20 个像素的孤立云块。云场的空间相关性很强,孤立点大概率是散射噪声,不是真实云。

5. 实验报告、答辩PPT与python源码交付的写作套路

5.1 实验报告只回答三个问题

实验报告不需要堆满公式。开头三行写清三件事:任务定义(检测还是分类)、数据规模、最终指标。主体按四个板块组织:问题定义、方法对比(规则基线与 UNet)、实验设置(数据划分和训练参数表)、结果分析(指标表加错分样例)。图要带一句结论,不要只贴图没有说明。阈值扫描曲线和混淆矩阵放结果分析里,能同时覆盖“方法对比”和“误差分析”。

5.2 答辩PPT做成10分钟故事线

PPT 控制在 12 页内,结构固定:封面、数据展示(原图加标签各 3 张)、方法路线图(单页全覆盖)、定性结果(原图/标签/预测三通道并列)、定量表格、错分案例 5 张、结论与改进方向。错分案例每张配一句误差类型判断,比如“把高亮积云误判为冰云”,比笼统写“识别不准”更有说服力。

5.3 源码交付前做一次干净环境复现

源码包里的 README 只保留三部分:环境依赖清单、数据目录结构、训练与推理命令。入口只保留 train.py 和 infer.py 两个脚本,其余中间产物不要入库。超参数集中放在 config 常量区,答辩时改一个阈值重新推理很方便。打包前在干净环境执行一次python train.py --epochs 1python infer.py --image xxx.npy,能跑通再提交。答辩现场常见追问是“为什么用 UNet”“类别为什么合并成三类”“如何证明没有过拟合”,把这些问题的答案各准备三句话,比多写十页报告更有用。

提示:阈值扫描曲线是答辩里性价比最高的一张图。把 250 K 到 270 K 的 F1 变化曲线放大放在最后第二页,评审在这张图上停留的时间往往最长。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询