
简介这是一份已修正编译错误的显著性检测源码包源自2012年CVPR论文《Saliency Filters》。工程面向计算机视觉与图像处理领域的研究者、学习者特别适合希望复现快速显著性检测算法或将其嵌入自有项目的读者。压缩包共17个文件由7个cpp源文件、6个h头文件、3个txt说明和1个hpp头文件组成涵盖了permutohedral滤波、显著度核心计算、超像素生成、测试与评估等模块并附带CMakeLists构建脚本在配置好OpenCV的平台上可直接编译运行。整个包体积仅37KB轻量且便于阅读目前已有536人学习浏览。通过阅读这套源码可以直观理解基于Permutohedral lattice的高效滤波是如何实现快速对比度计算的同时掌握显著性特征提取、空间加权与归一化等关键流程基于清晰的模块划分读者也能方便地修改参数或替换子模块开展自己的对比实验。 动手跑一遍 Saliency Filter 的源码比看十篇论文都管用。做实验的朋友估计都有这种经历在 GitHub 上搜 saliency filter 或 sailencyFilter出来的项目一抓一大把点进去要么 README 写了一半要么依赖还停留在五年前的版本真正能 clone 下来直接编译通过、跑出效果图的反而不多。我前阵子做视觉注意机制相关的对比实验需要一份稳定的显著性检测基线代码折腾了差不多一个周末最后整理出一套可以编译的 sailencyFilter 源码方案。这篇文章就是把我整个踩坑过程、编译步骤、核心算法原理、以及跑实验时需要注意的参数调节都说清楚。适合正准备跑显著性检测 baseline、或者想快速看懂 Saliency Filter 这个算法到底怎么实现的朋友看完照着做基本能少走一半弯路。1. 为什么非要找一个“能编”的源码来跑1.1 显著性检测是干嘛的显著性检测Saliency Detection简单说就是模拟人眼注意力机制给图像里每个像素打一个分分数越高代表这个位置越容易吸引人注意。应用场景很广比如图像分割前的预处理、视频缩略图生成、广告投放里的重点区域识别甚至目标检测里用来做候选区域提取。而 Saliency Filter 是 2012 年 CVPR 上提出的经典算法作者是 Perazzi 等人。它最大的特点是同时考虑了颜色对比度和空间关系在保持边缘细节的同时还能让显著区域内部足够均匀不会出现一块红一块绿这种割裂感。放到今天的深度学习时代它依然经常被拿来当作无监督基线和后处理手段。1.2 网上源码不少真正常亮起来的没几个我在 GitHub 上前后翻过至少五个 saliencyFilter 项目大致是这么几类论文作者挂出来的实验版本里面引用了一堆自制库路径写死很难直接编。学生作业版本只实现了核心算法的一小部分跑出来效果明显不对。完整实现但依赖很老比如用 Eigen 2.x 的 API、OpenCV 2.x 的接口在现代编译器上一编译就是几十个错误。最后我挑了一份代码结构相对干净、没有多余依赖的实现做了修正把 OpenCV 调用、CMake 配置、代码里的废弃接口全部更新了一遍才有了这份能直接编译的版本。所以这里的信息我建议先收藏后面不管你用哪个平台的源码排查编译错误的思路都是通用的。2. 环境准备依赖选型是编译的第一步2.1 我的编译环境清单这套源码强依赖 OpenCV 和 Eigen编译工具用的 CMake。我的环境是 Ubuntu 20.04编译器 GCC 9.4具体版本如下组件版本说明OpenCV4.5.x含 contrib需要 ximgproc 模块跑 SLIC 超像素Eigen3.3.x头文件库用来做矩阵运算CMake3.16生成构建系统GCC9.4支持 C11 即可Windows 用户也可以用 MSVC 或者 MinGW 跑但注意 OpenCV 预编译包要选对应版本contrib 模块也要提前确认好。2.2 OpenCV 不带 contribSLIC 就编不出来这是我最想提醒的一点。Saliency Filter 的第一步要做超像素分割原论文用的就是 SLIC。OpenCV 的 SLIC 实现在ximgproc模块里这个模块只有在编译 OpenCV 时附加了 contrib 仓库才会有。如果你只是apt install libopencv-dev大概率是没有这个头文件和库的。另外 SLICO 是 SLIC 的零参数版本不需要手动调紧密度源码里一般也是调 SLICO 模式。如果编译时出现undefined reference to cv::ximgproc::createSuperpixelSLIC基本就是 OpenCV 没带 contrib 或者 CMake 链接时漏掉了这个模块。Eigen 就省心很多它是纯头文件库装好之后不用关心链接的问题只要 CMake 能找到头文件路径就行sudo apt install libeigen3-dev3. 源码目录与核心实现拆解3.1 拿到源码先看哪几个文件一份干净的可编译工程通常目录不会太复杂我整理的这个版本长这样saliencyFilter/ ├── CMakeLists.txt ├── README.md ├── data/ │ ├── input.jpg │ └── output.jpg ├── include/ │ └── saliencyFilter.h ├── src/ │ ├── saliencyFilter.cpp │ └── main.cppCMakeLists.txt负责找依赖、生成构建规则saliencyFilter.h/cpp是算法核心封装了一个 SaliencyFilter 类main.cpp负责命令行参数解析、读图、调用算法、写结果。拿到任何网上的源码先看这三个文件基本就能判断这项目能不能跑起来。如果 CMakeLists 里写死了绝对路径、或者代码里 include 了不存在的头文件那就要有心理准备了。3.2 三个阶段抽象、对比度、分配Saliency Filter 的核心流程可以拆成三个阶段理解了这三个阶段你后面调参就有的放矢。第一个阶段是抽象Abstraction也就是用 SLIC 超像素把图像变成一堆小区域。这样做的好处是计算量从像素级降到区域级比如一张 500x500 的图有 25 万像素分割成 400 个超像素之后对比度计算只在 400 个区域之间做速度能快几个数量级。这一步每个超像素会计算出一个平均 Lab 颜色和一个质心坐标。第二阶段是对比度计算Contrast Computation这是整个算法最核心的部分。对每个超像素区域计算它和其他所有区域的颜色距离再用测地距离Geodesic Distance做权重。这里测地距离是精髓它指的是在超像素图沿着邻接关系走出来的最短路径距离而不是直接算两个区域质心之间的欧氏距离。这么一搞即使两个区域颜色相同、彼此却隔着其他物体它们的显著性也不会互相误伤边缘会被保留得比较好。第三阶段是显著性分配Saliency Assignment把区域级的显著值转换回像素级。常见做法是根据每个像素所在超像素的显著值再结合像素和超像素中心颜色的高斯权重做一次上采样得到跟原图一样大的显著图。这步如果做得粗糙效果会明显有块状感也就是所谓的马赛克感。关键代码片段大概是这样的逻辑// 1. SLIC 超像素分割 cv::Ptrcv::ximgproc::SuperpixelSLIC slic cv::ximgproc::createSuperpixelSLIC(imageLab, cv::ximgproc::SLICO, numSuperpixels, compactness); slic-iterate(10); slic-getLabels(labels); // 2. 区域对比度计算 3. 像素级显著值分配 // 具体实现见 saliencyFilter.cpp 中的 computeSaliency()我自己在代码里加了比较详细的注释核心的矩阵操作都标出了维度方便对照论文看。4. 编译与运行全流程4.1 CMake 配置要点CMakeLists.txt 是整个工程能否编译的关键。这里有几个点必须注意要找到 OpenCV 的 ximgproc 组件、要引入 Eigen 头文件路径、链接时不能漏库。cmake_minimum_required(VERSION 3.10) project(SaliencyFilter) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(OpenCV REQUIRED COMPONENTS core imgproc imgcodecs ximgproc) find_package(Eigen3 REQUIRED) add_executable(saliency_filter src/main.cpp src/saliencyFilter.cpp ) target_include_directories(saliency_filter PRIVATE ${OpenCV_INCLUDE_DIRS} ${EIGEN3_INCLUDE_DIR} ) target_link_libraries(saliency_filter ${OpenCV_LIBS} Eigen3::Eigen )注意 OpenCV 4 里OpenCV_INCLUDE_DIRS可能为空因为头文件路径已经被 OpenCVConfig.cmake 管理了建议用target_link_libraries时配合${OpenCV_LIBS}即可。如果遇到找不到头文件的问题再用include_directories(${OpenCV_INCLUDE_DIRS})兜底。你还可以加一个-fPIC选项避免后面集成到别的项目时出现地址重定位问题set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -fPIC)4.2 编译、运行、出图配置好之后就是标准的 CMake 三板斧mkdir build cd build cmake .. make -j$(nproc)编译完成后会生成saliency_filter可执行文件。运行只需要指定输入图和输出图路径其他参数有默认值./saliency_filter --input ../data/input.jpg --output result.jpg如果图片路径不对程序会打印一行错误然后退出这个逻辑在 main.cpp 里有做检查。参数方面我建议首次跑的时候用这个组合./saliency_filter \ --input ../data/input.jpg \ --output result.jpg \ --superpixels 400 \ --compactness 20 \ --weight-color 0.5 \ --weight-depth 0.5跑完之后打开result.jpg正常的话你会看到显著区域是亮的、背景是暗的物体边缘相对清晰。如果你发现整张图都亮或者都暗先别急着怀疑程序多数情况下是参数没调对。5. 编译期常见问题排查速查表这部分是我实际踩坑的记录整理成表格方便你直接对号入座错误信息原因解决方案undefined reference to cv::ximgproc::createSuperpixelSLICOpenCV 没有编译 contrib 模块重新编译带 contrib 的 OpenCV或者在安装时选择带 contrib 的包fatal error: Eigen/Dense: No such file or directoryEigen 未安装或 CMake 找不到路径sudo apt install libeigen3-dev再检查 find_package 配置CMake Error: Could NOT find OpenCVOpenCV 不在默认搜索路径手动指定OpenCV_DIR指向 OpenCVConfig.cmake 所在目录error: no matching function for call to ...代码用了过期 API比如旧版 cv:: 接口对照当前 OpenCV 版本逐个替换比如CV_LOAD_IMAGE_COLOR改cv::IMREAD_COLORrelocation R_X86_64_32 against .rodata生成位置无关代码失败CMake 里加CMAKE_CXX_FLAGS -fPIC或者切到 Release 模式链接时打开版本的报错比如对cv::Mat::ones之类的符号 not found编译和链接的 OpenCV 版本不一致统一 CMake 里找到的 OpenCV 路径和运行时动态库路径除了表格里这些还有一个不太常见的坑如果你是从某些老项目里扒的源码在 x86 环境下自己编译 OpenCV 的时候偶尔会看到汇编阶段的unreferenced label告警。这种一般来自 OpenCV 自带的第三方库比如 libjpeg、ittnotify跟你的显著性算法代码没关系可以先不动它只要最终可执行文件生成了就没有影响。排查编译问题的时候最忌讳的是直接整段重写代码。我一般的做法是先看第一个报错解决完再编译一次第二次报错往往就和第一次完全不一样了。编译器的错误提示虽然没有那么智能但它告诉你第一个错误的位置通常是最需要关注的位置。6. 跑通之后参数调节与扩展方向6.1 几个关键参数的感性认识代码里给我印象最深的三个参数是--weight-color、--weight-depth和--superpixels。--weight-color控制颜色对比度的影响程度值越大颜色跳变剧烈的区域越容易被判定为显著--weight-depth控制空间距离的影响程度值越大越看重区域之间隔着多远。两者比例关系最好在 0.3 到 0.8 之间试我实测下来很多自然图像在 0.5 附近都能拿到不错的结果。--superpixels控制超像素数量这个参数直接决定算法速度和边缘质量。超像素太少边缘糊成一片显著性图看起来像色块拼图超像素太多计算量变大但边缘细节保留得好。我一般从 400 开始调大图或者需要精细边缘的时候提到 800 左右。顺带一提这类项目在搜索时GitHub 上 saliencyFilter 和 sailencyFilter 两种拼写都有人用找资料时两个拼写都试一下能多翻出不少参考。6.2 从基线到改进跑通基准以后扩展方向其实很多。你可以把输出显著图当成二值掩码的输入配合大津法或者自适应阈值做前景背景分割也可以把它接在传统目标检测流程前面先生成可能包含目标的候选区域再交给分类器判断。如果你准备拿这个算法做对比实验一定要把随机种子固定住因为 SLIC 初始化时带有随机性同样参数跑两次可能在边界上有一两个像素的差异。对于追求稳定的实验建议在代码里给 SLIC 传入确定的初始化方式或者跑多次取平均。这个细节在论文实验部分一般不会写但在实际对比不同算法时影响还挺明显的。最后说点自己的体会。Saliency Filter 毕竟是 2012 年的算法和现在基于深度学习的显著性模型相比在某些复杂场景下精度确实有差距。但它有一个很大的优势不需要训练数据、不需要 GPU单张图几十毫秒就能出结果。我用它做过批量数据清洗把一批图片里最显著的区域抠出来做缩略图效果比固定裁中心好不少。这种轻量级、可解释性强的算法在很多工程场景里依然是很有价值的选择。如果你也打算拿它跑实验建议先把超像素数量调大一点看看分割粒度对最终显著图的影响这个感觉建立起来之后再回去调权重参数会顺手很多。本文还有配套的精品资源点击获取