目錄
RGB/HSI顔色空間解讀
直方圖概念
基于opencv-python繪制RGB直方圖
繪制opencv-python繪制H-S直方圖
直方圖比較方法
python中其他繪制2D直方圖方法
顯示直方圖
參考資料:
RGB/HSI顔色空間解讀
一般用的都是RGB圖像。但是由于HSI顔色空間更符合人體感覺,是以很多工作都需要先将RGB轉為HSI。在岡薩瓦斯的《數字圖像處理》中是這樣支援使用HSI空間的:
也就是說,由于H,S對于光強的魯棒性,它們是我們重點關注的兩個量。在易受光強變化影響的場景下,使用HSI空間是比較可靠的。具體的轉換公式:
小寫是表示歸一化的狀态。即未拉伸擴充。(常用的是把歸一化的h,s,i值做擴充便于了解。見下面程式。)
由于opencv中沒有自帶轉HSI的函數(有轉HSV空間的,但是和HSI還有有些差别)。故自行用python實作顔色空間轉換:
import numpy as np
import math
def rgb2hsi(img_rgb):
rows = int(img_rgb.shape[0])
cols = int(img_rgb.shape[1])
B, G, R = cv2.split(img_rgb)
# 歸一化到[0,1]
B = B / 255.0
G = G / 255.0
R = R / 255.0
img_hsi = img_rgb.copy()
H, S, I = cv2.split(img_hsi)
for i in range(rows):
for j in range(cols):
num = 0.5 * ((R[i, j] - G[i, j]) + (R[i, j] - B[i, j]))
den = np.sqrt((R[i, j] - G[i, j]) ** 2 + (R[i, j] - B[i, j]) * (G[i, j] - B[i, j]))
theta = float(np.arccos(num / den))
if den == 0:
H = 0
elif B[i, j] <= G[i, j]:
H = theta
else:
H = 2 * np.pi - theta
min_RGB = min(min(B[i, j], G[i, j]), R[i, j])
sum = B[i, j] + G[i, j] + R[i, j]
if sum == 0:
S = 0
else:
S = 1 - 3 * min_RGB / sum
H = H / (2 * np.pi)
I = sum / 3.0
# 為了便于了解,常常對結果做擴充,即 [0°,360°],[0,100],[0,255]
# img_hsi[i, j, 0] = H * 360
# img_hsi[i, j, 1] = S * 100
# img_hsi[i, j, 2] = I * 255
# 或者為了便于計算直方圖,都擴充為0~255(同RGB)
img_hsi[i, j, 0] = H * 255
img_hsi[i, j, 1] = S * 255
img_hsi[i, j, 2] = I * 255
return img_hsi
需要注意的是H、S、I三個取值的值域。
但是,opencv官網中給出一個繪制H-S直方圖的案例,其中直接調用了BGR2HSV自帶方法,即把RGB轉成HSV空間了。雖然有點不同,但是H和S的概念還是相同的。如果想在HSI空間下考察圖像的顔色特征,其實不必大費周章自己把RGB轉成HSI,因為真正有用的資訊隻是H和S兩個分量,是以V和I的定義不一緻也就罷了。這樣一來,我們直接使用OpenCV中提供的方法就可以快速獲得H-S直方圖。
直方圖概念
直方圖是學數字圖像的人必須掌握的基礎。如果你還不懂其概念,建議先自行百度一下,或查閱數字圖像處理相關書籍。簡單直白地說,直方圖是一種像素值分布的機率模型,比如一張灰階圖像,像素值0~255,直方圖就可以告訴我們像素值為0的點有幾個,為1的有幾個,為2的有多少個……但是這樣太複雜了,運算量太大,是以引入“灰階級”概念,比如0~16的都算作第一級,16~32的都算作第二級……這樣橫軸隻需要16個刻度值就可以表達完整個圖像的灰階情況。
那麼既然灰階值可以做直方圖(一維),那麼二維、三維的當然也可以做,原理是一樣的。比如可以把R,G,B三個分量分别提取做來做直方圖,就可以得到一張圖上3條曲線。
還有一些重要概念,比如直方圖均衡化。請自行查閱資料了解一下。
本文讨論如何繪制多元直方圖(顔色直方圖)。
基于opencv-python繪制RGB直方圖
自己編寫的程式:
import cv2
import numpy as np
class Hisogram(object):
def create_rgb_hist(self,image,color_type=1):
"""
擷取彩色空間直方圖
"""
h, w, c = image.shape
rgHist = np.zeros([16*16*16, 1], np.float32) #必須是float型
print(rgHist)
hsize = 256/16
for row in range (0, h, 1):
for col in range (0, w, 1):
b = image[row, col, 0]
g = image[row, col, 1]
r = image[row, col, 2]
index = np.int(b/hsize)*16*16 + np.int(g/hsize)*16 + np.int(r/hsize)
rgHist[np.int(index), 0] = rgHist[np.int(index), 0] + 1
return rgHist
def hist_compare(self,image1, image2):
"""
比較兩個直方圖
"""
hist1 = self.create_rgb_hist(image1)
hist2 = self.create_rgb_hist(image2)
match1 = cv2.compareHist(hist1, hist2, cv2.HISTCMP_BHATTACHARYYA)
match2 = cv2.compareHist(hist1, hist2, cv2.HISTCMP_CORREL)
match3 = cv2.compareHist(hist1, hist2, cv2.HISTCMP_CHISQR)
match4 = cv2.compareHist(hist1,hist2 ,cv2.HISTCMP_INTERSECT)
print("巴氏距離:%s,相關性:%s,卡方:%s,HISTCMP_INTERSECT:%s " % (match1, match2, match3,match4))
def hist_image(self,image):
color = ("Hue", "Saturity", "Intensity")
for i, color in enumerate(color):
hist = cv2.calcHist([image], [i], None, [256], [0, 256]) #計算rgb的直方圖
# hist = cv.calcHist([image], [0,1], None, [180,256], [0,180,0,256]) #計算H-S直方圖
print(hist)
if __name__ == '__main__':
image1 = cv2.imread("shuibo1/2.jpg")[190:220,220:250,:]
image2 = cv2.imread("shuibo1/2.jpg")[190:220,250:280,:]
#hist_image(image1)
myHist=Hisogram()
myHist.hist_compare(image1, image2)
繪制opencv-python繪制H-S直方圖
此處可以直接借助opencv官網給的案例:非常容易實作!
import numpy as np
import cv2 as cv
img = cv.imread('home.jpg')
hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV)
hist = cv.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])
calcHist是得到直方圖的函數:
images是原始圖像,需要使用[]修飾;cv2.calcHist()函數一次隻能得到一張圖檔的其中一個通道的直方圖,是以,channels傳入的是本次需要處理的元素圖檔的通道的索引号,同樣需要使用
[]
修飾。如果輸入的元素圖像是灰階圖,它的值就是[0]; 如果輸入的元素圖像是彩色圖像的話,傳入的參數可以是[0], [1], [2],它們分别對應着通道B, G, R。
直方圖比較方法
畫好了直方圖後,我們往往希望借助直方圖比較兩幅圖像的顔色相似程度。即顔色直方圖可以用來作為顔色特征來比較。那麼利用直方圖來比較的方法有哪些呢?
調用方法:(我們在第二節“基于opencv-python繪制RGB直方圖”的程式中也已經使用了這個方法,可參考如何使用)
opencv中自帶的方法有:
具體含義:
python中其他繪制2D直方圖方法
python的numpy子產品也提供了2D直方圖的繪制方法。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('home.jpg')
hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV)
hist, xbins, ybins = np.histogram2d(h.ravel(),s.ravel(),[180,256],[[0,180],[0,256]])
顯示直方圖
方法1:cv.imshow()
方法2:使用Matplotlib:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('home.jpg')
hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV)
hist = cv.calcHist( [hsv], [0, 1], None, [180, 256], [0, 180, 0, 256] )
plt.imshow(hist,interpolation = 'nearest')
plt.show()
參考資料:
2D直方圖,opencv官方教程:https://docs.opencv.org/3.4.0/dd/d0d/tutorial_py_2d_histogram.html
以及它的中文翻譯by網友:https://blog.csdn.net/JS_XH/article/details/79270584
直方圖函數的詳解:https://www.cnblogs.com/aobosir/p/5928676.html(排版不是很好…)
顔色空間轉換的python代碼:https://blog.csdn.net/qq_38328871/article/details/85060459