暗通道先验+Django:图像去雾系统从算法到部署实践

发布时间:2026/10/6 17:34:48
暗通道先验+Django:图像去雾系统从算法到部署实践 简介面向计算机视觉与Web开发方向的毕业设计这份Python-Django图像去雾算法研究系统源码包提供了一套包含前后端与数据库的完整可运行项目。系统基于Django框架与HTML技术构建支持用户上传含雾图片并实时展示去雾结果算法层面涵盖暗通道先验、Retinex理论以及物理模型等经典去雾方法。压缩包共250个文件、约48.39MB主要包含37个Python后端源码文件、38个pyc编译文件、35个JavaScript脚本、14个CSS样式表、9个HTML页面以及SQL数据库脚本、PPT演示文稿、Word/Markdown说明文档并附带大量GIF/PNG/JPG图像素材用于界面展示与效果对比前端还集成了Bootstrap、Layui等常用框架。目前已有71人学习下载。项目同时提供详细说明文档与答辩PPT覆盖开发背景、设计思路、算法数学原理、代码关键实现及使用方法适合作为毕业设计参考目录结构清晰便于直接运行体验或基于源码进行二次开发与算法扩展。1. 毕设图像去雾系统这类源码拿到手先别急着装环境图像去雾算法研究系统是毕业设计里很成熟的一类题目输入一张雾图后台跑去雾算法前端页面展示原图和处理图再用 MySQL 把算法参数、处理记录和图片路径持久化。你看到的这份 Python-Django-html 源码本质是把「暗通道先验等去雾算法」从单脚本包装成 Web 系统补齐前后端和数据库后能直接演示、截图、跑对比实验。这类系统适合两类人一是想快速交付的毕设党二是打算往图像处理或 Web 开发方向做课程设计的人。它技术栈不冷门复现成本主要在环境Django MySQL不在算法。2. 去雾算法怎么选先用暗通道先验在本地跑出清晰图2.1 为什么先定算法再决定页面怎么设计拿到这种系统源码第一步不该是打开页面截图而是先确认算法核心能不能跑通。去雾算法常见的路线有三条直方图均衡化、Retinex 系列、暗通道先验。直方图均衡化最简单但对浓雾图容易颜色发灰Retinex 也能去雾调参麻烦做完之后很难跟答辩老师讲清楚“我到底改了什么”。暗通道先验是 2009 年何恺明那篇经典论文的思路原理有明确公式效果直观且推导只要三步暗通道、大气光、透射率非常适合写进毕设论文的算法章节。另外很多毕设题目会要求“多种算法对比”这时候不要一开始就写三套算法。正确顺序是先把暗通道先验做成一个独立的dehaze.py在命令行里验证几张图再接入 Django 视图层。这样后面换 Retinex 或者直方图均衡时只需要在视图里替换一个函数前端和数据库完全不用动。先跑通单函数再谈系统架构能省下大量调试时间。2.2 暗通道先验的三步推导暗通道、大气光、透射率暗通道先验的核心观察是户外无雾清晰图的局部区域里至少有一个颜色通道的亮度很低接近 0。雾天图像因为大气散射暗通道不再“暗”所以我们可以把这个偏差反推回去。估算大气光 A 时通常先求暗通道图再取暗通道中最亮的前 0.1% 像素去原图上找这些位置对应的 RGB 最大值作为全局大气光。透射率 t 表示光线没有被散射衰减的比例公式是 t 1 - ω * 暗通道(I / A) 其中 ω 通常取 0.95目的是保留一点点雾避免恢复图太“假”。最后恢复公式是 J (I - A) / max(t, t0) At0 设为 0.1 防止分母为 0。整个过程不涉及训练、不依赖数据集纯数值计算所以答辩时能现场讲清楚每一步在干什么这是它成为毕设首选算法的根本原因。2.3 最小可运行 dehaze.py三十行代码跑通第一张图装依赖只需要 OpenCV 和 NumPy在项目根目录执行pip install opencv-python numpy然后写dehaze.py这部分可以直接放进后续 Django 应用里复用。import cv2 import numpy as np def dark_channel(img, size15): # img 是 0~1 的浮点图(H, W, 3) b, g, r cv2.split(img) min_ch cv2.min(cv2.min(r, g), b) kernel cv2.getStructuringElement(cv2.MORPH_RECT, (size, size)) return cv2.erode(min_ch, kernel) def estimate_atmosphere(img, dark, percent0.001): h, w img.shape[:2] num max(int(h * w * percent), 1) flat_dark dark.ravel() flat_img img.reshape(-1, 3) index np.argsort(flat_dark)[::-1][:num] return np.max(flat_img[index], axis0) def dehaze(img_bgr, omega0.95, t00.1, size15): norm img_bgr.astype(np.float64) / 255.0 A estimate_atmosphere(norm, dark_channel(norm, size)) norm_A np.clip(A, 1e-3, 1.0) # 注意暗通道要在 I/A 上计算不是直接对 I 算 t 1.0 - omega * dark_channel(norm / norm_A, size) t np.clip(t, t0, 1.0) J np.empty_like(norm) for i in range(3): J[:, :, i] (norm[:, :, i] - A[i]) / t A[i] return np.clip(J * 255, 0, 255).astype(np.uint8) def dehaze_file(src_path, dst_path, omega0.95, t00.1): img cv2.imread(src_path) if img is None: raise ValueError(f图片读不出来: {src_path}) result dehaze(img, omegaomega, t0t0) cv2.imwrite(dst_path, result)逻辑说明dark_channel里先用cv2.split拆三通道逐个取最小值再用cv2.erode做最小值滤波效果等价于在局部窗口里找暗像素。estimate_atmosphere取暗通道最亮像素在原图中的 RGB 最大值np.argsort(...)[::-1]是从大到小排序。dehaze里最容易被忽略的是norm / norm_A这一行保证透射率估算在不同颜色通道上分别做归一化否则恢复图会偏灰。参数说明size15是暗通道窗口大小窗口越大透射率越平滑但也容易出现光晕伪影omega0.95控制保留雾的程度想更通透调到 1.0但天空区域容易过饱和t00.1是透射率下限防止原始代码在t接近 0 的地方出现噪声放大。测试时先用一张带白色天空的雾图如果天空部分出现色块说明size太大或omega太高。2.4 用 PSNR/SSIM 提前验收算法而不是等答辩翻车很多同学只看视觉效果凭“看起来清晰了”就写进论文结果答辩时老师一问“你的去雾效果怎么量化”回答不上来。常见的量化指标是 PSNR 和 SSIM需要一张参考清晰图通常从网上的标准雾图数据集拿例如经典的“纽约雾图集”。在项目里按下面思路写一个快速校验脚本import cv2 from skimage.metrics import peak_signal_noise_ratio, structural_similarity gt cv2.imread(gt/clear.png) out cv2.imread(output/result.png) psnr peak_signal_noise_ratio(gt, out) ssim structural_similarity(gt, out, channel_axis2) print(fPSNR{psnr:.2f} dB, SSIM{ssim:.4f})如果没有真实参考图至少截图保存处理前后的局部细节放大后放进论文作主观对比。流程上建议先跑通dehaze_file生成一张结果图再用这个脚本算指标最后才进 Django。因为网页里上传图片、保存记录、更换路径涉及的变量比命令行多算法问题混在里面会非常难排查。3. Django 搭建 Web 闭环上传、去雾、展示、入库3.1 解压源码后先确认骨架manage.py、app、模板与 staticDjango 项目实战新手最容易犯的错是把整个 zip 解压后不管三七二十一就运行python manage.py runserver然后被一堆依赖报错打懵。正确的接手步骤是先看骨架根目录有没有manage.py有没有requirements.txt有没有一个或多个 app 目录再确认模板和静态文件放哪。先做一次冷启动cd D:\dehaze_project python -m venv venv venv\Scripts\activate pip install django opencv-python numpy pillow pymysql python manage.py migrate python manage.py runserver 8000如果压缩包里自带requirements.txt先执行pip install -r requirements.txt没有就按上面的包列表补。注意一定要用虚拟环境不能用全局 Python否则以后装别的项目依赖会互相污染。启动后浏览器打开http://127.0.0.1:8000如果能看到页面或 API 返回说明基础环境没问题。接下来确认 app。如果源码里已经建好了业务 app就不要执行python manage.py startappDjango 创建 app 的命令很简单但把新 app 注册进settings.py反而容易改出问题。如果确实要新建命令是python manage.py startapp imageapp然后把imageapp加到INSTALLED_APPS。新手建议只看已有 app 的views.py、models.py、urls.py三个文件先理解现有逻辑再决定改动范围。你不需要一开始看懂所有代码只需要找到处理上传的那条路由从它开始往下追。3.2 定义记录表原图、结果图、算法名、参数与创建时间你要在 MySQL 里存的数据通常是每次去雾操作的原图路径、结果图路径、算法类型、关键参数 omega 和 t0以及处理时间。用 Django ORM 定义模型比直接写 SQL 语句方便得多而且后台 admin 可以直接录入和查询。常见的模型写法如下from django.db import models class DehazeRecord(models.Model): origin_image models.ImageField(upload_toorigin/%Y%m%d/) result_image models.ImageField(upload_toresult/%Y%m%d/, blankTrue, nullTrue) algorithm models.CharField(max_length50, defaultdark_channel_prior) omega models.FloatField(default0.95) t0 models.FloatField(default0.1) created_at models.DateTimeField(auto_now_addTrue) class Meta: ordering [-created_at] db_table dehaze_record def __str__(self): return f{self.algorithm} {self.created_at:%Y-%m-%d %H:%M}逻辑说明ImageField需要 Pillow 库它会自动把上传文件写入MEDIA_ROOT对应的目录并在数据库里存相对路径。upload_toorigin/%Y%m%d/意思是按日期分子目录。blankTrue, nullTrue是因为结果图要等算法跑完才写入先创建记录再更新。ordering [-created_at]让 Django admin 默认按时间倒序展示这个排序规则在后面的数据管理里非常实用。模型建好后迁移写进数据库python manage.py makemigrations python manage.py migrate参数说明db_table是可选的用来指定 MySQL 表名不写的话 Django 会根据 app 名和模型名自动生成。auto_now_addTrue表示第一次创建时自动写当前时间不适合做“更新时间”字段。3.3 视图里接住上传文件调用 dehaze 并保存结果视图层是前后端交接的地方。前端上传一张雾图后端要接收文件、保存原图、调用算法、保存结果图、返回展示页面。下面是一段能直接跑通的视图代码import os from django.conf import settings from django.shortcuts import render from .models import DehazeRecord from .dehaze import dehaze_file def upload_and_dehaze(request): if request.method ! POST: return render(request, index.html) image request.FILES.get(image) if not image: return render(request, index.html, {error: 请选择图片}) omega float(request.POST.get(omega, 0.95)) t0 float(request.POST.get(t0, 0.1)) # 先存原图得到数据库里对应的文件路径 record DehazeRecord.objects.create( origin_imageimage, algorithmdark_channel_prior, omegaomega, t0t0, ) src_path record.origin_image.path out_dir os.path.join(settings.MEDIA_ROOT, results) os.makedirs(out_dir, exist_okTrue) # 结果文件名沿用原图文件名有重名风险见第 5.5 节处理 out_name os.path.basename(src_path) out_path os.path.join(out_dir, out_name) dehaze_file(src_path, out_path, omegaomega, t0t0) record.result_image fresults/{out_name} record.save(update_fields[result_image]) return render(request, result.html, {record: record})逻辑说明request.FILES是 Django 对上传文件的封装靠request.FILES.get(image)拿到文件对象文件名、内容都从这里来。DehazeRecord.objects.create(...)会自动把上传图片保存到MEDIA_ROOT/origin/日期/下同时生成一条数据库记录。record.origin_image.path是文件的绝对路径算法处理要读这个路径。处理完成后手动把相对路径写入result_image字段。参数说明settings.MEDIA_ROOT必须提前配好否则文件会跑到默认空路径。update_fields[result_image]表示只更新这个字段能减少一次完整保存的冲突风险。omega和t0从前端表单读HTML 里写两个输入框名字要和这里一致。模板里result.html只要输出两条图片 URL 即可{% if record %} h3原图/h3 img src{{ record.origin_image.url }} alt原图 h3去雾结果/h3 img src{{ record.result_image.url }} alt去雾结果 {% endif %}这里用url而不是手动拼/media/因为图片路径依赖MEDIA_URL配置直接拼硬编码很容易在改目录结构后失效。3.4 模板里把图片路径渲染正确MEDIA_URL 和 {% static %} 的边界HTML 网页制作里最常翻车的是图片 404。Django 对两类文件处理方式不同自己写的index.html、CSS、JS 一般放在static/用{% load static %}和{% static css/style.css %}引用用户上传的图片放在media/用MEDIA_URL映射。很多初学者把上传图放进static然后改后端代码越改越乱。正确配置如下# settings.py MEDIA_URL /media/ MEDIA_ROOT os.path.join(BASE_DIR, media)然后在项目根路由urls.py末尾加上from django.conf import settings from django.conf.urls.static import static urlpatterns [ # 已有路由 ] if settings.DEBUG: urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)逻辑说明static(settings.MEDIA_URL, ...)只在DEBUGTrue时生效这是开发环境的便利写法。当项目部署到服务器并关闭 DEBUG 后静态文件交集要给 Nginx 或云存储Django 自己不处理上传文件的读取。毕业设计现场演示时让 DEBUG 保持 True 没问题但你要在论文里写清楚开发环境与生产环境的差异这是答辩老师喜欢追问的点。参数说明MEDIA_URL的值必须前后都有斜杠/media/。如果改成/files/模板里的record.origin_image.url会自动变成/files/...不需要改模板。上传文件的根目录MEDIA_ROOT建议用绝对路径或基于BASE_DIR拼接不要写死C:\xxx否则换个电脑又得改。4. MySQL 配置与数据操作从建库到 Django 查询删除对象4.1 先在 Windows 把 MySQL 装好字符集选 utf8mb4Windows 10 上安装 MySQL常见做法是用 MySQL Installer 装社区版组件选 MySQL Server版本选 8.0 以上。安装过程注意端口默认 3306、root 密码要记牢服务名通常是 MySQL80。安装完成后打开命令行先确认服务在跑net start MySQL80 mysql -uroot -p进入 MySQL 后先别急着创建业务表把数据库和专用账号准备好CREATE DATABASE dehaze_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER django_userlocalhost IDENTIFIED BY your_password; GRANT ALL PRIVILEGES ON dehaze_db.* TO django_userlocalhost; FLUSH PRIVILEGES;字符集选 utf8mb4 是为了让数据库能存中文算法名、中文路径和说明文档内容不能用默认的 latin1否则插入中文会报Incorrect string value。另建django_user而不是直接用 root是为了避免后面代码里一旦出现 SQL 注入或误操作直接把整个 MySQL 数据目录搞坏。毕设项目不用分太多权限给dehaze_db库全权限就够。4.2 settings.py 连接 MySQLmysqlclient 还是 pymysqlDjango 官方默认的 MySQL 连接驱动是mysqlclient但在 Windows 上安装它经常卡在django.core.exceptions.ImproperlyConfigured或编译报错因为需要 Visual C 14.0 的构建工具。另一个方案是pymysql纯 Python 实现兼容 MySQLdb 接口拿来跑毕设没有问题。推荐组合是pip install pymysql然后在项目同名包的__init__.py里做一次替换。比如项目目录是dehaze_project/dehaze_project/__init__.py加入import pymysql pymysql.install_as_MySQLdb()然后settings.py里把数据库配置换成DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: dehaze_db, USER: django_user, PASSWORD: your_password, HOST: 127.0.0.1, PORT: 3306, OPTIONS: { charset: utf8mb4, autocommit: True, }, } }逻辑说明pymysql.install_as_MySQLdb()会骗过 Django 的数据库后端检查让它以为MySQLdb已经存在。这样ENGINE保持django.db.backends.mysql就可以。HOST写127.0.0.1比localhost更稳因为 Windows 上 localhost 有时被解析到 IPv6 的::1MySQL 服务若只监听 IPv4 就会连接失败。参数说明CHARSET和OPTIONS里的charset要同时保证一致避免 Django 连接层和 MySQL 表字符集不一致。autocommitTrue保证每次写入立即生效避免事务没提交导致数据查不到。如果你在自己电脑上装的是 MySQL 5.7同样可以用这套配置但 Django 4.2 对 MySQL 版本有最低要求建议直接用 MySQL 8.0。4.3 用 admin 和 ORM 管理去雾记录排序、删除、清空测试数据数据库建好后Django admin 是最快的调试入口。在admin.py注册模型就能在后台看到所有去雾历史记录并手动删除错误数据。from django.contrib import admin from .models import DehazeRecord admin.register(DehazeRecord) class DehazeRecordAdmin(admin.ModelAdmin): list_display [id, algorithm, origin_image, result_image, created_at] list_filter [algorithm] ordering [-created_at]创建超级管理员后登录/admin/就能直接看图删记录python manage.py createsuperuser python manage.py runserver 8000如果要在代码里清理数据Django 执行查询-删除对象的标准写法是先用查询集筛出对象再调用 delete# 删除单条 DehazeRecord.objects.get(id3).delete() # 删除所有没有人脸或处理结果为空的记录 DehazeRecord.objects.filter(result_image__isnullTrue).delete() # 清空所有演示数据 DehazeRecord.objects.all().delete() # 按时间倒序取最近 10 条 records DehazeRecord.objects.order_by(-created_at)[:10]逻辑说明filter(...).delete()会返回(受影响行数, 明细字典)可以打印出来确认删了多少条。注意 ORM 删除只影响数据库记录和 ImageField 的字段值不会自动删除media/目录里对应的物理图片文件。如果你要彻底清空磁盘上的图片需要手动删掉media/origin和media/results下的文件否则下次迁移到别的机器会把一堆旧图带过去。5. 避坑图像去雾系统从能跑到不翻车的 5 个检查点5.1 恢复图发灰偏色透射率求错位置现象暗通道先验处理出的结果图整体发灰远景尤其明显像是蒙了一层雾没去掉。原因暗通道先验的透射率公式里必须对I/A求暗通道而不是对原始图求暗通道。写错之后t 的估算在高亮区域严重偏大导致恢复不足。解决检查代码里是否写了dark_channel(norm / norm_A, size)确保 A 先被广播到每个通道再做最小值滤波。同时确认 A 是从原图上对应的最亮像素区域取值而不是直接取暗通道矩阵里像素的颜色值。5.2 页面图片裂MEDIA_URL 配了但模板没拼对现象上传后原图和结果图都能在后台看到记录但页面上的img显示 404。原因很多人在模板里写img srcmedia/{{ record.result_image }}少了{{ ... }}自动生成的完整 URL或者根本没配MEDIA_URL。解决模板里一律用{{ record.origin_image.url }}和{{ record.result_image.url }}不要在 HTML 里手动拼/media/前缀。检查项目根urls.py是否在最外层路由末尾追加了static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)少这一行 Django 不会自动提供媒体文件访问。5.3 migrate 连不上 MySQLPyMySQL 没装或没启用现象执行python manage.py migrate时提示ModuleNotFoundError: No module named MySQLdb或者报Cant connect to MySQL server on 127.0.0.1。原因前者是没装pymysql也没调用install_as_MySQLdb()后者是 MySQL 服务没启动、密码错误、端口不对或者 MySQL 8.0 默认认证插件与旧客户端不兼容。解决先确认服务在跑命令行执行net start MySQL80再确认settings.py的PASSWORD和USER能直接登录最后检查项目__init__.py里有没有pymysql.install_as_MySQLdb()。如果是认证插件问题在 MySQL 里执行ALTER USER django_userlocalhost IDENTIFIED WITH mysql_native_password BY your_password;再刷权限。5.4 大图处理卡死没有先下采样现象小图测试正常一传手机原图浏览器转圈几十秒甚至直接报内存不足。原因暗通道的cv2.erode、np.argsort对超大图非常不友好一张 4000 像素乘 3000 像素的图光暗通道矩阵就有千万级元素Python 循环再一叠加速度会慢到不可接受。解决在dehaze_file入口先判断图片长边是否超过 1280超过就缩到 1280 处理处理完再放大回原始尺寸保存或者在视图层限制上传文件大小例如request.FILES[image].size 5 * 1024 * 1024时直接返回提示。去雾算法按区域处理缩图不会让效果差太多速度却能提升十倍以上。5.5 中文文件名导致上传记录错乱给图片加 UUID现象用户上传雾天风景.jpg这类中文文件名数据库里记录创建成功但结果页图片打不开或结果图和原图互相覆盖。原因浏览器和 Windows 控制台对中文编码处理不一致ImageField 保存文件名时可能被转成%xx而os.path.basename拿到的文件名又和MEDIA_URL里的编码对不上。解决在创建DehazeRecord之前重写文件名使用 UUID 和原扩展名import os from uuid import uuid4 ext os.path.splitext(image.name)[1].lower() image.name f{uuid4().hex}{ext} record DehazeRecord.objects.create(origin_imageimage)这样数据库和磁盘上的文件名都是纯 ASCII不会因为中文编码出问题也避免同名覆盖。处理结果的文件名也可以直接取这个 UUID不再用os.path.basename(src_path)。6. 答辩前必做的三件事批量跑图、指标对比、清空现场最后阶段不建议再改算法而是把现有的东西稳定复现给老师看。我会做三件事先写一个批量跑图脚本把测试目录里的雾图全部处理后输出到test_out避免现场演示时一张一张手动上传浪费时间。import glob import os import cv2 from dehaze import dehaze os.makedirs(test_out, exist_okTrue) for path in glob.glob(test_imgs/*.jpg): img cv2.imread(path) result dehaze(img, omega0.95, t00.1) out_path os.path.join(test_out, os.path.basename(path)) cv2.imwrite(out_path, result) print(fdone: {path} - {out_path})第二步是跑指标。有多张带参考清晰图的样本时计算平均 PSNR 和 SSIM 并做成表格写进 LW 或论文的实验章节。主观对比放三组浓雾、薄雾、夜晚/背光场景每组放原图、结果图、放大细节图。答辩时老师看完图再看到表格就会认为你做了完整验证。说明文档里把环境要求、启动步骤、账号密码、测试样例四件事列清楚PPT 按“问题背景、算法原理、系统设计、结果展示、总结”五页走十分钟讲完绰绰有余。第三件事是清空现场。我会把所有测试记录在 admin 里删除media目录下杂乱结果清掉再用冷启动命令重新跑一次runserver、登录 admin、上传雾图、确认展示页三个动作全部录屏。带过那么多毕设翻车最多的不是算法写不出来而是现场演示时 MySQL 没启动、图片路径不对、只跑通一张图。把这些变成固定检查清单比临时抱佛脚有用得多。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

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

尧图内容编辑团队

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

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

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

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

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

网站改版的5个关键决策

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

获取专属建站方案

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

立即免费咨询