# =============================================
# Author : Mikigo
# Time : 2021/12/1
# =============================================
一、一句話簡介
OpenCV (Open Source Computer Vision Library) 是用 C++ 語言編寫,提供 Python、Java 等語言 API的一個開源計算機視覺庫。
二、安裝
1、Debian 系使用 apt 安裝 OpencCV:
sudo apt install python-opencv
在 UOS 系統上,也可以使用:
sudo apt install python3-opencv
支持 AMD、ARM、MIPS 架構安裝。
2、安裝 NumPy:
NumPy 是 Python中的一個運算速度非常快的數學庫,數組玩到起飛,如果你玩數據科學、機器學習,這是必學庫。所有 OpenCV 數組結構都轉換為 NumPy 數組,要想 OpenCV 學得好,必須熟悉它,學習 OpenCV 基礎多少需要了解一點。
sudo apt -y install python3-numpy
好多同學安裝 Python 庫都習慣使用 pip 安裝,實際上 OpenCV 也可以,但是目前只支持在 AMD,所以考慮到兼容性還是建議使用 apt 進行安裝,並且官方文檔也是建議使用 apt 安裝。
三、入門基礎
所有 OpenCV 類和函數都放在 cv 名稱空間中,在 py 文件中導入:
import cv2 as cv
后續內容默認都使用了導入。
1、圖像
1.1、讀圖像
img = cv.imread()
- 參數1:文件路徑。(str)
- 參數2:讀取圖像的方式。
- cv.IMREAD_COLOR 加載一個彩色圖像,忽略 alpha 通道。(默認值)
- cv.IMREAD_GRAYSCALE 加載圖像為灰度模式。
- cv.IMREAD_UNCHANGED 加載圖像,包括 alpha 通道。
- 還可以簡單地分別傳遞整數1、0或 -1。
1.2、顯示圖像
cv.show()
- 參數1:窗口名稱。(str)
- 參數2:圖像。(obj)
在窗口中顯示圖像,窗口自動適合圖像大小。
銷毀窗口
cv.destroyAllWindows() # 銷毀所有窗口
cv.destroyWindow("window_name") # 銷毀某個窗口,參數傳入窗口名稱
1.3、寫圖像
cv.imwrite()
- 參數1:文件名。
- 參數2:圖像。(obj)
2、視頻
2.1、從攝像頭捕獲視頻
cap = cv.VideoCapture(0)
它的參數可以是設備索引或視頻文件的名稱。設備索引就是指定哪個攝像頭的數字。一般我們連接一個攝像頭,所以傳0(或-1)。當然可以通過傳遞1來選擇第二個相機,以此類推。
import cv2 as cv
cap = cv.VideoCapture(0)
# 判斷是否打開
if not cap.isOpened():
exit()
while True:
# 逐幀捕獲
ret, frame = cap.read()
# 判斷是否讀取到
if not ret:
break
# 轉灰度
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
# 顯示圖像
cv.imshow('frame', gray)
# 監控鍵盤信號
if cv.waitKey(1) == ord('q'):
break
# 釋放
cap.release()
cv.destroyAllWindows()
一切都看起來很和諧哈。
需要注意其他的兩個判斷:
- 有時候攝像頭可能沒有初始化成功,所以用
cap.isOpened()來判斷,並退出。 - 可能沒有接收到幀,停止循環。
獲取視頻的一些屬性:
cap.get(id)
id 是一個從0到18的數字,每個數字表示視頻的一個屬性。每個屬性代表什么→(https://docs.opencv.org/4.1.2/d4/d15/group__videoio__flags__base.html#gaeb8dd9c89c10a5c63c139bf7c4f5704d)
cap.set() 屬於進階用法,這里按下不表。
2.2、從文件中播放視頻
原理和從攝像頭中捕獲是一樣的,區別在於:
cap = cv.VideoCapture('vtest.avi')
參數是文件名稱。
2.3、保存視頻
out = VideoWriter()
- 參數1:文件名
- 參數2:fourcc = cv.VideoWriter_fourcc(*'XVID')
- FourCC 是一個4字節的代碼,用於指定視頻編解碼器。
- DIVX, XVID, MJPG, X264, WMV1, WMV2
- 不同的操作系統有差異 https://www.fourcc.org/codecs.php
- 參數3:幀率
- 參數3:分辨率
import cv2 as cv
cap = cv.VideoCapture(0)
fourcc = cv.VideoWriter_fourcc(*'XVID')
out = cv.VideoWriter('output.avi', fourcc, 20.0, (640, 480))
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# 旋轉后寫入
frame = cv.flip(frame, 0)
out.write(frame)
cv.imshow('frame', frame)
if cv.waitKey(1) == ord('q'):
break
# 釋放
cap.release()
out.release()
cv.destroyAllWindows()
結合前面攝像頭捕獲視頻的代碼,看起來很簡單~
3、繪圖
3.1、畫線
cv.line()
舉例:創建一個黑色圖像,並從左上角到右下角在上面畫一條藍線
import numpy as np
import cv2 as cv
# 寫一個黑色的圖像
img = np.zeros((512,512,3), np.uint8)
# 畫一根5像素款的藍線
cv.line(img,(0,0),(511,511),(255,0,0),5)
(255,0,0) 代表藍色,最后一個參數是線的寬度。
3.2、畫圓
cv.circle(img,(447,63), 63, (0,0,255), -1)
參數:中心坐標和半徑
3.3、畫矩形
cv.rectangle(img,(384,0),(510,128),(0,255,0),3)
參數:矩形的左上角和右下角
支持其他圖形。。。
四、核心功能
這部分內容需要對 NumPy 多少有點了解,它是 Python中的一個運算速度非常快的數學庫,數組玩到起飛,如果你玩數據科學、機器學習,這是必學庫。
1、圖像的基本操作
1.1、訪問和修改像素值
import numpy as np # 官方建議固定寫法
import cv2 as cv
img = cv.imread('messi5.jpg')
px = img[100,100]
# (100, 100)的像素值[157 166 200](B、G和R值)
blue = img[100,100,0]
修改
img[100,100] = [255,255,255]
直接賦值即可修改,但是直接訪問非常緩慢,更好的像素訪問和編輯方法:
img.item(10,10,2) # 訪問R值
img.itemset((10,10,2),100) # 修改R值
1.2、訪問圖像屬性
圖像屬性包括行數、列數和通道數、圖像數據類型、像素數等。
img.shape
# (342,548,3)
返回一個行、列和通道數的元組。
img.size # 訪問像素總數
1.3、特定區域
機器學習中經常需要摳圖,比如人臉識別時,一般會先選擇人臉區域,搜索其中的眼睛,而不是搜索整個圖像,以提供精確度。
ball = img[280:340, 330:390] # 摳一個區域
img[273:333, 100:160] = ball # 復制到另一個區域
1.4、圖像信道的分裂與合並
b,g,r = cv.split(img) # 分裂
img = cv.merge((b,g,r)) # 合並
或者
b = img[:,:,0]
比如將所有紅色像素設置為零
img[:,:,2] = 0
2、圖像的算術運算
2.1、圖片添加
cv.add()
可以添加兩個圖像
x = np.uint8([250])
y = np.uint8([10])
cv.add(x + y) # 250+10 = 260 => 255
OpenCV 的加法和 Numpy 的加法是有區別的。OpenCV 加法是一個飽和操作,而 Numpy 加法是一個除余操作。
x + y # 250+10 = 260 % 256 = 4
2.2、圖像混合
cv.addWeighted()
也是圖像添加,但不同的權重給予圖像,使它給人一種混合或透明的感覺。
- 參數1、2:圖像1及其權重。
- 參數3、4:圖像2及其權重。
- 參數5:圖像混合的α 值,通過改變 α 從0→1,你可以在一張圖片到另一張圖片之間進行一個很魔幻的轉換。
img1 = cv.imread('ml.png')
img2 = cv.imread('opencv-logo.png')
# 第一幅圖像的權重為0.7,第二幅圖像的權重為0.3
dst = cv.addWeighted(img1,0.7,img2,0.3,0)
cv.imshow('dst',dst)
cv.waitKey(0)
cv.destroyAllWindows()
2.3、按位操作
按位 AND、 OR、 NOT 和 XOR 操作。
可以理解為圖像的邏輯運算:
img1 = cv.imread('messi5.jpg')
img2 = cv.imread('opencv-logo-white.png')
# 把標志放在左上角,所以創建了一個roi
rows,cols,channels = img2.shape
roi = img1[0:rows, 0:cols ]
# 創建一個標志圖案,以及它的反面
img2gray = cv.cvtColor(img2,cv.COLOR_BGR2GRAY) # 灰度處理
ret, mask = cv.threshold(img2gray, 10, 255, cv.THRESH_BINARY)
mask_inv = cv.bitwise_not(mask)
# 黑掉 ROI 中的 logo 區域
img1_bg = cv.bitwise_and(roi,roi,mask = mask_inv)
# 只取標志圖像中的標志區域
img2_fg = cv.bitwise_and(img2,img2,mask = mask)
# 將 logo 放入 ROI 中,並修改主圖像
dst = cv.add(img1_bg,img2_fg)
img1[0:rows, 0:cols ] = dst
cv.imshow('res',img1)
cv.waitKey(0)
cv.destroyAllWindows()
五、圖像處理
1、改變色彩空間
OpenCV 目前有150多種顏色空間轉換方法。但其中兩個最廣泛使用的:
1.1、Gray
cv.COLOR_BGR2GRAY
顧名思義:BGR → Gray
這個圖像識別中經常使用,可以提供識別速度和准確度。
1.2、HSV
cv.COLOR_BGR2HSV
顧名思義:BGR → HSV
HSV 圖像可以用它來提取有色物體在 HSV 中,比在 BGR 顏色空間中更容易表示顏色。
import cv2 as cv
import numpy as np
cap = cv.VideoCapture(0)
while True:
# 取出視頻的每一幀
_, frame = cap.read()
# 從 BGR 到 HSV 顏色空間的轉換
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
# 定義 HSV 中藍色的范圍
lower_blue = np.array([110,50,50])
upper_blue = np.array([130,255,255])
# 得到藍色
mask = cv.inRange(hsv, lower_blue, upper_blue)
# 按位-and 蒙版和原始圖像
res = cv.bitwise_and(frame,frame, mask= mask)
cv.imshow('frame',frame)
cv.imshow('mask',mask)
cv.imshow('res',res)
k = cv.waitKey(5) & 0xFF
if k == 27:
break
cv.destroyAllWindows()
2、幾何變換
2.1、縮放
res = cv.resize(InputArray, OutputArray, Size, fx, fy, interpolation)
| 參數 | 解釋 |
|---|---|
| nputArray src | 輸入圖片 |
| OutputArray dst | 輸出圖片 |
| Size | 輸出圖片尺寸 |
| fx, fy | 沿x軸,y軸的縮放系數 |
| interpolation | 插入方式 |
interpolation 默認情況下,使用的插值方法是 cv.INTER_LINEAR,用於所有調整大小。
舉例:
import numpy as np
import cv2 as cv
img = cv.imread('messi5.jpg')
res = cv.resize(img,None,fx=2, fy=2, interpolation = cv.INTER_CUBIC)
2.2、旋轉
cv.getRotationMatrix2D()
旋轉90度:
img = cv.imread('messi5.jpg',0)
rows,cols = img.shape
# cols-1 and rows-1 are the coordinate limits.
M = cv.getRotationMatrix2D(((cols-1)/2.0,(rows-1)/2.0),90,1)
dst = cv.warpAffine(img,M,(cols,rows))
3、圖像閾值化
3.1、自適應閾值分割
如果像素值小於閾值,則將其設置為0,否則將其設置為最大值。
ret,thresh1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
- cv.THRESH_BINARY
- cv.THRESH_BINARY_INV
- cv.THRESH_TRUNC
- cv.THRESH_TOZERO
- cv.THRESH_TOZERO_INV
如果一個圖像在不同的區域有不同的照明條件,全局使用一個閾值一般是不可用的,對同一幅圖像的不同區域采用不同的閾值,對不同光照條件下的圖像取得了較好的效果。
- cv.ADAPTIVE_THRESH_MEAN_C 閾值是鄰近區域的平均值減去常數 c
- cv.ADAPTIVE_THRESH_GAUSSIAN_C 閾值是鄰域值減去常數 c 的高斯加權和
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('sudoku.png',0)
img = cv.medianBlur(img,5)
ret,th1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
th2 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY,11,2)
th3 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY,11,2)
titles = ['Original Image', 'Global Thresholding (v = 127)',
'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]
for i in xrange(4):
plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
4、模板匹配
4.1、單目標匹配
模板匹配是一種在較大圖像中搜索和查找模板圖像位置的方法。
cv.matchTemplate()
- 參數1:大圖
- 參數2:小圖
- 參數3:匹配方法
- cv.TM_CCOEFF
- cv.TM_CCOEFF_NORMED
- cv.TM_CCORR
- cv.TM_CCORR_NORMED
- cv.TM_SQDIFF
- cv.TM_SQDIFF_NORMED
查找最大/最小值
cv.minMaxLoc()
res = cv.matchTemplate(source, template, cv.TM_CCOEFF_NORMED)
cv.minMaxLoc(res)
4.2、多目標匹配
cv.minMaxLoc()不會給出所有位置。
通過閾值處理多個模板匹配。
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img_rgb = cv.imread('mario.png')
img_gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY)
template = cv.imread('mario_coin.png',0)
w, h = template.shape[::-1]
res = cv.matchTemplate(img_gray,template,cv.TM_CCOEFF_NORMED)
loc = np.where( res >= 0.8)
for pt in zip(*loc[::-1]):
cv.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2)
cv.imwrite('res.png',img_rgb)
