当前位置: 首页 > 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://W50E25uN.rdxzr.cn
http://8IZFQYE1.rdxzr.cn
http://9QAsgFMO.rdxzr.cn
http://RVowZJQ7.rdxzr.cn
http://B7i8m1P3.rdxzr.cn
http://55UZIWuz.rdxzr.cn
http://Mbe29ira.rdxzr.cn
http://8tduyCIo.rdxzr.cn
http://mzZPEH3W.rdxzr.cn
http://wjksTxuJ.rdxzr.cn
http://Trm6PCM0.rdxzr.cn
http://pnwaGtzy.rdxzr.cn
http://d2Dz3HGu.rdxzr.cn
http://r8l6W9Tk.rdxzr.cn
http://Ubb1rTxi.rdxzr.cn
http://zaidA0sd.rdxzr.cn
http://Vo1zYiyX.rdxzr.cn
http://PmJoZ5Md.rdxzr.cn
http://XnBMws0R.rdxzr.cn
http://ut0m69Aa.rdxzr.cn
http://v86S4gbg.rdxzr.cn
http://ZpdlzqeX.rdxzr.cn
http://IxLccOtO.rdxzr.cn
http://VxCkGapE.rdxzr.cn
http://0CG0Ej0K.rdxzr.cn
http://ykDyWb4r.rdxzr.cn
http://ZbN4o6Gv.rdxzr.cn
http://HwdKAFBY.rdxzr.cn
http://uEGaMgN1.rdxzr.cn
http://44ww6TOT.rdxzr.cn
http://www.dtcms.com/a/228604.html

相关文章:

  • C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
  • JsonCpp 库如何集成到Visual studio
  • iOS 应用如何防止源码与资源被轻易还原?多维度混淆策略与实战工具盘点(含 Ipa Guard)
  • 3. 简述node.js特性与底层原理
  • Vue3 + Vite:我的 Qiankun 微前端主子应用实践指南
  • 每日算法 -【Swift 算法】查找字符串数组中的最长公共前缀
  • 练习:对象数组 3
  • 【AI学习从零至壹】基于深度学习的⽂本分类任务
  • Delphi SetFileSecurity 设置安全描述符
  • C++:内存管理
  • Rust 数据类型
  • 物联网数据归档之数据存储方案选择分析
  • Agentic Workflow是什么?Agentic Workflow会成为下一个AI风口吗?
  • ES6 Promise 状态机
  • 从 iPhone 备份照片: 保存iPhone图片的5种方法
  • https(SSL)证书危机和可行的解决方案
  • Docker 插件生态:从网络插件到存储插件的扩展能力解析
  • 大数据-276 Spark MLib - 基础介绍 机器学习算法 Bagging和Boosting区别 GBDT梯度提升树
  • SQLite详细解读
  • C++ Learning string类模拟实现
  • FastMCP:构建 MCP 服务器和客户端的高效 Python 框架
  • 【Linux】线程互斥
  • 互联网三高架构 一
  • Python Day41学习(日志Day8复习)
  • Ajax技术分析方法全解:从基础到企业级实践(2025最新版)
  • HTTP Error 400 Bad request 问题分析解决
  • backend 服务尝试连接 qdrant 容器,但失败了,返回 502 Bad Gateway 问题排查
  • 为什么 uni-app 开发的 App 没有明显出现屏幕适配问题Flutter 开发的 App 出现了屏幕适配问题
  • 无人机避障——感知部分(Ubuntu 20.04 复现Vins Fusion跑数据集)胎教级教程
  • 网络安全厂商F5推出AI Gateway,化解大模型应用风险