OpenCV是一個基於BSD許可(開源)發行的跨平台的計算機視覺和機器學習軟件庫,可以運行在Linux、Windows、Android和Mac OS操作系統上。它輕量級而且高效——由一系列 C 函數和少量 C++ 類構成,同時提供了Python、Ruby、MATLAB等語言的接口,實現了圖像處理和計算機視覺方面的很多通用算法。
OpenCV在圖像分割、人臉識別、物體識別、動作跟蹤、動作分析、機器視覺等領域都有廣泛的應用。
以下是OpenCV的基本操作及其應用案例。
1. OpenCV基本操作
1.1 讀取、顯示以及保存操作
import cv2
image = cv2.imread("test.jpg") # 讀取操作
cv2.imshow("test", image) # 顯示操作
cv2.waitKey() # 等待按鍵
cv2.imwrite("save.jpg") # 保存操作
1.2 改變色彩空間
image = cv2.imread("test.jpg")
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 轉換到HSV空間
hls = cv2.cvtColor(image, cv2.COLOR_BGR2HLS) # 轉換到HLS空間
lab = cv2.cvtColor(image, cv2.COLOR_BGR2Lab) # 轉換到Lab空間
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 轉換到GRAY空間(灰度圖)
HSV這個模型中顏色的參數分別是:色調(H),飽和度(S),明度(V),該模型常用來做綠幕分割。
在圖像檢測中,可以對樣本進行色彩空間轉換實現數據增強,如將訓練數據直接轉換到HSV空間,或者調整V(明度)通道的大小,改變圖片的明暗,再轉到BGR格式。
1.3 幾何變換--縮放、平移、旋轉
a. 縮放
image = cv2.imread("test.jpg")
resize = cv2.resize(image, (), fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA) # 長寬縮小到0.5倍
b. 平移
在對圖像作平移操作時,需創建2行3列變換矩陣,M矩陣表示水平方向上平移為x,豎直方向上的平移距離為y。
import cv2
import numpy as np
image = cv2.imread("test.jpg")
rows, cols, channels = image.shape
M = np.float32([[1,0,100],[0,1,50]])
res = cv.warpAffine(image, M, (cols, rows))
c. 旋轉
旋轉所需的變換矩陣可以通過函數cv2.getRotationMatrix2D得到。
image = cv2.imread('test.jpg')
rows, cols, channels = image.shape
rotate = cv2.getRotationMatrix2D((rows*0.5, cols*0.5), 45, 1) # 第一個參數:旋轉中心點 第二個參數:旋轉角度 第三個參數:縮放比例
res = cv2.warpAffine(image, rotate, (cols, rows))
1.4 平滑處理--模糊、濾波
模糊濾波操作去除圖像中的椒鹽噪聲、提高圖像的對比度、實現銳化處理、提高立體感等。
image = cv2.imread('test.jpg')
blur = cv2.blur(image, (5, 5)) # 均值濾波 第二個參數是卷積核大小
median_blur = cv2.medianBlur(image, 5) # 中值濾波
gaussian_blur = cv2.GussianBlur(image, (5, 5)) # 高斯模糊
1.5 膨脹、侵蝕
a. 圖像形態學操作
圖像形態學操作是基於形狀的一系列圖像處理操作的合集,主要是基於集合論基礎上的形態學數學。
- 形態學有四個基本操作:侵蝕、膨脹、開、閉
- 膨脹與腐蝕是圖像處理中最常用的形態學操作手段
- 膨脹就是圖像中的高亮部分進行膨脹,“領域擴張”,效果圖擁有比原圖更大的高亮區域。侵蝕就是原圖中的高亮部分被腐蝕,“領域被蠶食”,效果圖擁有比原圖更小的高亮區域
b. 膨脹與侵蝕
它們能實現多種多樣的功能,主要如下:
- 消除噪聲
- 分割出獨立的圖像元素,在圖像中連接相鄰的元素
- 尋找圖像中的明顯的極大值區域或極小值區域
- 求出圖像的梯度
image = cv2.imread("test.jpg")
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(3, 3)) # 獲取卷積核
eroded = cv2.erode(image, kernel) # 腐蝕圖像
dilated = cv2.dilate(image, kernel) # 膨脹圖像
c. 開運算和閉運算
- 開運算:先腐蝕后膨脹,用於移除由圖像噪音形成的斑點
- 閉運算:先膨脹后腐蝕,用來連接被誤分為許多小塊的對象
1.6 查找繪制輪廓
a. 查找輪廓
輪廓查找在圖像檢測領域有很廣泛的應用,比如查找圖像中明顯的色塊、條紋、物體邊緣等等,查找輪廓前先要對圖像進行二值化處理。
# opencv版本大於3
contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) # 第一個參數:查找的二值圖像 第二個參數:輪廓檢索模式 第三個參數:輪廓近似方法
# 返回值contours為查找到的輪廓列表,hierarhy為輪廓之間的層級關系
b. 繪制輪廓
查找到輪廓后可以通過drawContours函數繪制出輪廓
cv2.drawContours(temp,contours,-1,(0,255,0),3) # 第一個參數:畫布,可以是原圖 第二個參數:查找到的輪廓 第三個參數:-1表示全畫 第四個參數:顏色 第五個參數:輪廓寬度
2. OpenCV的工程應用-色塊檢測
2.1 問題背景
游戲畫面因為美術資源缺失、程序bug都會產生各種色塊,常見的有白色、紫色等,怎么通過程序篩選出這類異常畫面,加快測試過程呢?
2.2 問題分析
通過觀察,我們發現色塊類的異常都是一些比較規則的矩形圖像,色彩差很明顯,基於這些特點,我們可以很容易的篩選出色塊。
2.3 程序設計
a. 圖像二值化
通過RGB通道的數值大小剔除掉其它的顏色,得到黑白二值圖
import cv2
import numpy as np
image = cv2.imread("test.jpg")
b, g, r = cv2.split(self.image) # 分離B、G、R通道
b = np.where(b >= 250, 1, 0) # 找到G通道符合要求的像素點置1,不符合置0
g = np.where(g >= 250, 1, 0) # 找到G通道符合要求的像素點置1,不符合置0
r = np.where(r >= 250, 1, 0) # 找到G通道符合要求的像素點置1,不符合置0
gray = b + g + r # 將三個通道疊加成一個通道
gray = np.where(gray==3, 255, 0).astype(np.uint8) # 像素點為3的即為滿足要求的點設置為白色,不符合設置為黑色
b. 查找輪廓
得到的黑白二值圖由於除了色塊外其它圖像位置也有接近色塊顏色的位置被保留了下來,需要剔除
通過查找二值圖輪廓的方法我們可以篩選出分離的小色塊
contours, hierarchy = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) # 獲取輪廓及層級關系
c. 輪廓篩選
通過輪廓周長和面積剔除掉小色塊,這些色塊很可能就是提取的正常區域顏色較相似的點。
def screen_contour(contour):
contour_area = cv2.contourArea(contour)
if contour_area > self.area_limit:
return True
return False
pass_contours = []
for contour in contours:
if screent_contour(contour):
pass_contours.append(contour)
由於色塊更接近於矩形,我們可以通過輪廓計算出色塊的寬和高,色塊的面積可以估算出來,色塊面積與估算面積越接近,說明檢測到的色塊更接近矩形,通過這個方法,可以篩選出大部分不規則的色塊。
def screent_contour(contour):
width = np.max(contour[:,:,0]) - np.min(contour[:,:,0])
height = np.max(contour[:,:,1]) - np.min(contour[:,:,1])
block_area = width * heigh
contour_area = cv2.contourArea(contour)
slimier = cv2.contourArea(contour) / block_area
if slimier > self.simliar_rate:
return True
return False
pass_contours = []
for contour in contours:
if screent_contour(contour):
pass_contours.append(contour)
3. OpenCV與矩陣
OpenCV是如何完成以上這些操作的呢?
3.1 圖像讀取
OpenCV在讀取圖像的時候是將圖像信息轉換成了矩陣,默認矩陣為(height,width,channel),channel對應的是B、G、R通道,每個像素點的顏色由三個通道一起決定,和三元色的關系是一樣的,B、G、R的大小代表的是色彩比例。
3.2 色彩空間轉換
色彩空間的轉換是將圖像數據從一種表示關系變換到了另一種表示關系,比如BGR轉換到HSV顏色空間是將原本的三原色表示法轉換到了色調(H),飽和度(S),明度(V)表示法,每個channel所表示的含義發生了變化。
從信息的角度考慮,攝像機將光照信息采集后轉化成了數字的形式(圖像矩陣),顏色空間的轉換是將圖像數據從一種表示方法變換到另一種表示方法,信息的轉化也會引起信息的丟失或者引入噪聲,比如攝像機在采集光照信息的時候很容易采集到椒鹽噪聲,丟棄光照信息中的一些頻段,清晰度降低等等,在做色彩空間轉換的時候也可能發生信息丟失,比如從彩色圖片轉化到灰度圖。深度學習中的圖像檢測、人臉識別就是要從這些圖像信息中提取我們想要的信息。
3.3 圖像旋轉的數學含義
在做圖像幾何變換的時候,我們需要提供變換矩陣,矩陣是怎么完成這些操作的呢?
我們知道矩陣代表的就是一種空間映射,nxm的矩陣(列向量線性無關)代表的是n維空間到m維空間的映射,如上圖所示,第一個立方體所在的空間通過一個3x3的矩陣映射到了第二個立方體所在的空間,立方體的形狀發生了變化,再比如第一個立方體所在的空間通過一個3x2的矩陣映射到了它的影子所在的空間,立方體被壓縮成了一個平面。
立方體可以通過矩陣(實數范圍)映射成球體嗎?因為其代表的是線性變換,所以是不可以的。
我們在處理圖像問題的時候要將信息從一個空間映射到另一個空間,由於問題的復雜性所以線性映射是滿足不了要求的,這也就是為什么在深度學習中需要加入激活函數的原因。
4. OpenCV與機器學習
從上一節的分析中我們發現,圖像的處理過程就是從數據中找規律,將圖像信息從一種表示變換到另一種表示,這個工作正好是機器學習的強項,再延申一下,不管是圖像數據、文本數據、音頻數據,要從數據中找規律,都會用到機器學習,從信息的角度考慮,這些問題本質上是沒有區別的。
openCV已經集成了很多機器學習算法、如K近鄰(KNN),支持向量機(SVM)、決策樹、隨機森林、Boost、邏輯回歸、ANN等。
下圖是摘自scikit-learn的一張圖片,很形象的展示了不同的機器學習算法是如何對數據進行處理的,從宏觀角度來講就是如何將信息變換到不同的空間。
5. 視頻書籍推薦
篇幅限制,還有很多有趣的東西,沒辦法一一列舉,推薦一些視頻和書籍,供細細品嘗。
5.1 線性代數
3blue1brown《線性代數的本質》系列視頻通過形象圖像的動畫直觀的解釋了線性代數的魔力,還有其它系列也很棒。
《程序員的數學3 線性代數》也是一本很不錯的線性代數教材,細細讀完,受益匪淺。
5.2 概率論
《程序員的數學2 概率統計》很系統的講解了概率統計的知識,不是很理解線性代數的話,推薦先看線性代數。
5.3 OpenCV
《學習OpenCV》是一本很不錯的工具書。
PS:
我們是行者AI,我們在“AI+游戲”中不斷前行。
如果你也對游戲感興趣,對AI充滿好奇,那就快來加入我們(hr@xingzhe.ai)。