尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

显著性检测经典算法LC/HC/AC/FT原理与OpenCV实现

显著性检测经典算法LC/HC/AC/FT原理与OpenCV实现 简介面向计算机视觉学习者和研究者的显著性检测算法合集涵盖HC、LC、AC、FT四种经典方法对应C实现源码与测试图片可帮助快速复现论文效果并对比不同策略在显著性提取上的差异。资源包共16个文件包括4个C源文件与4个头文件以及8张JPG测试图像整体压缩后仅337KB轻量易用。其中HC基于局部对比度与边缘强度LC采用多尺度层次融合AC结合局部强度与全局频率FT则利用纹理和颜色信息均配有可编译工程适合用于图像摘要、目标检测、视频监控等场景的预研或课程设计。目前已有2756人参与学习足见其实用价值。通过阅读代码可掌握算法核心数学原理也可将自己的图片输入进行验证作为显著性检测入门与进阶的实用参考资料。1. 显著性检测入门从 LC/HC/AC/FT 开始四种经典算法的定位上传一张图片之前先回答“哪里最吸引人”这是显著性检测Saliency Detection最朴素的定义。HC、LC、AC、FT 这四个缩写分别对应直方图对比度Histogram Contrast、局部对比度Local Contrast、多尺度窗口对比度Achanta et al. 的 AC和频率调谐Frequency-Tuned。它们不依赖深度学习框架不要求 GPU几十行 OpenCV 代码就能在单张图片上输出全分辨率的显著图。这四种算法恰好覆盖了显著性建模的两条主线空域上的邻域/全局对比度LC、HC、AC和频域上的带通滤波FT。先弄清它们的计算边界和参数含义再决定要不要上深度网络往往能省下大量标注和调参时间这对做图像分割预处理、缩略图自动裁剪、工业外观缺陷定位的工程师尤其适用。2. LC 与 HC 的显著性建模从邻域窗口到全局直方图对比LC 和 HC 代表了空域对比度方法的两极一个只看邻域一个统计全图。前者计算量随窗口尺寸线性增长后者则需要先解决颜色量化的问题。两条路线在输出上有非常不同的纹理响应特性。2.1 LC 算法局部对比度的定义与加速实现LC 算法的假设非常直接一个像素与它周围像素的颜色差异越大它在局部就越显眼。对于图像中的像素 p设其邻域为 N(p)LC 的显著性值定义为所有邻域像素的颜色差之和S(p) Σ_{q∈N(p)} ‖I(p) − I(q)‖这里的 I 可以是灰度值也可以是 Lab 色彩空间中的某个通道。邻域 N(p) 最常见的是正方形窗口3×3 时每个像素做 8 次差分5×5 时做 24 次差分。窗口尺寸是 LC 唯一的自由参数但它直接改变检测语义小窗口捕捉边缘和纹理突变大窗口更倾向于把完整的目标区域与背景分开。实际项目中建议从 5×5 起步然后观察显著图是否出现过多细小亮斑。若亮斑太多就增大窗口若目标边缘大面积粘连就减小窗口。2.1.1 窗口尺寸和边界填充如何影响显著性输出窗口尺寸对输出的影响不是线性的。3×3 窗口下图像中的随机噪点也能产生很高的显著性响应因为单个噪点和周围 8 个像素都有明显的强度差。窗口增大到 9×9 或更大时邻域内包含更多背景像素噪点的平均差被摊薄显著性响应下降真正的大块前景目标反而因为内部像素趋于一致、边缘像素持续高响应而保留下来。另一个容易被忽略的因素是边界填充。LC 在图像边缘无法取得完整窗口必须对边界做扩展常见做法是复制边缘像素BORDER_REPLICATE。如果换成常数填充边缘像素的邻域里会出现一圈虚假的固定值显著性图在图像四边会产生明显的伪响应后续做二值化时边缘出现一圈细线很难滤除。2.1.2 用移位差分避免逐像素循环的 LC 代码最直观的 LC 实现是双重循环逐像素取窗口但 Python 的 for 循环在一张 1080p 图像上可能要跑数分钟。工程上更高效的写法是“移位差分”窗口内的每个偏移量 (dy, dx) 对应一次图像平移将所有平移后的图像与原始图像做绝对值差再累加效果与逐像素取窗口完全等价但耗时从 O(H×W×win²) 降至 O(H×W×win²) 的向量化实现实际提速可达百倍以上。import cv2 import numpy as np def lc_saliency(gray, win5): gray gray.astype(np.float32) / 255.0 h, w gray.shape r win // 2 padded cv2.copyMakeBorder(gray, r, r, r, r, cv2.BORDER_REPLICATE) sal np.zeros((h, w), dtypenp.float32) for dy in range(-r, r 1): for dx in range(-r, r 1): if dx 0 and dy 0: continue shifted padded[r dy: r dy h, r dx: r dx w] sal np.abs(gray - shifted) sal cv2.normalize(sal, None, 0, 255, cv2.NORM_MINMAX) return sal.astype(np.uint8)代码里的核心是padded[rdy:rdyh, rdx:rdxw]它通过切片把原始图像沿 (dx, dy) 方向平移后与原始图像对齐从而绕开了逐个取窗口的循环。copyMakeBorder的 BORDER_REPLICATE 参数决定了边缘扩展策略。请注意归一化使用的是全局最小最大值若输入图像中存在明显的孤立噪点显著图的动态范围会被压缩这时可在归一化前用cv2.medianBlur做一次 3×3 中值滤波。2.2 HC 算法颜色量化与全局概率加权HCHistogram Contrast来自程明明团队 2011 年的 CVPR 论文它的视角从局部转向全局某个颜色在整幅图中越罕见它的显著性权重就越高。设颜色 c 在量化直方图中的出现概率为 P(c)HC 的显著值定义为该颜色与全图所有其他颜色的加权距离之和S(c) Σ_{c} P(c) ‖C(c) − C(c)‖这里的 C(c) 是颜色 c 在 Lab 空间的向量。累加对象是全图出现过的所有颜色所以计算复杂度直接依赖颜色种类数量 M。不量化时真彩图的 M 可能达到数十万必须先把三通道量化到有限的网格里。2.2.1 量化粒度和距离矩阵的大小控制量化粒度直接决定距离矩阵的内存占用。把每个通道量化到 12 个等级M 最大为 12³1728 个 bin距离矩阵大小为 1728×1728约 300 万个 float 数值内存可接受。量化到 16 级后 M4096矩阵膨胀到 1670 万项内存增长近 5 倍。超过 16 级时建议改为只对图像中实际出现过的颜色建立距离矩阵而不是对全部 bin 做计算。还要注意量化本身会引入颜色混淆两个视觉上差异明显的颜色可能被分到同一个 bin显著性响应随之衰减所以 bin_n 不宜低于 8。2.2.2 HC 的可运行实现与查找表映射HC 的实现分为三个步骤量化颜色、统计概率加权距离、把颜色级映射回像素。下面是带查找表加速的完整版本。def hc_saliency(lab_img, bin_n12): lab_img np.clip(lab_img, 0, 255) bins np.linspace(0, 256, bin_n 1)[:-1].astype(np.float32) lab_q np.stack([ np.digitize(lab_img[:, :, i], bins) - 1 for i in range(3) ], axis2) quant_id (lab_q[:, :, 0] * bin_n lab_q[:, :, 1]) * bin_n lab_q[:, :, 2] ids, counts np.unique(quant_id.ravel(), return_countsTrue) probs counts.astype(np.float32) / counts.sum() l (ids // (bin_n * bin_n)) * (256 // bin_n) (256 // bin_n) // 2 a ((ids // bin_n) % bin_n) * (256 // bin_n) (256 // bin_n) // 2 b (ids % bin_n) * (256 // bin_n) (256 // bin_n) // 2 lab_vals np.stack([l, a, b], axis1).astype(np.float32) diff lab_vals[:, None, :] - lab_vals[None, :, :] dist np.sqrt((diff ** 2).sum(axis2)) sal dist.dot(probs) sal (sal - sal.min()) / (sal.max() - sal.min() 1e-9) * 255 lut np.zeros(bin_n ** 3, dtypenp.float32) lut[ids] sal return lut[quant_id].astype(np.uint8)np.digitize一次性完成三通道的 bin 分配np.unique统计出现过的颜色 id 和次数。距离矩阵dist与概率向量probs做矩阵向量乘法比 for 循环逐颜色累加快一个量级。最后把显著性值填入一张长度为 bin_n³ 的查找表lut再通过lut[quant_id]一次性把全图像的量化 id 映射为显著值避免逐像素赋值。2.3 LC 与 HC 在同一张图上的表现差异LC 输出的是邻域梯度响应对纹理密集区域草地、砖墙、衣物褶皱非常敏感因为局部像素跳变本就密集哪些区域都会被标出来。HC 则把全图颜色分布当作背景只要某个颜色出现频率低哪怕它只是背景里的一个小色块也会被高亮。两种算法在“目标面积超过图像 1/3”的场景下都会明显退化大目标颜色占据主频后全局对比度的权重被稀释显著性响应向目标边缘收缩。下面是常见场景的选型参考场景推荐算法选择原因文字与图标区域定位LC局部响应强边缘位置精确缩略图自动裁剪HC全局抑制背景大面积区域更稳定工业缺陷预处理FT 或 LC纹理响应高细小缺陷不易丢失视频帧显著性粗检测AC多尺度窗口对尺度变化更鲁棒3. AC 与 FT 的显著性建模多尺度窗口与频率滤波FT 和 AC 都出自 Achanta 团队分别从频域和多尺度空域两个角度做了进一步简化。FT 计算量最小AC 在目标尺度不确定时更稳。这一章先把两个算法的公式讲清楚再给出可直接复现的实现。3.1 FT 算法整图均值与高斯模糊的差值FTFrequency-Tuned的核心思想是背景大多由低频成分构成前景目标同时含有高频和低频信息。逐像素地比较整图平均颜色向量与低频版本之间的差异就能把偏离全局均值的高频部分提取出来。公式为S(p) ‖I_μ − I_whc(p)‖其中 I_μ 是整幅图像在 Lab 空间的平均向量I_whc(p) 是经过高斯滤波后的逐像素低频估计‖·‖ 是 Lab 空间的三通道欧氏距离。3.1.1 Lab 色彩空间里的距离度量为什么更稳使用 RGB 空间做同样的差值计算会得到不稳定结果原因在于 RGB 三个通道高度相关两个感知上接近的颜色在 RGB 欧氏距离上可能相差很大。Lab 空间的 L亮度、a绿红、b蓝黄通道经过对感知的线性化处理欧氏距离与实际视觉差异更接近。OpenCV 的COLOR_BGR2LAB转换后各通道范围分别是 L 在 0~255、a 和 b 大致在 0~255 的偏移范围直接计算可让三个通道对距离的贡献接近均衡。3.1.2 FT 测试时 sigma 与核大小的联动FT 的可调参数是高斯核大小和 sigma。工程实现中kernel_size 和 sigma 并非完全独立sigma 决定滤波的频率响应转折点kernel_size 决定实际参与卷积的像素范围。若 kernel_size 太小高频噪声没有被充分抑制显著图会出现颗粒感若 sigma 过大目标内部与背景的差异反而被抹平。建议从 kernel_size3、sigma3 开始然后用一组图像做 FT 测试观察输出显著图的信噪比变化。def ft_saliency(lab_img, kernel_size3, sigma3): avg_lab cv2.mean(lab_img)[:3] avg np.array(avg_lab, dtypenp.float32).reshape(1, 1, 3) blurred cv2.GaussianBlur(lab_img, (kernel_size, kernel_size), sigma) diff avg - blurred.astype(np.float32) sal np.sqrt((diff ** 2).sum(axis2)) return cv2.normalize(sal, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)cv2.mean返回四通道均值前三个是 Lab。reshape(1,1,3)让均值向量可以直接与 H×W×3 的模糊图像做广播减法。diff三通道求平方和再开方就是每个像素到整图均值向量的空间距离。3.2 AC 算法多尺度局部均值对的差异叠加ACAdaptive Contrast思路比 LC 更进一步不要只用单一邻域而是用多个不同尺寸的窗口分别估计局部背景再把不同尺度下窗口内均值的差异叠加起来作为显著性响应。设窗口 W₁、W₂、W₃ 以目标像素为中心F_i(p) 是窗口 i 内的平均 Lab 向量则S(p) Σ_{ij} ‖F_i(p) − F_j(p)‖两个窗口的平均向量相差越大说明该像素周围在不同尺度上的环境差异越明显也就越显著。若目标小于最小窗口则三个窗口的均值都会包含一部分背景差异被背景主导目标显著性下降若目标远大于最大窗口则目标内部的像素在三窗口下均值趋于一致显著性响应又会被削弱。所以窗口序列的覆盖范围需要结合目标尺度来设定。3.2.1 窗口尺寸序列怎么选工程实现里常用等差或等比序列比如 (5, 11, 25)。窗口过小如 3等于高频滤波窗口过大超过图像短边的 1/3会缺失局部信息。经验做法是按图像短边比例计算候选窗口再取 3 个离散值例如短边 480 时取 (7, 15, 31)短边 720 时取 (11, 23, 47)。在目标尺寸未知的任务中序列跨度越大覆盖的尺度范围越广但计算量也随之上升。def ac_saliency(lab_img, window_sizes(5, 11, 25)): lab lab_img.astype(np.float32) means [] for ws in window_sizes: kernel np.ones((ws, ws), np.float32) / (ws * ws) means.append(cv2.filter2D(lab, -1, kernel, borderTypecv2.BORDER_REPLICATE)) sal np.zeros(lab.shape[:2], dtypenp.float32) for i in range(len(means)): for j in range(i 1, len(means)): diff means[i] - means[j] sal np.sqrt((diff ** 2).sum(axis2)) return cv2.normalize(sal, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)filter2D对三通道图像做逐通道均值滤波核为全 1 的归一化矩阵。ocv.filter2D 的 borderType 默认是 BORDER_REFLECT_101这里显式指定 REPLICATE 是为了和 LC 一致避免边界填充策略不同导致后续比较时出现偏差。3.3 四种算法的显著性输出形态对比四种算法输出都是 0~255 的单通道灰度图但像素值分布差异明显。LC 的输出偏向细碎纹理HC 偏向大块区域FT 把目标内部均匀高亮但边缘不锐利AC 则介于 HC 与 LC 之间。从计算资源角度看FT 最轻LC 次之HC 和 AC 在颜色数或窗口数增大时开销明显上升。算法计算域核心操作主要参数复杂度参考输出特点LC空域邻域差分求和winO(H×W×win²)边缘与纹理响应强HC颜色空间量化直方图距离加权bin_nO(H×WM²)大块区域均匀高亮AC空域多尺度窗口均值差异叠加window_sizesO(H×W×N)目标与背景边界清晰FT频域均值与模糊之差sigma/kernelO(H×W)背景抑制较好边缘略粗4. 用 OpenCV 在真实图片上跑通 LC/HC/AC/FT 完整流程把四种算法串成一条可复用的流水线关键不在算法本身而在统一的数据入口、尺寸策略和输出规范。本节给出可直接保存运行的代码并解释每个环节为什么这样做。4.1 统一预处理缩放尺寸与 BGR 到 Lab 转换显著性检测对绝对分辨率不敏感绝大多数任务里把图像短边缩放到 400~480 像素即可。缩放有三个好处减少逐像素计算的耗时、压制高频噪声、让窗口参数的意义相对稳定。另一个关键是色彩空间转换顺序。OpenCV 读入的是 BGR先转 Lab 再转为 float32后续所有算法共享这套数据避免每处单独转换产生不一致。def load_preprocess(img_path, target_short480): bgr cv2.imread(img_path) if bgr is None: raise FileNotFoundError(img_path) h, w bgr.shape[:2] scale target_short / min(h, w) if scale 1.0: bgr cv2.resize(bgr, (int(w * scale), int(h * scale)), interpolationcv2.INTER_AREA) lab_u8 cv2.cvtColor(bgr, cv2.COLOR_BGR2LAB) lab lab_u8.astype(np.float32) gray cv2.cvtColor(bgr, cv2.COLOR_BGR2GRAY) return bgr, lab, gray缩放只在 scale 1.0 时触发避免小图被放大的无意义计算。Lab 转换基于 uint8 图像完成再转 float32这样能保证 HC 的np.digitize处理到的是稳定范围内的值。4.2 四个算法的可执行集成代码将前面定义的lc_saliency、hc_saliency、ac_saliency、ft_saliency统一放到一个文件里再写一个 runner 完成加载、计算和保存。4.2.1 加载图像并生成四张显著图if __name__ __main__: img_path sample.jpg bgr, lab, gray load_preprocess(img_path) sal_lc lc_saliency(gray, win5) sal_hc hc_saliency(lab, bin_n12) sal_ac ac_saliency(lab, window_sizes(5, 11, 25)) sal_ft ft_saliency(lab, kernel_size3, sigma3) outputs [(LC, sal_lc), (HC, sal_hc), (AC, sal_ac), (FT, sal_ft)] for name, sal in outputs: cv2.imwrite(f{name}.png, sal)LC 只接收灰度图其余三个算法接收 float32 的 Lab 图像。输出统一保存为 PNG格式无损且便于后续二值化时直接处理。需要注意的是cv2.imwrite对 uint8 单通道图会保存为灰度 PNG不要误传 float32 数组。4.3 参数调整对照与常见误用四个算法各有一个核心参数调整方向不同会产生截然不同的输出。下表列出常用默认值和调整方向上的表现可作为第一轮调参的起点参数所属算法常用默认值调大后的效果调小后的效果winLC5目标区域更完整边缘变粗细节更多噪声更突出bin_nHC12颜色区分度提高计算变慢相近颜色被合并响应面积变大window_sizesAC(5, 11, 25)对更大目标敏感细节减少细节增多大目标响应减弱sigmaFT3背景抑制更强目标边缘收缩保留更多低频纹理目标响应下降一个常见的误用是同时调整两个参数而不知道各自的影响。例如 FT 里把 sigma 从 3 调到 5 后若显著图变模糊不应继续调 sigma而应该调小 kernel_size让高斯核的实际覆盖范围和 sigma 重新匹配。4.4 灰度图输入时的特殊处理HC、AC、FT 都需要三通道颜色信息如果原始输入是灰度图直接复制成三通道再转 Lab 会导致 a、b 通道全为同一个常数距离计算退化为亮度差异颜色对比部分的优势完全丢失。遇到灰度图时应明确告知下游算法只做亮度显著性或者干脆在预处理阶段就把任务限定为“基于亮度的显著性检测”不要把三通道复制后的结果当作彩色图来用。5. 显著图二值化、评估方法与三个落地技巧四张显著图最终要变成可供下一步使用的掩码或候选框这一章只说验证和二值化阶段最容易被忽视的三个问题。5.1 自适应阈值和 Otsu 二值化的边界拿到显著图后最常见的操作是固定阈值 127 截断但这在目标面积占比变化大的场景下会失效。Otsu 方法按直方图分布自动寻找阈值比固定阈值鲁棒得多但它的前提是显著图呈现双峰分布。实际效果上FT 和 AC 的显著图直方图通常呈明显双峰Otsu 效果较好LC 的显著图会有大量中等值像素Otsu 容易把噪声一起带入前景。_, mask cv2.threshold(sal, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU)若目标面积占比小于 5%Otsu 会把前景整个吞掉这时需要改用基于面积的阈值搜索例如从高到低累加像素直到达到目标面积比例再取对应阈值。5.2 真实项目中三个容易踩的工程坑第一个坑是归一化基准漂移。cv2.normalize的 NORM_MINMAX 依赖输入图自己的最大最小值同一场景在不同光照下显著图的绝对值会整体漂移跨图比较时无法统一尺度。这时候改用直方图拉伸或固定上限的方式做归一化保证不同图之间的可比较性。第二个坑是边缘伪影。LC 和 AC 使用 BORDER_REPLICATE 后靠近图像边缘的区域显著性值异常偏高二值化后四边容易残留一圈细线。处理方式是在生成 mask 后把边界 2~3 个像素清零或从显著图的统计中去掉边缘区域再求归一化参数。第三个坑是显著图直接做目标分割。注意这些算法输出的是“视觉注意力响应”不保证像素级目标完整性。可用区域连通或 GrabCut 一步把掩码转换成目标二值图。验证时建议用以下三层指标分割掩码与人工标注的 IoU、检测框的召回率、以及显著性排序和真实注视点之间的相关性。最后正式接入流程前把四种算法的输出叠加取加权平均往往比单独选一种更稳。_significance_map None本文还有配套的精品资源点击获取
返回列表