074、超分在遥感图像中的应用:卫星图像增强与目标检测协同
去年接了个遥感项目,甲方给的数据是0.5米分辨率的卫星图,要检测出0.3米以下的小型车辆。第一次跑YOLOv5,召回率只有可怜的0.12,检测框基本都在瞎猜。当时盯着屏幕上的马赛克状车辆,脑子里蹦出的第一个念头就是:得先让图像“看得清”,再谈“找得准”。
这个场景在遥感领域太典型了——卫星受限于轨道高度和传感器物理极限,分辨率永远不够用。超分辨率重建在这里不是炫技,是刚需。
遥感图像超分的特殊性
普通自然图像超分,你关心的是纹理细节是否自然、边缘是否锐利。遥感图像超分,核心诉求完全不同:保持地物几何结构的保真度。一辆车超分后变成两辆车,或者把圆形储油罐变成椭圆形,这种“细节”就是灾难。
我踩过最深的坑是直接用EDSR在遥感数据上训练。EDSR对自然图像效果很好,但遥感图像有大量重复纹理(农田、屋顶)、规则几何结构(道路、建筑),EDSR的残差块容易产生伪影——把农田的纹理误判为建筑边缘,生成出根本不存在的房屋轮廓。
后来换用RCAN(Residual Channel Attention Networks),通道注意力机制对遥感图像更友好。RCAN能自动聚焦到关键频段,对规则纹理的保持比EDSR好一个档次。但RCAN参数量太大,在轨部署时推理速度感人。
实战:RCAN+遥感数据微调
代码实现上,我习惯用PyTorch从零搭RCAN,而不是直接调现成库。原因很简单:遥感数据预处理和自然图像差异太大,现成库的归一化策略往往不适用。
# 这里踩过坑:遥感图像通常是16位TIFF,直接归一化到[0,1]会丢失暗部细节defnormalize_remote(img,percentile=2):# 别这样写:img = (img - img.min()) / (img.max() - img.min())# 遥感图像有大量极暗区域(水体)和极亮区域(云顶),全局归一化会把中间地物压扁low=np.percentile(img,percentile)high=np.percentile(img,100-percentile)img=np.clip(img,low,high)return(img-low)/(high-low+1e-8)RCAN的核心是残差组(Residual Group)和通道注意力。训练时有个关键trick:先用L1损失训50个epoch,再切到感知损失。L1损失保证像素级保真度,感知损失提升视觉质量。直接上感知损失,遥感图像的边缘容易产生振铃效应。
classRCAN(nn.Module):def__init__(self,n_resgroups=10,n_resblocks=20,n_feats=64):super().__init__()# 头部的卷积层,这里用3x3而不是5x5,遥感图像高频信息集中在局部self.head=nn.Conv2d(3,n_feats,3,padding=1)# 残差组,每个组包含多个残差块self.body=nn.Sequential(*[ResidualGroup(n_resblocks,n_feats)for_inrange(n_resgroups)])# 上采样部分,用亚像素卷积而不是反卷积# 反卷积在遥感图像上容易产生棋盘格伪影,亚像素卷积更稳定self.upsample=nn.Sequential(nn.Conv2d(n_feats,n_feats*4,3,padding=1),nn.PixelShuffle(2))训练数据准备上,别直接用公开的遥感数据集(如UC Merced)——分辨率太低,超分效果有限。我自己的做法是:从Google Earth Engine下载0.3米分辨率的WorldView-3图像,下采样到0.6米作为LR,原图作为HR。这样构造的配对数据,超分效果在真实场景中泛化最好。
超分与检测的协同设计
很多人把超分和检测做成串行流水线:先超分,再检测。这种做法有个致命问题——超分模型不知道下游检测任务需要什么。超分模型可能把车辆轮廓锐化得过于夸张,导致检测模型误判为其他物体。
我尝试过端到端联合训练,但效果并不理想。遥感图像超分和检测的loss尺度差异太大,超分的L1 loss在0.01量级,检测的classification loss在1量级,直接联合训练,超分部分很快被压制。
最终采用的方案是特征级协同:让超分模型和检测模型共享backbone的前几层,在特征空间进行超分,而不是在像素空间。
# 共享backbone设计,别把超分和检测完全分开classSharedBackbone(nn.Module):def__init__(self):super().__init__()# 前3层共享,提取低级特征self.shared=nn.Sequential(nn.Conv2d(3,64,3,padding=1),nn.ReLU(),nn.Conv2d(64,64,3,padding=1),nn.ReLU(),nn.Conv2d(64,128,3,padding=1,stride=2))# 超分分支:从共享特征重建高分辨率图像self.sr_branch=SRBranch(128)# 检测分支:从共享特征做目标检测self.det_branch=DetectionBranch(128)训练时采用两阶段策略:第一阶段只训练共享backbone+超分分支,第二阶段冻结共享backbone,只训练检测分支。这样既保证了超分质量,又让检测分支在高质量特征上学习。
在轨部署的妥协
卫星上算力有限,FPGA或NPU为主,GPU很少。RCAN的参数量(约15M)在轨部署太奢侈。我做过一个轻量化版本:把残差组从10个压缩到4个,残差块从20个压缩到8个,通道数从64降到32。PSNR从38.2dB降到36.1dB,但推理速度提升了5倍,在Xilinx Zynq上能跑到30fps。
另一个trick是量化感知训练。直接训练FP32模型再量化到INT8,PSNR会掉2-3dB。用QAT(量化感知训练)在训练过程中模拟量化误差,最终INT8模型只掉0.5dB,对检测任务的影响几乎可以忽略。
个人经验
别迷信公开数据集。遥感图像超分的数据分布和自然图像差异巨大,自己构造配对数据是绕不开的。花一周时间在GEE上爬数据,比花一个月调模型参数更有价值。
检测任务才是最终评价标准。PSNR高不代表检测效果好。我见过PSNR 40dB的超分结果,检测mAP反而比PSNR 38dB的低——因为超分模型过度锐化产生了虚假纹理。建议在验证集上同时监控PSNR和检测mAP,以mAP为最终优化目标。
多尺度训练是王道。遥感图像中地物尺度差异极大(从几米的车辆到几百米的建筑),单一尺度超分效果有限。我采用随机裁剪+多尺度下采样策略,让模型适应2x、3x、4x不同倍率,实际部署时根据目标大小动态选择倍率。
注意云遮挡。遥感图像常有云层遮挡,超分模型会把云层纹理误判为地物细节。一个简单处理:在训练数据中加入云层掩码,让模型学习忽略遮挡区域。效果立竿见影。
别忽略后处理。超分后的图像直接送检测,效果往往不如加一个轻量的去噪模块。我习惯在超分输出后接一个3x3的引导滤波,能有效抑制伪影,对检测mAP提升约2-3个百分点。
这个方向还在快速演进,最近Transformer-based的超分模型(如SwinIR)在遥感图像上表现不错,但推理速度是瓶颈。如果你有在轨部署需求,建议还是以CNN为主,Transformer作为离线增强手段。