ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

图像去雾算法研究系统:基于Django的毕设实战解析

2026/8/29 2:56:37 拓冰建站 浏览量
图像去雾算法研究系统:基于Django的毕设实战解析 简介图像去雾是计算机视觉中的经典难题旨在恢复雾霾天气下低对比度、细节丢失的图像。其核心依赖于大气散射模型通过估计全局大气光与透射率实现清晰化。暗通道先验算法以其简单有效成为主流方法结合导向滤波可优化透射率有效消除块状伪影。在工程实践中如何将算法封装成可用的Web系统是技术落地的重要环节。Django框架凭借其完善的ORM、认证系统及后台管理能力为算法研究系统提供了高效开发路径。本文以图像去雾毕业设计项目为例面向算法原理、系统架构、数据库设计及部署调试等核心环节剖析从算法到工程落地的完整流程。通过前后端协同实现图像上传、算法处理、结果展示与历史记录管理。这种算法与工程结合的项目模式既能体现技术深度又能满足实际应用场景适合作为计算机相关专业毕业设计的参考范本。 图像去雾放在毕业设计题目里怎么看都带着点研究气质但实际上这是一个实践性很强的项目。我毕设做的就是基于 Python、Django、HTML 的图像去雾算法研究系统前后端完整数据落在 MySQL 里除了系统本体还配了说明文档、论文LW和答辩 PPT。这个题目最让我满意的一点是它不是网上随便找个增删改查管理系统充数而是真正有一个算法内核、一个可演示的完整 Web 应用算法能力和工程能力两套东西都有发挥空间。如果你正在纠结毕设选题或者手里刚拿到一个类似的“算法研究系统”源码不知道从哪下手这篇文章会把你可能要踩的坑提前排一遍。我会把选题逻辑、技术栈选择、算法实现、系统设计、部署调试、论文答辩一条条拆开讲按我实际做过来的顺序写内容里涉及的路径、代码、参数都是可以照着用的。1. 项目整体设计与技术选型1.1 这个毕设题目到底在考察什么图像去雾是计算机视觉里的经典方向核心是解决雾霾天气下图像对比度低、细节丢失、颜色偏移的问题。把它做成毕业设计本质上是要求你把“算法研究”和“软件工程”两边都跑通。单纯调一个去雾的函数跑几张图撑不起毕设的工作量单纯做一个上传图片、展示结果的网站又显得没有技术深度。所以题目里“算法研究系统”这几个字很关键它要求你既要理解算法的数学原理又要能把它封装成可以被用户使用的 Web 服务。这套系统的用户流程很简单用户注册登录后上传一张有雾图像系统调用去雾算法处理返回清晰图像同时把处理记录存到数据库里之后可以在历史记录中查看。管理员可以浏览所有用户的操作记录。从表面看是个典型的管理类系统但核心价值全在算法层算法效果好不好直接决定答辩时老师愿不愿意往下追问。还要注意这套完整资料里包含的内容结构说明文档、论文、PPT 一个不少。这意味着除了代码本身你还要把“为什么这么做”想清楚。很多同学代码跑通了但论文写不出来或者答辩讲不明白问题就出在设计阶段没有记录每步决策的理由。我在后面几个小节会把每个技术选择的理由一并讲出来这些内容可以直接迁移到论文的设计章节。1.2 为什么选 Django 而不是 Flask 或 Spring Boot当时选型时我纠结过三套方案Flask、Django、Spring Boot。Spring Boot 是 Java 生态虽然企业里用得很多但和 Python 算法生态衔接不如 Python 后端顺手。图像去雾算法要用到 OpenCV、NumPy、SciPy 这些库算法代码和 Web 后端同语言会省掉大量接口对接工作所以最终锁定 Python 后端。Flask 和 Django 之间我选了 Django理由很直接毕设要的是一个“完整系统”不是 API 演示。Django 自带 Admin 后台、ORM、认证系统、表单处理、模板引擎几乎开箱即用。我不用额外写登录鉴权不用自己拼 SQL后台管理界面还白送一个这在写系统测试和功能说明时能省很多时间。Flask 灵活但它把所有事情都变成“可选项”数据库要自己配 SQLAlchemyAdmin 要自己集成用户认证要自己写等把这些组件都装齐复杂度反而比 Django 更高。还有一个容易被忽视的点Django 项目结构是标准化的论文里描述系统架构时可以直接对照 MTV 模式写目录结构、请求流程、数据流都是固定范式评审老师一看就懂。这对毕设这种需要清楚表达系统设计的场景非常友好。数据库选 MySQL 同样是个稳妥选择。MySQL 是毕设里最常见的数据库老师熟悉、安装资料多、面试也会问到。Django 可以通过配置轻松对接 MySQLORM 会自动生成 SQL不需要手写建表语句。这里我踩过一个坑Django 默认的驱动是 MySQLdb在 Python 3 环境下经常装不上需要改用 PyMySQL 并加一段兼容代码这个在第 5.2 节详细说。1.3 整体架构分层设计让答辩更有底气这套系统的架构我分了四层从上到下依次是展示层、业务层、算法层和数据层。展示层负责 HTML 页面渲染和用户交互业务层处理登录、上传、记录管理等业务逻辑算法层封装暗通道先验去雾、透射率优化等核心算法数据层用 MySQL 存储用户、图片记录、系统配置。分层的核心好处是算法代码和 Web 代码完全解耦我可以独立调试算法不影响 Web 功能如果后面想换更好的去雾算法只需要替换算法层的实现Web 层几乎不用改。从项目目录也能看出这种分层设计image_dehaze_system/ ├── manage.py ├── requirements.txt ├── config/ # Django 项目配置 │ ├── settings.py │ └── urls.py ├── apps/ │ ├── user/ # 用户模块注册、登录、个人中心 │ ├── image/ # 图像处理模块上传、调用算法、历史记录 │ └── admin/ # 后台管理相关 ├── algorithms/ # 算法层和 Web 解耦 │ ├── dark_channel.py # 暗通道先验去雾 │ ├── guided_filter.py # 导向滤波 │ └── metrics.py # PSNR / SSIM 计算 ├── templates/ # HTML 模板 ├── static/ # 前端静态资源 └── docs/ # 说明文档前端部分我直接用了 Django 模板引擎加原生 HTML、CSS、JavaScript没有做前后端分离。原因很简单这个系统的交互复杂度不高模板渲染完全够用不需要额外起一个 Vue 或 React 开发服务器。如果前后端分离还得处理跨域问题、Token 认证、静态资源分离部署对毕设来说属于不必要的复杂度。当然如果你想展示更现代的技术前后端分离可以作为论文里的扩展点提一下我当时在“总结与展望”里就写了这个改进方向。2. 图像去雾算法核心原理与实现2.1 大气散射模型一切去雾算法的起点去雾算法不是玄学它建立在物理模型上。目前主流的去雾方法基本都基于 McCartney 提出的大气散射模型表达式是I(x) J(x)t(x) A(1 - t(x))这个公式里I(x) 是相机接收到的有雾图像也就是输入图像J(x) 是场景辐射度也就是我们要恢复的清晰无雾图像A 是全局大气光代表天空区域的光照强度t(x) 是透射率代表光线穿过雾气到达相机的比例。雾气越浓t(x) 越小晴天无雾t(x) 接近 1。用大白话理解相机看到的像素能量一部分来自场景物体反射光经过衰减后到达相机另一部分是大气光散射进相机的环境光。去雾过程就是已知 I(x)反推 J(x)。但这里有个核心难点式子里的 A 和 t(x) 都是未知的我们只有一个方程和三个未知数这是个病态反问题。所以几乎所有去雾算法的本质都是用某种先验知识把 A 和 t(x) 估计出来再带入公式求解 J(x)。我在论文里画图时特别喜欢用这个模型来展开讲因为它把抽象的“图像增强”变成了具体的“参数估计问题”。老师往往会顺着这个模型问你是怎么估计大气光的你的透射率怎么做到平滑如果你能把这套公式的每一步推导讲清楚答辩的基础就非常稳了。2.2 暗通道先验核心算法与代码实现暗通道先验Dark Channel Prior是何恺明在 2009 年提出的一种统计先验它基于一个观察在户外无雾图像的绝大多数局部区域中至少有一个颜色通道的像素值非常低趋近于零。这个“局部区域里所有像素求最小通道值再在这个区域内求最小值”的结果就是暗通道数学表达式是J_dark(x) min_{c in {r,g,b}} ( min_{y in Ω(x)} J_c(y) )其中 Ω(x) 是以 x 为中心的局部区域c 表示 RGB 三个通道。为什么无雾图像的暗通道趋近于零因为自然界中的物体表面总有一些鲜艳的颜色、阴影或暗色物体比如绿树、红墙、阴影区域它们在某个通道上的值一定很低。而雾气是白色的雾越浓每个通道的值都被抬高暗通道里的最小值就变大。所以暗通道的值直接反映了雾的浓度。基于这个先验可以推导出透射率的估计公式。把大气散射模型两边同时除以 A再取暗通道运算经过一系列推导透射率的估计式为t(x) 1 - ω * min_{c} ( min_{y∈Ω(x)} ( I_c(y) / A_c ) )其中 ω 是一个调节因子保留少量雾气让图像看起来更自然通常取 0.95。大气光 A 的估计方法是先在暗通道图中取亮度最高的前 0.1% 像素再在原图中找这些像素位置上的最大灰度值作为 A。核心算法我用 Python 实现只依赖 OpenCV 和 NumPy代码不长但每一步都很关键import cv2 import numpy as np def dark_channel(img, patch_size15): # 输入: BGR 图像, 0-255 b, g, r cv2.split(img) # 取每个像素的 RGB 三通道最小值 min_channel cv2.min(cv2.min(r, g), b) # 在局部区域内做最小值滤波腐蚀操作 kernel cv2.getStructuringElement(cv2.MORPH_RECT, (patch_size, patch_size)) dark cv2.erode(min_channel, kernel) return dark def estimate_atmosphere(img, dark, ratio0.001): # 暗通道中最亮的前 0.1% 像素位置 h, w dark.shape pixel_count max(int(h * w * ratio), 1) flat_dark dark.ravel() # 取亮度最高的索引 indices np.argsort(flat_dark)[-pixel_count:] # 在原图中找这些位置灰度最高的值作为 A flat_img img.reshape(-1, 3) max_intensity 0 A np.array([255, 255, 255], dtypenp.float32) for idx in indices: intensity np.sum(flat_img[idx]) if intensity max_intensity: max_intensity intensity A flat_img[idx].astype(np.float32) return A def estimate_transmission(img, A, omega0.95, patch_size15): norm_img img.astype(np.float32) / A dark_norm dark_channel(norm_img, patch_size) transmission 1 - omega * dark_norm # 限制透射率范围避免除零 transmission np.clip(transmission, 0.1, 1.0) return transmission def dehaze(img, omega0.95, patch_size15, t00.1): img img.astype(np.float32) dark dark_channel(img, patch_size) A estimate_atmosphere(img, dark) t estimate_transmission(img, A, omega, patch_size) # 恢复无雾图像 result np.zeros_like(img) for c in range(3): result[:, :, c] (img[:, :, c] - A[c]) / np.maximum(t, t0) A[c] return np.clip(result, 0, 255).astype(np.uint8)这段代码是算法层的骨架我把它单独放在algorithms/dark_channel.py里。注意透射率估计那里做了np.clip(t, 0.1, 1.0)这就是 t0 参数目的是防止透射率过小导致恢复时除零放大噪声。如果把 t0 调大比如 0.3结果会更亮更平滑但会保留更多雾感调小到 0.05细节更锐利但噪声也更明显。这个参数在系统设置页里我做了可配置项方便做对比实验。2.3 透射率优化导向滤波解决块状伪影基础暗通道先验有一个知名问题直接用固定窗口最小值滤波估计透射率会产生明显的块状效应恢复出的图像在景深突变处会有光晕halo和梯度不连续。原理在于最小滤波是局部区域内的最值操作它假设区域内透射率恒定但实际图像中物体边缘两侧的深度差异很大透射率是突变的。解决这个问题有两种经典做法一种是何恺明原论文里的软抠图Soft Matting把透射率图作为待优化变量求解一个大型稀疏线性方程组效果最好但速度慢另一种是后来提出的导向滤波Guided Filter用原图灰度图作为导向图对透射率图做边缘保持平滑速度远快于软抠图而且效果非常接近。导向滤波的直觉理解是用一个“结构引导图”通常是原图的灰度版本来指导透射率图的平滑过程。在结构引导图梯度大的地方边缘滤波权重小保留透射率的跳变在梯度平缓的地方滤波权重大把透射率的块状不均匀抹平。这样透射率既平滑又不破坏边缘结构。OpenCV 的扩展模块里已经内置了导向滤波如果你安装的是 opencv-contrib-python可以直接调用cv2.ximgproc.guidedFilter。如果没装扩展模块我用 NumPy 写了一个简化版实现思路是计算局部均值、局部方差和相关系数然后对每个像素做线性模型变换。实际部署时我建议直接用 OpenCV 内置函数输入参数是导向图、输入图、半径和正则化系数def guided_filter(src, transmission, radius40, eps1e-3): # src: 导向图 (单通道灰度) # transmission: 待平滑的透射率图 return cv2.ximgproc.guidedFilter(src, transmission, radius, eps) # 在 dehaze 函数中替换透射率优化部分 gray cv2.cvtColor(img.astype(np.uint8), cv2.COLOR_BGR2GRAY) t_optimized guided_filter(gray, t, radius40, eps1e-3)参数调优是我的血泪经验。radius 控制在 30 到 60 之间比较合适太小保留不了平滑效果太大会把细节抹掉eps 正则化参数控制在 1e-4 到 1e-2 之间越大越平滑但对边缘保留越差。我当时用一组带雾的测试图做了网格搜索最终固定为 radius40、eps1e-3这套参数在大部分户外场景下都稳定。2.4 算法效果评价用数据说话论文里不能只贴图还需要量化评价。我实现了三个指标PSNR峰值信噪比、SSIM结构相似性和的信息熵。PSNR 衡量恢复图像与参考清晰图像的像素误差越高越好SSIM 衡量两幅图像在亮度、对比度、结构上的相似性越接近 1 越好信息熵衡量图像细节丰富程度熵越大细节越多。计算 PSNR 和 SSIM 直接调用 skimage 库from skimage.metrics import peak_signal_noise_ratio, structural_similarity def calc_psnr(original, dehazed): # original / dehazed 都是 0-255 的 uint8 图像 return peak_signal_noise_ratio(original, dehazed) def calc_ssim(original, dehazed): # 需要转灰度或指定 channel_axis return structural_similarity( original, dehazed, channel_axis-1 )注意 skimage 新版本的structural_similarity多了一个channel_axis参数老版本是multichannelTrue版本不同接口差异确实容易坑人我当时在这个地方查了半天文档。对比实验我做了四组原图、直方图均衡化、暗通道先验无优化、暗通道先验加导向滤波。表格放在论文第四章数据如下算法PSNR (dB)SSIM信息熵耗时 (ms)有雾原图15.320.5126.21-直方图均衡化18.450.6236.878暗通道先验原始21.760.7217.12320暗通道先验 导向滤波24.130.8057.45410测试数据集的选取也要在论文里交代清楚我从公开数据集中选了 30 张不同雾浓度的图像包含城市、道路、风景等场景。这里有个实用经验PSNR 和 SSIM 需要参考无雾图像才能计算但真实有雾图像是没有配对的清晰参考图像的。所以做量化对比时要先造合成数据用清晰的图像人工加雾生成有雾图再让算法去恢复这样才有参考标准。人工加雾的方式就是给图像加一层均匀或随距离变化的雾本质上是反着用大气散射模型。这个细节在论文里一定要写清楚否则数据来源经不起推敲。3. 系统核心模块与数据库设计3.1 功能模块拆解登录、上传、处理、历史、管理后台整个系统我划分为五个功能模块用户模块、图像处理模块、历史记录模块、系统管理模块和算法参数模块。用户模块负责注册、登录、会话保持用的是 Django 自带的认证机制安全性和实现成本都比较省。图像处理模块是核心负责接收上传文件、校验格式、调用算法、保存结果。历史记录模块展示当前用户处理过的所有图片方便回看和对比。系统管理模块借助 Django Admin 实现可以查看所有注册用户和处理记录。算法参数模块比较有特色我把暗通道窗口大小、ω、t0、导向滤波的 radius 和 eps 做成了数据库中的可配置项。这样做有两个好处一是管理员可以在后台调节参数生成不同效果直观感受参数对结果的影响二是论文里可以写“系统设计了参数可配置机制支持算法对比实验”这是加分项。页面数量不多但覆盖了完整的用户路径登录页、注册页、首页上传和处理、结果展示页、历史记录页、个人中心页。这些页面用了同一个基础模板和公共导航栏代码复用度很高。3.2 MySQL 表结构设计从需求到建表数据库设计这块我把它当成论文里“系统详细设计”章节的重要素材。Django ORM 让我不用手写 SQL但表结构的逻辑必须自己理清楚。整个系统一共有三张核心业务表用户表Django 默认的 auth_user 扩展、图像记录表和算法配置表。图像记录表是核心我用 Django 的 models.py 定义如下from django.db import models from django.contrib.auth.models import User class ImageRecord(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE, related_namerecords) original_image models.ImageField(upload_touploads/%Y%m%d/) result_image models.ImageField(upload_toresults/%Y%m%d/, blankTrue, nullTrue) transmission_image models.ImageField(upload_totransmissions/%Y%m%d/, blankTrue, nullTrue) algorithm models.CharField(max_length50, defaultdark_channel) omega models.FloatField(default0.95) patch_size models.IntegerField(default15) t0 models.FloatField(default0.1) processing_time models.FloatField(blankTrue, nullTrue) # 单位秒 created_at models.DateTimeField(auto_now_addTrue) class Meta: ordering [-created_at] def __str__(self): return f{self.user.username} - {self.id} - {self.created_at}设计这张表时我刻意把算法参数冗余存储进来而不是只存一个 algorithm 字段。原因是算法处理结果和参数强相关同一张原图在不同 ω、t0 参数下效果不同。如果发现某次结果异常可以通过历史记录精确还原当时的环境对论文中的实验分析非常有帮助。这个设计在答辩时被老师专门夸过说考虑到了实验可复现性。上面这些字段在数据库中对应的是auth_user、image_record、algorithm_config三张表。Django 迁移时会自动生成CREATE TABLE image_record ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, original_image VARCHAR(100) NOT NULL, result_image VARCHAR(100), transmission_image VARCHAR(100), algorithm VARCHAR(50), omega DOUBLE, patch_size INT, t0 DOUBLE, processing_time DOUBLE, created_at DATETIME(6) NOT NULL, CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES auth_user(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;字符集一定要用 utf8mb4。我之前用默认的 latin1 建库存中文字段时乱码排查了很久才发现是建库时没有指定字符集。Django 的 settings.py 里可以通过OPTIONS传入 charset 参数避免这个问题。3.3 文件存储策略与静态资源配置图像文件有两种存法直接存数据库 BLOB 字段或者存文件系统、数据库只存路径。BLOB 方式在文件数量少时似乎省事但数据库会迅速膨胀备份和迁移都很痛苦读取时还要多一次序列化和反序列化。我选择的是存文件系统路径Django 的 ImageField 自动管理文件路径数据库只保存相对路径字符串。在 settings.py 中配置MEDIA_URL /media/ MEDIA_ROOT BASE_DIR / media STATIC_URL /static/ STATICFILES_DIRS [BASE_DIR / static]开发环境下要能访问上传的媒体文件需要在 urls.py 里加一行from django.conf import settings from django.conf.urls.static import static urlpatterns [...] static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)这里有个很容易被忽略的小坑项目上线或把 DEBUG 关闭后Django 默认不再提供静态文件和媒体文件服务所有图片都会 404。解决方法是生产环境用 Nginx 或白噪声库whitenoise来服务静态文件。毕设演示一般在本地用 DEBUGTrue 模式所以我当时直接把配置文件说明写进了文档提醒使用者在 DEBUG 模式之外要注意这个问题。4. Django 前后端交互与实现细节4.1 MTV 模式下视图与路由怎么组织Django 的 MTV 模式里视图函数是请求入口。我按功能拆分了 url 路径所有路由都设计成语义化地址既有层次感也方便论文描述/login → user.views.login_view /register → user.views.register_view /logout → user.views.logout_view / → image.views.index /upload → image.views.upload /result/id → image.views.result_detail /history → image.views.history上传处理的核心视图逻辑是这样的先判断用户是否登录未登录就重定向到登录页再校验表单和文件类型然后保存 ImageRecord接着调用算法层处理最后把结果路径写回记录重定向到结果页。我贴一下处理逻辑的核心部分from django.contrib.auth.decorators import login_required from django.shortcuts import render, redirect from .models import ImageRecord from .forms import UploadForm from algorithms.dark_channel import dehaze import cv2, time, os from django.conf import settings login_required def upload(request): if request.method POST: form UploadForm(request.POST, request.FILES) if form.is_valid(): record form.save(commitFalse) record.user request.user record.save() # 调用算法层 img cv2.imread(record.original_image.path) start time.time() result dehaze(img, omegarecord.omega, patch_sizerecord.patch_size, t0record.t0) elapsed time.time() - start # 保存结果图 result_name fresult_{record.id}.jpg result_rel_path fresults/{record.created_at:%Y%m%d}/{result_name} result_abs_dir os.path.join(settings.MEDIA_ROOT, fresults/{record.created_at:%Y%m%d}) os.makedirs(result_abs_dir, exist_okTrue) cv2.imwrite(os.path.join(settings.MEDIA_ROOT, result_rel_path), result) record.result_image result_rel_path record.processing_time elapsed record.save() return redirect(result_detail, record_idrecord.id) else: form UploadForm() return render(request, image/upload.html, {form: form})这段代码有几个细节值得提。login_required装饰器直接帮忙拦截未登录请求不用自己写 session 判断。commitFalse先不提交数据库等把 user 字段赋值后再保存这是 Django 表单最常见的扩展姿势。算法调用和结果图保存放在视图里的写法对毕设来说完全够用但如果并发量上来同步处理会阻塞请求我后续在 4.3 节讨论了改进方案。4.2 前端页面实现模板继承、表单和异步上传前端页面我用 Django 模板引擎加原生 HTML/CSS/JavaScript。为了不重复写导航栏用了模板继承base.html 里写公共框架抽出来几个 block 给子页面填充内容。!-- templates/base.html -- !DOCTYPE html html langzh-cn head meta charsetutf-8 title{% block title %}图像去雾系统{% endblock %}/title link relstylesheet href{% static css/style.css %} /head body nav...导航栏.../nav main {% block content %}{% endblock %} /main script src{% static js/main.js %}/script {% block extra_js %}{% endblock %} /body /html上传页面我做了两个版本。第一版是普通表单提交上传后整个页面跳转等待后端处理大图可能十几秒没反应体验很差。第二版改成了 JavaScript 异步上传用户选择图片后前端先用 FileReader 预览原图再通过 FormData 对象以 AJAX 方式提交到后端等待过程中显示加载动画后端返回处理结果后局部刷新页面显示对比效果。第二版本体验好很多而且演示时更有观赏性。前端 JavaScript 里要注意处理 CSRF TokenDjango 的表单提交默认要校验这个 token。用原生 JS 异步提交时可以从 cookie 中读取 csrftoken 并添加到请求头function getCookie(name) { let cookieValue null; if (document.cookie document.cookie ! ) { document.cookie.split(;).forEach(c { const m c.trim().split(); if (m[0] name) cookieValue decodeURIComponent(m[1]); }); } return cookieValue; } const formData new FormData(); formData.append(image, fileInput.files[0]); fetch(/upload/, { method: POST, headers: { X-CSRFToken: getCookie(csrftoken) }, body: formData }) .then(res res.json()) .then(data { if (data.success) { document.getElementById(result-img).src data.result_url; } });这里有个小坑fetch 请求里不要手动设Content-Type: multipart/form-data浏览器会自动生成带 boundary 的请求头手动设置反而会导致后端解析不到文件。4.3 异步任务与性能优化当算法耗时偏长时同步处理会让用户一直等。我测试过暗通道先验加导向滤波处理一张 1200×800 的图像耗时约 400 到 500 毫秒单用户场景下完全能接受但如果在同一秒内多个用户上传请求会排队响应时间直线上升。针对这个场景论文里我提了两个优化方向第一个是在视图层做线程池或进程池把算法调用放到线程中执行请求先返回“处理中”状态前端轮询进度第二个是引入 Celery 任务队列把算法调用变成异步任务后端返回 task_id前端定时查询任务状态完成后获取结果。Celery 在小项目里有点重但作为扩展点写进论文是很好的加分项说明你意识到了同步阻塞的问题并知道行业解决方案。页面性能优化方面我做了三件事给历史记录列表的分页查询加数据库索引给上传后的图片做类型转换统一存成 JPEG控制文件大小在静态资源引用上开启 Django 的压缩能力减少重复加载。这些优化点放在系统测试章节里描述可以让“性能测试”那部分内容更丰满。5. 项目部署与调试记录5.1 本地环境搭建与项目运行步骤拿到项目源码后先在本地跑起来是第一关。环境总体是Windows 10/11 或 Linux 都可以Python 3.8 以上Django 3.2 或 4.xMySQL 5.7 或 8.0依赖库见 requirements.txt。我按顺序把步骤列一下每一步都标注了可能踩坑的地方。第一步安装 Python 和虚拟环境。Windows 用户要注意勾选“Add Python to PATH”否则命令行找不到 python。建议用 venv 建虚拟环境避免和系统环境冲突python -m venv venv # Windows 激活 venv\Scripts\activate # Linux/macOS 激活 source venv/bin/activate第二步安装依赖。requirements.txt 内容大致是 Django、mysqlclient 或 PyMySQL、opencv-python、opencv-contrib-python、numpy、scikit-image、pillow。特别要注意 opencv-python 和 opencv-contrib-python 不能同时安装两个 base 版本否则会冲突我是只装了 opencv-contrib-python 这一个包它里面已经包含了 guidedFilter 函数。第三步配置数据库连接。在 settings.py 中把 DATABASES 改成 MySQL 配置import pymysql pymysql.install_as_MySQLdb() DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: image_dehaze, USER: root, PASSWORD: your_password, HOST: 127.0.0.1, PORT: 3306, OPTIONS: { charset: utf8mb4, }, } }这里pymysql.install_as_MySQLdb()就是解决 Django 默认找不到 MySQLdb 的兼容方案。如果直接使用 mysqlclient 包这一行就不需要。我在 Windows 上遇到过 mysqlclient 编译失败的问题最后果断改用 PyMySQL两分钟就通了。第四步创建数据库并迁移mysql -u root -p CREATE DATABASE image_dehaze DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; exit python manage.py makemigrations python manage.py migrate python manage.py createsuperuser第五步启动开发服务器python manage.py runserver浏览器打开 http://127.0.0.1:8000 就能看到系统首页。5.2 常见报错与避坑指南这个项目从零搭到完全跑通我记录了不少报错挑几个典型的放在这里每条都是真实记录。比如迁移时提示django.db.utils.OperationalError: (1045, Access denied for user ...)这种就是数据库账号密码错误或者 MySQL 用户权限没给够需要确认 root 密码和远程访问权限。还有上传图片时报SuspiciousFileOperation: The joined path ... is located outside of the base path component这通常是 MEDIA_ROOT 配置不对Django 对上传路径做了安全检查不允许跳出自定义目录。解决办法是确保 upload_to 参数和 MEDIA_ROOT 拼接后路径合法而且 MEDIA_ROOT 要配置成绝对路径或基于 BASE_DIR 的稳定路径。还有去雾结果图特别暗、像蒙了一层灰的问题这个不是代码 bug而是参数没调好。透射率的下界 t0 设得太小恢复时会把暗部噪声放大大气光 A 如果估计偏高整体会偏暗。建议先用默认参数跑测试确认流程没问题后再做参数网格搜索。我把核心问题整理成一张速查表问题现象可能原因解决方案migrate 失败提示数据库连接拒绝MySQL 服务未启动 / 密码错误 / 端口不对确认 MySQL 服务运行检查 settings 配置上传图片时提示路径非法MEDIA_ROOT 配置不正确用BASE_DIR / media拼接路径静态文件加载不出来DEBUGFalse 或 static 目录配置缺失开发环境保持 DEBUGTrue或配置静态文件服务前端异步上传一直 403CSRF Token 缺失在 fetch 请求头中加 X-CSRFToken去雾结果出现明显分块透射率未做平滑优化调用导向滤波radius 调到 30~60图像颜色明显偏移大气光估计不准确检查暗通道图中前 0.1% 像素取点逻辑运行 pip install opencv-contrib-python 后 import cv2 报错与 opencv-python 包冲突卸载两个包只保留 opencv-contrib-python还有一个非常实用的建议在整个项目里规范日志输出。每次算法处理都打印模型参数、图像尺寸、耗时这样出了问题能立刻定位是算法层还是 Web 层。我在algorithms/里加了一个简单的log_processing函数用 Python 的 logging 模块把每次处理的信息写入日志文件。这个习惯在调试时省了太多时间。5.3 数据库和文件备份方案毕设项目数据量不大但数据库和媒体文件备份依然要做。我写了一个简单的备份脚本导出数据库内容并同时 copy 整个 media 目录。这样万一系统崩了或论文需要对比不同参数下的结果图都能从备份中找回来。做法是在项目根目录建backup.sh#!/bin/bash date_str$(date %Y%m%d_%H%M%S) mkdir -p backup/$date_str mysqldump -uroot -p你的密码 image_dehaze backup/$date_str/db.sql cp -r media backup/$date_str/Windows 用户可以用同样的逻辑写一个.bat脚本。这是小事但很多时候你会在最后一个月要补实验数据时发现早先的测试结果图找不到了那时候有备份就会非常庆幸。6. 毕业设计论文与答辩准备6.1 论文结构从算法到系统的完整叙事论文结构是我花了不少时间打磨的。很多人写毕设论文像记流水账但其实论文的叙事逻辑应该是提出问题分析问题解决问题验证结果。我整篇论文的章节安排如下第一章绪论写课题背景研究意义、国内外研究现状。国内外现状这一节要重点写暗通道先验、基于深度学习的去雾方法如 DehazeNet 和 AOD-Net掌握这些能证明你做过文献调研。第二章相关技术介绍写大气散射模型、暗通道先验的原理、Django 框架、MySQL、HTML 前端技术。这一章是凑工作量最容易的部分但也是答辩老师可能提问的地方尤其是大气散射模型的公式推导必须做到能够现场写出来。第三章算法研究与改进写暗通道先验去雾算法的推导、透射率优化方法、参数分析。这是论文的核心章节我把 2.2 到 2.4 的内容系统化写进去加入了不同参数下透射率图的对比效果。第四章系统设计与实现写需求分析、功能模块设计、数据库设计、关键功能实现。需要放系统架构图、功能模块图、用例图、时序图、ER 图以及最终的页面截图。第五章系统测试写功能测试用例表、性能测试结果、算法效果对比实验。注意每个测试用例都要写清楚输入输出和预期结果不能只说“软件运行正常”。第六章总结与展望。这一段不要长总结已完成的工作指出系统的不足比如算法在天空区域仍存在色彩失真、没有实现实时视频去雾、算法复杂度还有优化空间然后提出后续改进方向。把“深度学习方法”作为未来展望是安全且有学术价值的写法。6.2 答辩演示与高频问题提前准备答辩时系统演示环节容易翻车我的建议是准备多个备用方案。最稳的做法是做一个预先录制的演示视频展示从打开系统、注册、登录、上传图片、算法处理、查看历史记录、后台管理的完整流程到了现场再实时演示一遍。如果现场网络或环境出问题就用视频兜底。其次要提前准备好演示用的测试图片选择雾浓度适中、场景清晰的照片效果对比才明显。切忌现场临时搜索图片你无法预测算法在未知图片上的表现。答辩老师的高频提问我整理过一批回答思路也一并写在说明文档里第一个问题暗通道先验为什么成立哪些情况会失效回答思路统计先验来自对大量户外无雾图像的观察局部区域至少一个通道接近零。失效场景是图像中存在大片天空、白色物体或高亮区域这些区域暗通道值不低会导致透射率估计偏大从而出现色彩失真和光晕。第二个问题大气光 A 是怎么估计的回答思路取暗通道图中最亮的前 0.1% 像素在原图中找这些位置的灰度最大值。要强调这是论文中的经典方法。第三个问题透射率 t 为什么要做优化回答思路最小值滤波导致块状效应和光晕导向滤波利用原图结构引导透射率平滑在边缘处保持跳变在平滑区域去除块状。第四个问题系统为什么用 Django 不用别的回答思路Python 生态契合、自带的认证和 Admin 降低开发量、MTV 结构清晰方便论文描述。第五个问题数据库表为什么这么设计回答思路图像记录表冗余保存算法参数确保实验可复现不同实体拆分到独立表遵循第三范式。这些回答思路不是让你死记硬背而是提醒你在做项目过程中就要带着答辩意识去理解每个技术决策。能讲出“为什么”比“做了什么”重要得多。这个项目做完我最大的体会是毕设的价值不在于技术多前沿而在于你能否把一个完整的事情从算法原理到系统实现讲清楚。图像去雾这个方向正好踩在“有一点研究深度又有明确的工程落地场景”这个平衡点上。算法部分你可以在论文里推导公式、做实验、分析参数系统部分又能展示完整的 Web 开发能力一套源码覆盖两方面的考核点。最后分享一个非常实用的小技巧把每个关键算法函数都写一个独立的命令行接口比如python algorithms/dark_channel.py --image test.jpg --omega 0.95这样在调试算法时不用每次启动 Web 服务处理结果直接输出到磁盘跑批量实验方便得多。这套系统的扩展空间也很大你可以沿着深度学习去雾的方向替换算法层也可以把系统改成前后端分离还可以给上传页面加上批处理功能。做完一版再改一版是真正能学到东西的路径。本文还有配套的精品资源点击获取