[OpenCV]基礎教學筆記:影像讀取、前處理(with python)-001
reference: OpenCV Course — Full Tutorial with Python by freeCodeCamp.org
Why OpenCV?
由Intel發起並打造,是目前發展最完整的電腦視覺(Computer Vision)開源資源,並且可以用各種不同語言(如Java, Python, C/C++)進行搭配撰寫、應用、部署。
透過OpenCV進行圖像辨識與應用,可以做到影像取得、圖片預處理與特徵萃取,並適用於Machine Learning(Scikit-learn)/Deep Learning(PyTorch)。
應用場景如:AR/VR、臉部辨識、手勢辨識、動態追蹤、影像切割等等。
坊間大概也有許多課程不斷推陳出新,但若有興趣,其實上YT就有很多開放資源,再踏入這個領域之前,也許透過免費資源先試試。
OpenCV基礎學什麼
本篇文章整理影片前半段(~2:00:00)涵蓋的內容,包含:
- 圖片與影片圖取、繪圖
- 灰階轉換
- 基本圖像處理(裁切/翻轉/模糊化/輪廓化)
那麼接下來就一步步介紹與紀錄吧。
若沒安裝OpenCV於Python環境,記得先執行
# 打開cmd執行安裝程序(若需更新可參考官方文件)
pip install opencv-python# 於python環境執行指令前先導入package
import cv2 as cv
一、圖片與影像讀取
圖片讀取很簡單,但端看是否要呈現出來(show),注意若使用 colab或遠端環境,會是遠端的資源開啟圖片,本地/連線端是看不到的。
# 讀取圖片
img = cv.imread(‘../Resources/Photos/cats.jpg’)
cv.imshow(‘Cats’, img)
cv.waitKey(0)
影片讀取的概念也是根據圖像進行播放,根據影片幀率(FPS, Frames per second)會影響讀取速度與呈現的流暢度。
- FPS越高畫面越流暢,因為每一秒包含圖像(frames)更多,相對檔案也較大。
- 越高FPS要求的硬體規格也更高,採用前須確認硬體是否支援。或硬體本身擷取畫面的FPS有其極限,例如:一般筆電鏡頭穩定時約30 FPS,是經過OS處理過後的最大值。
# 讀取影片
capture = cv.VideoCapture(‘../Resources/Videos/dog.mp4’)while True:
isTrue, frame = capture.read()
if isTrue:
cv.imshow(‘Video’, frame)
# 讀取過程中若按下 q 則離開
if cv.waitKey(27) & 0xFF==ord(‘q’):
break
# 持續讀取影片,直到讀取完畢
else:
breakcapture.release()
cv.destroyAllWindows()
二、用OpecCV繪圖
繪圖並不困難,困難的是參數要怎麼放,代表什麼意思。
# 這邊導入numpy套件
import cv2 as cv
import numpy as np
為什麼需要 numpy? Ans: 展開黑色畫布用 (建立一個都為 0的三維矩陣)。
blank = np.zeros((500,500,3), dtype=’uint8')# 1. 畫指定位置(直接給位置參數)
blank[200:300, 300:400] = 255,255,100
cv.imshow('Cyan', blank)#2. 畫方框(畫布, 起點, 終點, 顏色, 粗度)
# 如果 thickness=-1則為填滿
cv.rectangle(blank, (0,0), (250,250), (0,255,0), thickness = 3)
cv.imshow('Green', blank)# 3. 畫圓形(畫布, 圓心位置, 半徑radius, 顏色, 粗度)
# 如果 thickness=-1則為填滿
cv.circle(blank, (blank.shape[1]//2, blank.shape[0]//2), 40, (0,0,255), thickness = 3)
cv.imshow('Red', blank)# 4. 畫線條(畫布, 起點, 終點, 顏色, 粗度)
cv.line(blank ,(0,0), (blank.shape[1]//2, blank.shape[0]//2), (255,255,255), thickness = 3)
cv.imshow('Red', blank)# 5. 加入文字(畫布, 文字內容, 起點, 字體, 大小, 顏色, 粗度)
cv.putText(blank, 'This is Jimmy.', (150,225), cv.FONT_HERSHEY_TRIPLEX, 1.0, (0,255,0), 2)
cv.imshow('Text', blank)# 等待圖片關閉
cv.waitKey(0)
三、圖像預處理與形態學(Morphology)
圖片預處理有很多種,包含模糊化(blur)、輪廓化(edge cascade)、膨脹、侵蝕、先膨脹後侵蝕、先侵蝕後膨脹,具有不同的效果,並且有許多小技巧可以進行調整,影片在此先介紹到最基礎的內容。
img = cv.imread(‘../Resources/Photos/cats.jpg’)# 糊糊化(blur)
# 注意 kernel size必須是奇數(odd),kernel size越大越模糊。
# 模糊化也有許多不同的方法可以進行,讓 kernel依據需求進行運算。
blur = cv.GaussianBlur(img, (7,7), cv.BORDER_DEFAULT)
cv.imshow(‘Blur Cats’, blur)# 輪廓化(edge cascade)
# 小技巧,可以先將圖片模糊化,再進行輪廓化,可以抓到比較少雜訊。
canny = cv.Canny(blur, 125, 175)
cv.imshow(‘Canny Cats’, canny)# 注意膨脹、侵蝕用的照片是已經輪廓化處理過。雜訊會較少。
# 膨脹 dilating
dilated = cv.dilate(canny, (7,7), iterations=3)
cv.imshow(‘Dilated’, dilated)# 侵蝕 eroding
eroded = cv.erode(canny, (3,3), iterations=1)
cv.imshow(‘Eroded’, eroded)cv.waitKey(0)
在輪廓化的技法有許多變形,一般Canny 是直接採用Sobel方式進行。同樣的也有進行角點標點的技法。
輪廓化動態影片介紹如:膨脹 Dilation、侵蝕 Erosion。
進階如 Opening與 Closing
- Opening:先做Erosion 再做 Dilation。效果為將圖形凸出的銳角給鈍化。
- Closing:先做Dilation 再做 Erosion。效果為將圖形內陷的銳角給鈍化。
其他在影片中也先介紹到了圖片位置移動、旋轉等方式。
並且寫成function,取用時就相當方便。
移動圖片位置 (Translation)
img = cv.imread(‘../Resources/Photos/cats.jpg’)# 移動圖片位置 (Translation)
def translate(img, x, y):
transMat = np.float32([[1,0,x],[0,1,y]])
dimensions = (img.shape[1], img.shape[0])
return cv.warpAffine(img, transMat, dimensions)# -x → 往左移動 ; x → 往右移動
# -y → 往上移動 ; y → 往下移動translated = translate(img, -100, 100)
cv.imshow(‘Translated’, translated)
旋轉圖片(Rotation)
img = cv.imread(‘../Resources/Photos/cats.jpg’)# 旋轉圖片
def rotate(img, angle, rotPoint=None):
(height,width) = img.shape[:2]if rotPoint is None:
rotPoint = (width//2,height//2)
rotMat = cv.getRotationMatrix2D(rotPoint, angle, 1.0)
dimensions = (width,height)return cv.warpAffine(img, rotMat, dimensions)# 輸入角度作為參數
rotated = rotate(img, -45)
cv.imshow(‘Rotated’, rotated)rotated_rotated = rotate(img, -90)
cv.imshow(‘Rotated Rotated’, rotated_rotated)
四、圖片結構(輪廓與層級) Contours & Hierarchies
算是最難理解的部分,但基本上就是根據Canny輪廓化的結果(或採其他方式),進行運算,找出輪廓裡外的關係,計算總共有幾層結構。
img = cv.imread(‘../Resources/Photos/cats.jpg’)
blank = np.zeros(img.shape, dtype=’uint8')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
blur = cv.GaussianBlur(gray, (5,5), cv.BORDER_DEFAULT)
canny = cv.Canny(blur, 125, 175)
contours, hierarchies = cv.findContours(canny, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)# 計算總共有幾個輪廓 contours
print(f’{len(contours)} contour(s) found!’)# 畫出當前所有的 contours
cv.drawContours(img, contours, -1, (255,0,0), 1)
cv.imshow(‘Contours Drawn on img’, img)# 標示 contours
cv.drawContours(blank, contours, -1, (0,255,0), 1)
cv.imshow(‘Contours Drawn on blank’, blank)cv.waitKey(0)
每一個Hierarchy
都儲存該層級參數,記錄方式為[Next, Previous, First_Child, Parent]
。
- Next :同一
Hierarchy
結構中下一個輪廓Contours
。若無則為-1。 - Previous:同一
Hierarchy
結構中前一個輪廓Contours
。 若無則為-1。 - First_Child :目前輪廓位置的第一個子輪廓index。若無則為-1。
- Parent :目前輪廓位置的父輪廓index。若無則為-1。
這裡的概念較為複雜,若有興趣理解,可以參考OpenCV官方文件。簡中翻譯與實作也有人貼文。
另外,Youtube也有更多教學影片。在這裡附上一位有執行動態影片標出圖片輪廓與層級介紹影片做收尾(手把手教學)。
感謝閱讀至此 ^^。你/妳的閱讀,是我持續撰寫的最大動力。