使用 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 进行图像直方图比较!

相关推荐
猷咪12 小时前
C++基础
开发语言·c++
IT·小灰灰12 小时前
30行PHP,利用硅基流动API,网页客服瞬间上线
开发语言·人工智能·aigc·php
快点好好学习吧12 小时前
phpize 依赖 php-config 获取 PHP 信息的庖丁解牛
android·开发语言·php
秦老师Q12 小时前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
烟锁池塘柳012 小时前
解决Google Scholar “We‘re sorry... but your computer or network may be sending automated queries.”的问题
开发语言
是誰萆微了承諾12 小时前
php 对接deepseek
android·开发语言·php
CSDN_RTKLIB12 小时前
WideCharToMultiByte与T2A
c++
2601_9498683612 小时前
Flutter for OpenHarmony 电子合同签署App实战 - 已签合同实现
java·开发语言·flutter
星火开发设计13 小时前
类型别名 typedef:让复杂类型更简洁
开发语言·c++·学习·算法·函数·知识
蒹葭玉树13 小时前
【C++上岸】C++常见面试题目--操作系统篇(第二十八期)
linux·c++·面试