颜色衰减先验去雾算法实现
前言图像去雾是计算机视觉领域的一个重要问题雾天条件下拍摄的图像通常对比度低、颜色失真严重影响后续视觉任务的性能。本文将详细介绍一种经典的单图像去雾算法——Color Attenuation Prior (CAP)该算法由Zhu等人提出发表于CVPR 2015。与传统的基于暗通道先验DCP的方法不同CAP算法利用颜色衰减先验建立雾天图像中像素的饱和度、亮度与场景深度之间的线性关系从而恢复出清晰的无雾图像。论文地址A Fast Single Image Haze Removal Algorithm Using Color Attenuation Prior仓库代码GitHub - JiamingMai/Color-Attenuation-Prior-Dehazing: MATLAB codes for the paper A Fast Single Image Haze Removal Algorithm using Color Attenuation Prior · GitHub完整python代码可在此处获得PGS2Net/models/baseline/cap at main · Auorui/PGS2Net算法原理在计算机视觉中雾天图像的形成通常用大气散射模型来描述图像去雾的目标就是从观测图像中恢复出、和。CAP算法的核心假设是在雾天图像中场景深度与像素的饱和度和亮度之间存在线性关系。更具体地通过统计分析和回归可以得到其中是像素的亮度是像素的饱和度回归系数随机误差项。根据论文中的实验数据回归系数分别为0.1217790.959710−0.780245。这个先验的直观理解是在雾天场景中远处的物体由于受到更多大气散射的影响饱和度降低而亮度升高。一旦获得深度图d(x)透射率可以通过下式计算其中 β 是用户定义的散射系数控制去雾强度。大气光 A 通常从雾最浓的区域估计。在CAP中选择深度图中最亮的0.1%像素然后选择其中RGB向量范数最大的像素作为大气光值。获得透射率 t 和大气光A后通过求解大气散射模型得到清晰图像代码实现我们使用了原论文的图作为测试下面为使用python的实现import cv2 import numpy as np from scipy.ndimage import minimum_filter class CAP: Color Attenuation Prior Dehazing def __init__( self, beta1.0, radius60, epsilon1e-3, depth_radius15, top_percent0.001, t_min0.05, seed0, ): self.radius radius self.epsilon epsilon self.depth_radius depth_radius self.beta beta self.top_percent top_percent self.t_min t_min self.seed seed staticmethod def _to_float(img): if img.dtype np.float32: return img return img.astype(np.float32) / 255. def _guided_filter(self, I, p): r 2 * self.radius 1 eps self.epsilon I self._to_float(I) p p.astype(np.float32) Ir, Ig, Ib I[:, :, 0], I[:, :, 1], I[:, :, 2] Ir_mean cv2.blur(Ir, (r, r)) Ig_mean cv2.blur(Ig, (r, r)) Ib_mean cv2.blur(Ib, (r, r)) Irr_var cv2.blur(Ir * Ir, (r, r)) - Ir_mean * Ir_mean eps Irg_var cv2.blur(Ir * Ig, (r, r)) - Ir_mean * Ig_mean Irb_var cv2.blur(Ir * Ib, (r, r)) - Ir_mean * Ib_mean Igg_var cv2.blur(Ig * Ig, (r, r)) - Ig_mean * Ig_mean eps Igb_var cv2.blur(Ig * Ib, (r, r)) - Ig_mean * Ib_mean Ibb_var cv2.blur(Ib * Ib, (r, r)) - Ib_mean * Ib_mean eps Irr_inv Igg_var * Ibb_var - Igb_var * Igb_var Irg_inv Igb_var * Irb_var - Irg_var * Ibb_var Irb_inv Irg_var * Igb_var - Igg_var * Irb_var Igg_inv Irr_var * Ibb_var - Irb_var * Irb_var Igb_inv Irb_var * Irg_var - Irr_var * Igb_var Ibb_inv Irr_var * Igg_var - Irg_var * Irg_var cov Irr_inv * Irr_var Irg_inv * Irg_var Irb_inv * Irb_var Irr_inv / cov Irg_inv / cov Irb_inv / cov Igg_inv / cov Igb_inv / cov Ibb_inv / cov p_mean cv2.blur(p, (r, r)) Ipr_mean cv2.blur(Ir * p, (r, r)) Ipg_mean cv2.blur(Ig * p, (r, r)) Ipb_mean cv2.blur(Ib * p, (r, r)) Ipr_cov Ipr_mean - Ir_mean * p_mean Ipg_cov Ipg_mean - Ig_mean * p_mean Ipb_cov Ipb_mean - Ib_mean * p_mean ar Irr_inv * Ipr_cov Irg_inv * Ipg_cov Irb_inv * Ipb_cov ag Irg_inv * Ipr_cov Igg_inv * Ipg_cov Igb_inv * Ipb_cov ab Irb_inv * Ipr_cov Igb_inv * Ipg_cov Ibb_inv * Ipb_cov b p_mean - ar * Ir_mean - ag * Ig_mean - ab * Ib_mean ar cv2.blur(ar, (r, r)) ag cv2.blur(ag, (r, r)) ab cv2.blur(ab, (r, r)) b cv2.blur(b, (r, r)) return ar * Ir ag * Ig ab * Ib b def _cal_depth_map(self, img): np.random.seed(self.seed) hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) s hsv[:, :, 1].astype(np.float32) / 255. v hsv[:, :, 2].astype(np.float32) / 255. sigma 0.041337 noise np.random.normal( 0, sigma, (img.shape[0], img.shape[1]) ) depth ( 0.121779 0.959710 * v - 0.780245 * s noise ) depth_refine minimum_filter( depth, (self.depth_radius, self.depth_radius) ) return depth_refine, depth def _estimate_airlight(self, img, depth): img self._to_float(img) h, w depth.shape n int(np.ceil(self.top_percent * h * w)) idx np.argsort(depth.reshape(-1)) pixels img.reshape(-1, 3) candidates pixels[idx[-n:]] mag np.linalg.norm(candidates, axis1) A candidates[np.argmax(mag)] return A def recover(self, img): depth_refine, depth_pixel self._cal_depth_map(img) depth_refine self._guided_filter(img, depth_refine) transmission np.exp(-self.beta * depth_refine) transmission np.clip(transmission, self.t_min, 1) A self._estimate_airlight(img, depth_refine) I self._to_float(img) J (I - A) / transmission[..., None] A return np.clip(J, 0, 1) def __call__(self, img): return self.recover(img) if __name____main__: cap CAP( radius60, epsilon1e-3, depth_radius15, beta1.0 ) I cv2.imread(input.png) J cap(I) cv2.imwrite( output.png, (J * 255).astype(np.uint8) )torch实现如下import torch import torch.nn as nn import torch.nn.functional as F import numpy as np class CAP(nn.Module): Color Attenuation Prior Dehazing PyTorch实现输入输出为torch张量 def __init__( self, beta: float 1.0, guided_filter_radius: int 60, min_filter_radius: int 15, eps: float 1e-3, top_percent: float 0.001, t_min: float 0.05, ): Args: beta: 大气散射系数控制去雾强度 guided_filter_radius: 引导滤波半径 min_filter_radius: 最小值滤波半径 (对应depth_radius) eps: 引导滤波正则化参数 top_percent: 大气光估计时选取最亮区域的百分比 t_min: 最小透射率 super(CAP, self).__init__() self.beta beta self.guided_filter_radius guided_filter_radius self.min_filter_radius min_filter_radius self.eps eps self.top_percent top_percent self.t_min t_min def rgb_to_hsv(self, rgb: torch.Tensor) - torch.Tensor: RGB转HSV (模拟OpenCV的cv2.COLOR_RGB2HSV) Args: rgb: [B, 3, H, W], 值范围 [0, 1] Returns: hsv: [B, 3, H, W], H:[0,1], S:[0,1], V:[0,1] # 分离RGB通道 r, g, b rgb[:, 0:1, :, :], rgb[:, 1:2, :, :], rgb[:, 2:3, :, :] # 计算最大值、最小值 max_val, _ torch.max(rgb, dim1, keepdimTrue) min_val, _ torch.min(rgb, dim1, keepdimTrue) delta max_val - min_val # 计算饱和度 S (OpenCV公式) s torch.where(max_val 0, delta / max_val, torch.zeros_like(max_val)) # 计算亮度 V v max_val # 计算色调 H (OpenCV RGB2HSV公式) h torch.zeros_like(max_val) # 当R为最大值时 mask_r (r max_val) (delta 0) h torch.where(mask_r, ((g - b) / delta) % 6, h) # 当G为最大值时 mask_g (g max_val) (delta 0) h torch.where(mask_g, ((b - r) / delta) 2, h) # 当B为最大值时 mask_b (b max_val) (delta 0) h torch.where(mask_b, ((r - g) / delta) 4, h) h h / 6.0 # 归一化到[0,1] h torch.where(delta 0, torch.zeros_like(h), h) return torch.cat([h, s, v], dim1) def guided_filter(self, guide, target, radius40, eps1e-3): guide: 引导图 (B, C, H, W) 通常用灰度图或原图 target: 待滤波图 (B, 1, H, W) 即透射率图 B, C, H, W guide.shape # 转换为灰度引导图 if guide.shape[1] 3: guide_gray 0.299 * guide[:, 0:1, :, :] 0.587 * guide[:, 1:2, :, :] 0.114 * guide[:, 2:3, :, :] else: guide_gray guide # 确保target和guide_gray尺寸一致 if target.shape[2] ! H or target.shape[3] ! W: # 如果尺寸不一致调整target尺寸 target F.interpolate(target, size(H, W), modebilinear, align_cornersFalse) # 均值滤波可用平均池化替代 def box_filter(x, r): # 使用更高效的实现 kernel torch.ones(1, 1, 2 * r 1, 2 * r 1).to(x.device) / (2 * r 1) ** 2 return nn.functional.conv2d(x, kernel, paddingr, groupsx.shape[1]) mean_g box_filter(guide_gray, radius) mean_t box_filter(target, radius) mean_gt box_filter(guide_gray * target, radius) mean_gg box_filter(guide_gray * guide_gray, radius) var_g mean_gg - mean_g * mean_g cov_gt mean_gt - mean_g * mean_t a cov_gt / (var_g eps) b mean_t - a * mean_g mean_a box_filter(a, radius) mean_b box_filter(b, radius) return mean_a * guide_gray mean_b def compute_depth_map(self, rgb: torch.Tensor) - torch.Tensor: 计算深度图 Args: rgb: [B, 3, H, W], RGB图像, 值范围 [0, 1] Returns: depth: [B, 1, H, W] 深度图 B, C, H, W rgb.shape # 1. RGB转HSV hsv self.rgb_to_hsv(rgb) s hsv[:, 1:2, :, :] # 饱和度 v hsv[:, 2:3, :, :] # 亮度 # 2. 计算深度图 (添加高斯噪声模拟随机性) sigma 0.041337 noise torch.randn_like(v) * sigma depth 0.121779 0.959710 * v - 0.780245 * s noise # 3. 最小值滤波 - 使用更稳健的实现 r self.min_filter_radius # 使用unfold操作实现最小值滤波确保尺寸不变 pad r depth_padded F.pad(depth, (pad, pad, pad, pad), modereflect) depth_refine -F.max_pool2d(-depth_padded, kernel_size2 * r 1, stride1, padding0) # 计算裁剪后的尺寸 crop_h depth_refine.shape[2] - 2 * pad crop_w depth_refine.shape[3] - 2 * pad # 如果尺寸不匹配进行裁剪 if crop_h ! H or crop_w ! W: # 计算裁剪的起始位置 start_h (depth_refine.shape[2] - H) // 2 start_w (depth_refine.shape[3] - W) // 2 depth_refine depth_refine[:, :, start_h:start_h H, start_w:start_w W] else: depth_refine depth_refine[:, :, pad:pad H, pad:pad W] # 限制范围 depth_refine torch.clamp(depth_refine, 0, 1) return depth_refine def estimate_atmosphere(self, rgb: torch.Tensor, depth: torch.Tensor) - torch.Tensor: 估计大气光 Args: rgb: [B, 3, H, W] RGB图像 depth: [B, 1, H, W] 深度图 Returns: A: [B, 3, 1, 1] 大气光值 B, C, H, W rgb.shape # 展平 depth_flat depth.view(B, -1) rgb_flat rgb.view(B, C, -1) # 计算最亮像素的数量 (0.1%) n_bright max(1, int(self.top_percent * H * W)) # 获取最亮像素的索引 _, indices torch.topk(depth_flat, n_bright, dim1) # 收集候选大气光像素 Acand torch.zeros((B, n_bright, C), devicergb.device) for b in range(B): Acand[b, :, :] rgb_flat[b, :, indices[b]].permute(1, 0) # 计算每个候选像素的RGB向量范数 Amag torch.norm(Acand, dim2) # 选择范数最大的像素 _, max_idx torch.max(Amag, dim1) A torch.zeros((B, C, 1, 1), devicergb.device) for b in range(B): A[b, :, 0, 0] Acand[b, max_idx[b], :] return A def forward(self, x: torch.Tensor) - torch.Tensor: 完整去雾流程 Args: x: [B, 3, H, W] 或 [3, H, W], RGB图像, 值范围 [0, 1] 如果输入范围是[-1, 1]会自动转换到[0, 1] Returns: J: [B, 3, H, W] 或 [3, H, W] 去雾后的RGB图像 # 检查输入范围并转换 if x.min() 0: # 检测到输入是[-1,1]范围 x (x 1) / 2 # 转换到[0,1] # 确保是4维张量 if x.dim() 3: x x.unsqueeze(0) single_image True else: single_image False # 1. 计算深度图 depth self.compute_depth_map(x) # 2. 引导滤波细化深度图 depth_refined self.guided_filter(x, depth) # 3. 计算透射率 transmission torch.exp(-self.beta * depth_refined) transmission torch.clamp(transmission, minself.t_min, max1.0) # 4. 估计大气光 A self.estimate_atmosphere(x, depth) # 5. 图像复原 J (x - A) / transmission A J torch.clamp(J, 0, 1) # 恢复原始形状 if single_image: return J.squeeze(0) return J if __name__ __main__: model CAP( beta1.0, guided_filter_radius60, min_filter_radius15, eps1e-3 ) import cv2 img cv2.imread(input.png) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 转换为RGB img_tensor torch.from_numpy(img).float() / 255.0 img_tensor img_tensor.permute(2, 0, 1).unsqueeze(0) with torch.no_grad(): output model(img_tensor) output_np output.squeeze(0).permute(1, 2, 0).numpy() output_np np.clip(output_np * 255, 0, 255).astype(np.uint8) output_np cv2.cvtColor(output_np, cv2.COLOR_RGB2BGR) cv2.imwrite(output_cap.png, output_np)此处torch的实现不如直接对图像进行处理可能是引导滤波和其中的一些核心实现的问题。总结Color Attenuation Prior是一种简洁而有效的单图像去雾算法。它通过建立颜色衰减与场景深度的统计模型避免了复杂的物理参数估计实现了高效的去雾处理。虽然在某些极端场景下可能存在局限性但CAP算法凭借其计算效率和良好的去雾效果在实际应用中仍有重要价值。

相关新闻

物联网+区块链落地传统农业|茶叶智联网全产业链数字化系统架构解析

物联网+区块链落地传统农业|茶叶智联网全产业链数字化系统架构解析

传统茶产业属于典型的传统特色农业与民生流通产业,长期存在种植标准化程度低、生产数据无留存、流通溯源体系缺失、产销数据严重割裂等行业痛点。多数茶园依旧依赖老农经验种植,品质参差不齐;茶叶流通环节层级繁多,假货、陈茶窜货…

2026/7/29 22:28:33 阅读更多 →
UE4SS脚本注入框架:从原理到实战,解锁UE4游戏模组开发

UE4SS脚本注入框架:从原理到实战,解锁UE4游戏模组开发

1. 项目概述:UE4SS是什么,以及为什么你需要它如果你是一名虚幻引擎4(UE4)的开发者或Modder,尤其是对《幻兽帕鲁》这类基于UE4的热门游戏进行功能扩展或逆向研究感兴趣,那么“UE4SS”这个名字你肯定不陌生。…

2026/7/29 17:19:37 阅读更多 →
2016年Android开发工具与框架全景解析

2016年Android开发工具与框架全景解析

1. 2016年Android开发生态全景扫描2016年是Android开发工具链爆发式增长的关键年份。作为一名从Eclipse时代就开始接触Android开发的老兵,我亲眼见证了这一年各种工具和框架如何彻底改变了我们的开发方式。当时Android Studio刚刚完成从2.0到2.2版本的迭代&#xff…

2026/7/27 0:27:14 阅读更多 →

最新新闻

机器人3D可视化方案

机器人3D可视化方案

ROS/ROS2 生态里 Web 端 3D 可视化已经有一批开源项目可以参考,从"零开发直接用"到"源码级参考自己写"都有,分三层推荐。 一、零开发直接用:Foxglove Studio 如果你现阶段只想"看到 3D 轨迹",不想写…

2026/7/30 23:25:23 阅读更多 →
地方志到底有什么用?

地方志到底有什么用?

如果想了解一个地方,最常见的方式是看地图、读通史,或者查几篇地方介绍。但这些材料往往只能告诉我们一个地区的大致轮廓:它在哪里、属于哪个省市、有什么名胜、历史上发生过什么大事。真正细密的地方经验,常常藏在另一类文献里&a…

2026/7/30 23:25:23 阅读更多 →
【紧急预警】AI配音语速偏差超±12%将导致用户留存率断崖式下跌!立即执行这4项实时调控协议

【紧急预警】AI配音语速偏差超±12%将导致用户留存率断崖式下跌!立即执行这4项实时调控协议

更多请点击: https://kaifayun.com 第一章:AI配音语速偏差的用户留存影响机制 AI配音语速偏差并非孤立的技术参数问题,而是直接作用于用户认知负荷与情感反馈的关键触点。当合成语音语速偏离人类自然对话节奏(通常为120–160字/分…

2026/7/30 23:25:23 阅读更多 →
ReconX震撼发布:革命性稀疏视图3D场景重建技术,告别数百张照片依赖!

ReconX震撼发布:革命性稀疏视图3D场景重建技术,告别数百张照片依赖!

ReconX震撼发布:革命性稀疏视图3D场景重建技术,告别数百张照片依赖! 【免费下载链接】ReconX [TIP 2026] ReconX: Reconstruct Any Scene from Sparse Views with Video Diffusion Model 项目地址: https://gitcode.com/gh_mirrors/rec/Rec…

2026/7/30 23:25:23 阅读更多 →
compose-rules常见问题解答:新手必知的15个关键知识点

compose-rules常见问题解答:新手必知的15个关键知识点

compose-rules常见问题解答:新手必知的15个关键知识点 【免费下载链接】compose-rules Lint rules for ktlint/detekt aimed to contribute to a healthier usage of Compose. Actively maintained and evolved fork of the Twitter Compose rules. 项目地址: htt…

2026/7/30 23:25:23 阅读更多 →
DeepSeek降AI指令实战:25条核心技巧提升内容自然度

DeepSeek降AI指令实战:25条核心技巧提升内容自然度

1. 项目概述:DeepSeek降AI指令实战指南最近在AI工具圈里,DeepSeek的热度持续攀升。作为一名长期关注AI应用落地的从业者,我发现很多用户在使用过程中都遇到了一个共性问题:如何有效降低AI生成内容的"AI感"。经过半年多的…

2026/7/30 23:24:22 阅读更多 →

日新闻

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 您是否曾因Windows系统盘空间不足而烦恼?是否遇到过设…

2026/7/30 0:00:13 阅读更多 →
如何3步掌握Video Download Helper:网页视频下载的完整实战指南

如何3步掌握Video Download Helper:网页视频下载的完整实战指南

如何3步掌握Video Download Helper:网页视频下载的完整实战指南 【免费下载链接】VideoDownloadHelper Chrome Extension to Help Download Video for Some Video Sites. 项目地址: https://gitcode.com/gh_mirrors/vi/VideoDownloadHelper 你是否曾经在浏览…

2026/7/30 0:00:13 阅读更多 →
“双减”后首个AI备课压力测试报告:覆盖32所中小学的176节AI辅助课,暴露4大隐性增负节点

“双减”后首个AI备课压力测试报告:覆盖32所中小学的176节AI辅助课,暴露4大隐性增负节点

更多请点击: https://intelliparadigm.com 第一章:AI 教师备课辅助 AI 教师备课辅助系统正逐步成为教育数字化转型的核心支撑工具,它并非替代教师,而是通过语义理解、知识图谱与多模态生成能力,将教师从重复性劳动中解…

2026/7/30 0:00:13 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/29 22:18:20 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/29 14:34:28 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/29 15:00:03 阅读更多 →

月新闻