基于QGIS二次开发的遥感云检测与地物恢复系统实战解析 简介这套基于Qgis二次开发的遥感图像云检测及地物恢复系统以Python实现面向地理信息系统、数字图像处理与深度学习交叉领域适合作为计算机、遥感等专业毕业设计或课程设计参考。资源包共包含74个文件压缩包大小51.21MB其中以21个Python脚本及对应pyc字节码为主辅以Qt界面设计文件.ui、资源配置.ico/.png/.qrc、遥感影像样例.tif及说明文档.md/.txt目录结构清晰便于按模块检索与二次开发。代码经过完整测试并成功运行集成大气校正、波段合成、图层属性管理等桌面端常用功能可作为汇集多种云检测及去云算法的原型系统帮助读者快速理解Qgis插件开发流程与遥感图像处理思路。目前已有254人学习浏览适合具备一定Python基础、希望深入遥感图像处理或拓展毕设功能的在校学生与开发者。1. 为什么值得拆这套QGIS二次开发毕设云检测只是入口地物恢复才是难点遥感方向的毕设十个有八个做云检测但真正能拿到高分答辩的往往是那些把算法收进完整系统里的人。这套基于 QGIS 二次开发的遥感图像云检测及地物恢复系统用 Python 把大气校正、波段组合、云检测、地物恢复全链路集成到一个桌面端窗口里。它解决的不只是「把云找出来」而是「找完云之后还能把云下面的地物信息还原出来」——这一步才是导师和评审真正看重的地方。适合两类人一类是计算机、地信、遥感相关专业正愁毕设选题的学生另一类是已经能调通 opencv 和 numpy但不知道遥感影像在 QGIS 里怎么落地的从业者。2. 环境搭建与项目导入把 PyQGIS 跑起来整套系统才算落地2.1 三种 QGIS 二次开发姿势插件、独立进程、控制台脚本拿到这套源码后第一件事不是读算法而是想清楚它运行在哪种形态里。QGIS 的 Python 二次开发通常有三条路写插件放进 QGIS 插件目录写独立脚本在系统 Python 环境里跑或者在 QGIS 内置 Python 控制台里执行。这套系统的目录结构很明显走了第二条和第三条的混合路线——main.py是独立入口MyMainWin.py负责构建主窗口同时项目里的QgisUtils工具包又把菜单挂载和图层管理封装成了可复用模块。判断依据在哪看__init__.py和requirements.txt。有requirements.txt说明作者希望你在自己的 Python 环境里装依赖有main.py说明存在一条不依赖 QGIS 界面启动的独立运行路径。这种设计的优点是调试方便PyCharm 里打断点、看变量都比在 QGIS 插件沙箱里舒服得多。缺点是初始化 QGIS 环境这段代码写起来容易栽跟头后面会专门讲。我一般拿到这类项目会先打开README.md但很多毕设的 README 写得极简所以更可靠的办法是直接检查import qgis.core能不能成功。如果报ModuleNotFoundError说明环境根本没接上后面所有代码都白搭。2.2 版本配对是第一道坎QGIS、Python 与 PyQt 的兼容关系PyQGIS 最折磨人的地方在于QGIS 自带的 Python 是经过定制的它内置了qgis.core、qgis.gui这些库还有一套对应版本的 PyQt 绑定。你用系统里单独的 Python 去pip install pyqgis并没有官方支持真正稳妥的玩法是直接用 QGIS 安装目录里那个 Python。常见做法是装 QGIS 3 系列桌面版安装时它会附带一个完整 Python 环境。以 Windows 为例QGIS 安装路径下通常有apps/qgis/python和apps/Python3x两个关键目录前者存放 QGIS 自己的库后者是配套解释器。你在 PyCharm 里新建项目时要把解释器指到apps/Python3x/python.exe而不是系统里另装的 Python不然 import 阶段就会炸。判断版本是否对齐在配置好解释器后跑一段检查代码import sys from qgis.core import Qgis, QgsApplication print(Python:, sys.version) print(QGIS:, Qgis.QGIS_VERSION) QgsApplication.setPrefixPath(C:/Program Files/QGIS/apps/qgis, True) qgs QgsApplication([], False) qgs.initQgis() print(PyQGIS init ok)这段代码的逻辑是先打印 Python 和 QGIS 的版本号确认解释器找对了然后调用setPrefixPath告诉 QGIS 核心库去哪个目录找插件、图标和配置文件。最后一个参数True表示使用默认设置在 Windows 下这么写就够了。QgsApplication([], False)创建了一个不带 GUI 的 QGIS 应用实例第二个参数False意味着不需要启动界面事件循环所以它能在后台做栅格计算而不弹窗口。initQgis()负责加载 GDAL、OGR 等数据提供者不调用它的话后面读 TIF 会直接失败。2.3 两步把项目跑通PYTHONPATH 与启动初始化很多人把解释器切到 QGIS 的 Python 之后发现from qgis.core import QgsApplication还是报错。原因是 PyCharm 里虽然解释器换对了但 PYTHONPATH 没有包含 QGIS 的 python 目录。这个环境变量决定了 Python 去哪找qgis这个包必须手动加进去。以 Windows 为例我在 PyCharm 的 Run Configuration 里会加两行环境变量PYTHONPATHC:\Program Files\QGIS\apps\qgis\python;C:\Program Files\QGIS\apps\qgis\bin PATHC:\Program Files\QGIS\apps\qgis\bin;%PATH%第一行是关键它把qgis包和Qt绑定库的所在位置暴露给解释器。第二行是因为 QGIS 的 bin 目录里有gdal.dll等一堆运行时库Windows 加载 DLL 时默认只搜系统路径和当前目录不把 bin 加进 PATH 的话就算 import 通过了实际读取 TIF 时还是会挂在底层库上。加上之后基本就能解决。这套配置方式对这套项目同样有效main.py开头如果能看到QgsApplication初始化代码那它内部逻辑和我们上面写的一致。2.4 项目结构速览拿到压缩包以后先按模块认源码解压后文件很多但真正要掌握的模块就六个。核心算法全在RsAlgorithm里包括AtomosphericThred_landsat8.py、AtomosphericThred_GF1.py、BandcomThred_landsat8.py三个处理线程以及辐射定标参数文件RadiometricCorrectionParameter.json。界面层是根目录的MyMainWin.py和ui文件夹里的对话框文件。资源层有MyRc.qrc和编译后的MyRc_rc.py负责图标和背景图。GMTED2km.tif是 2 公里分辨率的高程模型参与大气校正过程中的地形因子计算。QgisUtils里两个核心工具qgisMenu.py负责往 QGIS 菜单栏挂动作QgisLayerUtils.py负责把栅格和矢量图层加载进图层面板。搞清楚这个结构你就知道改算法去RsAlgorithm改界面去ui和MyMainWin改菜单挂载去QgisUtils不会出现改错文件、怎么跑都没效果的迷惑场景。3. 核心算法模块拆解大气校正、波段组合与云检测的工程化链路3.1 RsAlgorithm 里的三个线程类为什么必须用线程打开RsAlgorithm目录三个以Thread命名的文件是这套系统的发动机。AtomosphericThred_landsat8.py处理 Landsat8 数据的大气校正AtomosphericThred_GF1.py处理高分一号数据BandcomThred_landsat8.py做波段组合。命名里带Thread说明它们不是普通函数而是跑在独立线程里的处理任务。为什么要做成线程因为大气校正是逐像元运算一幅整景 Landsat8 影像有上亿个像元单线程跑起来界面会彻底卡死拖拽地图、点击按钮全部无响应用户会以为程序崩了。用 QThread 把算法扔到后台主线程只负责刷新进度条和接收结果体验完全不一样。这也是毕设答辩时老师爱问的点——「你的系统为什么用多线程」答案就藏在这三个类名里。我通常不把这些线程当黑匣子用而是直接看它的run()方法里调了哪些处理函数。这类毕设的代码习惯往往是run()里先读参数文件再逐波段处理最后发射信号通知界面层刷新。你拿到源码后如果只想改算法逻辑重点盯run()内部就好界面信号和槽的对接不用动。3.2 辐射校正参数文件RadiometricCorrectionParameter.json 决定校正质量RadiometricCorrectionParameter.json这个文件很多人会忽略但它直接影响校正结果。卫星传感器记录的原始 DN 值是无量纲的要变成地表反射率必须先辐射定标再大气校正。定标需要每颗卫星每个传感器的增益、偏移参数这些数据存在这个 JSON 里。不同卫星参数的存放结构略有差异但基本都按传感器分组。以常见格式为例Landsat8 的定标参数大体长这样{ landsat8: { bands: [ {band_id: 2, mult: 2.0e-05, add: -0.1}, {band_id: 3, mult: 2.0e-05, add: -0.1}, {band_id: 4, mult: 2.0e-05, add: -0.1} ] } }这里mult对应元数据文件里的RADIANCE_MULT_BAND_xadd对应RADIANCE_ADD_BAND_x每个波段的辐射亮度通过L mult * DN add计算。我在这里吃过亏有人拿了别的项目的 JSON 直接套用没有核对卫星的传感器类型结果校正出来的影像整体偏移地物反射率明显偏大。正确做法是去 USGS 下载影像时把随图附带的 MTL 元数据文件打开把里面的定标系数和 JSON 里的值逐项对齐。如果只是做毕设演示确认波段号对上、数量级在1e-5附近基本就不会出大问题。3.3 云检测与地物恢复的完整数据链路这套系统的核心链路可以拆成五步辐射定标、大气校正、云检测、地物恢复、波段组合。我按常见工程实现给你拆一下每步对应RsAlgorithm里的一段逻辑import numpy as np def cloud_detect_and_recover(band_paths, dem_path, cloud_thresh0.2): # 1 辐射定标DN 值转辐射亮度 radiances [radiometric_calibrate(band, params) for band in band_paths] # 2 大气校正基于 DEM 估算暗像元逐波段求地表反射率 reflectances [atmospheric_correct(rad, dem_path) for rad in radiances] # 3 云检测可见光高反射 云与地物的光谱差异 cloud_mask detect_cloud(reflectances, cloud_thresh) # 4 地物恢复掩膜内像元用邻域统计值替换边缘羽化 recovered recover_ground(reflectances, cloud_mask) # 5 波段组合按 432 真彩色顺序合成 RGB rgb band_composite(recovered, [3, 2, 1]) return rgb, cloud_mask第一步定标是把原始 DN 变成物理量第二步大气校正消除大气散射和吸收影响这两步做完影像才是真正能用于分析的地表反射率数据。第三步云检测在反射率域做比在 DN 域做可靠得多——因为不同地区、不同时相影像的 DN 值范围差异巨大直接设阈值翻车概率极高。第四步地物恢复是这个项目区别于普通云检测系统的关键它不是把云像元直接置零而是用周围地物的统计特征去填充边缘还要做平滑这样目视效果才不会出现明显色块。最后一步把恢复完的多波段数据按红光、绿光、蓝光顺序合成彩色图[3, 2, 1]是零索引坐标下的 4、3、2 波段。这套链路的价值在于它把遥感处理里最难啃的「又好又快」做到了工程可用。你写论文时这个流程图比任何文字描述都有说服力答辩时评委一看就知道你不是只调了个 OpenCV 库。3.4 GMTED2km 在流程里扮演什么角色项目里那个GMTED2km.tif不是装饰品它在大气校正里承担地表高程输入。大气校正的很多模型都需要知道地形起伏——海拔高了大气路径变短气溶胶散射影响变小。GMTED2010 是 NASA 发布的全球 2 公里分辨率高程数据对毕设场景足够用文件体积又小加载快不会在处理中浪费太多 IO 时间。项目里把它做成单独 tif 文件而不是代码内嵌这个设计是聪明的。做实验的时候你可以换更高分辨率的高程数据来做对比分析论文里就能多一张精度对比表。一般做法是保持文件名不变替换文件内容即可代码里如果写死了GMTED2km.tif这个路径替换文件时别改文件名就行。4. 界面层与业务层拆解ui、qrc、py 三套文件到底怎么协作4.1 MyMainWin 与对话框家族主窗口和各处理对话框的分工这套系统的界面文件分两层根目录的MyMainWin.py是主窗口负责菜单栏、工具栏、图层面板和地图画布的整合ui目录下的LandSat8_AtmosphericDialog.ui、GF1_AtmosphericDialog.ui、LandSat8_BandcomDialog.ui是三个参数对话框分别对应 Landsat8 大气校正、高分一号大气校正、Landsat8 波段组合时的参数输入界面。这种「主窗口 模态对话框」的结构很典型。主窗口不直接调用算法它只是菜单动作的接收器。你点击「大气校正」菜单主窗口弹出对话框对话框确认后创建对应线程线程跑完再通过信号把结果返回。职责边界非常清楚。MyFirstWin.py大概是早期原型后面逐步演化成了MyMainWin.py看名字也能猜出这种演进关系。对于想修改界面的新手我建议只改.ui文件和MyMainWin.py算法部分别动这能把出 bug 的范围控制到最小。4.2 .ui 文件与 .py 文件的同步pyuic5 编译和手工继承的选择项目里既有.ui文件又有同名.py文件说明作者用了pyuic5命令把 Qt Designer 画的界面编译成了 Python 代码。但这两者的关系需要理清楚.ui是 XML 格式的界面描述Python 不认识它.py才是可执行代码。你如果拖拽了界面控件必须重新编译否则改动不生效。编译命令在项目根目录下运行pyuic5 -o ui/LandSat8_AtmosphericDialog.py ui/LandSat8_AtmosphericDialog.ui这条命令把 Designer 生成的.ui文件转换成等价的 Python 类。-o指定输出路径后面是输入文件。编译完还要检查输出文件的 import 语句确保它引用的资源模块名和项目里的MyRc_rc一致。我自己的习惯是尽量不用loadUi运行时加载而是编译成.py。理由很简单毕设答辩论证时你要展示代码编译后的 py 文件逻辑更直白评委一眼能看出你懂界面与业务分离。运行时加载虽然省事但把界面隐藏在 XML 里代码评审观感会打折扣。4.3 MyRc.qrc 资源编译图标空白的根源在这里项目根目录的MyRc.qrc管理着所有图标资源——打开 TIF 的图标、打开 SHP 的图标、大气校正按钮的图标都挂在这个资源系统上。QGIS 和 Qt 应用里资源文件不能直接被 Python 调用必须先编译成模块pyrcc5 -o MyRc_rc.py MyRc.qrcpyrcc5会把MyRc.qrc里列出的所有图片、图标以二进制形式打包进MyRc_rc.py。程序运行的时候PyQt 通过import MyRc_rc拿到图标数据。这里有个新手的坑.qrc里记录的图片路径是相对路径如果图片文件被移动了或者路径中有中文字符编译过程不会报错但运行界面上对应位置就是空白。这套项目里已经带了编译好的MyRc_rc.py正常情况下直接跑没问题。但如果你想把图标换成自己的论文配图记住改完图片后必须重新执行上面的命令。别改完图片只刷新程序那是没用的资源系统的黑匣子就在这里。4.4 QgisUtils 工具类菜单挂载与图层操作的复用点QgisUtils目录下的qgisMenu.py和QgisLayerUtils.py是这整套系统里复用价值最高的两个文件。qgisMenu.py封装了往 QGIS 主窗口菜单栏挂动作的重复代码不用每次新建菜单都写一遍addAction、连接信号、设置图标的样板逻辑。QgisLayerUtils.py则封装了图层操作加载栅格、加载矢量、移除图层、缩放至图层范围。这两个工具类的存在说明作者写代码时有抽象意识——界面层不直接触碰 QGIS API而是通过工具类中转。我维护这个项目时最大的体会是把QgisLayerUtils.py里的load_raster函数单独拿出来可以省掉一半重复劳动。比如你只需要写from QgisUtils.QgisLayerUtils import load_raster layer load_raster(D:/data/LC08_L1TP.tif, landsat8)这个函数内部处理了QgsRasterLayer的创建、有效性检查、添加到地图画布等一堆琐碎动作。返回值是一个QgsRasterLayer对象可以直接参与后续波段计算。比你在每个对话框里重复写十几行 QGIS API 调用干净得多。5. 常见问题与排查这套系统最容易翻车的六个细节5.1 外部 IDE 双击运行必炸import qgis.core 直接 ModuleNotFoundError现象是把你从 PyQGIS 环境切到系统 Python双击main.py控制台报ModuleNotFoundError: No module named qgis.core。原因是 QGIS 自带 Python 环境没有激活系统解释器里根本不存在qgis包这是环境变量问题不是代码问题。解决方法是把 PyCharm 的解释器配置到 QGIS 安装目录下 Python3x 的解释器同时按前面 2.3 节把PYTHONPATH配好。之后在 PyCharm 里每次运行前都会自动带上这些路径不会再犯。5.2 TIF 路径带中文或空格影像加载后是空的现象是图层面板里有这个图层但地图画布上一片漆黑属性表的波段统计全为零。原因是 GDAL 对中文路径和空格的处理在不同版本下表现不一致常见的现象是文件打开了但数据读取失败返回的是空栅格。解决方法是项目工作目录和数据目录全部用英文路径中间不含空格。这是遥感图像处理的通用建议不光是 QGISENVI 和 ArcGIS 在中文路径下也会冒出各种奇怪问题。5.3 界面图标全部空白但程序能正常运行现象是菜单栏和工具栏的按钮文字都在图片位置却是白底。原因是.qrc编译成的MyRc_rc.py没有正确导入或者.qrc里的图片绝对路径已经失效。解决方法是检查主窗口代码里有没有import MyRc_rc没有就补上有的话检查MyRc.qrc里文件名和实际图片文件名是否完全一致改了其中一个没改另一个就会出现这种「能编译但没图」的状态。重新执行pyrcc5 -o MyRc_rc.py MyRc.qrc之后务必重启程序。5.4 大气校正后影像整体偏白或偏黑现象是校正完的影像输出在 QGIS 里打开不是白茫茫一片就是黑乎乎一团地物完全无法分辨。原因是 16 位反射率数据没有做线性拉伸就显示。遥感影像原始 DN 是 0 到 65535 的整型大气校正后变成了浮点反射率取值范围集中在 0.01 到 0.3 之间直接按默认方式显示当然接近全黑。解决方法是输出前把反射率裁剪到 0 到 1 范围然后映射到 0 到 255 的 8 位整型或者保存在 QGIS 里对图层启用对比度拉伸用Percentile stretch把 2% 到 98% 的像元拉伸到全动态范围。5.5 打开大气校正对话框后主窗口卡死现象是点击菜单弹出对话框后QGIS 主窗口立刻失去响应转圈转个不停。原因是算法处理被放在了主线程里同步执行。对话框的确认按钮槽函数里直接调用了大气校正函数这个函数跑完之前界面事件循环被阻塞。解决方法是把处理逻辑放进RsAlgorithm里的线程类中通过信号把处理进度和完成通知发回界面。如果这套项目在某些版本下你发现卡死优先检查是不是在槽函数里直接同步调用了线程的run()方法正确做法是调start()。我自己的习惯是写一个最小复现脚本只加载一幅小尺寸影像测试对话框流程把问题数据范围缩小到交互层别把 500 兆原始影像也一起拖进来。5.6 numpy 版本过高老代码里 np.int 报 AttributeError现象是运行到某个波段计算位置抛出AttributeError: module numpy has no attribute int。原因是 numpy 1.24 以上版本移除了np.int、np.float这些历史别名。老毕设代码里常见这种写法。解决方法是全局搜索.py文件里的np.int、np.float替换成int、float或者np.int64、np.float64。在 PyCharm 里用全局替换一次搞定顺手把np.bool也一起处理掉能省不少事。6. 拿真实影像把链路验证一遍从「能跑」到「能答辩」的最后一公里代码跑通不等于毕设能过你还需要一套验证方法让评委信服这套系统不是玩具。我常用的验证顺序是三步走第一步找一幅 Landsat8 的 L1 级数据裁剪一个小范围区域比如 2000×2000 像元先用它跑通整条链路。小范围数据迭代快出错容易定位别一上来就处理整景影像处理一次十几分钟调试效率太低。第二步固定其他参数不变只调云检测阈值观察不同阈值下云掩膜的变化。这一步能直接展示参数敏感性分析论文里的图表就靠它生成。第三步用目视对比的方法找一幅同一地区无云的影像把恢复结果和无云影像做波段差值统计算出均方根误差。验证过程里有两组参数值得反复调参数推荐范围调大后的效果调小后的效果云检测阈值0.1~0.4掩膜变小漏检增多掩膜变大误检增多地物恢复邻域半径3~9 像元边缘更平滑细节丢失边缘锐利可能残留云斑云检测阈值是这套系统最核心的旋钮。阈值定 0.1 时薄云和雾会被大量圈进掩膜恢复区域很大阈值拉到 0.4只有厚厚的云团能被检测到。毕设答辩时建议做一个三组阈值的对比图清晰展示系统对不同云况的适应能力。地物恢复半径则要结合影像分辨率来定2 公里高程数据参与处理时恢复范围过大容易在影像上拉出补丁感。验证完成后把每一步的中间结果导出成图整理到论文附录里。辐射定标前后的影像对比、大气校正前后的光谱曲线对比、云掩膜叠加图、地物恢复后的局部放大图——这几张图是答辩现场的底气所在。从那以后我每次拿到新的遥感毕设代码都强制自己先跑一遍基线数据、再调阈值、最后做目视验证不等代码完全跑通绝不写论文结论。这套流程帮我避开了很多「程序能跑但结果没法看」的尴尬希望也能帮到你。本文还有配套的精品资源点击获取