使用 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. 输出结果: 显示比较得分。

代码实现

cpp 复制代码
#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;
}

代码详解

  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 的编译和链接标志。

bash 复制代码
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 复制代码
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})

然后编译:

bash 复制代码
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 进行图像直方图比较!

相关推荐
yxc_inspire2 分钟前
基于Qt的app开发第十三天
c++·qt·app·tcp·面向对象
jndingxin24 分钟前
OPenCV CUDA模块目标检测----- HOG 特征提取和目标检测类cv::cuda::HOG
人工智能·opencv·目标检测
come1123427 分钟前
Claude 写 PHP 项目的完整小白教程
开发语言·php
虾球xz30 分钟前
CppCon 2015 学习:Concurrency TS Editor’s Report
开发语言·c++·学习
潇-xiao33 分钟前
Qt 按钮类控件(Push Button 与 Radio Button)(1)
c++·qt
板鸭〈小号〉36 分钟前
命名管道实现本地通信
开发语言·c++
清醒的兰41 分钟前
OpenCV 图像像素的逻辑操作
人工智能·opencv·计算机视觉
火兮明兮1 小时前
Python训练第四十五天
开发语言·python
我爱Jack2 小时前
ObjectMapper 在 Spring 统一响应处理中的作用详解
java·开发语言
小白杨树树2 小时前
【SSM】SpringMVC学习笔记8:拦截器
java·开发语言