利用C++和OpenCV庫計(jì)算圖像顏色直方圖并比較相似度
簡介
圖像直方圖是圖像中像素強(qiáng)度分布的圖形表示。對(duì)于彩色 圖像,我們通常會(huì)為每個(gè)顏色通道(例如 BGR 或 HSV)計(jì)算直方圖。通過比較兩張圖片的直方圖,我們可以獲得它們?cè)陬伾植忌系南嗨瞥潭取penCV 提供了 cv::calcHist 函數(shù)用于計(jì)算直方圖,以及 cv::compareHist 函數(shù)用于比較兩個(gè)直方圖。
先決條件
- C++ 編譯器: 如 G++ (MinGW for Windows), Clang, MSVC。
- OpenCV 庫: 需要正確安裝并配置好編譯環(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è)通道的一維直方圖。
- 歸一化直方圖 (可選但推薦): 為了消除圖像尺寸差異帶來的影響,通常會(huì)對(duì)直方圖進(jìn)行歸一化,使其和為1??梢允褂?nbsp;
cv::normalize
。 - 比較直方圖: 使用
cv::compareHist
函數(shù),選擇一種比較方法(如相關(guān)性、卡方、交集、巴氏距離)來計(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ò)誤: 無法加載一張或兩張圖像!" << 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
來獲取 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 的包含目錄、庫目錄,并鏈接相應(yīng)的 OpenCV 庫文件。
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 中打開生成的項(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)用場景選擇合適的比較方法。例如,相關(guān)性和交集是衡量相似度的,而卡方和巴氏距離是衡量差異度的。
總結(jié)與擴(kuò)展
直方圖比較提供了一種快速評(píng)估圖像顏色分布相似性的方法。雖然它不考慮空間信息(即像素在哪里),但在許多場景下仍然非常有用。
可擴(kuò)展的方向包括:
- 不同顏色空間: 嘗試在 BGR 或 Lab 等其他顏色空間計(jì)算直方圖。
- 一維直方圖: 可以為每個(gè)顏色通道分別計(jì)算一維直方圖,然后組合比較結(jié)果。
- 加權(quán)直方圖: 在計(jì)算直方圖時(shí),可以根據(jù)像素位置或其他特征給予不同的權(quán)重。
- 結(jié)合其他特征: 將直方圖特征與其他圖像特征(如紋理、形狀)結(jié)合起來,以獲得更魯棒的圖像比較。
- 自適應(yīng) bin 數(shù)量: 根據(jù)圖像內(nèi)容動(dòng)態(tài)調(diào)整直方圖的 bin 數(shù)量。
希望本文能幫助你理解并使用 OpenCV 進(jìn)行圖像直方圖比較!
以上就是利用C++和OpenCV庫計(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ì)分析,通過對(duì)網(wǎng)絡(luò)流量的細(xì)致觀察和分析,幫助管理員了解和優(yōu)化網(wǎng)絡(luò)的性能,本文將通過C++實(shí)現(xiàn)網(wǎng)絡(luò)流量分析工具,有需要的可以參考下2023-10-10Vscode配置C/C++環(huán)境使用minGW(保姆級(jí)配置過程)
本文主要介紹了Vscode配置C/C++環(huán)境使用minGW(保姆級(jí)配置過程),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02C++實(shí)現(xiàn)LeetCode(125.驗(yàn)證回文字符串)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(驗(yàn)證回文字符串).本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07Visual Studio 如何創(chuàng)建C/C++項(xiàng)目問題
這篇文章主要介紹了Visual Studio 如何創(chuàng)建C/C++項(xiàng)目問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02C++ Opencv自寫函數(shù)實(shí)現(xiàn)膨脹腐蝕處理技巧
這篇文章主要介紹了C++ Opencv 自寫函數(shù)實(shí)現(xiàn)膨脹腐蝕處理,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10Qt中PaintEvent繪制實(shí)時(shí)波形圖的實(shí)現(xiàn)示例
本文主要介紹了Qt中PaintEvent繪制實(shí)時(shí)波形圖的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06