news 2026/10/10 11:17:13

图像去雾算法研究系统:暗通道先验与Django/MySQL全栈实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
图像去雾算法研究系统:暗通道先验与Django/MySQL全栈实现

简介:这是一套面向毕业设计的完整图像去雾算法研究系统,借助Python与Django框架搭建,覆盖前端交互、后端逻辑和MySQL数据库存储,适合计算机相关专业学生进行课程设计或毕业设计参考。压缩包共250个文件,约48.39MB,包含37个Python源码、38个编译后的pyc文件、76个演示动图、35个前端脚本、14个样式表、9个网页文件,以及数据库脚本、答辩演示文稿和详尽的说明文档。系统允许用户上传含雾图片并实时观察去雾效果,后端可集成暗通道先验、基于Retinex理论等多种常见算法,便于开展算法对比与效果调试。资源目录呈现清晰,已具备bootstrap、layui等前端框架文件,目前已有71人学习使用。借助附带说明文档与PPT,可帮助理解系统架构、模块划分、算法数学原理和关键代码实现,对图像去雾应用的二次开发、功能扩展或论文撰写具有较高参考价值。

1. 图像去雾算法研究系统:毕业设计里最容易被问崩的一个点

图像去雾算法研究系统,说白了就是把一张带雾的图片上传到网页,后端跑一遍去雾算法,再把清晰结果返回给浏览器,同时把处理前后的图片、处理时间、算法参数存进数据库,供用户随时回看。很多人在答辩时被问崩,不是因为算法跑不出来,而是被问到“你的去雾原理是什么”“数据库为什么这么设计”“图片存哪里了”时答不上来。这篇笔记把整条链路拆开讲——暗通道先验算法怎么落地成 Python 代码、Django 怎么接图片上传、MySQL 表怎么建、哪几个坑是毕业设计必踩的。适合正在做毕设、想把“算法 + Web 系统”做成一套完整交付物的人。

2. 暗通道先验去雾:核心算法怎么落地成可复现的代码

2.1 为什么毕业设计首选暗通道先验

图像去雾算法有很多条路线,基于深度学习的 AOD-Net、DehazeNet 效果确实好,但普通本科毕设跑深度学习模型要面对数据集标注、GPU 资源、训练时长这些问题,周期拉长,风险变大。传统方法里,暗通道先验(Dark Channel Prior)是何恺明 2009 年提出的经典工作,效果肉眼可见,公式推导能讲清楚,实现代码不超过 50 行,不用 GPU,普通笔记本就能跑——这三个特点让它成为毕业设计里性价比最高的选择。

暗通道先验的核心观察是:在无雾的清晰户外图像里,绝大多数局部区域(除了天空区域)的 RGB 三个通道里,至少有一个通道的像素值非常低,接近 0。这个“局部区域最低通道值”构成的图,就是暗通道。而有雾图像因为大气光散射,暗通道值会被抬高。基于这个差异,可以从单张有雾图反推透射率和大气光,进而恢复清晰图像。

2.2 最小可复现的暗通道去雾代码

我一般会把算法单独拆成一个dehaze.py文件,不混进 Django 的 views 里,这样以后换算法、调参、写单元测试都方便。下面是完整可跑的最小实现:

import cv2 import numpy as np def dark_channel(img, size=15): """计算暗通道图 :param img: BGR 图像,0-255 :param size: 最小值滤波窗口大小 :return: 暗通道图,单通道 """ b, g, r = cv2.split(img) min_channel = cv2.min(cv2.min(r, g), b) # 逐像素取三通道最小值 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (size, size)) dark = cv2.erode(min_channel, kernel) # 最小值滤波相当于腐蚀 return dark def estimate_atmospheric_light(img, dark, top_percent=0.001): """估计全局大气光 A 值 选取暗通道中最亮的前 0.1% 像素,取原图中这些像素的最大值作为 A """ h, w = dark.shape num_pixels = h * w top_num = max(1, int(num_pixels * top_percent)) dark_flat = dark.ravel() img_flat = img.reshape(-1, 3) indices = np.argsort(dark_flat)[-top_num:] # 暗通道最亮的像素下标 atmospheric = np.max(img_flat[indices], axis=0) # 对应原图中的最大值 return atmospheric.astype(np.float64) def transmission_estimate(img, A, omega=0.95, size=15): """估计透射率 t(x)""" normalized = img.astype(np.float64) / A dark_normalized = dark_channel(normalized, size).astype(np.float64) t = 1.0 - omega * dark_normalized return np.clip(t, 0.1, 1.0) # 下限 0.1 防止分母过小导致噪声放大 def guided_filter(guide, src, radius=40, eps=1e-3): """导向滤波,细化透射率图,避免边缘 halo 效应""" guide = guide.astype(np.float64) src = src.astype(np.float64) mean_i = cv2.boxFilter(guide, ddepth=-1, ksize=(radius, radius)) mean_p = cv2.boxFilter(src, ddepth=-1, ksize=(radius, radius)) mean_ip = cv2.boxFilter(guide * src, ddepth=-1, ksize=(radius, radius)) cov_ip = mean_ip - mean_i * mean_p mean_ii = cv2.boxFilter(guide * guide, ddepth=-1, ksize=(radius, radius)) var_i = mean_ii - mean_i * mean_i a = cov_ip / (var_i + eps) b = mean_p - a * mean_i mean_a = cv2.boxFilter(a, ddepth=-1, ksize=(radius, radius)) mean_b = cv2.boxFilter(b, ddepth=-1, ksize=(radius, radius)) return mean_a * guide + mean_b def dehaze(img, omega=0.95, window_size=15): """主流程:输入 BGR 雾图,返回去雾图""" img = img.astype(np.float64) dark = dark_channel(img, window_size) A = estimate_atmospheric_light(img, dark) t = transmission_estimate(img, A, omega, window_size) # 以原图灰度图作为导向图,细化透射率 gray = cv2.cvtColor(img.astype(np.uint8), cv2.COLOR_BGR2GRAY) t_refined = guided_filter(gray, t) # 恢复公式 J = (I - A) / t + A t_stack = np.stack([t_refined] * 3, axis=-1) result = (img - A) / t_stack + A return np.clip(result, 0, 255).astype(np.uint8)

这段代码的逻辑链很直白:先算暗通道图,从暗通道最亮的 0.1% 像素里估计出大气光 A;再用 A 归一化图像,算出透射率 t;导向滤波把 t 细化一遍,避免物体边缘出现白色光晕;最后套恢复公式得到清晰图。

三个参数要记住:omega控制去雾强度,0.85 到 0.95 之间常用,调太高画面偏暗、颜色失真;window_size是暗通道的最小值滤波窗口,15 是论文默认值,窗口越大暗通道越“粗糙”,天空区域容易过曝;导向滤波的radius=40控制边缘保真度,图像分辨率超过 2000 像素时建议调到 60。

2.3 批量测试工具:拿真实雾图验证效果

算法写完了,单张图看不出好坏,我习惯写一个批量脚本,把测试目录里的雾图全部跑一遍,输出结果图和一个简单的 CSV 评估记录。这样答辩时能直接展示“我测了 50 张图,平均处理时间多少秒”——这比口头说“效果不错”有说服力得多。

import os import time import csv import cv2 from dehaze import dehaze input_dir = "test_foggy" output_dir = "test_result" os.makedirs(output_dir, exist_ok=True) records = [] for name in os.listdir(input_dir): path = os.path.join(input_dir, name) img = cv2.imread(path) if img is None: continue start = time.time() result = dehaze(img) elapsed = time.time() - start out_path = os.path.join(output_dir, name) cv2.imwrite(out_path, result) records.append([name, f"{elapsed:.3f}s", img.shape[1], img.shape[0]]) print(f"{name} 完成,耗时 {elapsed:.3f}s") with open("eval_results.csv", "w", newline="") as f: writer = csv.writer(f) writer.writerow(["文件名", "耗时", "宽度", "高度"]) writer.writerows(records)

跑批量之前先确认输入目录里没有中文文件名,OpenCV 的imread遇到中文路径会静默返回None,程序不报错但会跳过图片。另外,如果测试图里有纯白天空占比很大的图,暗通道先验会把天空区域错误增强成灰黑色块,这不是代码 bug,而是算法本身的局限,答辩时被问到就如实说“暗通道先验在天空区域的先验假设不成立,可以加分割或亮通道补偿”。

3. Django 后端与图片上传链路:把一张雾图从浏览器送到算法再返回

3.1 创建项目与 App 划分:算法、业务和用户分开

拿到一套 Django 项目源码,第一件事不是急着跑,而是先看目录结构。标准做法是:一个项目放全局配置,两个 App 分别管用户与业务、管算法调用。我自己的习惯是拆成account和dehaze_app,account管登录注册和用户信息,dehaze_app管图片上传、去雾任务记录、历史列表展示。这样后面加“管理员审核图片”或者“给不同用户分配不同算法权限”时,不需要动全局配置文件。

django-admin startproject dehaze_system cd dehaze_system python manage.py startapp dehaze_app python manage.py startapp account

创建完 App 后,需要去settings.py的INSTALLED_APPS里把dehaze_app和account加进去,同时配置数据库连接和静态文件目录。这一步漏掉是新手最常见的翻车点——App 没注册,跑makemigrations时系统根本找不到模型。

3.2 视图层怎么接文件、调算法、写记录

核心视图只需要两个:一个处理上传和去雾,一个展示历史记录。上传视图的职责是:接收request.FILES里的图片 → 校验格式 → 存一份原图到media目录 → 调用dehaze()处理 → 把结果图也落盘 → 把任务记录写进 MySQL → 跳转到结果页。

import os import uuid from datetime import datetime from django.conf import settings from django.shortcuts import render, redirect from django.contrib.auth.decorators import login_required from .models import DehazeTask from .dehaze import dehaze import cv2 @login_required def upload_and_dehaze(request): if request.method == "POST": image_file = request.FILES.get("image") if not image_file: return render(request, "upload.html", {"error": "请选择图片"}) # 1. 校验扩展名,防止上传非图片文件 ext = os.path.splitext(image_file.name)[1].lower() if ext not in (".jpg", ".jpeg", ".png", ".bmp"): return render(request, "upload.html", {"error": "仅支持 jpg/png/bmp 图片"}) # 2. 保存原图到 media/origin/ 下,文件名用 uuid 避免冲突 origin_path = os.path.join(settings.MEDIA_ROOT, "origin") os.makedirs(origin_path, exist_ok=True) unique_name = f"{uuid.uuid4().hex}{ext}" origin_full = os.path.join(origin_path, unique_name) with open(origin_full, "wb") as f: for chunk in image_file.chunks(): f.write(chunk) # 3. 调用算法处理 img = cv2.imread(origin_full) result_img = dehaze(img) # 4. 保存结果图 result_dir = os.path.join(settings.MEDIA_ROOT, "result") os.makedirs(result_dir, exist_ok=True) result_full = os.path.join(result_dir, unique_name) cv2.imwrite(result_full, result_img) # 5. 写 MySQL 记录 task = DehazeTask.objects.create( user=request.user, origin_image=f"origin/{unique_name}", result_image=f"result/{unique_name}", algorithm_name="DarkChannelPrior", created_at=datetime.now(), ) return redirect("result_page", task_id=task.id) return render(request, "upload.html")

要注意两个细节:第一,request.FILES拿到的文件对象必须用chunks()分块写入,不要一次性read()到内存,大图会被撑爆;第二,cv2.imwrite写中文路径会失败,所以文件名全部改用uuid,彻底绕开编码问题。settings.MEDIA_ROOT在settings.py里要配好,同时MEDIA_URL = "/media/"也要设置,否则网页上<img src="...">无法访问到这些文件。

3.3 结果页展示:处理前后对比图

结果页要把原图和去雾图放在一起,配合任务信息展示。这里有个容易踩的坑:Django 模板里引用media文件时,如果MEDIA_URL没配对,图片会 404。模板里比较稳妥的写法是:

{% if task %} <div class="compare-box"> <div class="item"> <h3>原图</h3> <img src="{{ MEDIA_URL }}{{ task.origin_image }}" alt="原图"> </div> <div class="item"> <h3>去雾结果</h3> <img src="{{ MEDIA_URL }}{{ task.result_image }}" alt="去雾结果"> </div> <p>算法:{{ task.algorithm_name }} | 处理时间:{{ task.created_at }}</p> </div> {% endif %}

模板里直接用{{ MEDIA_URL }}的前提是,在settings.py里配好MEDIA_URL并且在urls.py中用static()辅助函数挂载了 media 目录。另一个高频问题是历史记录页面查询出多条记录时,Django 默认懒加载外键会导致 N+1 查询,每显示一条记录就多查一次用户表。这时要用到select_related:

from django.views.generic import ListView from .models import DehazeTask class HistoryView(ListView): model = DehazeTask template_name = "history.html" paginate_by = 10 def get_queryset(self): # select_related 一次 join 查出 user,避免 N+1 查询 return (DehazeTask.objects .filter(user=self.request.user) .select_related("user") .order_by("-created_at"))

这个列表页上的分页器、日期显示、图片缩略图,构成了“完整前后端”里前端最直观的部分。不需要写复杂 JavaScript,Django 模板 + 简单的 CSS 就够一个毕设系统用了。

4. MySQL 数据模型设计:用户、图片任务和算法记录怎么建表

4.1 两张核心表的结构与设计理由

图像去雾系统的数据模型其实很简单,核心就是“用户”和“去雾任务”两张表。任务表通过外键关联用户表,一个用户可以有多个任务,这就是一对多关系。建表时要注意字段类型:图片路径存varchar,不存二进制大对象;算法参数存 JSON 字符串,方便以后扩展;状态字段用tinyint,0 表示处理中、1 表示成功、2 表示失败。

-- 用户表 CREATE TABLE `account_user` ( `id` int NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL, `password_hash` varchar(128) NOT NULL, `email` varchar(100) DEFAULT NULL, `is_active` tinyint(1) NOT NULL DEFAULT '1', `date_joined` datetime(6) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 去雾任务表 CREATE TABLE `dehaze_app_dehazetask` ( `id` int NOT NULL AUTO_INCREMENT, `user_id` int NOT NULL, `origin_image` varchar(255) NOT NULL, `result_image` varchar(255) NOT NULL, `algorithm_name` varchar(50) NOT NULL DEFAULT 'DarkChannelPrior', `params_json` longtext, `status` tinyint(1) NOT NULL DEFAULT '0', `created_at` datetime(6) NOT NULL, `finished_at` datetime(6) DEFAULT NULL, PRIMARY KEY (`id`), KEY `user_id` (`user_id`), CONSTRAINT `fk_task_user` FOREIGN KEY (`user_id`) REFERENCES `account_user` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

表名要跟 Django 模型的Meta类里db_table对应,不然迁移时会创建新的默认表名,跟已有 SQL 脚本对不上。字符集统一用utf8mb4,因为utf8在 MySQL 里存不了 Emoji,虽然用户名一般用不到,但统一总归省心。

4.2 用 django 的 models.py 描述同一套结构

写 Django 的models.py时,要跟上面的 SQL 保持字段一致。这样既可以用 Django 的 ORM 做查询迁移,也可以理解底层表结构——答辩时老师大概率会问“任务表为什么要有status字段”。

from django.db import models from django.contrib.auth.models import AbstractUser class User(AbstractUser): """扩展 Django 默认用户模型,保留 username/password 之外加手机号等可选字段""" phone = models.CharField(max_length=20, blank=True, null=True, verbose_name="手机号") class Meta: db_table = "account_user" class DehazeTask(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="所属用户") origin_image = models.CharField(max_length=255, verbose_name="原图路径") result_image = models.CharField(max_length=255, verbose_name="结果图路径") algorithm_name = models.CharField(max_length=50, default="DarkChannelPrior", verbose_name="算法名称") params_json = models.TextField(blank=True, null=True, verbose_name="算法参数JSON") status = models.BooleanField(default=False, verbose_name="是否完成") created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") finished_at = models.DateTimeField(null=True, blank=True, verbose_name="完成时间") class Meta: db_table = "dehaze_app_dehazetask" ordering = ["-created_at"]

ForeignKey(on_delete=models.CASCADE)表示用户被删除时,他的去雾任务一并删除,这是最常见的级联策略。ordering = ["-created_at"]让所有查询默认按创建时间倒序,历史记录页不用每次写order_by。

4.3 外键查询优化:select_related 的对应 SQL

在历史记录页里,每一条任务记录都要显示用户名。如果不做优化,查 10 条记录会执行 1 条任务查询 + 10 条用户查询,这是典型的 N+1 问题。select_related会把 JOIN 提前到一次查询完成,对应的 SQL 大致是:

SELECT * FROM dehaze_app_dehazetask INNER JOIN account_user ON dehaze_app_dehazetask.user_id = account_user.id WHERE dehaze_app_dehazetask.user_id = 1 ORDER BY dehaze_app_dehazetask.created_at DESC LIMIT 10;

除了select_related处理一对多查询,还有prefetch_related处理多对多查询。在本系统里,一个任务只属于一个用户,用select_related就够了。把这条区别讲清楚,答辩时是一个加分点。

5. 跑通系统的避坑清单:从环境到数据库的 5 个典型翻车现场

5.1 Django 版本与 MySQL 驱动不兼容

现象:执行python manage.py runserver后,访问登录页正常,但一注册用户就报django.db.utils.OperationalError: (2059, "Authentication plugin 'caching_sha2_password' cannot be loaded")。

原因:MySQL 8.0 默认加密插件是caching_sha2_password,而老版本 PyMySQL 或 mysqlclient 不支持这个协议。

解决:装新版本驱动,并在settings.py的数据库配置里加"OPTIONS": {"charset": "utf8mb4"}。如果还报错,就用requirements.txt锁定版本组合。常见组合是:

pip install django==4.2 pymysql==1.1.0 cryptography

然后在项目__init__.py里加两句:

import pymysql pymysql.install_as_MySQLdb()

5.2 media 文件在模板里 404

现象:上传图片成功后,结果页<img>标签地址是/media/origin/xxxx.jpg,浏览器直接 404。

原因:开发环境没挂载 media 目录。Django 的runserver默认只处理静态文件,不处理 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_root=settings.MEDIA_ROOT)

5.3 cv2.imread 读不到中文路径图片

现象:图片上传成功,算法也没报错,但处理结果是一张纯黑图。

原因:OpenCV 的imread在 Windows 上不支持中文路径,静默失败返回None,转成空数组后处理结果全黑。

解决:上传的图片一律用uuid重命名为纯英文文件名,同时在测试阶段用os.path.exists加上断言检查:

assert os.path.exists(origin_full), f"文件不存在: {origin_full}" img = cv2.imread(origin_full) assert img is not None, "图片读取失败,检查路径是否含中文"

5.4 上传大图导致内存溢出或超时

现象:上传一张 5000x3000 的高清雾图,页面转圈很久最后 502,或者内存直接吃满。

原因:暗通道算法里np.argsort(dark_flat)对 5000x3000 的图要排 1500 万个元素,耗时和内存都很夸张。同时 Django 默认上传大小没有硬限制,但 Web 服务器有超时时间。

解决:前端用accept="image/*"过滤类型,后端在视图中限制图片尺寸和文件大小。文件超过 5MB 或像素超过 2500 万就直接拒绝。算法侧可以先把长边压缩到 1600 像素再处理,肉眼基本看不出差别。

MAX_PIXELS = 2500 * 10000 # 2500 万像素 MAX_FILESIZE = 5 * 1024 * 1024 # 5MB if image_file.size > MAX_FILESIZE: return render(request, "upload.html", {"error": "图片不能超过 5MB"})

5.5 注册完 App 忘记迁移,数据库没有表

现象:python manage.py runserver不报错,但登录反而提示no such table: dehaze_app_dehazetask。

原因:Django 不会自动建表。改了models.py之后必须执行makemigrations再migrate。如果是直接导入别人的 SQL 脚本,要确认脚本里的表名跟models.py里db_table完全一致。

解决:

python manage.py makemigrations dehaze_app account python manage.py migrate

migrate之后用python manage.py showmigrations检查是否全部打勾。如果中途改过字段,直接migrate可能报冲突,这时可以python manage.py migrate dehaze_app zero回退后重新迁移——这是后悔药,但会丢表数据,开发阶段无所谓,有正式数据时谨慎使用。

6. 把系统做成能答辩的完整项目:定量评估、说明文档和 PPT 怎么组织

系统跑通只是第一步,毕设评分的差距往往在“能不能讲清楚效果”。去雾算法最容易被问的是“你怎么证明你的算法有效”。单说“看起来清晰了”不够,需要定量指标。实践中最常用的两个指标是 PSNR(峰值信噪比)和 SSIM(结构相似性)。PSNR 越高表示像素还原越接近参考图,SSIM 越接近 1 表示结构信息保留越好。计算时用 OpenCV 自带的函数就行。

import cv2 def calc_metrics(foggy_path, result_path, clear_path): foggy = cv2.imread(foggy_path) result = cv2.imread(result_path) clear = cv2.imread(clear_path) psnr_foggy = cv2.PSNR(foggy, clear) psnr_result = cv2.PSNR(result, clear) ssim_result = cv2.compareSSIM(result, clear, full=False) print(f"原图 PSNR: {psnr_foggy:.2f} dB") print(f"去雾后 PSNR: {psnr_result:.2f} dB,提升 {psnr_result - psnr_foggy:.2f} dB") print(f"SSIM: {ssim_result:.4f}")

需要先准备成对的质量评估数据,也就是同一场景的雾图和清晰图。公开数据集常见的是 RESIDE 标准数据集里的合成雾图,有对应的 ground truth 清晰图。拿 20 张图算一组平均指标,然后把“处理前 PSNR/处理后 PSNR”的对比表截图放进 PPT,比任何渲染图都有说服力。

关于 LW(论文)和 PPT 的整理顺序,我一般遵循“先做图表,再写文字”的顺序。先把上传页截图、历史列表截图、去雾对比图、指标对比表这四类图准备好,再按“研究背景 → 算法原理 → 系统设计 → 实验结果 → 总结展望”的结构填充文字。论文里放算法的公式推导时,建议把暗通道定义的公式单独拎出来解释两句,不要直接整段公式堆上去——答辩老师的耐心有限,他能看到你理解了自己的公式,比看到你背了十页推导更关键。

最后说一个我自己的教训:很多毕设翻车不在功能上,而在环境复制上。明明代码是好的,换了电脑跑不起来。所以交付前一定要把requirements.txt锁好版本,把README.md写完整,包括 Python 版本、Django 版本、MySQL 初始化脚本怎么执行、测试账号密码是什么。这套系统真正做完时,你会发现自己花在调试环境上的时间可能比写算法还多,而提前把这些写进说明文档的人,在答辩现场是真的能从容很多。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 11:16:55

Java编译原理语义分析实战:sectionnef资源包解析与符号表验证

简介&#xff1a;这份资源是编译原理课程实验三的语义分析实现包&#xff0c;面向正在学习编译原理、需要动手完成编译器前端实验的高校学生与自学者。它聚焦词法分析与语法分析之后的语义检查环节&#xff0c;帮助理解类型检查、作用域解析、常量折叠等核心概念在Java中的落地…

作者头像 李华
网站建设 2026/10/10 11:16:38

基于YOLOv8的牙科解剖数据集标注与训练实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/10 11:15:39

汽车制造智能体落地:工业级AI Agent实施白皮书

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/10 11:15:39

PCA9422+PIC18F86K22嵌入式电源管理实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/10 11:13:11

PCA9422与PIC32联合实现低功耗多路可调电源系统设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华