天天看點

基于openCV-python:HSI顔色空間與H-S直方圖比較

目錄

RGB/HSI顔色空間解讀

直方圖概念

基于opencv-python繪制RGB直方圖

繪制opencv-python繪制H-S直方圖

直方圖比較方法

python中其他繪制2D直方圖方法

顯示直方圖

參考資料:

RGB/HSI顔色空間解讀

一般用的都是RGB圖像。但是由于HSI顔色空間更符合人體感覺,是以很多工作都需要先将RGB轉為HSI。在岡薩瓦斯的《數字圖像處理》中是這樣支援使用HSI空間的:

基于openCV-python:HSI顔色空間與H-S直方圖比較

也就是說,由于H,S對于光強的魯棒性,它們是我們重點關注的兩個量。在易受光強變化影響的場景下,使用HSI空間是比較可靠的。具體的轉換公式:

基于openCV-python:HSI顔色空間與H-S直方圖比較

小寫是表示歸一化的狀态。即未拉伸擴充。(常用的是把歸一化的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是得到直方圖的函數:

基于openCV-python:HSI顔色空間與H-S直方圖比較

images是原始圖像,需要使用[]修飾;cv2.calcHist()函數一次隻能得到一張圖檔的其中一個通道的直方圖,是以,channels傳入的是本次需要處理的元素圖檔的通道的索引号,同樣需要使用

[]

修飾。如果輸入的元素圖像是灰階圖,它的值就是[0]; 如果輸入的元素圖像是彩色圖像的話,傳入的參數可以是[0], [1], [2],它們分别對應着通道B, G, R。

基于openCV-python:HSI顔色空間與H-S直方圖比較

直方圖比較方法

畫好了直方圖後,我們往往希望借助直方圖比較兩幅圖像的顔色相似程度。即顔色直方圖可以用來作為顔色特征來比較。那麼利用直方圖來比較的方法有哪些呢?

調用方法:(我們在第二節“基于opencv-python繪制RGB直方圖”的程式中也已經使用了這個方法,可參考如何使用)

基于openCV-python:HSI顔色空間與H-S直方圖比較

opencv中自帶的方法有:

基于openCV-python:HSI顔色空間與H-S直方圖比較

具體含義:

基于openCV-python:HSI顔色空間與H-S直方圖比較
基于openCV-python:HSI顔色空間與H-S直方圖比較

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()
           
基于openCV-python:HSI顔色空間與H-S直方圖比較

參考資料:

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

基于openCV-python:HSI顔色空間與H-S直方圖比較