当前位置: 首页 > news >正文

使用 C++/OpenCV 图像直方图比较两个图片相似度

使用 C++/OpenCV 进行图像直方图比较

本文介绍如何利用 C++ 和 OpenCV 库计算图像的颜色直方图,并使用不同的方法比较两张图片的相似度。直方图比较是图像检索、目标识别等领域中一种简单而有效的技术。


目录

  1. 简介
  2. 先决条件
  3. 核心步骤
  4. 代码实现
  5. 代码详解
  6. 编译与运行
  7. 结果解读
  8. 总结与扩展

简介

图像直方图是图像中像素强度分布的图形表示。对于彩色图像,我们通常会为每个颜色通道(例如 BGR 或 HSV)计算直方图。通过比较两张图片的直方图,我们可以获得它们在颜色分布上的相似程度。OpenCV 提供了 cv::calcHist 函数用于计算直方图,以及 cv::compareHist 函数用于比较两个直方图。


先决条件

  • C++ 编译器: 如 G++ (MinGW for Windows), Clang, MSVC。
  • OpenCV 库: 需要正确安装并配置好编译环境 (版本 3.x 或 4.x)。
  • 两张待比较的图像: 准备好两张图片文件(例如 image1.jpgimage2.jpg)。

核心步骤

  1. 加载图像: 使用 cv::imread 读取两张待比较的图像。
  2. 色彩空间转换 (可选但推荐): 将图像从 BGR 转换到 HSV 色彩空间。HSV 对光照变化的鲁棒性通常比 BGR 好,尤其是 H (Hue) 和 S (Saturation) 通道。
  3. 计算直方图:
    • 定义直方图参数(如通道、bins 数量、取值范围)。
    • 使用 cv::calcHist 为每张图像计算 H-S 二维直方图或单个通道的一维直方图。
  4. 归一化直方图 (可选但推荐): 为了消除图像尺寸差异带来的影响,通常会对直方图进行归一化,使其和为1。可以使用 cv::normalize
  5. 比较直方图: 使用 cv::compareHist 函数,选择一种比较方法(如相关性、卡方、交集、巴氏距离)来计算两个直方图之间的相似度/差异度。
  6. 输出结果: 显示比较得分。

代码实现

#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 通道索引为 1cv::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;
}

代码详解

  1. 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] 范围,以便比较。
  2. 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 进行图像直方图比较!

http://www.xdnf.cn/news/780067.html

相关文章:

  • 奥威BI+AI数据分析:企业数智化转型的加速器
  • Redis 缓存粒度如何控制?缓存整个对象还是部分字段?
  • 三目标微网对经济性进行优化调度
  • Hadoop HDFS 体系结构与文件读写流程剖析
  • 宝塔面板安装nodejs后,通过node -v获取不到版本号,报错node: command not found
  • Qwen与Llama分词器核心差异解析
  • 【RabbitMQ】- Channel和Delivery Tag机制
  • 【mysql】BIGINT UNSIGNED字段被表示为float科学计数法 丢失精度问题
  • 学习路之PHP--easyswoole使用视图和模板
  • MFC Resource.h 文件详解与修改指南
  • nginx+tomcat动静分离、负载均衡
  • JavaScript性能优化实战:从核心原理到工程实践的全流程解析
  • 【大模型:知识图谱】--1.py2neo连接图数据库neo4j
  • Neo4j 数据建模:原理、技术与实践指南
  • Java详解LeetCode 热题 100(25):LeetCode 141. 环形链表(Linked List Cycle)详解
  • JVM—垃圾收集算法和HotSpot算法实现细节
  • Kerberos面试内容整理-Kerberos 的配置与排障
  • 力扣每日一题——分发糖果
  • React Native图片预加载:让你的应用图片预览像德芙一样丝滑
  • 实验设计与分析(第6版,Montgomery著,傅珏生译) 第10章拟合回归模型10.9节思考题10.1 R语言解题
  • Python趣学篇:从零打造智能AI井字棋游戏(Python + Tkinter + Minimax算法)
  • 编译 Linux openssl
  • 黑客利用GitHub现成工具通过DevOps API发起加密货币挖矿攻击
  • C++语法系列之类型转换
  • Catboost算法原理及应用场景
  • 生成对抗网络(GAN)基础原理深度解析:从直观理解到形式化表达
  • C语言学习—数据类型20250603
  • NLP学习路线图(二十):FastText
  • K8S上使用helm部署 Prometheus + Grafana
  • Grafana-State timeline状态时间线