OpenCV-Python 快速指南



OpenCV Python - 概述

OpenCV 代表 **開源計算機視覺**,它是一個函式庫,可用於即時計算機視覺應用程式程式設計。計算機視覺是指使用計算機程式對數字影像和影片進行分析的一門學科。計算機視覺是現代學科(如人工智慧和機器學習)的重要組成部分。

OpenCV 最初由英特爾開發,是一個跨平臺庫,用 C++編寫,但也有 C 介面。許多其他程式語言(如 Java 和 Python)的 OpenCV 包裝器也已開發出來。本教程將介紹 OpenCV 的 Python 庫的功能。

OpenCV-Python

**OpenCV-Python** 是 OpenCV 庫 C++ 實現的 Python 包裝器。它利用 NumPy 庫進行數值運算,是計算機視覺問題的快速原型工具。

OpenCV-Python 是一個跨平臺庫,可在所有作業系統 (OS) 平臺上使用,包括 Windows、Linux、MacOS 和 Android。OpenCV 還支援圖形處理單元 (GPU) 加速。

本教程是為希望在計算機視覺應用領域獲得專業知識的計算機科學學生和專業人士設計的。瞭解 OpenCV-Python 的功能需要具備 Python 和 NumPy 庫的預備知識。

OpenCV Python - 環境搭建

在大多數情況下,使用 pip 就足以在您的計算機上安裝 OpenCV-Python。

用於安裝 pip 的命令如下:

pip install opencv-python

建議在新虛擬環境中執行此安裝。當前版本的 OpenCV-Python 為 4.5.1.48,可以透過以下命令驗證:

>>> import cv2
>>> cv2.__version__
'4.5.1'

由於 OpenCV-Python 依賴於 NumPy,因此它也會自動安裝。或者,您可以安裝 Matplotlib 來呈現某些圖形輸出。

在 Fedora 上,您可以透過以下命令安裝 OpenCV-Python:

$ yum install numpy opencv*

也可以從其原始碼 http://sourceforge.net 安裝 OpenCV-Python。請按照給出的安裝說明進行操作。

OpenCV Python - 讀取影像

**CV2** 包(OpenCV-Python 庫的名稱)提供 **imread()** 函式來讀取影像。

讀取影像的命令如下:

img=cv2.imread(filename, flags)

flags 引數是以下常量的列舉:

  • cv2.IMREAD_COLOR (1) - 載入彩色影像。
  • cv2.IMREAD_GRAYSCALE (0) - 以灰度模式載入影像
  • cv2.IMREAD_UNCHANGED (-1) - 按原樣載入影像,包括 alpha 通道

該函式將返回一個影像物件,可以使用 imshow() 函式渲染。使用 imshow() 函式的命令如下:

cv2.imshow(window-name, image)

影像顯示在命名視窗中。使用 AUTOSIZE 標誌建立新視窗。**WaitKey()** 是一個鍵盤繫結函式。其引數是以毫秒為單位的時間。

該函式等待指定的毫秒數,並在按下按鍵之前保持視窗顯示。最後,我們可以銷燬所有建立的視窗。

該函式等待指定的毫秒數,並在按下按鍵之前保持視窗顯示。最後,我們可以銷燬所有建立的視窗。

顯示 OpenCV 徽標的程式如下:

import numpy as np
import cv2
# Load a color image in grayscale
img = cv2.imread('OpenCV_Logo.png',1)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

上面的程式顯示 OpenCV 徽標如下:

OpenCV Logo

OpenCV Python - 寫入影像

CV2 包具有 **imwrite()** 函式,該函式將影像物件儲存到指定檔案。

使用 imwrite() 函式儲存影像的命令如下:

cv2.imwrite(filename, img)

OpenCV 根據副檔名自動確定影像格式。OpenCV 支援 *.bmp、*.dib、*.jpeg、*.jpg、*.png、*.webp、*.sr、*.tiff、*.tif 等影像檔案型別。

示例

下面的程式載入 OpenCV 徽標影像,並在按下“s”鍵時儲存其灰度版本:

import numpy as np
import cv2
# Load an color image in grayscale
img = cv2.imread('OpenCV_Logo.png',0)
cv2.imshow('image',img)
key=cv2.waitKey(0)
if key==ord('s'):
   cv2.imwrite("opencv_logo_GS.png", img)
cv2.destroyAllWindows()

輸出

OpenCV logo greyscale

OpenCV Python - 使用 Matplotlib

Python 的 Matplotlib 是一個功能強大的繪相簿,包含大量用於各種繪圖型別的繪圖函式。它還具有 imshow() 函式來渲染影像。它提供了額外的功能,例如縮放、儲存等。

示例

在執行以下程式之前,請確保在當前工作環境中安裝了 Matplotlib。

import numpy as np
import cv2
import matplotlib.pyplot as plt
# Load an color image in grayscale
img = cv2.imread('OpenCV_Logo.png',0)
plt.imshow(img)
plt.show()

輸出

OpenCV Logo Matplotlib

OpenCV Python - 影像屬性

OpenCV 將影像資料讀取到 NumPy 陣列中。此 ndarray 物件的 **shape()** 方法揭示了影像屬性,例如尺寸和通道。

使用 shape() 方法的命令如下:

>>> img = cv.imread("OpenCV_Logo.png", 1)
>>> img.shape
(222, 180, 3)

在上面的命令中:

  • 前兩項 shape[0] 和 shape[1] 分別表示影像的寬度和高度。
  • Shape[2] 代表通道數。
  • 3 表示影像具有紅綠藍 (RGB) 通道。

類似地,size 屬性返回影像的大小。影像大小的命令如下:

>>> img.size
119880

ndarray 中的每個元素都表示一個影像畫素。

我們可以使用下面提到的命令訪問和操作任何畫素的值。

>>> p=img[50,50]
>>> p
array([ 1, 1, 255], dtype=uint8)

示例

以下程式碼將前 100X100 個畫素的顏色值更改為黑色。**imshow()** 函式可以驗證結果。

>>> for i in range(100):
   for j in range(100):
      img[i,j]=[0,0,0]

輸出

imshow

可以使用 **split()** 函式將影像通道分割成單個平面。可以使用 **merge()** 函式合併通道。

split() 函式返回一個多通道陣列。

我們可以使用以下命令分割影像通道:

>>> img = cv.imread("OpenCV_Logo.png", 1)
>>> b,g,r = cv.split(img)

您現在可以對每個平面進行操作。

假設我們將藍色通道中的所有畫素設定為 0,則程式碼如下:

>>> img[:, :, 0]=0
>>> cv.imshow("image", img)

生成的影像將如下所示:

Individual Planes

OpenCV Python - 位運算

位運算用於影像處理和提取影像中的重要部分。

OpenCV 中實現了以下運算子:

  • bitwise_and
  • bitwise_or
  • bitwise_xor
  • bitwise_not

示例 1

為了演示這些運算子的使用,我們使用了兩個帶有填充和空心圓的影像。

以下程式演示了在 OpenCV-Python 中使用位運算子:

import cv2
import numpy as np

img1 = cv2.imread('a.png')
img2 = cv2.imread('b.png')

dest1 = cv2.bitwise_and(img2, img1, mask = None)
dest2 = cv2.bitwise_or(img2, img1, mask = None)
dest3 = cv2.bitwise_xor(img1, img2, mask = None)

cv2.imshow('A', img1)
cv2.imshow('B', img2)
cv2.imshow('AND', dest1)
cv2.imshow('OR', dest2)
cv2.imshow('XOR', dest3)
cv2.imshow('NOT A', cv2.bitwise_not(img1))
cv2.imshow('NOT B', cv2.bitwise_not(img2))

if cv2.waitKey(0) & 0xff == 27:
   cv2.destroyAllWindows()

輸出

Bitwise Operators Bitwise Operators Bitwise Operators

示例 2

在另一個涉及位運算的示例中,OpenCV 徽標疊加在另一張影像上。在這裡,我們透過對徽標呼叫 **threshold()** 函式來獲得掩碼陣列,並在它們之間執行 AND 運算。

同樣,透過 NOT 運算,我們得到一個反向掩碼。此外,我們還與背景影像進行 AND 運算。

以下是確定位運算使用的程式:

import cv2 as cv
import numpy as np

img1 = cv.imread('lena.jpg')
img2 = cv.imread('whitelogo.png')
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)
# Now black-out the area of logo
img1_bg = cv.bitwise_and(roi,roi,mask = mask_inv)

# Take only region of logo from logo image.
img2_fg = cv.bitwise_and(img2,img2,mask = mask)
# Put logo in ROI
dst = cv.add(img2_fg, img1_bg)
img1[0:rows, 0:cols ] = dst
cv.imshow(Result,img1)
cv.waitKey(0)
cv.destroyAllWindows()

輸出

掩碼影像給出以下結果:

Bitwise Operators Mask

OpenCV Python - 繪製形狀和文字

在本章中,我們將學習如何使用 OpenCV-Python 在影像上繪製形狀和文字。讓我們首先了解如何在影像上繪製形狀。

在影像上繪製形狀

我們需要了解 OpenCV-Python 中所需的函式,這些函式可以幫助我們在影像上繪製形狀。

函式

OpenCV-Python 包(稱為 cv2)包含以下函式來繪製相應的形狀。

函式 描述 命令
cv2.line() 繪製連線兩點的線段。 cv2.line(img, pt1, pt2, color, thickness)
cv2.circle() 在給定點為中心的給定半徑處繪製一個圓。 cv2.circle(img, center, radius, color, thickness)
cv2.rectangle 繪製一個矩形,其點為左上角和右下角。 cv2.rectangle(img, pt1, pt2, color, thickness)
cv2.ellipse() 繪製簡單的或粗的橢圓弧或填充橢圓扇區。 cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color, thickness)

引數

上述函式的常用引數如下:

序號 函式和描述
1

img

要在其中繪製形狀的影像

2

color

形狀的顏色。對於 BGR,將其作為元組傳遞。對於灰度,只需傳遞標量值。

3

thickness

線或圓等的粗細。如果為封閉圖形(如圓)傳遞 -1,則將填充形狀。

4

lineType

線的型別,是 8 連通線、抗鋸齒線等。

示例

以下示例顯示如何在影像頂部繪製形狀。相應的程式如下:

import numpy as np
import cv2
img = cv2.imread('LENA.JPG',1)
cv2.line(img,(20,400),(400,20),(255,255,255),3)
cv2.rectangle(img,(200,100),(400,400),(0,255,0),5)
cv2.circle(img,(80,80), 55, (255,255,0), -1)
cv2.ellipse(img, (300,425), (80, 20), 5, 0, 360, (0,0,255), -1)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

輸出

Draw Shapes

繪製文字

提供 **cv2.putText()** 函式用於在影像上寫入文字。相應的命令如下:

img, text, org, fontFace, fontScale, color, thickness)

字型

OpenCV 支援以下字型:

字型名稱 字型大小
FONT_HERSHEY_SIMPLEX 0
FONT_HERSHEY_PLAIN 1
FONT_HERSHEY_DUPLEX 2
FONT_HERSHEY_COMPLEX 3
FONT_HERSHEY_TRIPLEX 4
FONT_HERSHEY_COMPLEX_SMALL 5
FONT_HERSHEY_SCRIPT_SIMPLEX 6
FONT_HERSHEY_SCRIPT_COMPLEX 7
FONT_ITALIC 16

示例

以下程式將文字標題新增到著名足球運動員萊昂內爾·梅西的照片中。

import numpy as np
import cv2
img = cv2.imread('messi.JPG',1)
txt="Lionel Messi"
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,txt,(10,100), font, 2,(255,255,255),2,cv2.LINE_AA)

cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

輸出

Draw Text

OpenCV Python - 處理滑鼠事件

OpenCV 能夠使用回撥函式註冊各種與滑鼠相關的事件。這樣做是為了根據滑鼠事件的型別啟動某些使用者定義的操作。

序號 滑鼠事件和描述
1

cv.EVENT_MOUSEMOVE

當滑鼠指標已移到視窗上時。

2

cv.EVENT_LBUTTONDOWN

表示左滑鼠按鈕已按下。

3

cv.EVENT_RBUTTONDOWN

表示右滑鼠按鈕已按下。

4

cv.EVENT_MBUTTONDOWN

表示滑鼠中鍵按下。

5

cv.EVENT_LBUTTONUP

滑鼠左鍵釋放時。

6

cv.EVENT_RBUTTONUP

滑鼠右鍵釋放時。

7

cv.EVENT_MBUTTONUP

表示滑鼠中鍵釋放。

8

cv.EVENT_LBUTTONDBLCLK

滑鼠左鍵雙擊時發生此事件。

9

cv.EVENT_RBUTTONDBLCLK

表示滑鼠右鍵雙擊。

10

cv.EVENT_MBUTTONDBLCLK

表示滑鼠中鍵雙擊。

11

cv.EVENT_MOUSEWHEEL

向前滾動為正,向後滾動為負。

要在滑鼠事件上觸發函式,必須藉助setMouseCallback()函式註冊。相應的命令如下:

cv2.setMouseCallback(window, callbak_function)

此函式將事件的型別和位置傳遞給回撥函式以進行進一步處理。

示例 1

以下程式碼在顯示影像作為背景的視窗上發生左鍵雙擊事件時繪製一個圓:

import numpy as np
import cv2 as cv
# mouse callback function
def drawfunction(event,x,y,flags,param):
   if event == cv.EVENT_LBUTTONDBLCLK:
      cv.circle(img,(x,y),20,(255,255,255),-1)
img = cv.imread('lena.jpg')
cv.namedWindow('image')
cv.setMouseCallback('image',drawfunction)
while(1):
   cv.imshow('image',img)
   key=cv.waitKey(1)
   if key == 27:
      break
cv.destroyAllWindows()

輸出

執行上述程式並在隨機位置雙擊。將出現類似的輸出:

Mouse Events

示例 2

以下程式根據使用者輸入(1、2或3)互動式地繪製矩形、線條或圓:

import numpy as np
import cv2 as cv
# mouse callback function

drawing=True
shape='r'

def draw_circle(event,x,y,flags,param):
   global x1,x2
   if event == cv.EVENT_LBUTTONDOWN:
      drawing = True
      x1,x2 = x,y
   elif event == cv.EVENT_LBUTTONUP:
      drawing = False
      if shape == 'r':
         cv.rectangle(img,(x1,x2),(x,y),(0,255,0),-1)
      if shape == 'l':
         cv.line(img,(x1,x2),(x,y),(255,255,255),3)
      if shape=='c':
         cv.circle(img,(x,y), 10, (255,255,0), -1)
img = cv.imread('lena.jpg')
cv.namedWindow('image')
cv.setMouseCallback('image',draw_circle)
while(1):
   cv.imshow('image',img)
   key=cv.waitKey(1)
   if key==ord('1'):
      shape='r'
   if key==ord('2'):
      shape='l'
   if key==ord('3'):
      shape='c'

   #print (shape)
   if key == 27:
      break
   cv.destroyAllWindows()

如果按下“1”,則在視窗表面上在滑鼠左鍵按下和抬起的座標之間繪製一個矩形。

如果使用者選擇2,則使用座標作為端點繪製一條線。

選擇3繪製圓形時,它將在滑鼠抬起事件的座標處繪製。

成功執行上述程式後,輸出影像如下:

Mouse Event

OpenCV Python - 新增軌跡條

OpenCV中的軌跡條是一個滑塊控制元件,它透過手動在條上滑動標籤來幫助從連續範圍內選擇變數的值。標籤的位置與值同步。

createTrackbar()函式使用以下命令建立一個軌跡條物件:

cv2.createTrackbar(trackbarname, winname, value, count, TrackbarCallback)

在下面的示例中,提供了三個軌跡條供使用者從灰度範圍0到255設定R、G和B的值。

使用軌跡條位置值,繪製一個填充顏色對應於RGB顏色的矩形。

示例

以下程式用於新增軌跡條:

import numpy as np
import cv2 as cv
img = np.zeros((300,400,3), np.uint8)
cv.namedWindow('image')
def nothing(x):
   pass

# create trackbars for color change
cv.createTrackbar('R','image',0,255,nothing)
cv.createTrackbar('G','image',0,255,nothing)
cv.createTrackbar('B','image',0,255,nothing)

while(1):
   cv.imshow('image',img)
   k = cv.waitKey(1) & 0xFF
   if k == 27:
      break
   # get current positions of four trackbars
   r = cv.getTrackbarPos('R','image')
   g = cv.getTrackbarPos('G','image')
   b = cv.getTrackbarPos('B','image')

   #s = cv.getTrackbarPos(switch,'image')
   #img[:] = [b,g,r]
   cv.rectangle(img, (100,100),(200,200), (b,g,r),-1)
   cv.destroyAllWindows()

輸出

Trackbar

OpenCV Python - 調整影像大小和旋轉

本章將學習如何使用OpenCV Python調整影像大小和旋轉影像。

調整影像大小

可以使用cv2.resize()函式放大或縮小影像。

resize()函式使用方法如下:

resize(src, dsize, dst, fx, fy, interpolation)

一般來說,插值是在已知資料點之間估計值的過程。

當圖形資料包含間隙,但在間隙的兩側或間隙內的幾個特定點上有可用資料時。插值允許我們估計間隙內的值。

在上文的resize()函式中,插值標誌決定了用於計算目標影像大小的插值型別。

插值型別

插值型別如下:

  • INTER_NEAREST - 最近鄰插值。

  • INTER_LINEAR - 雙線性插值(預設使用)

  • INTER_AREA - 使用像素面積關係進行重取樣。它是影像抽取的首選方法,但當影像縮放時,它類似於INTER_NEAREST方法。

  • INTER_CUBIC - 4x4畫素鄰域上的三次插值

  • INTER_LANCZOS4 - 8x8畫素鄰域上的Lanczos插值

首選的插值方法是cv2.INTER_AREA用於縮小和cv2.INTER_CUBIC(慢)和cv2.INTER_LINEAR用於放大。

示例

以下程式碼將“messi.jpg”影像的大小調整為其原始高度和寬度的一半。

import numpy as np
import cv2
img = cv2.imread('messi.JPG',1)
height, width = img.shape[:2]
res = cv2.resize(img,(int(width/2), int(height/2)), interpolation =
cv2.INTER_AREA)

cv2.imshow('image',res)
cv2.waitKey(0)
cv2.destroyAllWindows()

輸出

Resize an Image

旋轉影像

OpenCV使用仿射變換函式進行影像操作,例如平移和旋轉。仿射變換是一種可以表示為矩陣乘法(線性變換)後跟向量加法(平移)形式的變換。

cv2模組提供了兩個函式cv2.warpAffinecv2.warpPerspective,您可以使用它們進行各種變換。cv2.warpAffine採用2x3變換矩陣,而cv2.warpPerspective採用3x3變換矩陣作為輸入。

為了找到旋轉的變換矩陣,OpenCV提供了一個函式cv2.getRotationMatrix2D,如下所示:

getRotationMatrix2D(center, angle, scale)

然後,我們將warpAffine函式應用於getRotationMatrix2D()函式返回的矩陣,以獲得旋轉後的影像。

以下程式將原始影像旋轉90度,而不改變尺寸:

示例

import numpy as np
import cv2
img = cv2.imread('OpenCV_Logo.png',1)
h, w = img.shape[:2]

center = (w / 2, h / 2)
mat = cv2.getRotationMatrix2D(center, 90, 1)
rotimg = cv2.warpAffine(img, mat, (h, w))
cv2.imshow('original',img)
cv2.imshow('rotated', rotimg)
cv2.waitKey(0)
cv2.destroyAllWindows()

輸出

原始影像

Original Image

旋轉後的影像

Rotated Image

OpenCV Python - 影像閾值化

在數字影像處理中,閾值化是基於畫素強度閾值建立二值影像的過程。閾值化過程將前景畫素與背景畫素分離。

OpenCV提供函式執行簡單、自適應Otsu閾值化。

在簡單閾值化中,所有值小於閾值的畫素都設定為零,其餘設定為最大畫素值。這是最簡單的閾值化形式。

cv2.threshold()函式具有以下定義。

cv2.threshold((src, thresh, maxval, type, dst)

引數

影像閾值化的引數如下:

  • Src:輸入陣列。
  • Dst:相同大小的輸出陣列。
  • Thresh:閾值。
  • Maxval:最大值。
  • Type:閾值型別。

閾值型別

其他閾值型別如下所示:

序號 型別和函式
1

cv.THRESH_BINARY

如果src(x,y)>thresh,則dst(x,y) = maxval,否則為0

2

cv.THRESH_BINARY_INV

如果src(x,y)>thresh,則dst(x,y)=0,否則為maxval

3

cv.THRESH_TRUNC

如果src(x,y)>thresh,則dst(x,y)=threshold,否則為src(x,y)

4

cv.THRESH_TOZERO

如果src(x,y)>thresh,則dst(x,y)=src(x,y),否則為0

5

cv.THRESH_TOZERO_INV

如果src(x,y)>thresh,則dst(x,y)=0,否則為src(x,y)

這些閾值型別根據下圖對輸入影像進行操作:

Threshold

threshold()函式返回使用的閾值和閾值影像。

以下程式透過將閾值設定為127,從具有從255到0的灰度值的原始影像生成二值影像。

示例

使用Matplotlib庫並排繪製原始影像和生成的閾值二值影像。

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('gradient.png',0)
ret,img1 = cv.threshold(img,127,255,cv.THRESH_BINARY)

plt.subplot(2,3,1),plt.imshow(img,'gray',vmin=0,vmax=255)
plt.title('Original')
plt.subplot(2,3,2),plt.imshow(img1,'gray',vmin=0,vmax=255)
plt.title('Binary')
plt.show()

輸出

Threshold Binary

自適應閾值根據其周圍的小區域確定畫素的閾值。因此,可以獲得同一影像不同區域的不同閾值。這對於照明變化的影像提供了更好的結果。

cv2.adaptiveThreshold()方法採用以下輸入引數:

cv.adaptiveThreshold( src, maxValue, adaptiveMethod, thresholdType, blockSize, C[, dst] )

adaptiveMethod具有以下列舉值:

  • cv.ADAPTIVE_THRESH_MEAN_C - 閾值是鄰域區域的平均值減去常數C。

  • cv.ADAPTIVE_THRESH_GAUSSIAN_C - 閾值是鄰域值的加權高斯和減去常數C。

示例

在下面的示例中,原始影像(messi.jpg)應用了均值和高斯自適應閾值化。

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('messi.jpg',0)
img = cv.medianBlur(img,5)
th1 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_MEAN_C,\
   cv.THRESH_BINARY,11,2)
th2 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,\
   cv.THRESH_BINARY,11,2)
titles = ['Original', 'Mean Thresholding', 'Gaussian Thresholding']
images = [img, th1, th2]
for i in range(3):
   plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
   plt.title(titles[i])
   plt.xticks([]),plt.yticks([])
plt.show()

輸出

使用matplotlib繪製原始影像以及自適應閾值二值影像,如下所示:

Adaptive Threshold Binary

示例

OTSU演算法從影像直方圖自動確定閾值。我們需要除了THRESH-BINARY標誌之外還要傳遞cv.THRES_OTSU標誌。

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('messi.jpg',0)
# global thresholding
ret1,img1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
# Otsu's thresholding
ret2,img2 = cv.threshold(img,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
plt.subplot(2,2,1),plt.imshow(img,'gray',vmin=0,vmax=255)
plt.title('Original')
plt.subplot(2,2,2),plt.imshow(img1,'gray')

plt.title('Binary')
plt.subplot(2,2,3),plt.imshow(img2,'gray')
plt.title('OTSU')
plt.show()

輸出

matplotlib的繪圖結果如下:

Image Histogram

OpenCV Python - 影像濾波

影像基本上是由0到255之間的二進位制值表示的畫素矩陣,對應於灰度值。彩色影像將是一個三維矩陣,具有對應於RGB的多個通道。

影像濾波是平均畫素值以改變原始影像的陰影、亮度、對比度等的過程。

透過應用低通濾波器,我們可以去除影像中的任何噪聲。高通濾波器有助於檢測邊緣。

OpenCV庫提供cv2.filter2D()函式。它透過大小為3X3或5X5等的正方形矩陣的核對原始影像執行卷積。

卷積將核矩陣水平和垂直地滑過影像矩陣。對於每個位置,新增核下所有畫素,取核下畫素的平均值,並用平均值替換中心畫素。

對所有畫素執行此操作以獲得輸出影像畫素矩陣。請參考下圖:

Pixel Matrix

cv2.filter2D()函式需要輸入陣列、核矩陣和輸出陣列引數。

示例

下圖使用此函式獲得作為二維卷積結果的平均影像。相應的程式如下:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('opencv_logo_gs.png')
kernel = np.ones((3,3),np.float32)/9
dst = cv.filter2D(img,-1,kernel)
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(dst),plt.title('Convolved')
plt.xticks([]), plt.yticks([])
plt.show()

輸出

Pixel Matrix

濾波函式型別

OpenCV中的其他濾波函式型別包括:

  • BilateralFilter - 減少不需要的噪聲,同時保持邊緣完整。

  • BoxFilter - 這是一個平均模糊操作。

  • GaussianBlur - 消除高頻內容,如噪聲和邊緣。

  • MedianBlur - 它不取平均值,而是取核下所有畫素的中值,並替換中心值。

OpenCV Python - 邊緣檢測

這裡的邊緣是指影像中物體的邊界。OpenCV有一個cv2.Canny()函式,透過實現Canny演算法來識別影像中各種物體的邊緣。

Canny邊緣檢測演算法由John Canny開發。根據它,物體的邊緣是透過執行以下步驟確定的:

第一步是減少影像中的噪聲畫素。這是透過應用5X5高斯濾波器來完成的。

第二步涉及查詢影像的強度梯度。透過應用Sobel運算元獲得水平和垂直方向的一階導數(Gx和Gy)來過濾第一階段的平滑影像。

均方根值給出邊緣梯度,導數的反正切比給出邊緣的方向。

$$ \mathrm{Edge \:gradient\:G\:=\:\sqrt{G_x^2+G_y^2}} $$

$$ \mathrm{Angle\:\theta\:=\:\tan^{-1}(\frac{G_{y}}{G_{x}}) $$

獲得梯度幅度和方向後,將對影像進行全掃描以去除任何可能不構成邊緣的不需要的畫素。

下一步是使用minval和maxval閾值執行滯後閾值化。小於minval和maxval的強度梯度是非邊緣,因此被丟棄。介於兩者之間的根據其連通性被視為邊緣點或非邊緣點。

所有這些步驟都由OpenCV的cv2.Canny()函式執行,該函式需要輸入影像陣列和minval和maxval引數。

示例

這是一個Canny邊緣檢測的示例。程式如下:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('lena.jpg', 0)
edges = cv.Canny(img,100,200)
plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edges of original Image'), plt.xticks([]), plt.yticks([])
plt.show()

輸出

Canny Edge

OpenCV Python - 直方圖

直方圖顯示影像中的強度分佈。它在X軸上繪製畫素值(0到255),在Y軸上繪製畫素數量。

透過使用直方圖,可以瞭解指定影像的對比度、亮度和強度分佈。直方圖中的bin表示X軸上值的增量部分。

在我們的例子中,它是畫素值,預設bin大小為1。

在OpenCV庫中,函式**cv2.calcHist()**計算輸入影像的直方圖。函式命令如下:

cv.calcHist(images, channels, mask, histSize, ranges)

引數

**cv2.calcHist()**函式的引數如下:

  • **images** - 它是uint8或float32型別的源影像,用方括號括起來,即“[img]”。

  • **channels** - 它是我們計算直方圖的通道索引。對於灰度影像,其值為[0]。對於BGR影像,您可以傳遞[0]、[1]或[2]來計算每個通道的直方圖。

  • **mask** - 對於全圖,掩碼影像設定為“None”。對於影像的特定區域,您必須為此建立一個掩碼影像並將其作為掩碼。

  • **histSize** - 這表示我們的BIN數量。

  • **ranges** - 通常是[0,256]。

使用Matplotlib繪製直方圖

直方圖可以使用Matplotlib的**pyplot.plot()**函式或呼叫OpenCV庫中的**Polylines()**函式獲得。

示例

下面的程式計算影像(lena.jpg)中每個通道的直方圖,並繪製每個通道的強度分佈:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('lena.jpg')
color = ('b','g','r')
for i,col in enumerate(color):
   hist = cv.calcHist([img],[i],None,[256],[0,256])
   plt.plot(hist, color = col)
   plt.xlim([0,256])
plt.show()

輸出

Histogram

OpenCV Python - 顏色空間

顏色空間是一個描述如何表示顏色的數學模型。它在一個特定、可測量和固定的可能的顏色和亮度值範圍內進行描述。

OpenCV支援以下常用的顏色空間:

  • **RGB顏色空間** - 這是一個加色顏色空間。顏色值是透過紅色、綠色和藍色顏色值的組合獲得的。每個值由一個介於0到255之間的數字表示。

  • **HSV顏色空間** - H、S和V分別代表色相、飽和度和值。這是RGB的替代顏色模型。該模型應該更接近人眼感知任何顏色的方式。色相值介於0到179之間,而S和V值介於0到255之間。

  • **CMYK顏色空間** - 與RGB相反,CMYK是一種減色顏色模型。字母分別代表青色、品紅色、黃色和黑色。白光減去紅色得到青色,綠色從白色中減去得到品紅色,白色減去藍色得到黃色。所有值都在0到100%的範圍內表示。

  • **CIELAB顏色空間** - LAB顏色空間具有三個分量:L表示亮度,A表示從綠色到品紅色的顏色分量,B表示從藍色到黃色的分量。

  • **YCrCb顏色空間** - 這裡,Cr代表R-Y,Cb代表B-Y。這有助於將亮度與色度分離到不同的通道中。

OpenCV使用**cv2.cvtColor()**函式支援影像在顏色空間之間的轉換。

cv2.cvtColor()函式的命令如下:

cv.cvtColor(src, code, dst)

轉換程式碼

轉換由以下預定義的轉換程式碼控制。

序號 轉換程式碼 & 函式
1

cv.COLOR_BGR2BGRA

向RGB或BGR影像新增alpha通道。

2

cv.COLOR_BGRA2BGR

從RGB或BGR影像中刪除alpha通道。

3

cv.COLOR_BGR2GRAY

在RGB/BGR和灰度之間轉換。

4

cv.COLOR_BGR2YCrCb

將RGB/BGR轉換為亮度-色度

5

cv.COLOR_BGR2HSV

將RGB/BGR轉換為HSV

6

cv.COLOR_BGR2Lab

將RGB/BGR轉換為CIE Lab

7

cv.COLOR_HSV2BGR

HSV向RGB/BGR的反向轉換

示例

下面的程式顯示了將具有RGB顏色空間的原始影像轉換為HSV和灰度方案:

import cv2
img = cv2.imread('messi.jpg')
img1 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY )
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Displaying the image
cv2.imshow('original', img)
cv2.imshow('Gray', img1)
cv2.imshow('HSV', img2)

輸出

RGB Color Space

RGB Color Space

RGB Color Space

OpenCV Python - 形態學變換

基於影像形狀的簡單操作稱為形態學變換。兩種最常見的變換是**腐蝕和膨脹**。

腐蝕

腐蝕去除前景物件的邊界。類似於二維卷積,一個核心在影像A上滑動。如果核心下的所有畫素都是1,則保留原始影像中的畫素。

否則將其設為0,從而導致腐蝕。邊界附近的所有畫素都被丟棄。此過程用於去除白噪聲。

OpenCV中**erode()**函式的命令如下:

cv.erode(src, kernel, dst, anchor, iterations)

引數

OpenCV中的**erode()**函式使用以下引數:

src和dst引數是相同大小的輸入和輸出影像陣列。核心是用於腐蝕的結構元素矩陣。例如,3X3或5X5。

anchor引數預設為-1,這意味著錨元素位於中心。迭代次數是指應用腐蝕的次數。

膨脹

這正好與腐蝕相反。這裡,如果核心下至少有一個畫素為1,則畫素元素為1。結果,它增加了影像中的白色區域。

dilate()函式的命令如下:

cv.dilate(src, kernel, dst, anchor, iterations)

引數

**dilate()**函式具有與erode()函式相同的引數。這兩個函式都可以具有附加的可選引數,如BorderType和borderValue。

BorderType是影像邊界的列舉型別(CONSTANT、REPLICATE、TRANSPERANT等)。

borderValue用於常數邊界的情況。預設情況下,它是0。

示例

下面是一個示例程式,顯示了erode()和dilate()函式的使用:

import cv2 as cv
import numpy as np
img = cv.imread('LinuxLogo.jpg',0)
kernel = np.ones((5,5),np.uint8)
erosion = cv.erode(img,kernel,iterations = 1)
dilation = cv.dilate(img,kernel,iterations = 1)
cv.imshow('Original', img)
cv.imshow('Erosion', erosion)
cv.imshow('Dialation', dilation)

輸出

原始影像

Morphological

腐蝕

Erosion

膨脹

Dilation

OpenCV Python - 影像輪廓

輪廓是連線沿邊界所有連續點的曲線,這些點具有相同的顏色或強度。輪廓對於形狀分析和目標檢測非常有用。

查詢輪廓

在查詢輪廓之前,我們應該應用閾值或Canny邊緣檢測。然後,透過使用**findContours()**方法,我們可以在二值影像中找到輪廓。

**findContours()**函式用法的命令如下:

cv.findContours(image, mode, method, contours)

引數

**findContours()**函式的引數如下:

  • image - 源,一個8位單通道影像。
  • mode - 輪廓檢索模式。
  • method - 輪廓逼近方法。

mode引數的值如下所示:

  • **cv.RETR_EXTERNAL** - 只檢索最外層的輪廓。

  • **cv.RETR_LIST** - 檢索所有輪廓,而不建立任何層次關係。

  • **cv.RETR_CCOMP** - 檢索所有輪廓並將它們組織成兩級層次結構。

  • **cv.RETR_TREE** - 檢索所有輪廓並重建巢狀輪廓的完整層次結構。

另一方面,逼近方法可以是以下之一:

  • **cv.CHAIN_APPROX_NONE** - 儲存絕對所有輪廓點。

  • **cv.CHAIN_APPROX_SIMPLE** - 壓縮水平、垂直和對角線段,只留下它們的端點。

繪製輪廓

檢測到輪廓向量後,使用**cv.drawContours()**函式在原始影像上繪製輪廓。

cv.drawContours()函式的命令如下:

cv.drawContours(image, contours, contourIdx, color)

引數

**drawContours()**函式的引數如下:

  • image - 目標影像。
  • contours - 所有輸入輪廓。每個輪廓都儲存為一個點向量。
  • contourIdx - 指示要繪製的輪廓的引數。如果為負數,則繪製所有輪廓。
  • color - 輪廓的顏色。

示例

以下程式碼是在一個輸入影像上繪製輪廓的示例,該影像具有三個填充黑色顏色的形狀。

第一步,我們獲得一個灰度影像,然後執行Canny邊緣檢測。

在結果影像上,我們然後呼叫findContours()函式。其結果是一個點向量。然後我們呼叫drawContours()函式。

完整的程式碼如下:

import cv2
import numpy as np

img = cv2.imread('shapes.png')
cv2.imshow('Original', img)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

canny = cv2.Canny(gray, 30, 200)

contours, hierarchy = cv2.findContours(canny,
   cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
print("Number of Contours = " ,len(contours))
cv2.imshow('Canny Edges', canny)

cv2.drawContours(img, contours, -1, (0, 255, 0), 3)

cv2.imshow('Contours', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

輸出

原始影像、Canny邊緣檢測後的影像以及繪製輪廓後的影像將分別顯示在單獨的視窗中,如下所示:

separate windows

**Canny邊緣檢測**後,影像如下:

canny edge detection

**繪製輪廓**後,影像如下:

contours are drawn

OpenCV Python - 模板匹配

模板匹配技術用於檢測影像中與樣本或模板影像匹配的一個或多個區域。

OpenCV中定義了**Cv.matchTemplate()**函式,其命令如下:

cv.matchTemplate(image, templ, method)

其中image是需要查詢templ(模板)模式的輸入影像。method引數採用以下值之一:

  • cv.TM_CCOEFF,
  • cv.TM_CCOEFF_NORMED, cv.TM_CCORR,
  • cv.TM_CCORR_NORMED,
  • cv.TM_SQDIFF,
  • cv.TM_SQDIFF_NORMED

此方法將模板影像滑過輸入影像。這類似於卷積的過程,並比較模板和模板影像下輸入影像的影像塊。

它返回一個灰度影像,其每個畫素表示它與模板的匹配程度。如果輸入影像的大小為(WxH),模板影像的大小為(wxh),則輸出影像的大小將為(W-w+1, H-h+1)。因此,該矩形是您的模板區域。

示例

在下面的示例中,使用一張印有印度板球運動員Virat Kohli面部的影像作為模板,與另一張描繪他和另一位印度板球運動員M.S.Dhoni照片的影像進行匹配。

下面的程式使用80%的閾值,並在匹配的面部周圍繪製一個矩形:

import cv2
import numpy as np

img = cv2.imread('Dhoni-and-Virat.jpg',1)
cv2.imshow('Original',img)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

template = cv2.imread('virat.jpg',0)
cv2.imshow('Template',template)
w,h = template.shape[0], template.shape[1]

matched = cv2.matchTemplate(gray,template,cv2.TM_CCOEFF_NORMED)
threshold = 0.8

loc = np.where( matched >= threshold)

for pt in zip(*loc[::-1]):
   cv2.rectangle(img, pt, (pt[0] + w, pt[1] + h), (0,255,255), 2)

cv2.imshow('Matched with Template',img)

輸出

原始影像、模板和匹配結果影像如下:

原始影像

Template Matching

**模板**如下:

Templates

**與模板匹配**後的影像如下:

Matched Templates

OpenCV Python - 影像金字塔

有時,我們可能需要將影像轉換為與其原始大小不同的尺寸。為此,您可以放大影像(放大)或縮小影像(縮小)。

影像金字塔是由多個影像組成的集合(由單個原始影像構建),這些影像被連續下采樣指定的次數。

高斯金字塔用於對影像進行下采樣,而拉普拉斯金字塔則從金字塔中解析度較低的影像重建上取樣影像。

將金字塔視為一組層。影像如下所示:

Pyramid

金字塔較高層的影像尺寸較小。為了生成高斯金字塔中下一層的影像,我們將較低層影像與高斯核心進行卷積。

現在移除所有偶數行和偶數列。生成的影像面積將是其前身影像的 1/4。對原始影像迭代此過程會生成整個影像金字塔。

為了使影像更大,用零填充列。首先,將影像在每個維度上放大到原始影像的兩倍,用零填充新的偶數行,然後使用卷積核對缺失畫素的值進行近似。

cv.pyrUp() 函式將原始影像大小加倍,而 cv.pyrDown() 函式將其縮小一半。

示例

下面的程式根據使用者輸入的“I”或“o”分別呼叫 pyrUp() 和 pyrDown() 函式。

請注意,當我們減小影像大小時,影像資訊會丟失。一旦我們縮小影像,如果我們將其重新縮放為原始大小,我們將丟失一些資訊,並且新影像的解析度將遠低於原始影像。

import sys
import cv2 as cv

filename = 'chicky_512.png'

src = cv.imread(filename)

while 1:
   print ("press 'i' for zoom in 'o' for zoom out esc to stop")
   rows, cols, _channels = map(int, src.shape)
   cv.imshow('Pyramids', src)
   k = cv.waitKey(0)

   if k == 27:
      break

   elif chr(k) == 'i':
      src = cv.pyrUp(src, dstsize=(2 * cols, 2 * rows))

   elif chr(k) == 'o':
      src = cv.pyrDown(src, dstsize=(cols // 2, rows // 2))

cv.destroyAllWindows()

輸出

Image Pyramids

Gaussian Pyramids

Laplacian Pyramids

OpenCV Python - 影像加法

當影像被 imread() 函式讀取時,生成的影像物件實際上是一個二維或三維矩陣,這取決於影像是灰度影像還是 RGB 影像。

因此,cv2.add() 函式將兩個影像矩陣相加並返回另一個影像矩陣。

示例

下面的程式碼讀取兩張影像並執行它們的二元加法:

kalam = cv2.imread('kalam.jpg')
einst = cv2.imread('einstein.jpg')
img = cv2.add(kalam, einst)
cv2.imshow('addition', img)

結果

Image Addition

OpenCV 除了線性二元加法之外,還有一個 addWeighted() 函式可以執行兩個陣列的加權和。該函式的命令如下:

Cv2.addWeighted(src1, alpha, src2, beta, gamma)

引數

addWeighted() 函式的引數如下:

  • src1 − 第一個輸入陣列。
  • alpha − 第一個陣列元素的權重。
  • src2 − 第二個輸入陣列,大小和通道數與第一個相同。
  • beta − 第二個陣列元素的權重。
  • gamma − 加到每個和上的標量。

此函式根據以下等式新增影像:

$$\mathrm{g(x)=(1-\alpha)f_{0}(x)+\alpha f_{1}(x)}$$

上述示例中獲得的影像矩陣用於執行加權和。

透過將 α 從 0 更改為 1,可以實現從一個影像到另一個影像的平滑過渡,從而使它們融合在一起。

第一張影像的權重為 0.3,第二張影像的權重為 0.7。伽馬因子取為 0。

addWeighted() 函式的命令如下:

img = cv2.addWeighted(kalam, 0.3, einst, 0.7, 0)

可以看出,與二元加法相比,影像加法更平滑。

Gamma Factor

OpenCV Python - 使用影像金字塔進行影像混合

可以使用影像金字塔來最大限度地減少影像的不連續性。這將產生無縫混合的影像。

採取以下步驟以獲得最終結果:

首先載入影像併為兩者找到高斯金字塔。相應的程式如下:

import cv2
import numpy as np,sys

kalam = cv2.imread('kalam.jpg')
einst = cv2.imread('einstein.jpg')
### generate Gaussian pyramid for first
G = kalam.copy()
gpk = [G]
for i in range(6):
   G = cv2.pyrDown(G)
   gpk.append(G)
# generate Gaussian pyramid for second
G = einst.copy()
gpe = [G]
for i in range(6):
   G = cv2.pyrDown(G)
   gpe.append(G)

從高斯金字塔中獲得各自的拉普拉斯金字塔。相應的程式如下:

# generate Laplacian Pyramid for first
lpk = [gpk[5]]
for i in range(5,0,-1):
   GE = cv2.pyrUp(gpk[i])
   L = cv2.subtract(gpk[i-1],GE)
   lpk.append(L)

# generate Laplacian Pyramid for second
lpe = [gpe[5]]
for i in range(5,0,-1):
   GE = cv2.pyrUp(gpe[i])
   L = cv2.subtract(gpe[i-1],GE)
   lpe.append(L)

然後,將第一張影像的左半部分與第二張影像的右半部分連線到金字塔的每個級別。相應的程式如下:

# Now add left and right halves of images in each level
LS = []
for la,lb in zip(lpk,lpe):
   rows,cols,dpt = la.shape
   ls = np.hstack((la[:,0:int(cols/2)], lb[:,int(cols/2):]))
   LS.append(ls)

最後,從此聯合金字塔重建影像。相應的程式如下:

ls_ = LS[0]
for i in range(1,6):
   ls_ = cv2.pyrUp(ls_)
   ls_ = cv2.add(ls_, LS[i])
   cv2.imshow('RESULT',ls_)

輸出

混合結果應如下所示:

Blending Pyramids

OpenCV Python - 傅立葉變換

傅立葉變換用於將影像從其空間域變換到其頻域,方法是將其分解為正弦和餘弦分量。

對於數字影像,基本的灰度影像值通常在 0 和 255 之間。因此,傅立葉變換也需要是離散傅立葉變換 (DFT)。它用於查詢頻域。

在數學上,二維影像的傅立葉變換表示如下:

$$\mathrm{F(k,l)=\displaystyle\sum\limits_{i=0}^{N-1}\: \displaystyle\sum\limits_{j=0}^{N-1} f(i,j)\:e^{-i2\pi (\frac{ki}{N},\frac{lj}{N})}}$$

如果幅度在短時間內變化很快,則可以說它是高頻訊號。如果它變化緩慢,則它是低頻訊號。

對於影像,幅度在邊緣點或噪聲處急劇變化。因此,邊緣和噪聲是影像中的高頻內容。如果幅度變化不大,則它是低頻分量。

OpenCV 提供了cv.dft()cv.idft() 函式來實現此目的。

cv.dft() 執行一維或二維浮點陣列的離散傅立葉變換。相應的命令如下:

cv.dft(src, dst, flags)

這裡:

  • src − 可以是實數或複數的輸入陣列。
  • dst − 輸出陣列,其大小和型別取決於標誌。
  • flags − 變換標誌,表示 DftFlags 的組合。

cv.idft() 計算一維或二維陣列的逆離散傅立葉變換。相應的命令如下:

cv.idft(src, dst, flags)

為了獲得離散傅立葉變換,輸入影像被轉換為 np.float32 資料型別。然後使用獲得的變換將零頻率分量移到頻譜的中心,從中計算幅度譜。

示例

下面是使用 Matplotlib 的程式,我們繪製原始影像和幅度譜:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('lena.jpg',0)
dft = cv.dft(np.float32(img),flags = cv.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
magnitude_spectrum = 20*np.log(cv.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))
plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

輸出

Fourier Transform

OpenCV Python - 從攝像頭捕捉影片

透過使用 OpenCV 庫中的 VideoCapture() 函式,可以很容易地在 OpenCV 視窗上從攝像頭捕捉即時流。

此函式需要裝置索引作為引數。您的計算機可能連線了多個攝像頭。它們由從內建網路攝像頭開始的索引 0 開始的索引列舉。該函式返回一個 VideoCapture 物件。

cam = cv.VideoCapture(0)

開啟攝像頭後,我們可以藉助 read() 函式讀取連續的幀。

ret,frame = cam.read()

read() 函式讀取下一個可用幀和一個返回值 (True/False)。此幀現在使用 cvtColor() 函式在所需的色彩空間中呈現,並在 OpenCV 視窗中顯示。

img = cv.cvtColor(frame, cv.COLOR_BGR2RGB)
# Display the resulting frame
cv.imshow('frame', img)

要將當前幀捕獲到影像檔案,可以使用 imwrite() 函式。

cv2.imwrite(“capture.png”, img)

要將來自攝像頭的即時流儲存到影片檔案,OpenCV 提供了 VideoWriter() 函式。

cv.VideoWriter( filename, fourcc, fps, frameSize)

fourcc 引數是影片編解碼器的標準化程式碼。OpenCV 支援各種編解碼器,例如 DIVX、XVID、MJPG、X264 等。fps 和 framesize 引數取決於影片捕獲裝置。

VideoWriter() 函式返回一個 VideoWrite 流物件,捕獲的幀將依次寫入迴圈中。最後,釋放幀和 VideoWriter 物件以完成影片的建立。

示例

以下示例讀取內建網路攝像頭的即時影片,並將其儲存到 ouput.avi 檔案中。

import cv2 as cv
cam = cv.VideoCapture(0)
cc = cv.VideoWriter_fourcc(*'XVID')
file = cv.VideoWriter('output.avi', cc, 15.0, (640, 480))
if not cam.isOpened():
   print("error opening camera")
   exit()
while True:
   # Capture frame-by-frame
   ret, frame = cam.read()
   # if frame is read correctly ret is True
   if not ret:
      print("error in retrieving frame")
      break
   img = cv.cvtColor(frame, cv.COLOR_BGR2RGB)
   cv.imshow('frame', img)
   file.write(img)

   
   if cv.waitKey(1) == ord('q'):
      break

cam.release()
file.release()
cv.destroyAllWindows()

OpenCV Python - 從檔案中播放影片

VideoCapture() 函式也可以從影片檔案而不是攝像頭檢索幀。因此,我們只需將攝像頭索引替換為要在 OpenCV 視窗上播放的影片檔案的名稱即可。

video=cv2.VideoCapture(file)

雖然這足以開始渲染影片檔案,但如果影片檔案包含聲音,則聲音將不會一起播放。為此,您需要安裝 ffpyplayer 模組。

FFPyPlayer

FFPyPlayer 是 FFmpeg 庫的 Python 繫結,用於播放和寫入媒體檔案。要安裝,請使用以下命令使用 pip 安裝程式實用程式。

pip3 install ffpyplayer

此模組中 MediaPlayer 物件的 get_frame() 方法返回音訊幀,該音訊幀將與從影片檔案讀取的每個幀一起播放。

以下是播放包含音訊的影片檔案的完整程式碼:

import cv2

from ffpyplayer.player import MediaPlayer
file="video.mp4"

video=cv2.VideoCapture(file)
player = MediaPlayer(file)
while True:
   ret, frame=video.read()
   audio_frame, val = player.get_frame()
   if not ret:
      print("End of video")
      break
   if cv2.waitKey(1) == ord("q"):
      break
   cv2.imshow("Video", frame)
   if val != 'eof' and audio_frame is not None:
      #audio
      img, t = audio_frame
video.release()
cv2.destroyAllWindows()

OpenCV Python - 從影片中提取影像

影片只不過是一系列幀,每幀都是一幅影像。使用 OpenCV,可以透過執行 imwrite() 函式到影片結尾來提取構成影片檔案的所有幀。

cv2.read() 函式返回下一個可用幀。該函式還提供一個返回值,該返回值在流結束前繼續為 true。在這裡,計數器在迴圈內遞增,並用作檔名。

以下程式演示如何從影片中提取影像:

import cv2
import os

cam = cv2.VideoCapture("video.avi")

frameno = 0
while(True):
   ret,frame = cam.read()
   if ret:
      # if video is still left continue creating images
      name = str(frameno) + '.jpg'
      print ('new frame captured...' + name)

      cv2.imwrite(name, frame)
      frameno += 1
   else:
      break

cam.release()
cv2.destroyAllWindows()

OpenCV Python - 從影像生成影片

在上一章中,我們使用了 VideoWriter() 函式將來自攝像頭的即時流儲存為影片檔案。為了將多個影像拼接成影片,我們將使用相同的函式。

首先,確保所有必需的影像都在一個資料夾中。Python 的內建 glob 模組中的 glob() 函式構建一個影像陣列,以便我們可以迭代它。

從資料夾中的影像讀取影像物件,並將其附加到影像陣列。

以下程式解釋如何將多個影像拼接成影片:

import cv2
import numpy as np
import glob

img_array = []
for filename in glob.glob('*.png'):
   img = cv2.imread(filename)
   height, width, layers = img.shape
   size = (width,height)
   img_array.append(img)

使用 VideoWriter() 函式建立一個影片流,將影像陣列的內容寫入其中。以下是相應的程式:

out = cv2.VideoWriter('video.avi',cv2.VideoWriter_fourcc(*'DIVX'), 15, size)

for i in range(len(img_array)):
   out.write(img_array[i])
out.release()

您應該在當前資料夾中找到名為 ‘video.avi’ 的檔案。

OpenCV Python - 人臉檢測

OpenCV 使用基於 Haar 特徵的級聯分類器進行目標檢測。這是一種基於機器學習的演算法,其中級聯函式是從大量正影像和負影像訓練出來的。然後將其用於檢測其他影像中的物件。該演算法使用級聯分類器的概念。

可以從 https://github.com下載人臉、眼睛等的預訓練分類器。

對於以下示例,請從此 URL 下載並複製 haarcascade_frontalface_default.xmlhaarcascade_eye.xml。然後,載入我們的輸入影像,以灰度模式用於人臉檢測。

CascadeClassifier 類的 DetectMultiScale() 方法檢測輸入影像中的物件。它以矩形及其尺寸 (x,y,w,h) 的形式返回檢測到的人臉的位置。一旦我們獲得這些位置,我們就可以將其用於眼睛檢測,因為眼睛總是在臉上!

示例

人臉檢測的完整程式碼如下:

import numpy as np
import cv2

face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')

img = cv2.imread('Dhoni-and-virat.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
   img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
   roi_gray = gray[y:y+h, x:x+w]
   roi_color = img[y:y+h, x:x+w]
   eyes = eye_cascade.detectMultiScale(roi_gray)
   for (ex,ey,ew,eh) in eyes:
      cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)

cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

輸出

您將在輸入影像中的人臉周圍繪製矩形,如下所示:

Face Detection

OpenCV Python - 均值漂移和 CamShift

在本章中,讓我們學習 OpenCV-Python 中的均值漂移和 CamShift。首先,讓我們瞭解什麼是均值漂移。

均值漂移

均值漂移演算法識別資料集中具有高資料點濃度或聚類的位置。該演算法在每個資料點放置一個核心,並將它們加在一起以構成核心密度估計 (KDE)

KDE 將分別具有高資料點密度和低資料點密度的位置。均值漂移是一種非常有用的方法,可以跟蹤影片中特定物件。

對影片的每一幀都進行畫素分佈檢查。初始感興趣區域(ROI)通常是正方形或圓形。為此,位置是透過硬編碼指定的,並識別畫素分佈最大區域。

隨著影片播放,ROI視窗向畫素分佈最大區域移動。移動方向取決於跟蹤視窗中心與其內部所有k個畫素的質心之間的差異。

為了在OpenCV中使用Meanshift,首先找到目標的直方圖(其中只考慮色調),並將其目標反投影到每一幀中以進行Meanshift計算。我們還需要提供ROI視窗的初始位置。

我們重複計算直方圖的反投影並計算Meanshift以獲得跟蹤視窗的新位置。之後,我們使用其尺寸在幀上繪製一個矩形。

函式

程式中使用的OpenCV函式如下:

  • cv.calcBackProject() − 計算直方圖的反投影。

  • cv.meanShift() − 使用初始搜尋視窗和迭代搜尋演算法的停止條件來進行目標直方圖的反投影。

示例

以下是Meanshift的示例程式:

import numpy as np
import cv2 as cv

cap = cv.VideoCapture('traffic.mp4')

ret,frame = cap.read()

# dimensions of initial location of window
x, y, w, h = 300, 200, 100, 50
tracker = (x, y, w, h)

region = frame[y:y+h, x:x+w]
hsv_reg = cv.cvtColor(region, cv.COLOR_BGR2HSV)
mask = cv.inRange(hsv_reg, np.array((0., 60.,32.)), np.array((180.,255.,255.)))
reg_hist = cv.calcHist([hsv_reg],[0],mask,[180],[0,180])
cv.normalize(reg_hist,reg_hist,0,255,cv.NORM_MINMAX)

# Setup the termination criteria
criteria = ( cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 1 )

while(1):
   ret, frame = cap.read()

   if ret == True:
      hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
      dst = cv.calcBackProject([hsv],[0],reg_hist,[0,180],1)

      # apply meanshift
      ret, tracker = cv.meanShift(dst, tracker, criteria)

      # Draw it on image
      x,y,w,h = tracker
      img = cv.rectangle(frame, (x,y), (x+w,y+h), 255,2)
      cv.imshow('img',img)

      k = cv.waitKey(30) & 0xff
      if k==115:
         cv.imwrite('capture.png', img)
      if k == 27:
         break

程式執行時,Meanshift演算法將我們的視窗移動到密度最大的新位置。

輸出

以下是移動視窗的快照:

Meanshift

CamShift

Meanshift演算法的一個缺點是,跟蹤視窗的大小保持不變,而不管物體與攝像機的距離如何。此外,只有當物體在該物體的區域內時,窗口才會跟蹤該物體。因此,我們必須手動硬編碼視窗,並且必須小心地進行。

CAMshift(代表Continuously Adaptive Meanshift,連續自適應均值漂移)解決了這些問題。一旦meanshift收斂,CamShift演算法就會更新視窗的大小,以便跟蹤視窗可以改變大小甚至旋轉,以更好地與被跟蹤物體的運動相關聯。

在下面的程式碼中,使用了camshift()函式而不是meanshift()函式。

首先,它使用meanShift查詢物體中心,然後調整視窗大小並找到最佳旋轉。該函式返回物體的位姿、大小和方向。位置使用polylines()繪圖函式在幀上繪製。

示例

在之前的程式中,將Meanshift()函式替換為CamShift()函式,如下所示:

# apply camshift
ret, tracker = cv.CamShift(dst, tracker, criteria)
pts = cv.boxPoints(ret)
pts = np.int0(pts)
img = cv.polylines(frame,[pts],True, 255,2)
cv.imshow('img',img)

輸出

以下是修改後的程式結果的快照,顯示了跟蹤視窗的旋轉矩形:

Camshift

OpenCV Python - 特徵檢測

在影像處理的背景下,特徵是影像中關鍵區域的數學表示。它們是影像視覺內容的矢量表示。

特徵使得對它們進行數學運算成為可能。各種計算機視覺應用包括目標檢測、運動估計、分割、影像對齊等。

任何影像中的顯著特徵包括邊緣、角點或影像的一部分。OpenCV支援Haris角點檢測Shi-Tomasi角點檢測演算法。OpenCV庫還提供實現SIFT(尺度不變特徵變換)、SURF(加速魯棒特徵)和FAST角點檢測演算法的功能。

Harris和Shi-Tomasi演算法是旋轉不變的。即使影像旋轉,我們也可以找到相同的角點。但是,當影像放大時,如果影像的角點不再是角點,則可能不會是角點。下圖描述了這一點。

Shi Tomasi

D. Lowe的新演算法,尺度不變特徵變換(SIFT)提取關鍵點並計算其描述符。

這是透過以下步驟實現的:

  • 尺度空間極值檢測。
  • 關鍵點定位。
  • 方向分配。
  • 關鍵點描述符。
  • 關鍵點匹配。

就OpenCV中SIFT的實現而言,它從載入影像並將其轉換為灰度影像開始。cv.SHIFT_create()函式建立一個SIFT物件。

示例

呼叫其detect()方法可以獲得關鍵點,這些關鍵點繪製在原始影像的頂部。下面的程式碼實現了此過程

import numpy as np
import cv2 as cv
img = cv.imread('home.jpg')
gray= cv.cvtColor(img,cv.COLOR_BGR2GRAY)
sift = cv.SIFT_create()
kp = sift.detect(gray,None)
img=cv.drawKeypoints(gray,kp,img)
cv.imwrite('keypoints.jpg',img)

輸出

原始影像和繪製了關鍵點的影像如下所示:

這是一張原始影像

Scale Space

下圖是帶有關鍵點的影像

SIFT

OpenCV Python - 特徵匹配

OpenCV提供了兩種特徵匹配技術:暴力匹配和FLANN匹配器技術。

示例

以下示例使用暴力方法

import numpy as np
import cv2

img1 = cv2.imread('lena.jpg')
img2 = cv2.imread('lena-test.jpg')

# Convert it to grayscale
img1_bw = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
img2_bw = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

orb = cv2.ORB_create()

queryKeypoints, queryDescriptors = orb.detectAndCompute(img1_bw,None)
trainKeypoints, trainDescriptors = orb.detectAndCompute(img2_bw,None)

matcher = cv2.BFMatcher()
matches = matcher.match(queryDescriptors,trainDescriptors)

img = cv2.drawMatches(img1, queryKeypoints,
img2, trainKeypoints, matches[:20],None)

img = cv2.resize(img, (1000,650))

cv2.imshow("Feature Match", img)

輸出

Feature Matching

OpenCV Python - 使用KNN進行數字識別

KNN代表K近鄰,是一種基於監督學習的機器學習演算法。它試圖將新的資料點放入與現有類別最相似的類別中。所有可用資料都分類到不同的類別中,新的資料點根據相似性放入其中一個類別中。

KNN演算法的工作原理如下:

  • 最好選擇奇數作為要檢查的鄰居數K。
  • 計算它們的歐幾里得距離。
  • 根據計算出的歐幾里得距離,取K個最近的鄰居。
  • 統計每個類別中資料點的數量。
  • 資料點最多的類別就是新的資料點所屬的類別。

作為使用OpenCV實現KNN演算法的示例,我們將使用包含5000張手寫數字影像的digits.png影像,每張影像大小為20X20畫素。

KNN

首先,將此影像分成5000個數字。這是我們的特徵集。將其轉換為NumPy陣列。程式如下:

import numpy as np
import cv2

image = cv2.imread('digits.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

fset=[]
for i in np.vsplit(gray,50):
   x=np.hsplit(i,100)
   fset.append(x)

NP_array = np.array(fset)

現在,我們將此資料分成訓練集和測試集,每個大小為(2500, 20x20),如下所示:

trainset = NP_array[:,:50].reshape(-1,400).astype(np.float32)
testset = NP_array[:,50:100].reshape(-1,400).astype(np.float32)

接下來,我們必須為每個數字建立10個不同的標籤,如下所示:

k = np.arange(10)
train_labels = np.repeat(k,250)[:,np.newaxis]
test_labels = np.repeat(k,250)[:,np.newaxis]

我們現在可以開始KNN分類了。建立分類器物件並訓練資料。

knn = cv2.ml.KNearest_create()
knn.train(trainset, cv2.ml.ROW_SAMPLE, train_labels)

選擇k值為3,獲得分類器的輸出。

ret, output, neighbours, distance = knn.findNearest(testset, k = 3)

將輸出與測試標籤進行比較,以檢查分類器的效能和準確性。

該程式顯示出手寫數字檢測的準確率為91.64%。

result = output==test_labels
correct = np.count_nonzero(result)
accuracy = (correct*100.0)/(output.size)
print(accuracy)
廣告
© . All rights reserved.