图像去雾算法:从物理模型到深度学习实现

目录

  • 图像去雾算法:从物理模型到深度学习实现
    • 概述
    • [1. 大气散射模型与去雾原理](#1. 大气散射模型与去雾原理)
      • [1.1 大气散射模型](#1.1 大气散射模型)
      • [1.2 问题逆向求解](#1.2 问题逆向求解)
    • [2. 基于暗通道先验(DCP)的图像去雾算法](#2. 基于暗通道先验(DCP)的图像去雾算法)
      • [2.1 暗通道先验的定义](#2.1 暗通道先验的定义)
      • [2.2 基于DCP的透射率估计](#2.2 基于DCP的透射率估计)
      • [2.3 大气光 A A A 的估计](#2.3 大气光 A A A 的估计)
      • [2.4 透射率细化与图像恢复](#2.4 透射率细化与图像恢复)
    • [3. Python实现基于暗通道先验的去雾算法](#3. Python实现基于暗通道先验的去雾算法)
      • [3.1 环境配置与依赖](#3.1 环境配置与依赖)
      • [3.2 完整代码实现](#3.2 完整代码实现)
      • [3.3 代码分析与解释](#3.3 代码分析与解释)
      • [3.4 算法局限性及改进](#3.4 算法局限性及改进)
    • [4. 基于深度学习的图像去雾简介](#4. 基于深度学习的图像去雾简介)
      • [4.1 基本思想](#4.1 基本思想)
      • [4.2 经典网络结构示例](#4.2 经典网络结构示例)
      • [4.3 优势与挑战](#4.3 优势与挑战)
    • [5. 总结](#5. 总结)

图像去雾算法:从物理模型到深度学习实现

概述

在计算机视觉和图像处理领域,雾、霾等恶劣天气条件会严重降低图像质量,导致图像对比度下降、颜色失真、细节丢失,进而影响后续高级视觉任务(如目标检测、分割、识别)的性能。图像去雾技术旨在从退化的雾天图像中恢复出清晰的场景,具有重要的理论研究价值和广泛的应用前景,如自动驾驶、视频监控、航空遥感等。本文将系统性地介绍图像去雾的基本原理、经典算法,并重点使用Python实现基于暗通道先验(Dark Channel Prior, DCP)的去雾算法,同时探讨基于深度学习的去雾方法。我们将从物理模型出发,深入算法细节,并提供完整的、可读性强的代码实现。

1. 大气散射模型与去雾原理

图像去雾问题的研究建立在大气散射模型(Atmospheric Scattering Model)之上,该模型从物理上描述了雾天图像的成像过程。

1.1 大气散射模型

该模型将观测到的有雾图像 I ( x ) I(x) I(x) 表示为两部分的和:

I ( x ) = J ( x ) t ( x ) + A ( 1 − t ( x ) ) I(x) = J(x)t(x) + A(1 - t(x)) I(x)=J(x)t(x)+A(1−t(x))

其中:

  • x x x 表示图像中像素的空间坐标。
  • I ( x ) I(x) I(x) 是观测到的有雾图像(输入)。
  • J ( x ) J(x) J(x) 是待恢复的无雾清晰图像(我们的目标)。
  • A A A 是全球大气光值(Global Atmospheric Light),通常假设为全局常量,代表无穷远处的大气光强度。
  • t ( x ) t(x) t(x) 是媒介透射率(Medium Transmission),表示场景辐射在传播过程中的衰减程度,取值范围为 [ 0 , 1 ] [0, 1] [0,1]。 t ( x ) t(x) t(x) 越小,表示该位置雾浓度越高。

t ( x ) t(x) t(x) 与场景深度 d ( x ) d(x) d(x) 的关系由下式给出:
t ( x ) = e − β d ( x ) t(x) = e^{-\beta d(x)} t(x)=e−βd(x)

其中 β \beta β 是大气散射系数。去雾的核心任务就是从已知的 I ( x ) I(x) I(x) 中,估算出未知的 A A A 和 t ( x ) t(x) t(x),从而最终恢复出 J ( x ) J(x) J(x)。

1.2 问题逆向求解

从大气散射模型可以推导出恢复清晰图像的公式:
J ( x ) = I ( x ) − A t ( x ) + A J(x) = \frac{I(x) - A}{t(x)} + A J(x)=t(x)I(x)−A+A

然而,这是一个病态问题 (Underconstrained Problem),因为每个像素我们只有一个观测值 I ( x ) I(x) I(x),却需要求解 J ( x ) J(x) J(x)、 A A A 和 t ( x ) t(x) t(x) 三个未知数。为了解决这个问题,研究人员引入了各种先验知识(Prior)或假设来约束解空间。

flowchart TD A[输入有雾图像 I(x)] --> B[估算全局大气光 A] A --> C[估算透射率图 t(x)] B --> D[求解清晰图像 J(x)] C --> D subgraph Prior [先验知识约束估算] direction LR P1[暗通道先验 DCP] P2[颜色一致性先验] P3[深度学习先验] end Prior --> B Prior --> C D --> E[输出去雾图像 J(x)]

2. 基于暗通道先验(DCP)的图像去雾算法

何恺明等人于2009年提出的暗通道先验是图像去雾领域的里程碑式工作。其核心思想来自于对大量无雾户外图像的统计观察。

2.1 暗通道先验的定义

暗通道先验指出:在绝大多数非天空的户外无雾图像局部区域中,至少存在一个颜色通道的某些像素其强度值非常低,甚至接近于零。

对于一个清晰图像 J J J,其暗通道 J d a r k J^{dark} Jdark 的数学定义为:
J d a r k ( x ) = min ⁡ y ∈ Ω ( x ) ( min ⁡ c ∈ { r , g , b } J c ( y ) ) J^{dark}(x) = \min_{y \in \Omega(x)} \left( \min_{c \in \{r, g, b\}} J^c(y) \right) Jdark(x)=y∈Ω(x)min(c∈{r,g,b}minJc(y))

其中:

  • J c J^c Jc 表示 J J J 图像的某一个颜色通道。
  • Ω ( x ) \Omega(x) Ω(x) 是一个以像素 x x x 为中心的局部图像块(Patch)。
  • min ⁡ c ∈ { r , g , b } \min_{c \in \{r, g, b\}} minc∈{r,g,b} 是在 RGB 三个通道上取最小值。
  • min ⁡ y ∈ Ω ( x ) \min_{y \in \Omega(x)} miny∈Ω(x) 是在一个局部块内取最小值。

根据先验, J d a r k ( x ) → 0 J^{dark}(x) \to 0 Jdark(x)→0。

2.2 基于DCP的透射率估计

  1. 假设大气光 A A A 已知 ,且透射率在局部块 Ω ( x ) \Omega(x) Ω(x) 内为常数 t ~ ( x ) \tilde{t}(x) t~(x)。
  2. 将大气散射模型两边同时除以 A c A^c Ac(各通道的大气光)并进行归一化:
    I c ( x ) A c = t ( x ) J c ( x ) A c + 1 − t ( x ) \frac{I^c(x)}{A^c} = t(x)\frac{J^c(x)}{A^c} + 1 - t(x) AcIc(x)=t(x)AcJc(x)+1−t(x)
  3. 对上述等式两边同时进行两次 min ⁡ \min min 操作:
    min ⁡ y ∈ Ω ( x ) ( min ⁡ c I c ( y ) A c ) = t ~ ( x ) min ⁡ y ∈ Ω ( x ) ( min ⁡ c J c ( y ) A c ) + 1 − t ~ ( x ) \min_{y \in \Omega(x)} \left( \min_{c} \frac{I^c(y)}{A^c} \right) = \tilde{t}(x) \min_{y \in \Omega(x)} \left( \min_{c} \frac{J^c(y)}{A^c} \right) + 1 - \tilde{t}(x) y∈Ω(x)min(cminAcIc(y))=t~(x)y∈Ω(x)min(cminAcJc(y))+1−t~(x)
  4. 根据暗通道先验,无雾清晰图像的归一化暗通道 min ⁡ y ∈ Ω ( x ) ( min ⁡ c J c ( y ) A c ) \min_{y \in \Omega(x)} \left( \min_{c} \frac{J^c(y)}{A^c} \right) miny∈Ω(x)(mincAcJc(y)) 趋近于0。
  5. 因此,我们可以简化上式,得到透射率 t ~ ( x ) \tilde{t}(x) t~(x) 的粗略估计:
    t ~ ( x ) = 1 − ω ⋅ min ⁡ y ∈ Ω ( x ) ( min ⁡ c I c ( y ) A c ) \tilde{t}(x) = 1 - \omega \cdot \min_{y \in \Omega(x)} \left( \min_{c} \frac{I^c(y)}{A^c} \right) t~(x)=1−ω⋅y∈Ω(x)min(cminAcIc(y))
    其中引入了一个因子 ω \omega ω ( 0 < ω ≤ 1 0 < \omega \leq 1 0<ω≤1,通常取0.95) 来保留少量的雾,使得恢复后的图像看起来更自然,具有深度感。

2.3 大气光 A A A 的估计

原论文中的估计方法如下:

  1. 从有雾图像 I I I 的暗通道图中选取最亮的(即前0.1%)的像素点。
  2. 这些像素点通常对应着图像中雾浓度最高的区域(可能是天空或白色物体)。
  3. 在这些像素点对应的原始有雾图像 I I I 的位置中,选取强度最大的点的值作为全局大气光 A A A。通常取RGB三个通道中的最大值。

2.4 透射率细化与图像恢复

  1. 软抠图(Soft Matting):最初使用Matting算法来细化粗估计的透射率图,但其计算复杂度非常高。
  2. 导向滤波(Guided Filter) :何恺明等在后续工作中提出了使用导向滤波来细化透射率图,其在效果和效率上取得了非常好的平衡。导向滤波能够保持边缘特性,同时平滑同质区域。
  3. 恢复清晰图像 :在得到细化的透射率图 t ( x ) t(x) t(x) 和大气光 A A A 后,代入恢复公式即可。为避免除以零和保证数值稳定性,通常设置一个透射率下限 t 0 t_0 t0(如0.1):
    J ( x ) = I ( x ) − A max ⁡ ( t ( x ) , t 0 ) + A J(x) = \frac{I(x) - A}{\max(t(x), t_0)} + A J(x)=max(t(x),t0)I(x)−A+A
  4. 结果后处理 :恢复出的 J ( x ) J(x) J(x) 值可能超出 [ 0 , 255 ] [0, 255] [0,255] 的范围,需要进行截断和归一化,或者进行简单的自动色阶/对比度拉伸以改善视觉效果。

3. Python实现基于暗通道先验的去雾算法

下面我们将使用NumPy和OpenCV等库,逐步实现DCP去雾算法。

3.1 环境配置与依赖

首先,确保安装了必要的库:

bash 复制代码
pip install opencv-python numpy scipy

3.2 完整代码实现

python 复制代码
import cv2
import numpy as np
from scipy import ndimage
from scipy.spatial import distance


def guided_filter(I, p, win_size, eps):
    """
    导向滤波实现
    参考: K.He, J.Sun, and X.Tang. Guided Image Filtering. ECCV 2010.
    Args:
        I: 导向图像(Guidance Image),灰度图,范围[0, 1]
        p: 输入图像(Input Image),与I形状相同,范围[0, 1]
        win_size: 局部窗口半径
        eps: 正则化参数,防止a过大

    Returns:
        q: 滤波输出图像
    """
    mean_I = cv2.boxFilter(I, cv2.CV_64F, (win_size, win_size))
    mean_p = cv2.boxFilter(p, cv2.CV_64F, (win_size, win_size))
    mean_Ip = cv2.boxFilter(I * p, cv2.CV_64F, (win_size, win_size))
    cov_Ip = mean_Ip - mean_I * mean_p

    mean_II = cv2.boxFilter(I * I, cv2.CV_64F, (win_size, win_size))
    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, cv2.CV_64F, (win_size, win_size))
    mean_b = cv2.boxFilter(b, cv2.CV_64F, (win_size, win_size))

    q = mean_a * I + mean_b
    return q


def estimate_transmission_rough(img, atmos_light, patch_size=15, w=0.95):
    """
    粗略估计透射率图
    Args:
        img: 输入有雾图像,归一化到[0, 1]
        atmos_light: 估计的大气光值,形状为(3,)
        patch_size: 计算暗通道的局部块大小,必须是奇数
        w: 雾保留系数,控制去雾程度,默认0.95

    Returns:
        transmission_rough: 粗略的透射率图,范围[0, 1]
    """
    # 将图像除以大气光进行归一化
    normalized_img = img / atmos_light
    # 计算归一化图像的暗通道
    min_channel = np.min(normalized_img, axis=2)
    # 使用最小值滤波(形态学腐蚀)计算暗通道
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (patch_size, patch_size))
    dark_channel_norm = cv2.erode(min_channel, kernel)
    
    # 根据公式计算粗略透射率: t = 1 - w * dark_channel
    transmission_rough = 1 - w * dark_channel_norm
    # 限制透射率范围
    transmission_rough = np.clip(transmission_rough, 0.1, 0.9)
    return transmission_rough


def estimate_atmospheric_light(img, dark_channel, percent=0.001):
    """
    估计全局大气光值A
    Args:
        img: 输入有雾图像,范围[0, 255] (uint8)
        dark_channel: 对应的暗通道图,与img同宽高
        percent: 选取暗通道中 brightest 像素的比例,默认0.1%

    Returns:
        A: 估计的大气光值,形状为(3,)
    """
    # 计算要选取的像素数量
    num_pixels = int(dark_channel.size * percent)
    # 找到暗通道图中前%num_pixels个最亮的像素的坐标(扁平化后的索引)
    indices = np.argsort(dark_channel, axis=None)[-num_pixels:]
    # 将扁平化的索引转换为二维坐标
    coords = np.unravel_index(indices, dark_channel.shape)
    
    # 获取这些坐标点在原始有雾图像中的像素强度
    candidate_pixels = img[coords]
    # 计算每个像素点的亮度(例如,取RGB三通道的最大值)
    brightness = np.max(candidate_pixels, axis=1)
    # 找到最亮的候选像素点
    brightest_pixel_idx = np.argmax(brightness)
    # 将该点的RGB值作为大气光估计值
    A = candidate_pixels[brightest_pixel_idx]
    
    return A


def compute_dark_channel(img, patch_size=15):
    """
    计算图像的暗通道
    Args:
        img: 输入图像,可以是归一化[0,1]或[0,255]
        patch_size: 局部块大小

    Returns:
        dark_channel: 暗通道图,与img同宽高,单通道
    """
    # 如果图像是3通道,则在每个通道上取最小值
    if len(img.shape) == 3:
        min_channel = np.min(img, axis=2)
    else:
        min_channel = img.copy()
        
    # 使用最小值滤波(形态学腐蚀)得到暗通道
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (patch_size, patch_size))
    dark_channel = cv2.erode(min_channel, kernel)
    
    return dark_channel


def dehaze_dcp(img, patch_size=15, w=0.95, eps=1e-6, refine=True):
    """
    基于暗通道先验的主去雾函数
    Args:
        img: 输入有雾图像(BGR格式,uint8)
        patch_size: 用于计算暗通道和透射率的块大小,必须是奇数
        w: 雾保留系数,0<w<=1,越大保留雾越多,通常0.95
        eps: 导向滤波的正则化参数
        refine: 是否使用导向滤波细化透射率图

    Returns:
        result: 去雾后的图像(BGR格式,uint8)
        transmission: 最终估计的透射率图
        atmos_light: 估计的大气光值
    """
    # 0. 将图像从BGR转换为RGB并归一化到[0, 1]
    img_float = img.astype(np.float32) / 255.0
    img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0
    
    # 1. 计算暗通道(用于估算A)
    dark_channel = compute_dark_channel(img_rgb, patch_size)
    
    # 2. 估算全局大气光A
    # 注意:estimate_atmospheric_light 需要原图(0-255范围)
    img_rgb_255 = (img_rgb * 255).astype(np.uint8)
    atmos_light = estimate_atmospheric_light(img_rgb_255, dark_channel)
    atmos_light_norm = atmos_light.astype(np.float32) / 255.0
    
    # 3. 粗略估算透射率图
    transmission_rough = estimate_transmission_rough(img_rgb, atmos_light_norm, patch_size, w)
    
    # 4. 使用导向滤波细化透射率图
    if refine:
        # 导向图像使用有雾图像的灰度图
        guide = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY).astype(np.float32) / 255.0
        transmission_refined = guided_filter(guide, transmission_rough, win_size=patch_size, eps=eps)
        transmission = np.clip(transmission_refined, 0.1, 0.99) # 设置下限防止除零
    else:
        transmission = transmission_rough
        
    # 5. 根据大气散射模型恢复无雾图像
    # J = (I - A) / t + A
    J = np.zeros_like(img_rgb)
    for c in range(3): # 对RGB三个通道分别处理
        J[:, :, c] = (img_rgb[:, :, c] - atmos_light_norm[c]) / transmission + atmos_light_norm[c]
    
    # 6. 将结果截断到[0,1]并转换回[0,255]
    J = np.clip(J, 0, 1)
    J_uint8 = (J * 255).astype(np.uint8)
    
    # 7. (可选)后处理:简单的对比度拉伸可以改善视觉效果
    # 将结果转换回BGR格式用于OpenCV显示和保存
    result_bgr = cv2.cvtColor(J_uint8, cv2.COLOR_RGB2BGR)
    
    # 也将透射率图转换为uint8用于显示
    transmission_display = (transmission * 255).astype(np.uint8)
    
    return result_bgr, transmission_display, atmos_light


def main():
    """
    主函数:读取图像,进行去雾,并显示和保存结果。
    """
    # 读取输入有雾图像
    input_path = 'hazy_image.jpg'  # 替换为你的有雾图像路径
    img_hazy = cv2.imread(input_path)
    if img_hazy is None:
        print(f"错误:无法读取图像 {input_path}")
        return

    print("正在处理图像,请稍候...")
    # 调用去雾函数
    dehazed_result, transmission_map, A = dehaze_dcp(img_hazy, 
                                                    patch_size=15, 
                                                    w=0.95, 
                                                    refine=True)
    
    print(f"估计的大气光值 (BGR): {A}")
    
    # 显示原图、透射率图和去雾结果
    cv2.imshow('Original Hazy Image', img_hazy)
    cv2.imshow('Transmission Map', transmission_map)
    cv2.imshow('Dehazed Result', dehazed_result)
    
    # 保存结果
    cv2.imwrite('dehazed_result.jpg', dehazed_result)
    cv2.imwrite('transmission_map.jpg', transmission_map)
    print("去雾结果已保存为 'dehazed_result.jpg'")
    print("透射率图已保存为 'transmission_map.jpg'")
    
    # 等待按键后关闭所有窗口
    cv2.waitKey(0)
    cv2.destroyAllWindows()


if __name__ == '__main__':
    main()

3.3 代码分析与解释

  1. compute_dark_channel: 通过先取RGB三通道最小值,再进行最小值滤波(形态学腐蚀)来计算暗通道。
  2. estimate_atmospheric_light : 从暗通道中最亮的0.1%像素对应的原始图像位置中,寻找亮度最高的点作为大气光 A A A 的估计。
  3. estimate_transmission_rough : 根据归一化后的图像和大气光 A A A,利用公式 t ~ ( x ) = 1 − ω ⋅ J n o r m d a r k ( x ) \tilde{t}(x) = 1 - \omega \cdot J^{dark}_{norm}(x) t~(x)=1−ω⋅Jnormdark(x) 计算粗透射率图。
  4. guided_filter : 实现导向滤波,使用有雾图像的灰度图作为引导图像,对粗透射率图进行边缘保持的平滑细化,得到更精确的透射率图 t ( x ) t(x) t(x)。
  5. dehaze_dcp: 主函数,协调整个去雾流程:图像预处理 -> 计算暗通道 -> 估计A -> 估计粗透射率 -> 细化透射率 -> 恢复清晰图像 -> 后处理。
  6. main: 处理IO,调用去雾函数,并展示和保存结果。

3.4 算法局限性及改进

  • 天空区域失效:天空区域本身不符合暗通道先验,导致该区域透射率被严重低估,去雾后容易出现色差和噪声放大("天空变色"或"halo效应")。
  • 计算效率:尽管使用了导向滤波,DCP算法的计算量仍然较大,难以满足实时应用需求。
  • 先验假设:依赖于统计先验,对于室内图像或不符合该先验的特殊场景效果不佳。

常见改进方法

  • 天空区域检测与分割:单独处理天空区域,避免对其进行强去雾。
  • 更高效的滤波器:使用比导向滤波更快的双边滤波器或其他边缘保持滤波器。
  • 融合其他先验:结合颜色一致性等其他先验来提高鲁棒性。

4. 基于深度学习的图像去雾简介

近年来,深度学习在图像去雾领域取得了巨大成功,逐渐成为主流方法。

4.1 基本思想

深度学习模型(主要是CNN)通过学习大量有雾-无雾图像对,直接建立从 I ( x ) I(x) I(x) 到 J ( x ) J(x) J(x) 的端到端映射,或者学习估算大气散射模型中的参数 A A A 和 t ( x ) t(x) t(x)。

4.2 经典网络结构示例

一个简单的去雾CNN可能包含以下结构:

python 复制代码
# 一个简化的示例性去雾CNN结构(基于PyTorch框架伪代码)
import torch.nn as nn

class SimpleDehazeNet(nn.Module):
    def __init__(self):
        super(SimpleDehazeNet, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(64, 64, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(64, 64, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            # ... 更多卷积层 ...
            nn.Conv2d(64, 3, kernel_size=3, padding=1), # 输出3通道
            nn.Sigmoid() # 将输出限制在[0,1]
        )

    def forward(self, x):
        # x: 输入有雾图像 [batch, 3, H, W]
        residual = self.features(x)
        # 学习的是残差:J = I - R,或者直接学习J
        return residual # 或者 return x - residual

# 更先进的网络会使用U-Net、GAN、注意力机制等复杂结构。

4.3 优势与挑战

  • 优势
    • 效果卓越:在大量数据上训练的模型往往能产生比传统方法更清晰、更自然的结果。
    • 端到端:避免了复杂的手工设计特征和先验。
    • 速度快:前向推断速度可以非常快,易于部署。
  • 挑战
    • 数据依赖:需要大量成对的(有雾-无雾)训练数据,获取真实世界的数据对非常困难。
    • 泛化能力:在训练集分布之外的雾天图像上可能表现不佳。
    • 模型可解释性:不如传统模型物理意义清晰。

5. 总结

本文详细介绍了图像去雾的物理模型------大气散射模型,并重点阐述了基于暗通道先验这一经典算法的原理与实现细节。我们使用Python和OpenCV从头实现了一个完整的DCP去雾流程,包括暗通道计算、大气光估计、透射率估计与细化以及清晰图像恢复。代码结构清晰,注释完整,便于理解和实验。

尽管DCP算法存在一些局限性,但它奠定了现代图像去雾研究的基础,其思想至今仍有重要价值。同时,我们也简要介绍了基于深度学习的去雾方法,这代表了该领域的未来发展方向。在实际应用中,可以根据具体需求(对效果、速度、资源的要求)选择合适的算法。

相关推荐
咔咔学姐kk2 小时前
大模型微调技术宝典:Transformer架构,从小白到专家
人工智能·深度学习·学习·算法·transformer
Caaacy_YU3 小时前
多模态大模型研究每日简报【2025-09-10】
论文阅读·人工智能·深度学习·机器学习·计算机视觉
云边云科技3 小时前
门店网络重构:告别“打补丁”,用“云网融合”重塑数字竞争力!
大数据·人工智能·安全·智能路由器·零售
山海青风3 小时前
12 Prompt 模板化与参数化
人工智能·prompt
山海青风3 小时前
11 Prompt 工程进阶:Few-shot 与 Chain-of-Thought
人工智能·prompt
爱看科技3 小时前
AI/AR智能眼镜步入全球破圈增长期,五大科技大厂入局加剧生态市场角逐
人工智能·科技·ar
人有一心3 小时前
深度学习里的树模型TabNet
人工智能·深度学习
haogexiaole4 小时前
Dijkstra 算法
算法
Kyln.Wu4 小时前
【python实用小脚本-211】[硬件互联] 桌面壁纸×Python梦幻联动|用10行代码实现“开机盲盒”自动化改造实录(建议收藏)
开发语言·python·自动化