利用C++和OpenCV庫(kù)計(jì)算圖像顏色直方圖并比較相似度
簡(jiǎn)介
圖像直方圖是圖像中像素強(qiáng)度分布的圖形表示。對(duì)于彩色 圖像,我們通常會(huì)為每個(gè)顏色通道(例如 BGR 或 HSV)計(jì)算直方圖。通過(guò)比較兩張圖片的直方圖,我們可以獲得它們?cè)陬伾植忌系南嗨瞥潭取penCV 提供了 cv::calcHist 函數(shù)用于計(jì)算直方圖,以及 cv::compareHist 函數(shù)用于比較兩個(gè)直方圖。
先決條件
- C++ 編譯器: 如 G++ (MinGW for Windows), Clang, MSVC。
- OpenCV 庫(kù): 需要正確安裝并配置好編譯環(huán)境 (版本 3.x 或 4.x)。
- 兩張待比較的圖像: 準(zhǔn)備好兩張圖片文件(例如 image1.jpg 和 image2.jpg)。
核心步驟
- 加載圖像: 使用
cv::imread讀取兩張待比較的圖像。 - 色彩空間轉(zhuǎn)換 (可選但推薦): 將圖像從 BGR 轉(zhuǎn)換到 HSV 色彩空間。HSV 對(duì)光照變化的魯棒性通常比 BGR 好,尤其是 H (Hue) 和 S (Saturation) 通道。
- 計(jì)算直方圖:
- 定義直方圖參數(shù)(如通道、bins 數(shù)量、取值范圍)。
- 使用
cv::calcHist為每張圖像計(jì)算 H-S 二維直方圖或單個(gè)通道的一維直方圖。
- 歸一化直方圖 (可選但推薦): 為了消除圖像尺寸差異帶來(lái)的影響,通常會(huì)對(duì)直方圖進(jìn)行歸一化,使其和為1??梢允褂?nbsp;
cv::normalize。 - 比較直方圖: 使用
cv::compareHist函數(shù),選擇一種比較方法(如相關(guān)性、卡方、交集、巴氏距離)來(lái)計(jì)算兩個(gè)直方圖之間的相似度/差異度。 - 輸出結(jié)果: 顯示比較得分。
代碼實(shí)現(xiàn)
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
// 函數(shù):計(jì)算并歸一化圖像的 H-S 直方圖
cv::Mat calculateHSNormalizedHistogram(const cv::Mat& image) {
cv::Mat hsvImage;
cv::cvtColor(image, hsvImage, cv::COLOR_BGR2HSV);
// H-S 直方圖參數(shù)
// 我們只使用 H 和 S 兩個(gè)通道
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 };
// 我們計(jì)算 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 << "錯(cuò)誤: 無(wú)法加載一張或兩張圖像!" << std::endl;
return -1;
}
// 計(jì)算直方圖
cv::Mat hist1 = calculateHSNormalizedHistogram(image1);
cv::Mat hist2 = calculateHSNormalizedHistogram(image2);
// 比較直方圖的方法
// OpenCV 提供了多種比較方法,這里演示幾種常用的
// HISTCMP_CORREL: 相關(guān)性 (值越大越相似, 范圍 [-1, 1])
// HISTCMP_CHISQR: 卡方 (值越小越相似, 范圍 [0, inf))
// HISTCMP_INTERSECT: 交集 (值越大越相似, 范圍 [0, sum(hist1) or sum(hist2) after normalization])
// HISTCMP_BHATTACHARYYA: 巴氏距離 (值越小越相似, 范圍 [0, 1])
std::cout << "直方圖比較結(jié)果:" << std::endl;
double correlation = cv::compareHist(hist1, hist2, cv::HISTCMP_CORREL);
std::cout << "相關(guān)性 (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函數(shù):cv::cvtColor(image, hsvImage, cv::COLOR_BGR2HSV);: 將輸入的 BGR 圖像轉(zhuǎn)換為 HSV 圖像。hBins,sBins: 定義 H (色調(diào)) 和 S (飽和度) 通道直方圖的 bin (條柱) 的數(shù)量。hRanges,sRanges: 定義 H 和 S 通道像素值的范圍。OpenCV 中 H 的范圍是 [0, 179],S 和 V 的范圍是 [0, 255]。channels: 指定要計(jì)算直方圖的通道,這里是第 0 (H) 和第 1 (S) 通道。cv::calcHist(...): 計(jì)算 H-S 二維直方圖。&hsvImage: 輸入圖像的指針 (這里用數(shù)組是因?yàn)榭梢詡魅攵鄠€(gè)圖像,但我們只用一個(gè))。1: 圖像數(shù)量。channels: 要統(tǒng)計(jì)的通道列表。cv::Mat(): 可選的掩碼 (mask),這里不使用。hist: 輸出的直方圖。2: 直方圖的維度 (因?yàn)槭?H-S 二維直方圖)。histSize: 每個(gè)維度中 bin 的數(shù)量。ranges: 每個(gè)維度值的范圍。true: 直方圖是均勻的。false: 直方圖在計(jì)算時(shí)不累積。
cv::normalize(hist, hist, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());: 將直方圖歸一化到 [0, 1] 范圍,以便比較。
main函數(shù):- 加載兩張圖像。
- 調(diào)用
calculateHSNormalizedHistogram分別計(jì)算兩張圖像的 H-S 直方圖。 - 使用
cv::compareHist和不同的比較方法 (cv::HISTCMP_CORREL,cv::HISTCMP_CHISQR,cv::HISTCMP_INTERSECT,cv::HISTCMP_BHATTACHARYYA) 比較兩個(gè)歸一化后的直方圖。 - 打印比較結(jié)果。
- 可選地顯示圖像。
編譯與運(yùn)行
假設(shè)你的 C++ 文件名為 histogram_comparison.cpp,并且 OpenCV 已正確配置。
Linux / macOS (使用 g++):
你需要使用 pkg-config 來(lái)獲取 OpenCV 的編譯和鏈接標(biāo)志。
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):
你需要在項(xiàng)目中配置 OpenCV 的包含目錄、庫(kù)目錄,并鏈接相應(yīng)的 OpenCV 庫(kù)文件。
CMake (推薦的跨平臺(tái)方式):
創(chuàng)建一個(gè) 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 中打開(kāi)生成的項(xiàng)目并編譯 ./histogram_comparator path/to/image1.jpg path/to/image2.jpg
結(jié)果解讀
cv::compareHist 函數(shù)返回一個(gè) double 值,其含義取決于所選的比較方法:
- 相關(guān)性 (
cv::HISTCMP_CORREL): 結(jié)果范圍為 [-1, 1]。值越接近 1,表示兩直方圖越相似。接近 0 表示不相關(guān),接近 -1 表示負(fù)相關(guān)。 - 卡方 (
cv::HISTCMP_CHISQR): 結(jié)果范圍為 [0, ∞)。值越小,表示兩直方圖越相似。0 表示完全相同。 - 交集 (
cv::HISTCMP_INTERSECT): 對(duì)于歸一化到 [0,1] 的直方圖,如果兩個(gè)直方圖完全相同,則結(jié)果為1 (如果未歸一化到和為1,則為直方圖的總 bin 數(shù)或像素?cái)?shù))。值越大,表示重疊部分越多,越相似。 - 巴氏距離 (
cv::HISTCMP_BHATTACHARYYA): 結(jié)果范圍為 [0, 1]。值越小,表示兩直方圖越相似。0 表示完全相同。
根據(jù)應(yīng)用場(chǎng)景選擇合適的比較方法。例如,相關(guān)性和交集是衡量相似度的,而卡方和巴氏距離是衡量差異度的。
總結(jié)與擴(kuò)展
直方圖比較提供了一種快速評(píng)估圖像顏色分布相似性的方法。雖然它不考慮空間信息(即像素在哪里),但在許多場(chǎng)景下仍然非常有用。
可擴(kuò)展的方向包括:
- 不同顏色空間: 嘗試在 BGR 或 Lab 等其他顏色空間計(jì)算直方圖。
- 一維直方圖: 可以為每個(gè)顏色通道分別計(jì)算一維直方圖,然后組合比較結(jié)果。
- 加權(quán)直方圖: 在計(jì)算直方圖時(shí),可以根據(jù)像素位置或其他特征給予不同的權(quán)重。
- 結(jié)合其他特征: 將直方圖特征與其他圖像特征(如紋理、形狀)結(jié)合起來(lái),以獲得更魯棒的圖像比較。
- 自適應(yīng) bin 數(shù)量: 根據(jù)圖像內(nèi)容動(dòng)態(tài)調(diào)整直方圖的 bin 數(shù)量。
希望本文能幫助你理解并使用 OpenCV 進(jìn)行圖像直方圖比較!
以上就是利用C++和OpenCV庫(kù)計(jì)算圖像顏色直方圖并比較相似度的詳細(xì)內(nèi)容,更多關(guān)于C++ OpenCV圖像直方圖和相似度的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Linux?C/C++實(shí)現(xiàn)網(wǎng)絡(luò)流量分析工具
網(wǎng)絡(luò)流量分析的原理基于對(duì)數(shù)據(jù)包的捕獲、解析和統(tǒng)計(jì)分析,通過(guò)對(duì)網(wǎng)絡(luò)流量的細(xì)致觀察和分析,幫助管理員了解和優(yōu)化網(wǎng)絡(luò)的性能,本文將通過(guò)C++實(shí)現(xiàn)網(wǎng)絡(luò)流量分析工具,有需要的可以參考下2023-10-10
Vscode配置C/C++環(huán)境使用minGW(保姆級(jí)配置過(guò)程)
本文主要介紹了Vscode配置C/C++環(huán)境使用minGW(保姆級(jí)配置過(guò)程),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
C語(yǔ)言必背的一些經(jīng)典程序代碼實(shí)例
C語(yǔ)言是一種高級(jí)編程語(yǔ)言,具有很多優(yōu)點(diǎn),下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言必背的一些經(jīng)典程序代碼,文中通過(guò)詳細(xì)的實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-05-05
C++實(shí)現(xiàn)LeetCode(125.驗(yàn)證回文字符串)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(驗(yàn)證回文字符串).本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
Visual Studio 如何創(chuàng)建C/C++項(xiàng)目問(wèn)題
這篇文章主要介紹了Visual Studio 如何創(chuàng)建C/C++項(xiàng)目問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02
C++ Opencv自寫函數(shù)實(shí)現(xiàn)膨脹腐蝕處理技巧
這篇文章主要介紹了C++ Opencv 自寫函數(shù)實(shí)現(xiàn)膨脹腐蝕處理,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10
Qt中PaintEvent繪制實(shí)時(shí)波形圖的實(shí)現(xiàn)示例
本文主要介紹了Qt中PaintEvent繪制實(shí)時(shí)波形圖的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06

