前情提要
在【計(jì)算機(jī)視覺(一)圖像數(shù)據(jù)表示】中,我介紹了RGB和灰度兩種顏色空間,并且介紹了像素的概念以及在程序上如何訪問。
本期內(nèi)容
接下來介紹從RGB到灰度的轉(zhuǎn)換,以及兩種我常用的顏色空間HSV和二值空間(嚴(yán)格來說屬于灰度洪灯,只是只有0和255兩個(gè)值)。
一竟痰、RGB轉(zhuǎn)灰度
假如先不談原理签钩,RGB轉(zhuǎn)灰度你會(huì)怎么做?先從我們知道的信息入手凯亮,RGB是三通道的边臼,灰度只有一個(gè)通道,很自然的會(huì)聯(lián)想到怎么把三個(gè)通道“融合”成一個(gè)通道假消,最直接的想法柠并,也許是對(duì)于同一個(gè)RGB像素值,我們把這三個(gè)通道值求一個(gè)平均值作為灰度值富拗。用公式表示一下就是:Gray = R * 1/3 + G * 1/ 3 + B * 1/3 臼予。
好,那我們先來寫這個(gè)程序看看效果啃沪。
# coding: utf-8
import cv2
import numpy as np
'''
函數(shù)名:rgb2gray_mean
功能:通過求通道平均值得到灰度圖
輸入:
img 輸入的彩圖
返回:
result 灰度圖
'''
def rgb2gray_mean(img):
ratio = 1.0 / 3
# 轉(zhuǎn)換類型
int_img = img.astype(np.int32)
result = ratio * (int_img[...,0]+int_img[...,1]+int_img[...,2])
return result.astype(np.uint8)
# 程序入口
def main():
# 讀取lena圖
color = cv2.imread('../lena.jpg')
# 轉(zhuǎn)灰度
gray = rgb2gray_mean(color)
# 顯示
cv2.imshow('color', color)
cv2.imshow('gray', gray)
cv2.waitKey(0)
if __name__ == '__main__':
main()
注意:在rgb2gray_mean函數(shù)中我對(duì)img做了一個(gè)類型轉(zhuǎn)換并存放在int_img中粘拾,img的類型是numpy.uint8,也就是8位無符號(hào)整數(shù)创千,直接對(duì)里面的值進(jìn)行相加很可能造成數(shù)值溢出缰雇,也就是255+1會(huì)變成0,因此要先用一個(gè)能容納更大的值范圍的矩陣裝起來追驴,比如int32類型的械哟。這是新手經(jīng)常會(huì)遇到的坑,切記切記殿雪。
好那我們來看看運(yùn)行效果暇咆,再一次使用lena測(cè)試。
運(yùn)行的結(jié)果:
看起來還行丙曙。
OpenCV中也有自帶的轉(zhuǎn)換灰度的函數(shù)爸业,cvtColor,代碼也貼在這里:
gray = cv2.cvtColor(color, cv2.COLOR_BGR2GRAY)
注意第二個(gè)參數(shù)是BGR2GRAY亏镰,是的扯旷,OpenCV中默認(rèn)的彩圖通道排列是BGR(藍(lán)綠紅)而不是RGB,具體原因我也是道聽途說索抓,是因?yàn)橐婚_始相機(jī)制造商從Sensor拿到的數(shù)據(jù)就是BGR的钧忽,這是他們制定的標(biāo)準(zhǔn)某抓,盡管后來有很多軟件也是默認(rèn)采用RGB。
那么這個(gè)cvtColor的效果怎么樣呢惰瓜,請(qǐng)看
不知道你能否看出區(qū)別,不能的話請(qǐng)注意看頭發(fā)的明暗交界處汉矿,cvtColor在本來該黑的地方更黑崎坊,該白的地方更白,就是對(duì)比度更強(qiáng)烈洲拇,還是看不出來的話用程序幫我們看看奈揍,數(shù)一下灰度值不相等的地方有多少個(gè)。
print np.sum(gray != cv_gray)
結(jié)果是:244157赋续, 原圖的大小是512x512男翰,也就是總共262144個(gè)像素點(diǎn),那這么看下來絕大部分的像素值都不一樣纽乱,雖然我們看起來也要費(fèi)點(diǎn)力才能看出來不同蛾绎。這就說明OpenCV轉(zhuǎn)灰度的方法跟我們拍腦門想的是不一樣的,也就是對(duì)應(yīng)到三個(gè)通道各自乘的系數(shù)是不一樣的鸦列,假設(shè):
Gray = a * R + b * G + c * B (a + b + c = 1)
a租冠、b、c三個(gè)值應(yīng)該怎么取能讓圖像看起來比較舒服呢薯嗤?其實(shí)人眼對(duì)三原色的“偏好”是不一樣的顽爹,研究表明人眼對(duì)紅綠藍(lán)的權(quán)重接近3:6:1,更精確的骆姐,對(duì)于上面的公式镜粤,a=0.299,b=0.587玻褪,c=0.114肉渴,這就是通常所說的心理學(xué)模型。在OpenCV中归园,為了減少浮點(diǎn)運(yùn)算(浮點(diǎn)運(yùn)算在一般的CPU中很耗時(shí))黄虱,使用了類似下面的轉(zhuǎn)換方式:
'''
函數(shù)名:心理學(xué)模型轉(zhuǎn)換灰度
輸入:
img 輸入的彩圖
返回:
result 灰度圖
'''
def rgb2gray_mental(img):
# 轉(zhuǎn)換類型
int_img = img.astype(np.int32)
result = (int_img[...,2]*299 + int_img[...,1]*587 + int_img[...,0]*114 + 500) / 1000
return result.astype(np.uint8)
經(jīng)過測(cè)試,這跟OpenCV自帶的轉(zhuǎn)換函數(shù)cvtColor的效果是一模一樣的庸诱。
二捻浦、HSV空間
HSV空間是由RGB空間演變過來的,RGB空間在幾何上是一個(gè)正方體(詳情請(qǐng)查閱【計(jì)算機(jī)視覺(一)圖像數(shù)據(jù)表示】)桥爽,而當(dāng)你從正方體的一個(gè)頂點(diǎn)看向離它最遠(yuǎn)的另一個(gè)頂點(diǎn)時(shí)朱灿,就會(huì)看到一個(gè)六角錐體,這就是HSV空間的幾何表達(dá)钠四,如下圖:
通常盗扒,我們會(huì)把頂上的這個(gè)六邊形近似成一個(gè)圓跪楞,就變成了一個(gè)圓錐,像這樣:
HSV三個(gè)軸的走動(dòng)方向也在圖上標(biāo)出了侣灶,可以理解為一個(gè)有深度的極坐標(biāo)系甸祭,我們只看Hue軸,沿著圓周方向走褥影,可以看到每轉(zhuǎn)動(dòng)一定的角度池户,所表示的顏色就變了,而且不止紅綠藍(lán)凡怎,事實(shí)上這對(duì)應(yīng)了我們認(rèn)知上的所有顏色校焦,因此,使用HSV空間的好處就是只要一個(gè)維度我們就能表示物體本來的顏色统倒。那其他兩個(gè)軸是表達(dá)什么信息的呢寨典?具體可以參考百度百科,我這里只說些個(gè)人見解房匆。S維度表示飽和度耸成,更通俗的說,是打在物體上的白燈的亮度浴鸿,比如你在黑夜中用手電筒照著個(gè)蘋果墓猎,電量足夠的話蘋果看上去當(dāng)然是正常的紅色,電量不夠也許看上去是暗紅色赚楚,關(guān)掉手電也就啥都看不到黑漆漆一片了毙沾,這就是S維度的信息。V維度表示了一種物體自身的材質(zhì)信息宠页,是不是透明的左胞,透明度有多少,同樣拿照蘋果這個(gè)例子举户,如果現(xiàn)在這個(gè)蘋果換成了水晶蘋果烤宙,燈光打上去以后看到的顏色也是不一樣的,即使當(dāng)初涂的顏色跟真實(shí)的蘋果顏色一樣俭嘁,這是由于一些漫反射和折射造成的躺枕。
HSV的值域跟RGB的并不一樣,其中H維度的值域是0-180供填,其他兩個(gè)維度都是0-255拐云,H不取到0-360也許是因?yàn)槌^了uchar的范圍。
HSV空間對(duì)顏色的描述是用戶友好的近她,而RGB是硬件友好的叉瘩。HSV空間在我日常學(xué)習(xí)中更多是作為顏色篩選的基礎(chǔ),人眼能區(qū)分的不同顏色的范圍對(duì)應(yīng)HSV的值都比較固定粘捎,下面是一個(gè)對(duì)照表:
現(xiàn)在舉個(gè)實(shí)際應(yīng)用的案例薇缅,比如我要對(duì)下面的圖片做車牌識(shí)別危彩,我的第一步是要把車牌的區(qū)域摳出來,車牌的底色是藍(lán)色的泳桦,當(dāng)然寶馬的標(biāo)志也有藍(lán)色汤徽,但我們可以不管三七二十一先把藍(lán)色的東西都摳出來再做篩選,這時(shí)候就可以用HSV空間灸撰。
圖片剛讀入的時(shí)候是BGR格式的泻骤,這時(shí)候又要用到cvtColor轉(zhuǎn)換到HSV,示例代碼如:
hsv = cv2.cvtColor(bgr, cv2.COLOR_BGR2HSV)
顯示HSV的效果如下:
現(xiàn)在我們要的是藍(lán)色區(qū)域梧奢,查找上面的對(duì)照表就可以知道,我們要的是H值在100到124演痒,S值在43到255亲轨,V值在46到255之間的像素點(diǎn)。怎么表達(dá)這個(gè)“要”跟“不要”呢鸟顺?我們可以這么考慮惦蚊,對(duì)于每個(gè)像素點(diǎn)只有“要”和“不要”兩種狀態(tài),就像一盞燈的開關(guān)一樣讯嫂,于是我們可以創(chuàng)建一幅等大的圖蹦锋,在上面,“要”的像素點(diǎn)設(shè)為一個(gè)值欧芽,“不要”的像素點(diǎn)設(shè)為另一個(gè)值莉掂,這就是接下來要說的二值圖,我也喜歡稱其為掩碼圖千扔。
三憎妙、二值圖(掩碼圖)
二值圖本質(zhì)上是灰度圖,只是只用了0和255兩個(gè)值曲楚,用0表示“不要”厘唾,255表示“要”,整幅圖看起來就是符合條件的區(qū)域是白色的龙誊,不符合的是黑色的抚垃。對(duì)上面的HSV圖作這種條件篩選處理,叫做二值化趟大,OpenCV已經(jīng)為我們準(zhǔn)備了對(duì)應(yīng)的函數(shù)cv2.inRange鹤树,示例代碼如下:
mask = cv2.inRange(hsv, np.array([100,43,46]), np.array([124,255,255]))
處理完的結(jié)果如下圖:
可以看到,盡管有很多干擾的白色區(qū)域逊朽,車牌區(qū)域還是在里面魂迄,我們只要采取進(jìn)一步的手段就可以把車牌區(qū)域篩選出來,這就要涉及一些形態(tài)學(xué)處理惋耙,敬請(qǐng)期待下一篇文章捣炬。