Opencv顏色空間轉換、直方圖、Gamma變換
一、顏色空間轉換
cv2.cvtcolor(img,code)
code——轉換的標識,從什麼空間轉換到什麼空間,常用的有:cv2.COLOR_BGR2HSV、cv2.COLOR_HSV2BGR、cv2.COLOR_GRAY2BGR、cv2.COLOR_BGR2GRAY
HSV空間:HSV空間是由美國的圖形學專家A. R. Smith提出的一種顏色空間,HSV分別是色調(Hue),飽和度(Saturation)和明度(Value)。在HSV空間中進行調節就避免了直接在RGB空間中調節是還需要考慮三個通道的相關性。OpenCV中H的取值是[0, 180),其他兩個通道的取值都是[0, 256),用於調節色調和明暗。
示例:
import cv2 import numpy as np from matplotlib import pyplot as plt img = cv2.imread('scene.jpg') # 通過cv2.cvtColor把影象從BGR轉換到HSV img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # H空間中,通過增加畫素灰度值可以改變影象的色調 turn_green_hsv = img_hsv.copy() turn_green_hsv[:, :, 0] = (turn_green_hsv[:, :, 0]+150) % 180 turn_green_img = cv2.cvtColor(turn_green_hsv, cv2.COLOR_HSV2BGR) cv2.imwrite('turn_green.jpg', turn_green_img) # 減小飽和度會讓影象損失鮮豔,變得更灰 colorless_hsv = img_hsv.copy() colorless_hsv[:, :, 1] = 0.5 * colorless_hsv[:, :, 1] colorless_img = cv2.cvtColor(colorless_hsv, cv2.COLOR_HSV2BGR) cv2.imwrite('colorless.jpg', colorless_img) # 減小明度為原來一半 darker_hsv = img_hsv.copy() darker_hsv[:, :, 2] = 0.5 * darker_hsv[:, :, 2] darker_img = cv2.cvtColor(darker_hsv, cv2.COLOR_HSV2BGR) cv2.imwrite('darker.jpg', darker_img)
二、直方圖
無論是HSV還是RGB,我們都較難一眼就對畫素中值的分佈有細緻的瞭解,這時候就需要直方圖,Opencv中直方圖計算函式為:
cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate ]]) #返回hist
各引數:
channels:直方圖計算的通道[0]、[1]、[2]
mask:可選的操作掩碼。如果此掩碼不為空,那麼它必須為8位並且尺寸要和輸入影象images[i]一致
histSize:表示這個直方圖分成多少份(即多少個直方柱)
ranges:直方圖中各個畫素的值,[0,256]表示直方圖能表示從0到256畫素值的畫素
accumulate:布林量,表示直方圖是否可以疊加
import cv2
import numpy as np
import matplotlib.pyplot as plt
def calcAndDrawHist(image, color):
hist= cv2.calcHist([image], [0], None, [256], [0.0,255.0])
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(hist)
histImg = np.zeros([256,256,3], np.uint8)
hpt = int(0.9* 256);
for h in range(256):
intensity = int(hist[h]*hpt/maxVal) ###相當於歸一化
cv2.line(histImg,(h,256), (h,256-intensity), color)
return histImg
if __name__ == '__main__':
img = cv2.imread("scene.jpg")
b, g, r = cv2.split(img) ###########按通道劃分影象
histImgB = calcAndDrawHist(b, [255, 0, 0])
histImgG = calcAndDrawHist(g, [0, 255, 0])
histImgR = calcAndDrawHist(r, [0, 0, 255])
cv2.imshow("histImgB", histImgB)
cv2.imshow("histImgG", histImgG)
cv2.imshow("histImgR", histImgR)
cv2.imshow("Img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
其中函式minMaxLoc()在C++下表示為:
void cv::minMaxLoc ( InputArray src,
double * minVal,
double * maxVal = 0,
Point * minLoc = 0,
Point * maxLoc = 0,
InputArray mask = noArray()
)
引數解釋
- src: 輸入的單通道陣列
- minVal: double型別指標,用於返回最小值的指標,如果不需要返回則設定為NULL
- maxVal: double型別的指標,用於返回最大值指標,如果不需要返回則設定為NULL
- minLoc: 返回最小值位置指標(2D的情況下),如果不需要則設定為NULL
- maxLoc: 返回最大位置指標(2D情況下),如果不需要則設定為NULL
- mask: 可選掩模板。
表示成折線圖:
import cv2
import numpy as np
img = cv2.imread('scene.jpg')
h = np.zeros((256,256,3)) #建立用於繪製直方圖的全0影象
bins = np.arange(256).reshape(256,1) #直方圖中各bin的頂點位置
color = [ (255,0,0),(0,255,0),(0,0,255) ] #BGR三種顏色
for ch, col in enumerate(color):
originHist = cv2.calcHist([img],[ch],None,[256],[0,256])
cv2.normalize(originHist, originHist,0,255*0.9,cv2.NORM_MINMAX)
hist=np.int32(np.around(originHist))
pts = np.column_stack((bins,hist))
cv2.polylines(h,[pts],False,col)
h=np.flipud(h)
cv2.imshow('colorhist',h)
cv2.waitKey(0)
主要用到的函式包括cv2.normalize()、np.column_stack(),enumerate()、flipud()含義如下:
void cv::normalize ( InputArray src,
InputOutputArray dst,
double alpha = 1,
double beta = 0,
int norm_type = NORM_L2,
int dtype = -1,
InputArray mask = noArray()
)
引數解釋
- src: 輸入陣列
- dst: 輸出陣列,與src有相同的尺寸
- alpha: 將陣列歸一化範圍的最大值,有預設值1
- beta: 歸一化的最小值,有預設值0
- norm_type: 歸一化方式,可以檢視NormTypes()函式檢視詳細資訊,有預設值NORM_L2
- dtype: 當該值取負數時,輸出陣列與src有相同型別,否則,與src有相同的通道並且深度為CV_MAT_DEPTH(dtype)
- mask: 可選的掩膜版
三、Gamma變換
背景裡的暗部細節是非常弱的時候,一個常用方法是考慮用Gamma變換來提升暗部細節。Gamma變換是矯正相機直接成像和人眼感受影象差別的一種常用手段,簡單來說就是通過非線性變換讓影象從對曝光強度的線性響應變得更接近人眼感受到的響應。具體的定義和實現,還是接著上面程式碼中讀取的圖片,執行計算直方圖和Gamma變換的程式碼如下:
import cv2
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
img = cv2.imread('scene.jpg')
# 分通道計算每個通道的直方圖
hist_b = cv2.calcHist([img], [0], None, [256], [0, 256])
hist_g = cv2.calcHist([img], [1], None, [256], [0, 256])
hist_r = cv2.calcHist([img], [2], None, [256], [0, 256])
# 定義Gamma矯正的函式
def gamma_trans(img, gamma):
# 具體做法是先歸一化到1,然後gamma作為指數值求出新的畫素值再還原
gamma_table = [np.power(x/255.0, gamma)*255.0 for x in range(256)]
gamma_table = np.round(np.array(gamma_table)).astype(np.uint8)
# 實現這個對映用的是OpenCV的查表函式
return cv2.LUT(img, gamma_table)
# 執行Gamma矯正,小於1的值讓暗部細節大量提升,同時亮部細節少量提升
img_corrected = gamma_trans(img, 0.5)
cv2.imwrite('gamma_corrected.jpg', img_corrected)
# 分通道計算Gamma矯正後的直方圖
hist_b_corrected = cv2.calcHist([img_corrected], [0], None, [256], [0, 256])
hist_g_corrected = cv2.calcHist([img_corrected], [1], None, [256], [0, 256])
hist_r_corrected = cv2.calcHist([img_corrected], [2], None, [256], [0, 256])
fig = plt.figure()
pix_hists = [
[hist_b, hist_g, hist_r],
[hist_b_corrected, hist_g_corrected, hist_r_corrected]
]
pix_vals = range(256)
for sub_plt, pix_hist in zip([121, 122], pix_hists):
ax = fig.add_subplot(sub_plt, projection='3d')
for c, z, channel_hist in zip(['b', 'g', 'r'], [20, 10, 0], pix_hist):
cs = [c] * 256
#ax.bar(pix_vals, channel_hist, zs=z, zdir='y', color=cs, alpha=0.618, edgecolor='none', lw=0)
ax.plot3D(pix_vals, channel_hist, zs=z,zdir='y',color=c)
ax.set_xlabel('Pixel Values')
ax.set_xlim([0, 256])
ax.set_ylabel('Channels')
ax.set_zlabel('Counts')
plt.show()