目錄
一、圖像直方圖:讀懂圖像的 “像素分布報告”
1. 什么是圖像直方圖?
2. 圖像直方圖的核心作用
(1)分析亮度分布
(2)判斷對比度高低
(3)輔助圖像增強與閾值分割
(4)檢測色彩偏移
3、舉例
4. OpenCV 直方圖計算:cv2.calcHist()?詳解
(1)函數語法與參數解析
(2)實戰案例:計算灰度圖與彩色圖直方圖
(3)運行結果分析
二、掩碼圖像(Mask):精準定位感興趣區域
1. 什么是掩碼圖像?
2. 掩碼的核心應用場景
3. OpenCV 掩碼操作:cv2.bitwise_and()?詳解
(1)函數語法
(2)實戰案例:用掩碼提取局部區域并計算直方圖
3)運行結果分析
在計算機視覺領域,圖像直方圖和掩碼圖像是兩種基礎且核心的技術。直方圖是分析圖像像素分布的 “數據眼鏡”,掩碼則是精準定位感興趣區域的 “手術刀”。本文將從概念原理出發,結合 OpenCV 實戰代碼,詳細講解圖像直方圖的計算與應用、掩碼圖像的制作與使用,以及直方圖均衡化(含自適應均衡化)的實現,幫助大家掌握這兩項 OpenCV 高階技能。
一、圖像直方圖:讀懂圖像的 “像素分布報告”
1. 什么是圖像直方圖?
圖像直方圖是描述圖像像素值分布的統計圖形,它將圖像的像素值(通常 0-255)作為橫軸,像素值出現的頻次(或概率)作為縱軸,用柱狀圖或折線圖展示。
- 對灰度圖:直方圖反映不同灰度級(0-255)的像素數量;
- 對彩色圖:可分別展示藍(B)、綠(G)、紅(R)三通道的像素分布。
簡單來說,直方圖就像圖像的 “體檢報告”,通過它能快速判斷圖像的亮度、對比度等關鍵信息。
2. 圖像直方圖的核心作用
(1)分析亮度分布
- 若直方圖峰值集中在左側(低像素值):圖像整體偏暗;
- 若峰值集中在右側(高像素值):圖像整體偏亮;
- 若峰值分布均勻:圖像亮度適中。
(2)判斷對比度高低
- 直方圖寬度寬(像素值跨度大,從 0 到 255 覆蓋完整):對比度高,細節清晰;
- 直方圖寬度窄(像素值集中在某一區間):對比度低,圖像灰蒙蒙。
(3)輔助圖像增強與閾值分割
- 直方圖均衡化:通過重新分布像素值,擴大對比度;
- 閾值分割:利用直方圖的 “雙峰谷底” 確定分割閾值,分離前景與背景(如分割文字與背景)。
(4)檢測色彩偏移
對彩色圖三通道直方圖對比,若某一通道(如紅色)峰值偏移明顯,說明圖像存在色彩偏色。
3、舉例
灰度值在0 - 255范圍之間總共 256 個值,可以將我們的范圍劃分為子部分(稱為bins),例
4. OpenCV 直方圖計算:cv2.calcHist()
?詳解
(1)函數語法與參數解析
cv2.calcHist(images, channels, mask, histSize, ranges, accumulate=False)
(2)實戰案例:計算灰度圖與彩色圖直方圖
import cv2
import matplotlib.pyplot as plt
import numpy as np# 1. 讀取灰度圖并計算直方圖
# 讀取灰度圖像(以手機圖像為例)
gray_img = cv2.imread("phone.png", cv2.IMREAD_GRAYSCALE)
if gray_img is None:print("圖像讀取失敗,請檢查文件路徑!")exit()# 方法1:用matplotlib直接繪制(需將圖像展平為一維數組)
plt.figure(figsize=(12, 5))
plt.subplot(1, 3, 1)
plt.hist(gray_img.ravel(), bins=256, color='gray') # ravel()將二維圖像轉為一維
plt.title("灰度圖直方圖(matplotlib)")
plt.xlabel("像素值(0-255)")
plt.ylabel("像素數量")# 方法2:用OpenCV的cv2.calcHist()計算(bins=16,分組更粗)
hist_cv2 = cv2.calcHist([gray_img], [0], None, [16], [0, 256])
plt.subplot(1, 3, 2)
plt.plot(hist_cv2, color='black', marker='o') # 折線圖展示
plt.title("灰度圖直方圖(cv2.calcHist,bins=16)")
plt.xlabel("分組索引(0-15)")
plt.ylabel("像素數量")# 2. 讀取彩色圖并計算三通道直方圖
color_img = cv2.imread("phone.png") # OpenCV默認BGR格式
color = ('b', 'g', 'r') # 對應B/G/R通道plt.subplot(1, 3, 3)
for i, col in enumerate(color):# 分別計算B、G、R通道的直方圖hist_channel = cv2.calcHist([color_img], [i], None, [256], [0, 256])plt.plot(hist_channel, color=col, label=f"{col.upper()}通道")plt.title("彩色圖三通道直方圖")
plt.xlabel("像素值(0-255)")
plt.ylabel("像素數量")
plt.legend()
plt.tight_layout()
plt.show()# 展示原始圖像
cv2.imshow("灰度圖", gray_img)
cv2.imshow("彩色圖(BGR)", color_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
(3)運行結果分析
- 灰度圖直方圖:可直觀看到圖像像素集中在哪個區間(如集中在 100-200,說明圖像偏亮);
- 彩色圖三通道:若 B 通道峰值高于 G/R,說明圖像偏藍;反之則偏紅或偏綠;
bins=16
?時,直方圖更簡潔,適合快速觀察整體分布趨勢。
二、掩碼圖像(Mask):精準定位感興趣區域
1. 什么是掩碼圖像?
掩碼圖像是與原圖像尺寸完全相同的二進制圖像,像素值僅為?0
?或?255
(無符號 8 位整數,np.uint8
):
- 像素值為?
0
:屏蔽區域(后續操作不處理該區域); - 像素值為?
255
:保留區域(后續操作僅作用于該區域)。
形象地說,掩碼就像 “照片遮罩”,只讓感興趣的區域 “透出來” 參與處理。
2. 掩碼的核心應用場景
- 計算局部區域的直方圖(如僅分析人臉區域的亮度);
- 圖像修復(如僅修復口罩遮擋的面部區域);
- 目標分割(如僅提取圖像中的文字區域);
- 圖像融合(如將 logo 貼到指定區域)。
3. OpenCV 掩碼操作:cv2.bitwise_and()
?詳解
掩碼通常與按位與運算結合使用,原理是:原圖像像素 & 掩碼像素
,只有當掩碼像素為 255(二進制 11111111)時,原像素值才保留;若掩碼為 0(二進制 00000000),結果為 0(黑色)。
(1)函數語法
cv2.bitwise_and(src1, src2, dst=None, mask=None)
(2)實戰案例:用掩碼提取局部區域并計算直方圖
import cv2
import numpy as np
import matplotlib.pyplot as plt# 1. 讀取灰度圖像
gray_img = cv2.imread("phone.png", cv2.IMREAD_GRAYSCALE)
cv2.imshow("原始灰度圖", gray_img)
cv2.waitKey(0)# 2. 創建掩碼圖像(步驟:全0矩陣 → 局部設為255)
# 2.1 生成與原圖像尺寸相同的全0矩陣(純黑圖像)
mask = np.zeros(gray_img.shape[:2], dtype=np.uint8) # shape[:2]取高和寬
# 2.2 定義感興趣區域(ROI):[y1:y2, x1:x2](注意:OpenCV中坐標是(y, x))
# 此處以“手機屏幕區域”為例,需根據實際圖像調整坐標
mask[50:350, 100:470] = 255 # 該區域設為255(白色)# 展示掩碼圖像
cv2.imshow("掩碼圖像(白色為保留區域)", mask)
cv2.waitKey(0)# 3. 應用掩碼:提取感興趣區域
masked_img = cv2.bitwise_and(gray_img, gray_img, mask=mask)
cv2.imshow("掩碼提取后的圖像", masked_img)
cv2.waitKey(0)# 4. 對比:全圖直方圖 vs 掩碼區域直方圖
plt.figure(figsize=(10, 4))# 全圖直方圖
plt.subplot(1, 2, 1)
hist_full = cv2.calcHist([gray_img], [0], None, [256], [0, 256])
plt.plot(hist_full, color='black')
plt.title("全圖直方圖")
plt.xlabel("像素值")
plt.ylabel("數量")# 掩碼區域直方圖
plt.subplot(1, 2, 2)
hist_masked = cv2.calcHist([gray_img], [0], mask, [256], [0, 256])
plt.plot(hist_masked, color='red')
plt.title("掩碼區域(手機屏幕)直方圖")
plt.xlabel("像素值")
plt.ylabel("數量")plt.tight_layout()
plt.show()cv2.destroyAllWindows()
運行結果如下:
3)運行結果分析
- 掩碼圖像:黑色區域為屏蔽區,白色矩形為保留的 “手機屏幕區域”;
- 掩碼提取后的圖像:僅屏幕區域保留原像素,其他區域為黑色;
- 直方圖對比:若屏幕區域偏亮,掩碼直方圖峰值會比全圖直方圖更靠右,精準反映局部亮度分布。