简介
图像直方图是图像中像素强度分布的图形表示。对于彩色 图像,我们通常会为每个颜色通道(例如 bgr 或 hsv)计算直方图。通过比较两张图片的直方图,我们可以获得它们在颜色分布上的相似程度。opencv 提供了 cv::calchist 函数用于计算直方图,以及 cv::comparehist 函数用于比较两个直方图。
先决条件
- c++ 编译器: 如 g++ (mingw for windows), clang, msvc。
- opencv 库: 需要正确安装并配置好编译环境 (版本 3.x 或 4.x)。
- 两张待比较的图像: 准备好两张图片文件(例如 image1.jpg 和 image2.jpg)。
核心步骤
- 加载图像: 使用
cv::imread
读取两张待比较的图像。 - 色彩空间转换 (可选但推荐): 将图像从 bgr 转换到 hsv 色彩空间。hsv 对光照变化的鲁棒性通常比 bgr 好,尤其是 h (hue) 和 s (saturation) 通道。
- 计算直方图:
- 定义直方图参数(如通道、bins 数量、取值范围)。
- 使用
cv::calchist
为每张图像计算 h-s 二维直方图或单个通道的一维直方图。
- 归一化直方图 (可选但推荐): 为了消除图像尺寸差异带来的影响,通常会对直方图进行归一化,使其和为1。可以使用
cv::normalize
。 - 比较直方图: 使用
cv::comparehist
函数,选择一种比较方法(如相关性、卡方、交集、巴氏距离)来计算两个直方图之间的相似度/差异度。 - 输出结果: 显示比较得分。
代码实现
#include <opencv2/opencv.hpp> #include <iostream> #include <vector> // 函数:计算并归一化图像的 h-s 直方图 cv::mat calculatehsnormalizedhistogram(const cv::mat& image) { cv::mat hsvimage; cv::cvtcolor(image, hsvimage, cv::color_bgr2hsv); // h-s 直方图参数 // 我们只使用 h 和 s 两个通道 int hbins = 50; int sbins = 60; int histsize[] = { hbins, sbins }; // hue 范围 [0, 180], saturation 范围 [0, 256] float hranges[] = { 0, 180 }; float sranges[] = { 0, 256 }; const float* ranges[] = { hranges, sranges }; // 我们计算 h 和 s 通道的直方图 int channels[] = { 0, 1 }; // h 通道索引为 0, s 通道索引为 1 cv::mat hist; cv::calchist(&hsvimage, 1, channels, cv::mat(), hist, 2, histsize, ranges, true, false); cv::normalize(hist, hist, 0, 1, cv::norm_minmax, -1, cv::mat()); return hist; } int main(int argc, char** argv) { if (argc < 3) { std::cerr << "用法: " << argv[0] << " <图像1路径> <图像2路径>" << std::endl; return -1; } cv::mat image1 = cv::imread(argv[1]); cv::mat image2 = cv::imread(argv[2]); if (image1.empty() || image2.empty()) { std::cerr << "错误: 无法加载一张或两张图像!" << std::endl; return -1; } // 计算直方图 cv::mat hist1 = calculatehsnormalizedhistogram(image1); cv::mat hist2 = calculatehsnormalizedhistogram(image2); // 比较直方图的方法 // opencv 提供了多种比较方法,这里演示几种常用的 // histcmp_correl: 相关性 (值越大越相似, 范围 [-1, 1]) // histcmp_chisqr: 卡方 (值越小越相似, 范围 [0, inf)) // histcmp_intersect: 交集 (值越大越相似, 范围 [0, sum(hist1) or sum(hist2) after normalization]) // histcmp_bhattacharyya: 巴氏距离 (值越小越相似, 范围 [0, 1]) std::cout << "直方图比较结果:" << std::endl; double correlation = cv::comparehist(hist1, hist2, cv::histcmp_correl); std::cout << "相关性 (correlation): " << correlation << " (越高越相似)" << std::endl; double chisquare = cv::comparehist(hist1, hist2, cv::histcmp_chisqr); std::cout << "卡方 (chi-square): " << chisquare << " (越低越相似)" << std::endl; double intersection = cv::comparehist(hist1, hist2, cv::histcmp_intersect); std::cout << "交集 (intersection): " << intersection << " (越高越相似)" << std::endl; double bhattacharyya = cv::comparehist(hist1, hist2, cv::histcmp_bhattacharyya); std::cout << "巴氏距离 (bhattacharyya): " << bhattacharyya << " (越低越相似)" << std::endl; // 可选:显示图像 cv::imshow("image 1", image1); cv::imshow("image 2", image2); cv::waitkey(0); cv::destroyallwindows(); return 0; }
代码详解
calculatehsnormalizedhistogram
函数:cv::cvtcolor(image, hsvimage, cv::color_bgr2hsv);
: 将输入的 bgr 图像转换为 hsv 图像。hbins
,sbins
: 定义 h (色调) 和 s (饱和度) 通道直方图的 bin (条柱) 的数量。hranges
,sranges
: 定义 h 和 s 通道像素值的范围。opencv 中 h 的范围是 [0, 179],s 和 v 的范围是 [0, 255]。channels
: 指定要计算直方图的通道,这里是第 0 (h) 和第 1 (s) 通道。cv::calchist(...)
: 计算 h-s 二维直方图。&hsvimage
: 输入图像的指针 (这里用数组是因为可以传入多个图像,但我们只用一个)。1
: 图像数量。channels
: 要统计的通道列表。cv::mat()
: 可选的掩码 (mask),这里不使用。hist
: 输出的直方图。2
: 直方图的维度 (因为是 h-s 二维直方图)。histsize
: 每个维度中 bin 的数量。ranges
: 每个维度值的范围。true
: 直方图是均匀的。false
: 直方图在计算时不累积。
cv::normalize(hist, hist, 0, 1, cv::norm_minmax, -1, cv::mat());
: 将直方图归一化到 [0, 1] 范围,以便比较。
main
函数:- 加载两张图像。
- 调用
calculatehsnormalizedhistogram
分别计算两张图像的 h-s 直方图。 - 使用
cv::comparehist
和不同的比较方法 (cv::histcmp_correl
,cv::histcmp_chisqr
,cv::histcmp_intersect
,cv::histcmp_bhattacharyya
) 比较两个归一化后的直方图。 - 打印比较结果。
- 可选地显示图像。
编译与运行
假设你的 c++ 文件名为 histogram_comparison.cpp
,并且 opencv 已正确配置。
linux / macos (使用 g++):
你需要使用 pkg-config
来获取 opencv 的编译和链接标志。
g++ histogram_comparison.cpp -o histogram_comparator $(pkg-config --cflags --libs opencv4) ./histogram_comparator path/to/image1.jpg path/to/image2.jpg
如果你的 opencv 版本不是 4.x,或者 pkg-config 配置的是旧版本,你可能需要使用 opencv 替换 opencv4。
windows (使用 visual studio):
你需要在项目中配置 opencv 的包含目录、库目录,并链接相应的 opencv 库文件。
cmake (推荐的跨平台方式):
创建一个 cmakelists.txt 文件:
cmake_minimum_required(version 3.10) project(histogramcomparator) set(cmake_cxx_standard 11) # 或更高版本 find_package(opencv required) include_directories(${opencv_include_dirs}) add_executable(histogram_comparator histogram_comparison.cpp) target_link_libraries(histogram_comparator ${opencv_libs})
然后编译:
mkdir build && cd build cmake .. make # 或者在 visual studio 中打开生成的项目并编译 ./histogram_comparator path/to/image1.jpg path/to/image2.jpg
结果解读
cv::comparehist
函数返回一个 double
值,其含义取决于所选的比较方法:
- 相关性 (
cv::histcmp_correl
): 结果范围为 [-1, 1]。值越接近 1,表示两直方图越相似。接近 0 表示不相关,接近 -1 表示负相关。 - 卡方 (
cv::histcmp_chisqr
): 结果范围为 [0, ∞)。值越小,表示两直方图越相似。0 表示完全相同。 - 交集 (
cv::histcmp_intersect
): 对于归一化到 [0,1] 的直方图,如果两个直方图完全相同,则结果为1 (如果未归一化到和为1,则为直方图的总 bin 数或像素数)。值越大,表示重叠部分越多,越相似。 - 巴氏距离 (
cv::histcmp_bhattacharyya
): 结果范围为 [0, 1]。值越小,表示两直方图越相似。0 表示完全相同。
根据应用场景选择合适的比较方法。例如,相关性和交集是衡量相似度的,而卡方和巴氏距离是衡量差异度的。
总结与扩展
直方图比较提供了一种快速评估图像颜色分布相似性的方法。虽然它不考虑空间信息(即像素在哪里),但在许多场景下仍然非常有用。
可扩展的方向包括:
- 不同颜色空间: 尝试在 bgr 或 lab 等其他颜色空间计算直方图。
- 一维直方图: 可以为每个颜色通道分别计算一维直方图,然后组合比较结果。
- 加权直方图: 在计算直方图时,可以根据像素位置或其他特征给予不同的权重。
- 结合其他特征: 将直方图特征与其他图像特征(如纹理、形状)结合起来,以获得更鲁棒的图像比较。
- 自适应 bin 数量: 根据图像内容动态调整直方图的 bin 数量。
希望本文能帮助你理解并使用 opencv 进行图像直方图比较!
以上就是利用c++和opencv库计算图像颜色直方图并比较相似度的详细内容,更多关于c++ opencv图像直方图和相似度的资料请关注代码网其它相关文章!
发表评论