理解并運用 OpenCV 中的圖像直方圖 📊🖼?
圖像直方圖是計算機視覺和圖像處理中一種基本且強大的工具,它提供了圖像像素強度分布的圖形化表示。OpenCV 作為一個全面的計算機視覺庫,內置了計算和可視化直方圖的強大功能。本文將深入探討直方圖的概念、其在 OpenCV 中的實現以及一些常見的應用場景。
什么是圖像直方圖?🤔
圖像直方圖是一個統計圖表,顯示了圖像中每個強度級別(或顏色級別)的像素數量。對于灰度圖像,直方圖會顯示從 0(黑色)到 255(白色)每個灰度值出現的頻率。對于彩色圖像,可以為每個顏色通道(例如 BGR 或 HSV 的各個通道)分別計算直方圖,或者計算組合的顏色直方圖。
直方圖可以幫助我們理解:
- 圖像的亮度與對比度:直方圖的分布可以揭示圖像是偏暗、偏亮,還是對比度良好。
- 顏色分布:在彩色圖像中,可以了解哪些顏色占主導地位。
- 閾值選擇:直方圖的波谷通常是選擇分割閾值的良好指示。
- 圖像相似性:比較兩張圖像的直方圖可以作為衡量它們內容相似度的一種方法。
OpenCV 中的直方圖計算:cv::calcHist
🌟
OpenCV 中計算直方圖的核心函數是 cv::calcHist
。它非常靈活,可以處理單通道或多通道圖像,并允許用戶指定各種參數。
函數原型 (C++)
void cv::calcHist(const cv::Mat* images, // 輸入圖像數組 (通常只有一個圖像)int nimages, // 輸入圖像的數量const int* channels, // 需要計算直方圖的通道索引數組cv::InputArray mask, // 可選的掩碼,如果提供,則只計算掩碼區域內的像素cv::OutputArray hist, // 輸出的直方圖int dims, // 直方圖的維度 (通常為 1D, 2D, 或 3D)const int* histSize, // 每個維度上直方圖 "bin" (條柱) 的數量數組const float** ranges, // 每個維度上像素值的范圍數組bool uniform = true, // 直方圖的 bin 是否具有統一的大小bool accumulate = false // 如果為 true,則在多次調用中累積直方圖
);
參數詳解:
images
: 指向輸入圖像的指針數組。即使只處理一張圖像,也需要將其地址放入一個數組中。nimages
: 輸入圖像的數量。通常為1
。channels
: 一個整數數組,指定了要為哪些通道計算直方圖。例如,對于灰度圖,它是{0}
;對于 BGR 彩色圖的 B 通道,也是{0}
;如果想計算 B 和 G 通道的二維直方圖,則是{0, 1}
。mask
: 一個可選的cv::Mat
對象。如果非空,它必須是一個與輸入圖像大小相同的 8 位單通道圖像。只有掩碼中非零值的對應像素才會被包含在直方圖計算中。hist
: 輸出的直方圖,通常是一個cv::Mat
對象(浮點型)。dims
: 直方圖的維度。對于單通道灰度圖或單個顏色通道,通常是1
。histSize
: 一個整數數組,表示每個維度上 “bin” 的數量。例如,對于一個灰度圖,如果我們想將 0-255 的值分成 256 個 bin,那么histSize
就是{256}
。ranges
: 一個浮點型指針數組,定義了每個維度上像素值的范圍。例如,對于 8 位灰度圖,通常是{{0.0f, 256.0f}}
(注意上限是不包含的)。uniform
: 布爾值。如果為true
,則直方圖的 bin 具有統一的大小;否則,bin 的大小可以不均勻(通過ranges
定義)。默認為true
。accumulate
: 布爾值。如果為true
,則直方圖在多次調用calcHist
時會累積結果到hist
中,而不是重新開始計算。默認為false
。
繪制直方圖 🎨
計算出直方圖后(它是一個數值數組或矩陣),我們通常需要將其可視化以便更好地理解。OpenCV 本身不直接提供復雜的繪圖工具,但我們可以很容易地創建一個表示直方圖的圖像。
基本步驟如下:
- 找到直方圖中的最大值,用于歸一化。
- 創建一個空白圖像作為畫布。
- 對于直方圖中的每個 bin,根據其值(可能經過歸一化)繪制一條線或一個矩形。
示例:繪制單通道直方圖 (C++)
#include <opencv2/opencv.hpp>
#include <iostream>// ... (加載圖像到 srcImage) ...cv::Mat grayImage;
if (srcImage.channels() == 3) {cv::cvtColor(srcImage, grayImage, cv::COLORBGR2GRAY);
} else {grayImage = srcImage;
}// 設置直方圖參數
int histSizeNum = 256; // bin 的數量
float range[] = {0, 256}; // 像素值范圍 (不包括上限)
const float* histRange[] = {range};
bool uniform = true;
bool accumulate = false;
cv::Mat hist;// 計算直方圖
cv::calcHist(&grayImage, 1, 0, cv::Mat(), hist, 1, &histSizeNum, histRange, uniform, accumulate);// 創建用于繪制直方圖的圖像
int hist_w = 512; // 直方圖圖像寬度
int hist_h = 400; // 直方圖圖像高度
int bin_w = cvRound((double)hist_w / histSizeNum); // 每個 bin 的寬度cv::Mat histImage(hist_h, hist_w, CV_8UC3, cv::Scalar(20, 20, 20)); // 深灰色背景// 歸一化直方圖到 [0, histImage.rows]
cv::normalize(hist, hist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());// 繪制直方圖
for (int i = 1; i < histSizeNum; i++) {cv::line(histImage,cv::Point(bin_w * (i - 1), hist_h - cvRound(hist.at<float>(i - 1))),cv::Point(bin_w * (i), hist_h - cvRound(hist.at<float>(i))),cv::Scalar(200, 200, 200), // 淺灰色線條2, 8, 0);
}// 顯示原始圖像和直方圖
cv::imshow("Source Image", srcImage);
cv::imshow("Grayscale Image", grayImage);
cv::imshow("Histogram", histImage);
cv::waitKey(0);
直方圖的應用 🚀
1. 直方圖均衡化 (Histogram Equalization)
直方圖均衡化是一種通過重新分布圖像的像素強度來增強圖像對比度的方法。其目標是使直方圖盡可能平坦,從而擴展像素強度的動態范圍。OpenCV 提供了 cv::equalizeHist()
函數專門用于灰度圖像的直方圖均衡化。
對于彩色圖像,通常的做法是先將圖像轉換到像 HSV 或 YCrCb 這樣的顏色空間,然后對亮度通道(V 或 Y 通道)進行均衡化,最后再轉換回 BGR 空間。
2. 直方圖比較 (Histogram Comparison)
比較兩幅圖像的直方圖可以用來衡量它們的相似性。這在圖像檢索、對象識別等領域非常有用。OpenCV 的 cv::compareHist()
函數提供了多種比較方法,例如:
- 相關性 (Correlation):
cv::HISTCMP_CORREL
- 卡方 (Chi-Square):
cv::HISTCMP_CHISQR
- 交叉點 (Intersection):
cv::HISTCMP_INTERSECT
- 巴氏距離 (Bhattacharyya distance):
cv::HISTCMP_BHATTACHARYYA
(或cv::HISTCMP_HELLINGER
,它與巴氏距離等價)
3. 直方圖反向投影 (Histogram Backprojection)
這是一種基于顏色的圖像分割技術。首先,計算你感興趣的目標對象的顏色直方圖(模型直方圖)。然后,在輸入圖像中,對于每個像素,查找其顏色在模型直方圖中的概率(或 bin 值)。這個概率圖就是反向投影。概率高的區域表明該區域的顏色與目標對象的顏色相似。OpenCV 提供了 cv::calcBackProject()
函數。
總結 ?
圖像直方圖不僅是分析圖像像素分布的簡單工具,更是許多高級圖像處理和計算機視覺技術的基礎。通過 OpenCV 的 cv::calcHist
函數,我們可以方便地計算直方圖,并結合其他函數如 cv::equalizeHist
、cv::compareHist
和 cv::calcBackProject
來實現各種強大的功能,從圖像增強到對象檢測。掌握直方圖的原理和應用將極大地提升你在圖像處理項目中的能力。