目录
RGB/HSI颜色空间解读
直方图概念
基于opencv-python绘制RGB直方图
绘制opencv-python绘制H-S直方图
直方图比较方法
python中其他绘制2D直方图方法
显示直方图
参考资料:
一般用的都是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条曲线。
还有一些重要概念,比如直方图均衡化。请自行查阅资料了解一下。
本文讨论如何绘制多维直方图(颜色直方图)。
自己编写的程序:
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官网给的案例:非常容易实现!
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的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