文章目錄
- 1. Opencv 環境配置
-
- 1.1 建立opencv虛拟環境
- 1.2 讓notebook 進入虛拟環境
- 1.3 虛拟環境導入pycharm
- 2. 圖像的基本操作
-
- 2.1 讀取圖檔
-
- 2.1.1 基本操作
- 2.1.2 顔色通道提取
- 2.2 讀取視訊
- 2.3 邊界填充
- 2.4 圖像融合
-
- 2.4.1 數值計算
- 2.4.2 圖像融合
- 2.5 灰階圖
-
- 2.6 HSV
- 3. 門檻值和平滑處理
-
- 3.1 圖像門檻值
- 3.2 圖像平滑
- 4. 圖像形态學操作
-
- 4.1 腐蝕操作
- 4.2 膨脹操作
- 4.3 開運算和閉運算
- 4.4 梯度運算
- 4.5 禮帽與黑帽
- 5. 圖像梯度計算
-
- 5.1 Sobel 算子
- 5.2 scharr 和lapkacian 算子
- 6. 邊緣檢測
- 7. 圖像金字塔與輪廓檢測
-
- 7.1 圖像金字塔定義
- 7.2 圖像金字塔制作
- 7.3 圖像輪廓
- 7.4 輪廓特征與近似
- 7.5 模闆比對
- 8. 直方圖和傅裡葉變換
-
- 8.1 直方圖定義
- 8.2 直方圖均衡化
- 8.3 自适應直方圖均衡化
- 9. 圖像特征
-
- 9.1 圖像特征harris
- 圖像特征SIFT
- 10. 特征比對
-
- 10.1 Brute-Force比對
- 10.2 随機抽樣一緻算法(RANAC)
- 11. 光流估計
-
- 11.1 原理介紹
- 11.2 實戰演練
- 12. 背景模組化
-
- 12.1 原理介紹
- 12.2 實戰演練
- 13. 項目實戰
1. Opencv 環境配置
為了避免依賴的沖突和python版本的不比對,建議重建立立一個虛拟環境。
要確定python版本和opencv版本比對。在這裡,我采用的是opencv 3.8.8 和 opencv 4.5.3,并且已經配置好Anaconda。
1.1 建立opencv虛拟環境
-
建立虛拟環境(其中opencvEnv是自己起的名字,也可以用其他的名字)
Anaconda Prompt 中鍵入
conda create -n opencvEnv python=3.8.8
- 進入虛拟環境
activate opencvEnv
- 安裝opencv
pip install opencv-python
pip install opencv-contrib-python
- 安裝matplotlib庫(後續畫圖需要)
pip install opencv-python
1.2 讓notebook 進入虛拟環境
- 在目前虛拟環境下安裝
conda install ipykernel
- 将目前環境寫入notebook中
python -m ipykernel install --user --name opencvEnv --display-name opencvEnv
- 如果沒有安裝notebook ,則鍵入
pip install notebook
- 打開notebook
ipython notebook
- 切換目前環境為建立立的虛拟環境opencvEnv(在導航欄中點選“kernel","change kernel ",“opencvEnv”)
1.3 虛拟環境導入pycharm
- 點選"File" ,“setting”, “project”,“python interpreter”
- 點選
Opencv學習筆記——基礎知識1. Opencv 環境配置2. 圖像的基本操作3. 門檻值和平滑處理4. 圖像形态學操作5. 圖像梯度計算6. 邊緣檢測7. 圖像金字塔與輪廓檢測8. 直方圖和傅裡葉變換9. 圖像特征10. 特征比對11. 光流估計12. 背景模組化13. 項目實戰 - 點選"add"
- 點選"Conda Environment",“Existing environment”
- 選擇“opencvEnv”,“OK”
2. 圖像的基本操作
圖檔資料大小 h e i g h t ∗ w i d t h ∗ 3 height*width*3 height∗width∗3
h e i g h t ∗ w i d t h height*width height∗width: 像素點的個數
3 3 3:R,G,B三個顔色通道
是以圖檔可以有三個大小為height*width的矩陣構成,矩陣中的元素值表示目前顔色通道的亮度大小,取值範圍是[0,255], 數值越大,表明該區域越亮。
2.1 讀取圖檔
2.1.1 基本操作
- 導入庫
import cv2
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
-
讀取圖檔
opencv的讀取格式是BGR, 讀取格式是
, 參數是圖檔位置cv2.imread()
img=cv2.imread('cat.jpg') # 圖檔與源程式處于同一個目錄下 img # 列印img的矩陣格式
- 顯示圖檔
第一個參數是圖檔名字,第二個參數是已經讀取完的圖檔cv2.imshow( )
cv2.imshow('image',img) cv2.waitKey(0) # 等待時間,毫秒級,0表示任意鍵終止 cv2.destroyAllWindows()
定義顯示圖檔的函數
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
- 圖檔的大小
img.shape
- 灰階圖檔
cv2.imread(' ',cv2.IMREAD_GRAYSCALE)
- cv2.IMREAD_COLOR:彩色圖像
- cv2.IMREAD_GRAYSCALE:灰階圖像
顯示圖檔img=cv2.imread('cat.jpg',cv2.IMREAD_GRAYSCALE) img.shape #(414,500) 灰階圖檔隻有單通道,表示亮度
cv2.imshow('image',img) cv2.waitKey(10000) #等待10000毫秒後自動關閉 cv2.destroyAllWindows()
- 儲存圖檔
參數一:儲存的檔案名;參數二:儲存的圖檔cv2.imwrite(' ', )
-
圖檔的類别
圖檔在計算機中一ndarray的資料結構儲存
- 圖檔大小
- 儲存圖檔的資料類型
- 截取部分圖像資料
img=cv2.imread('cat.jpg') cat=img[0:50,0:200] cv_show('cat',cat)
2.1.2 顔色通道提取
- 顔色通道提取
, 參數是圖檔,傳回值分别為b,g,rcv2.split()
1. b,g,r=cv2.split(img) r.shape #(414,500)
- 顔色通道合成
, 參數是顔色通道組成的元組,傳回值是圖檔cv2.merge()
img=cv2.merge((b,g,r)) img.shape # (414, 500, 3)
-
保留單通道
隻保留R
cur_img = img.copy() cur_img[:,:,0] = 0 #b通道置為0 cur_img[:,:,1] = 0 #g通道置為0 cv_show('R',cur_img)
隻保留G
# 隻保留G
cur_img = img.copy()
cur_img[:,:,0] = 0
cur_img[:,:,2] = 0
cv_show('G',cur_img)
隻保留B
cur_img = img.copy()
cur_img[:,:,1] = 0
cur_img[:,:,2] = 0
cv_show('B',cur_img)
2.2 讀取視訊
- cv2.VideoCapture可以捕獲攝像頭,用數字來控制不同的裝置,例如0,1。
- 如果是視訊檔案,直接指定好路徑即可。
- 讀視訊
cv2.VideoCapture( )
參數是視屏路徑
檢查打開的是否正确,其中open 是bool類型的資料,為True表示可以正确打開,反之則不能; frame表示讀取視訊中的一幀資料。
if vc.isOpened(): oepn, frame = vc.read() else: open = False
while open: ret, frame = vc.read() if frame is None: #如果讀取完,則退出循環 break if ret == True: gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #将每一幀圖檔轉化為灰階圖檔 cv2.imshow('result', gray) # 顯示每一幀的灰階圖檔 if cv2.waitKey(100) & 0xFF == 27: #相鄰兩個幀展示間隔時間為100ms, 0xFF 是退出鍵 break vc.release() cv2.destroyAllWindows()
2.3 邊界填充
cv2.copyMakeBorder()
參數是待處理的圖檔,上下左右邊框大小,填充方式
- BORDER_REPLICATE:複制法,也就是複制最邊緣像素。
- BORDER_REFLECT:反射法,對感興趣的圖像中的像素在兩邊進行複制例如:fedcba|abcdefgh|hgfedcb
- BORDER_REFLECT_101:反射法,也就是以最邊緣像素為軸,對稱,gfedcb|abcdefgh|gfedcba
- BORDER_WRAP:外包裝法cdefgh|abcdefgh|abcdefg
- BORDER_CONSTANT:常量法,常數值填充。
top_size,bottom_size,left_size,right_size = (50,50,50,50) # 設定邊界大小
replicate = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REPLICATE)
reflect = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size,cv2.BORDER_REFLECT)
reflect101 = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT_101)
wrap = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_WRAP)
constant = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size,cv2.BORDER_CONSTANT, value=0)
畫圖展示處理效果
import matplotlib.pyplot as plt
plt.subplot(231), plt.imshow(img, 'gray'), plt.title('ORIGINAL')
plt.subplot(232), plt.imshow(replicate, 'gray'), plt.title('REPLICATE')
plt.subplot(233), plt.imshow(reflect, 'gray'), plt.title('REFLECT')
plt.subplot(234), plt.imshow(reflect101, 'gray'), plt.title('REFLECT_101')
plt.subplot(235), plt.imshow(wrap, 'gray'), plt.title('WRAP')
plt.subplot(236), plt.imshow(constant, 'gray'), plt.title('CONSTANT')
plt.show()
2.4 圖像融合
2.4.1 數值計算
img_cat=cv2.imread('cat.jpg')
img_dog=cv2.imread('dog.jpg')
img_cat2= img_cat +10 # 相當于在每一個像素點位置加10
img_cat[:5,:,0]
'''
array([[142, 146, 151, ..., 156, 155, 154],
[108, 112, 118, ..., 155, 154, 153],
[108, 110, 118, ..., 156, 155, 154],
[139, 141, 148, ..., 156, 155, 154],
[153, 156, 163, ..., 160, 159, 158]], dtype=uint8)
'''
img_cat2[:5,:,0]
'''
array([[152, 156, 161, ..., 166, 165, 164],
[118, 122, 128, ..., 165, 164, 163],
[118, 120, 128, ..., 166, 165, 164],
[149, 151, 158, ..., 166, 165, 164],
[163, 166, 173, ..., 170, 169, 168]], dtype=uint8)
'''
(img_cat + img_cat2)[:5,:,0] #相當于% 256
'''
array([[ 38, 46, 56, ..., 66, 64, 62],
[226, 234, 246, ..., 64, 62, 60],
[226, 230, 246, ..., 66, 64, 62],
[ 32, 36, 50, ..., 66, 64, 62],
[ 60, 66, 80, ..., 74, 72, 70]], dtype=uint8)
'''
cv2.add
将兩張圖檔相加時,當和超多255時,用255代替。
cv2.add(img_cat,img_cat2)[:5,:,0]
'''
array([[255, 255, 255, ..., 255, 255, 255],
[226, 234, 246, ..., 255, 255, 255],
[226, 230, 246, ..., 255, 255, 255],
[255, 255, 255, ..., 255, 255, 255],
[255, 255, 255, ..., 255, 255, 255]], dtype=uint8)
'''
2.4.2 圖像融合
當大小不同的兩張圖檔相加會報錯。
- 調整圖檔大小
參數是待處理的圖檔,和期望的寬高。cv2.resize()
還可以按照比例縮放img_dog = cv2.resize(img_dog, (500, 414)) img_dog.shape # (414, 500, 3)
res = cv2.resize(img, (0, 0), fx=1, fy=3) plt.imshow(res)
- 按照權重融合
參數是待處理的兩張圖檔和各自的權重cv2.addWeighted()
res = cv2.addWeighted(img_cat, 0.4, img_dog, 0.6, 0) plt.imshow(res)
2.5 灰階圖
cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
import cv2 #opencv讀取的格式是BGR
import numpy as np
import matplotlib.pyplot as plt#Matplotlib是RGB
%matplotlib inline
img=cv2.imread('cat.jpg')
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img_gray.shape #(414, 500)
展示圖檔
cv2.imshow("img_gray", img_gray)
cv2.waitKey(0)
cv2.destroyAllWindows()
2.6 HSV
- H - 色調(主波長)。
- S - 飽和度(純度/顔色的陰影)。
- V值(強度)
cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
hsv=cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
cv2.imshow("hsv", hsv)
cv2.waitKey(0)
cv2.destroyAllWindows()
3. 門檻值和平滑處理
3.1 圖像門檻值
ret, dst = cv2.threshold(src, thresh, maxval, type)
- src: 輸入圖,隻能輸入單通道圖像,通常來說為灰階圖
- dst: 輸出圖
- thresh: 門檻值
- maxval: 當像素值超過了門檻值(或者小于門檻值,根據type來決定),所賦予的值
- type:二值化操作的類型,包含以下5種類型: cv2.THRESH_BINARY; cv2.THRESH_BINARY_INV; cv2.THRESH_TRUNC; cv2.THRESH_TOZERO;cv2.THRESH_TOZERO_INV
- cv2.THRESH_BINARY 超過門檻值部分取maxval(最大值),否則取0
- cv2.THRESH_BINARY_INV THRESH_BINARY的反轉
- cv2.THRESH_TRUNC 大于門檻值部分設為門檻值,否則不變
- cv2.THRESH_TOZERO 大于門檻值部分不改變,否則設為0
- cv2.THRESH_TOZERO_INV THRESH_TOZERO的反轉
ret, thresh1 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh3 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TRUNC)
ret, thresh4 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO)
ret, thresh5 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO_INV)
titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
plt.subplot(2, 3, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
3.2 圖像平滑
圖像平滑的目的是降低噪聲點的影響
img = cv2.imread('lenaNoise.png')
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
濾波實際上時進行卷積
均值濾波:簡單的平均卷積操作
方框濾波: 基本和均值一樣,可以選擇歸一化
box = cv2.boxFilter(img,-1,(3,3), normalize=True)
高斯濾波:高斯模糊的卷積核裡的數值是滿足高斯分布,相當于更重視中間
中值濾波:相當于用中值代替
展示所有的處理效果圖
res = np.hstack((blur,aussian,median))
cv2.imshow('median vs average', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
4. 圖像形态學操作
4.1 腐蝕操作
讀入原始圖檔
img = cv2.imread('dige.png')
cv2.imshow('img', i
mg)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.erode()
, 參數分别是待處理的圖檔,卷積核,疊代次數
kernel = np.ones((3,3),np.uint8)
erosion = cv2.erode(img,kernel,iterations = 1)
cv2.imshow('erosion', erosion)
cv2.waitKey(0)
cv2.destroyAllWindows()
讀入原始圖檔
pie = cv2.imread('pie.png')
cv2.imshow('pie', pie)
cv2.waitKey(0)
cv2.destroyAllWindows()
觀察疊代次數對于腐蝕的影響
kernel = np.ones((30,30),np.uint8)
erosion_1 = cv2.erode(pie,kernel,iterations = 1)
erosion_2 = cv2.erode(pie,kernel,iterations = 2)
erosion_3 = cv2.erode(pie,kernel,iterations = 3)
res = np.hstack((erosion_1,erosion_2,erosion_3))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
4.2 膨脹操作
展示原始圖檔
img = cv2.imread('dige.png')
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
先進行腐蝕操作
kernel = np.ones((3,3),np.uint8)
dige_erosion = cv2.erode(img,kernel,iterations = 1)
cv2.imshow('erosion', erosion)
cv2.waitKey(0)
cv2.destroyAllWindows()
在進行膨脹操作
cv2.dilate()
膨脹操作, 參數是圖檔,卷積核,疊代次數
kernel = np.ones((3,3),np.uint8)
dige_dilate = cv2.dilate(dige_erosion,kernel,iterations = 1)
cv2.imshow('dilate', dige_dilate)
cv2.waitKey(0)
cv2.destroyAllWindows()
觀察疊代次數對于膨脹操作的影響
pie = cv2.imread('pie.png')
kernel = np.ones((30,30),np.uint8)
dilate_1 = cv2.dilate(pie,kernel,iterations = 1)
dilate_2 = cv2.dilate(pie,kernel,iterations = 2)
dilate_3 = cv2.dilate(pie,kernel,iterations = 3)
res = np.hstack((dilate_1,dilate_2,dilate_3))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
4.3 開運算和閉運算
開:先腐蝕,再膨脹
img = cv2.imread('dige.png')
kernel = np.ones((5,5),np.uint8)
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
cv2.imshow('opening', opening)
cv2.waitKey(0)
cv2.destroyAllWindows()
閉:先膨脹,再腐蝕
img = cv2.imread('dige.png')
kernel = np.ones((5,5),np.uint8)
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
cv2.imshow('closing', closing)
cv2.waitKey(0)
cv2.destroyAllWindows()
4.4 梯度運算
梯度=膨脹-腐蝕
pie = cv2.imread('pie.png')
kernel = np.ones((7,7),np.uint8)
dilate = cv2.dilate(pie,kernel,iterations = 5)
erosion = cv2.erode(pie,kernel,iterations = 5)
res = np.hstack((dilate,erosion))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
gradient = cv2.morphologyEx(pie, cv2.MORPH_GRADIENT, kernel) #計算梯度
cv2.imshow('gradient', gradient)
cv2.waitKey(0)
cv2.destroyAllWindows()
4.5 禮帽與黑帽
- 禮帽 = 原始輸入-開運算結果
- 黑帽 = 閉運算-原始輸入
禮帽
img = cv2.imread('dige.png')
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
cv2.imshow('tophat', tophat)
cv2.waitKey(0)
cv2.destroyAllWindows()
黑帽
img = cv2.imread('dige.png')
blackhat = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT, kernel)
cv2.imshow('blackhat ', blackhat )
cv2.waitKey(0)
cv2.destroyAllWindows()
5. 圖像梯度計算
5.1 Sobel 算子
展示原始圖檔
img = cv2.imread('pie.png',cv2.IMREAD_GRAYSCALE)
cv2.imshow("img",img)
cv2.waitKey()
cv2.destroyAllWindows()
定義展示圖檔的函數
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
dst = cv2.Sobel(src, ddepth, dx, dy, ksize)
- ddepth:圖像的深度
- dx和dy分别表示水準和豎直方向
- ksize是Sobel算子的大小
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
cv_show(sobelx,'sobelx')
白到黑是正數,黑到白就是負數了,所有的負數會被截斷成0,是以要取絕對值
cv2.convertScaleAbs()
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3) # 1,0
sobelx = cv2.convertScaleAbs(sobelx)
cv_show(sobelx,'sobelx')
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3) # 0,1
sobely = cv2.convertScaleAbs(sobely)
cv_show(sobely,'sobely')
分别計算x和y,再求和
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
cv_show(sobelxy,'sobelxy')
分别計算x和y,再求和
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
cv_show(sobelxy,'sobelxy')
不建議直接計算
sobelxy=cv2.Sobel(img,cv2.CV_64F,1,1,ksize=3)
sobelxy = cv2.convertScaleAbs(sobelxy)
cv_show(sobelxy,'sobelxy')
img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
cv_show(img,'img')
推薦方法
img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
cv_show(sobelxy,'sobelxy')
5.2 scharr 和lapkacian 算子
不同算子的差異
img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
scharrx = cv2.Scharr(img,cv2.CV_64F,1,0)
scharry = cv2.Scharr(img,cv2.CV_64F,0,1)
scharrx = cv2.convertScaleAbs(scharrx)
scharry = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx,0.5,scharry,0.5,0)
laplacian = cv2.Laplacian(img,cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)
res = np.hstack((sobelxy,scharrxy,laplacian))
cv_show(res,'res')
6. 邊緣檢測
- 使用高斯濾波器,以平滑圖像,濾除噪聲。
- 計算圖像中每個像素點的梯度強度和方向。
- 應用非極大值(Non-Maximum Suppression)抑制,以消除邊緣檢測帶來的雜散響應。
- 應用雙門檻值(Double-Threshold)檢測來确定真實的和潛在的邊緣。
- 通過抑制孤立的弱邊緣最終完成邊緣檢測。
-
高斯濾波器
先進行歸一化處理
H = [ 0.0924 0.1192 0.0924 0.1192 0.1538 0.1192 0.0924 0.1192 0.0924 ] H=\left[\begin{array}{lll} 0.0924 & 0.1192 & 0.0924 \\ 0.1192 & 0.1538 & 0.1192 \\ 0.0924 & 0.1192 & 0.0924 \end{array}\right] H=⎣⎡0.09240.11920.09240.11920.15380.11920.09240.11920.0924⎦⎤
e = H ∗ A = [ h 11 h 12 h 13 h 21 h 22 h 23 h 31 h 32 h 33 ] ∗ [ a b c d e f g h i ] = sum ( [ a × h 11 b × h 12 c × h 13 d × h 21 e × h 22 f × h 23 g × h 31 h × h 32 i × h 33 ] ) e=H * A=\left[\begin{array}{lll} \mathrm{h}_{11} & \mathrm{~h}_{12} & \mathrm{~h}_{13} \\ \mathrm{~h}_{21} & \mathrm{~h}_{22} & \mathrm{~h}_{23} \\ \mathrm{~h}_{31} & \mathrm{~h}_{32} & \mathrm{~h}_{33} \end{array}\right] *\left[\begin{array}{lll} a & b & c \\ d & e & f \\ g & h & i \end{array}\right]=\operatorname{sum}\left(\left[\begin{array}{ccc} \mathrm{a} \times \mathrm{h}_{11} & \mathrm{~b} \times \mathrm{h}_{12} & \mathrm{c} \times \mathrm{h}_{13} \\ \mathrm{~d} \times \mathrm{h}_{21} & \mathrm{e} \times \mathrm{h}_{22} & \mathrm{f} \times \mathrm{h}_{23} \\ \mathrm{~g} \times \mathrm{h}_{31} & \mathrm{~h} \times \mathrm{h}_{32} & \mathrm{i} \times \mathrm{h}_{33} \end{array}\right]\right) e=H∗A=⎣⎡h11 h21 h31 h12 h22 h32 h13 h23 h33⎦⎤∗⎣⎡adgbehcfi⎦⎤=sum⎝⎛⎣⎡a×h11 d×h21 g×h31 b×h12e×h22 h×h32c×h13f×h23i×h33⎦⎤⎠⎞
-
梯度和方向
G = G x 2 + G y 2 θ = arctan ( G y / G x ) S x = [ − 1 0 1 − 2 0 2 − 1 0 1 ] S y = [ 1 2 1 0 0 0 − 1 − 2 − 1 ] G x = S x ∗ A = [ − 1 0 1 − 2 0 2 − 1 0 1 ] ∗ [ a b c d e f g h i ] = sum ( [ − a 0 c − 2 d 0 2 f − g 0 i ] ) G y = S y ∗ A = [ 1 2 1 0 0 0 − 1 − 2 − 1 ] ∗ [ a b c d e f g h i ] = sum ( [ a 2 b c 0 0 0 − g − 2 h − i ] ) . \begin{aligned} &\begin{array}{l} G=\sqrt{G_{x}^{2}+G_{y}^{2}} \\ \theta=\arctan \left(G_{y} / G_{x}\right) \end{array} \quad S_{x}=\left[\begin{array}{ccc} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{array}\right] \quad S_{y}=\left[\begin{array}{ccc} 1 & 2 & 1 \\ 0 & 0 & 0 \\ -1 & -2 & -1 \end{array}\right] \\ &G_{x}=S_{x} * A=\left[\begin{array}{lll} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{array}\right] *\left[\begin{array}{ccc} a & b & c \\ d & e & f \\ g & h & i \end{array}\right]=\operatorname{sum}\left(\left[\begin{array}{ccc} -a & 0 & c \\ -2 d & 0 & 2 f \\ -g & 0 & i \end{array}\right]\right) \\ &G_{y}=S_{y} * A=\left[\begin{array}{ccc} 1 & 2 & 1 \\ 0 & 0 & 0 \\ -1 & -2 & -1 \end{array}\right] *\left[\begin{array}{ccc} a & b & c \\ d & e & f \\ g & h & i \end{array}\right]=\operatorname{sum}\left(\left[\begin{array}{ccc} a & 2 b & c \\ 0 & 0 & 0 \\ -g & -2 h & -i \end{array}\right]\right) . \end{aligned} G=Gx2+Gy2
θ=arctan(Gy/Gx)Sx=⎣⎡−1−2−1000121⎦⎤Sy=⎣⎡10−120−210−1⎦⎤Gx=Sx∗A=⎣⎡−1−2−1000121⎦⎤∗⎣⎡adgbehcfi⎦⎤=sum⎝⎛⎣⎡−a−2d−g000c2fi⎦⎤⎠⎞Gy=Sy∗A=⎣⎡10−120−210−1⎦⎤∗⎣⎡adgbehcfi⎦⎤=sum⎝⎛⎣⎡a0−g2b0−2hc0−i⎦⎤⎠⎞.
- 非極大值抑制
4. 雙門檻值檢測
img=cv2.imread("lena.jpg",cv2.IMREAD_GRAYSCALE)
v1=cv2.Canny(img,80,150)
v2=cv2.Canny(img,50,100)
res = np.hstack((v1,v2))
cv_show(res,'res')
img=cv2.imread("car.png",cv2.IMREAD_GRAYSCALE)
v1=cv2.Canny(img,120,250)
v2=cv2.Canny(img,50,100)
res = np.hstack((v1,v2))
cv_show(res,'res')
7. 圖像金字塔與輪廓檢測
7.1 圖像金字塔定義
- 高斯金字塔
- 拉普拉斯金字塔
7.2 圖像金字塔制作
讀取圖檔
img=cv2.imread("AM.png")
cv_show(img,'img')
print (img.shape) # (442, 340, 3)
向上采樣法
cv2.pyrUp
up=cv2.pyrUp(img)
cv_show(up,'up')
print (up.shape) #(884, 680, 3)
向下采樣法
cv2.pyrDown
down=cv2.pyrDown(img)
cv_show(down,'down')
print (down.shape) # (221, 170, 3)
up=cv2.pyrUp(img)
up_down=cv2.pyrDown(up)
cv_show(img-up_down,'img-up_down')
down=cv2.pyrDown(img)
down_up=cv2.pyrUp(down)
l_1=img-down_up
cv_show(l_1,'l_1')
7.3 圖像輪廓
cv2.findContours(img,mode,method)
mode:輪廓檢索模式
- RETR_EXTERNAL :隻檢索最外面的輪廓;
- RETR_LIST:檢索所有的輪廓,并将其儲存到一條連結清單當中;
- RETR_CCOMP:檢索所有的輪廓,并将他們組織為兩層:頂層是各部分的外部邊界,第二層是空洞的邊界;
- RETR_TREE:檢索所有的輪廓,并重構嵌套輪廓的整個層次;
method:輪廓逼近方法
- CHAIN_APPROX_NONE:以Freeman鍊碼的方式輸出輪廓,所有其他方法輸出多邊形(頂點的序列)。
- CHAIN_APPROX_SIMPLE:壓縮水準的、垂直的和斜的部分,也就是,函數隻保留他們的終點部分。
為了更高的準确率,使用二值圖像。
img = cv2.imread('contours.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
cv_show(thresh,'thresh')
繪制輪廓
drawContours()
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
'''
傳入繪制圖像,輪廓,輪廓索引,顔色模式,線條厚度
注意需要copy,要不原圖會變。。。
'''
draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2) # 第一個參數為-1,繪制所有輪廓
cv_show(res,'res')
draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, 0, (0, 0, 255), 2) # 參數為0,繪制第一個外輪廓
cv_show(res,'res')
7.4 輪廓特征與近似
輪廓特征
面積:
cv2.contourArea
;
周長:
cv2.arcLength()
cnt = contours[0]
cv2.contourArea(cnt) # 輪廓面積 8500.5
cv2.arcLength(cnt,True # 周長,True表示閉合的 437.9482651948929
輪廓近似
img = cv2.imread('contours2.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
draw_img = img.copy()
res = cv2.drawContours(draw_img, [cnt], -1, (0, 0, 255), 2)
cv_show(res,'res')
epsilon = 0.15*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)
draw_img = img.copy()
res = cv2.drawContours(draw_img, [approx], -1, (0, 0, 255), 2)
cv_show(res,'res')
邊界矩形
img = cv2.imread('contours.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
x,y,w,h = cv2.boundingRect(cnt)
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
cv_show(img,'img')
area = cv2.contourArea(cnt)
x, y, w, h = cv2.boundingRect(cnt)
rect_area = w * h
extent = float(area) / rect_area
print ('輪廓面積與邊界矩形比',extent) # 輪廓面積與邊界矩形比 0.5154317244724715
外接圓
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
img = cv2.circle(img,center,radius,(0,255,0),2)
cv_show(img,'img')
7.5 模闆比對
模闆比對和卷積原理很像,模闆在原圖像上從原點開始滑動,計算模闆與(圖像被模闆覆寫的地方)的差别程度,這個差别程度的計算方法在opencv裡有6種,然後将每次計算的結果放入一個矩陣裡,作為結果輸出。假如原圖形是AxB大小,而模闆是axb大小,則輸出結果的矩陣是(A-a+1)x(B-b+1)
讀入待處理的圖檔和模闆
img = cv2.imread('lena.jpg', 0)
template = cv2.imread('face.jpg', 0)
h, w = template.shape[:2]
img.shape #(263, 263)
template.shape # (110,85)
- TM_SQDIFF:計算平方不同,計算出來的值越小,越相關
- TM_CCORR:計算相關性,計算出來的值越大,越相關
- TM_CCOEFF:計算相關系數,計算出來的值越大,越相關
- TM_SQDIFF_NORMED:計算歸一化平方不同,計算出來的值越接近0,越相關
- TM_CCORR_NORMED:計算歸一化相關性,計算出來的值越接近1,越相關
- TM_CCOEFF_NORMED:計算歸一化相關系數,計算出來的值越接近1,越相關
methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']
res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)
res.shape # (154, 179)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
min_val # 39168.0
max_val # 74403584.0
min_loc # (107, 89)
max_loc # (159, 62)
人臉比對
for meth in methods:
img2 = img.copy()
method = eval(meth)
print (method) # 比對方法的真值
res = cv2.matchTemplate(img, template, method)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
'''
如果是平方差比對TM_SQDIFF或歸一化平方差比對TM_SQDIFF_NORMED,取最小值
'''
if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
top_left = min_loc
else:
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
cv2.rectangle(img2, top_left, bottom_right, 255, 2) #畫矩形
plt.subplot(121), plt.imshow(res, cmap='gray')
plt.xticks([]), plt.yticks([]) # 隐藏坐标軸
plt.subplot(122), plt.imshow(img2, cmap='gray')
plt.xticks([]), plt.yticks([])
plt.suptitle(meth)
plt.show()
比對多個對象
img_rgb = cv2.imread('mario.jpg')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread('mario_coin.jpg', 0)
h, w = template.shape[:2]
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.8 # 取比對程度大于%80的坐标
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]): # *号表示可選參數
bottom_right = (pt[0] + w, pt[1] + h)
cv2.rectangle(img_rgb, pt, bottom_right, (0, 0, 255), 2)
cv2.imshow('img_rgb', img_rgb)
cv2.waitKey(0)
8. 直方圖和傅裡葉變換
8.1 直方圖定義
cv2.calcHist(images,channels,mask,histSize,ranges)
- images: 原圖像圖像格式為 uint8 或 float32。當傳入函數時應 用中括号 [] 括來例如[img]
- channels: 同樣用中括号括來它會告函數我們統幅圖 像的直方圖。如果入圖像是灰階圖它的值就是[0] 如果是彩色圖像 的傳入的參數可以是 [0],[1],[2] 它們分别對應着 BGR。
- mask: 掩模圖像。統整幅圖像的直方圖就把它為 None。但是如 果你想統圖像某一分的直方圖的你就制作一個掩模圖像并 使用它。
- histSize:BIN 的數目。也應用中括号括來
- ranges: 像素值範圍常為 [0256]
img = cv2.imread('cat.jpg',0) #0表示灰階圖
hist = cv2.calcHist([img],[0],None,[256],[0,256])
hist.shape #(256, 1)
plt.hist(img.ravel(),256);
plt.show()
img = cv2.imread('cat.jpg')
color = ('b','g','r')
for i,col in enumerate(color):
histr = cv2.calcHist([img],[i],None,[256],[0,256])
plt.plot(histr,color = col)
plt.xlim([0,256])
mask操作
mask = np.zeros(img.shape[:2], np.uint8)
print (mask.shape)
mask[100:300, 100:400] = 255
cv_show(mask,'mask') # (414, 500)
img = cv2.imread('cat.jpg', 0)
cv_show(img,'img')
masked_img = cv2.bitwise_and(img,mask=mask)
cv_show(masked_img,'masked_img')
hist_full = cv2.calcHist([img], [0], None, [256], [0, 256])
hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256])
plt.subplot(221), plt.imshow(img, 'gray')
plt.subplot(222), plt.imshow(mask, 'gray')
plt.subplot(223), plt.imshow(masked_img, 'gray')
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask)
plt.xlim([0, 256])
plt.show()
8.2 直方圖均衡化
img = cv2.imread('clahe.jpg',0) #0表示灰階圖 #clahe
plt.hist(img.ravel(),256);
plt.show()
equ = cv2.equalizeHist(img)
plt.hist(equ.ravel(),256)
plt.show()
res = np.hstack((img,equ))
cv_show(res,'res')
8.3 自适應直方圖均衡化
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
res_clahe = clahe.apply(img)
res = np.hstack((img,equ,res_clahe))
cv_show(res,'res')
9. 圖像特征
9.1 圖像特征harris
cv2.cornerHarris()
img: 資料類型為 float32 的入圖像
blockSize: 角點檢測中指定區域的大小
ksize: Sobel求導中使用的視窗大小
k: 取值參數為 [0,04,0.06]
import cv2
import numpy as np
img = cv2.imread('test_1.jpg')
print ('img.shape:',img.shape)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# gray = np.float32(gray)
dst = cv2.cornerHarris(gray, 2, 3, 0.04)
print ('dst.shape:',dst.shape)
'''
img.shape: (800, 1200, 3)
dst.shape: (800, 1200)
'''
img[dst>0.01*dst.max()]=[0,0,255]
cv2.imshow('dst',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
圖像特征SIFT
在一定的範圍内,無論物體是大還是小,人眼都可以分辨出來,然而計算機要有相同的能力卻很難,是以要讓機器能夠對物體在不同尺度下有一個統一的認知,就需要考慮圖像在不同的尺度下都存在的特點。
尺度空間的擷取通常使用高斯模糊來實作
不同σ的高斯函數決定了對圖像的平滑程度,越大的σ值對應的圖像越模糊
每個特征點可以得到三個資訊(x,y,σ,θ),即位置、尺度和方向。具有多個方向的關鍵點可以被複制成多份,然後将方向值分别賦給複制後的特征點,一個特征點就産生了多個坐标、尺度相等,但是方向不同的特征點。
import cv2
import numpy as np
img = cv2.imread('test_1.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 得到特征點
sift = cv2.xfeatures2d.SIFT_create()
kp = sift.detect(gray, None)
# 畫特征點
img = cv2.drawKeypoints(gray, kp, img)
cv2.imshow('drawKeypoints', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 計算特征
kp, des = sift.compute(gray, kp)
print (np.array(kp).shape) # (6809,)
des.shape #(6809, 128)
10. 特征比對
10.1 Brute-Force比對
import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
img1 = cv2.imread('box.png', 0)
img2 = cv2.imread('box_in_scene.png', 0)
def cv_show(name,img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv_show('img1',img1)
cv_show('img2',img2)
sift = cv2.xfeatures2d.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
# crossCheck表示兩個特征點要互相匹,例如A中的第i個特征點與B中的第j個特征點最近的,并且B中的第j個特征點到A中的第i個特征點也是
#NORM_L2: 歸一化數組的(歐幾裡德距離),如果其他特征計算方法需要考慮不同的比對計算方式
bf = cv2.BFMatcher(crossCheck=True)
一對一的比對
matches = bf.match(des1, des2)
matches = sorted(matches, key=lambda x: x.distance)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:10], None,flags=2)
cv_show('img3',img3)
K對最佳比對
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1, des2, k=2)
good = []
for m, n in matches:
if m.distance < 0.75 * n.distance:
good.append([m])
img3 = cv2.drawMatchesKnn(img1,kp1,img2,kp2,good,None,flags=2)
cv_show('img3',img3)
如果需要更快速完成操作,可以嘗試使用cv2.FlannBasedMatcher
10.2 随機抽樣一緻算法(RANAC)
11. 光流估計
11.1 原理介紹
光流是空間運動物體在觀測成像平面上的像素運動的“瞬時速度”,根據各個像素點的速度矢量特征,可以對圖像進行動态分析,例如目标跟蹤。
亮度恒定:同一點随着時間的變化,其亮度不會發生改變。
小運動:随着時間的變化不會引起位置的劇烈變化,隻有小運動情況下才能用前後幀之間機關位置變化引起的灰階變化去近似灰階對位置的偏導數。
空間一緻:一個場景上鄰近的點投影到圖像上也是鄰近點,且鄰近點速度一緻。因為光流法基本方程限制隻有一個,而要求x,y方向的速度,有兩個未知變量。是以需要連立n多個方程求解。
11.2 實戰演練
cv2.calcOpticalFlowPyrLK()
參數:
prevImage 前一幀圖像
nextImage 目前幀圖像
prevPts 待跟蹤的特征點向量
winSize 搜尋視窗的大小
maxLevel 最大的金字塔層數
傳回:
nextPts 輸出跟蹤特征點向量
status 特征點是否找到,找到的狀态為1,未找到的狀态為0
import numpy as np
import cv2
cap = cv2.VideoCapture('test.avi')
# 角點檢測所需參數
feature_params = dict( maxCorners = 100,
qualityLevel = 0.3,
minDistance = 7)
# lucas kanade參數
lk_params = dict( winSize = (15,15),
maxLevel = 2)
# 随機顔色條
color = np.random.randint(0,255,(100,3))
# 拿到第一幀圖像
ret, old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
# 傳回所有檢測特征點,需要輸入圖像,角點最大數量(效率),品質因子(特征值越大的越好,來篩選)
# 距離相當于這區間有比這個角點強的,就不要這個弱的了
p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params)
# 建立一個mask
mask = np.zeros_like(old_frame)
while(True):
ret,frame = cap.read()
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 需要傳入前一幀和目前圖像以及前一幀檢測到的角點
p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
# st=1表示
good_new = p1[st==1]
good_old = p0[st==1]
# 繪制軌迹
for i,(new,old) in enumerate(zip(good_new,good_old)):
a,b = new.ravel()
c,d = old.ravel()
mask = cv2.line(mask, (int(a),int(b)),(int(c),int(d)), color[i].tolist(), 2)
frame = cv2.circle(frame,(int(a),int(b)),5,color[i].tolist(),-1)
img = cv2.add(frame,mask)
cv2.imshow('frame',img)
k = cv2.waitKey(150) & 0xff
if k == 27:
break
# 更新
old_gray = frame_gray.copy()
p0 = good_new.reshape(-1,1,2)
cv2.destroyAllWindows()
cap.release()
12. 背景模組化
12.1 原理介紹
幀差法
由于場景中的目标在運動,目标的影像在不同圖像幀中的位置不同。該類算法對時間上連續的兩幀圖像進行差分運算,不同幀對應的像素點相減,判斷灰階差的絕對值,當絕對值超過一定門檻值時,即可判斷為運動目标,進而實作目标的檢測功能。
幀差法非常簡單,但是會引入噪音和空洞問題
混合高斯模型
在進行前景檢測前,先對背景進行訓練,對圖像中每個背景采用一個混合高斯模型進行模拟,每個背景的混合高斯的個數可以自适應。然後在測試階段,對新來的像素進行GMM比對,如果該像素值能夠比對其中一個高斯,則認為是背景,否則認為是前景。由于整個過程GMM模型在不斷更新學習中,是以對動态背景有一定的魯棒性。最後通過對一個有樹枝搖擺的動态背景進行前景檢測,取得了較好的效果。
在視訊中對于像素點的變化情況應當是符合高斯分布
混合高斯模型學習方法
1.首先初始化每個高斯模型矩陣參數。
2.取視訊中T幀資料圖像用來訓練高斯混合模型。來了第一個像素之後用它來當做第一個高斯分布。
3.當後面來的像素值時,與前面已有的高斯的均值比較,如果該像素點的值與其模型均值差在3倍的方差内,則屬于該分布,并對其進行參數更新。
4.如果下一次來的像素不滿足目前高斯分布,用它來建立一個新的高斯分布。
混合高斯模型測試方法
在測試階段,對新來像素點的值與混合高斯模型中的每一個均值進行比較,如果其內插補點在2倍的方差之間的話,則認為是背景,否則認為是前景。将前景指派為255,背景指派為0。這樣就形成了一副前景二值圖。
12.2 實戰演練
import numpy as np
import cv2
#經典的測試視訊
cap = cv2.VideoCapture('test.avi')
#形态學操作需要使用
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
#建立混合高斯模型用于背景模組化
fgbg = cv2.createBackgroundSubtractorMOG2()
while(True):
ret, frame = cap.read()
fgmask = fgbg.apply(frame)
#形态學開運算去噪點
fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
#尋找視訊中的輪廓
contours, hierarchy = cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
#計算各輪廓的周長
perimeter = cv2.arcLength(c,True)
if perimeter > 188:
#找到一個直矩形(不會旋轉)
x,y,w,h = cv2.boundingRect(c)
#畫出這個矩形
cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)
cv2.imshow('frame',frame)
cv2.imshow('fgmask', fgmask)
k = cv2.waitKey(150) & 0xff
if k == 27:
break
cap.release()
cv2.destroyAllWindows()
運作結果
13. 項目實戰
opencv項目實戰(1)——信用卡數字識别
opencv項目實戰(2)——文檔掃描OCR識别
opencv項目實戰(3)——全景圖像拼接
opencv項目實戰(4)——答題卡識别判卷
opencv項目實戰(5)——DNN子產品
opencv項目實戰(6)—— 多目标追蹤
opencv項目實戰 (7) —— 基于dlib和ssd的追蹤
opencv項目實戰(8)—— 多程序目标追蹤
opencv項目實戰(9)——人臉五官檢測
opencv項目實戰(10)—— 疲勞檢測