OpenCV-Python實戰(1)——OpenCV簡介與影像處理基礎(內含大量示例,📕建議收藏📕)
- OpenCV介紹
- Python安裝OpenCV
- OpenCV主要模塊
- OpenCV應用場景
- OpenCV影像處理基礎
- 影像基礎
- 影像處理中的主要問題
- 影像處理流程
- 像素、顏色、通道、影像和顏色空間
- 影像描述
- 影像檔案型別
- OpenCV中的坐標系
- OpenCV中的通道順序
- 在不同顏色空間中訪問和操作OpenCV中的像素
- 彩色影像訪問和操作OpenCV中的像素
- 灰度影像訪問和操作OpenCV中的像素
- OpenCV影像處理基礎小結
OpenCV介紹
OpenCV 是一個的跨平臺計算機視覺庫,可以運行在 Linux、Windows 和 Mac OS 作業系統上,它輕量級而且高效——由一系列 C 函式和少量 C++ 類構成,同時也提供了 Python 介面,實作了影像處理和計算機視覺方面的很多通用演算法,在本文中,將介紹 OpenCV 庫,包括它的主要模塊和典型應用場景,
Python安裝OpenCV
對于 Linux 和 Windows 作業系統,首需要在 shell 或 cmd 中運行以下命令安裝 NumPy:
pip install numpy
然后再安裝 OpenCV,可以選擇兩種不同版本:
- 僅安裝主模塊包
pip install opencv-python - 安裝完整包(包括主模塊和附加模塊)
pip install opencv-contrib-python
OpenCV主要模塊
OpenCV 可以被劃分為不同模塊,其主要模塊如下:

下表整理介紹了各主要模塊的作用:
| 模塊 | 介紹 |
|---|---|
| core | 核心模塊,是定義基本資料結構的模塊,也包括庫中所有其他模塊使用的基本函式 |
| imgproc | 影像處理模塊,包括影像濾波、幾何影像變換、顏色空間變換和直方圖 |
| imgcodecs | 影像檔案讀寫 |
| highgui | 高級GUI,提供UI功能的介面,可以執行以下操作:創建和操作可以顯示的視窗、將滑動條添加到視窗、鍵盤命令和處理滑鼠事件等 |
| videoio | 視頻I/O,視頻捕獲和視頻編解碼器的介面 |
| video | 視頻分析模塊,包括背景減法、運動估計和目標跟蹤演算法 |
| calib3d | 攝像機標定和三維重建,包括基本的多視點幾何演算法、立體匹配演算法、目標姿態估計、單攝像機和立體攝像機標定以及三維重建 |
| features2d | 二維特征框架,該模塊包括特征檢測器、描述符和描述符匹配器 |
| objdetect | 目標檢測,檢測預定義類的物件和實體(例如,人臉、眼睛、人和汽車) |
| dnn | 深度神經網路(Deep neural network, DNN)模塊,本模塊包含以下內容:用于創建新層的API、一組預定義的常用層、從層構造和修改神經網路的API、從不同深度學習框架加載序列化網路模型的功能等 |
| ml | 機器學習庫(Machine Learning Library, MLL)是一組可用于分類、回歸和聚類目的的類和方法 |
| flann | 快速近似近鄰庫(Fast Library for Approximate Nearest Neighbors, FLANN)是一組非常適合快速近鄰搜索的演算法,用于多維空間中的聚類和搜索 |
| photo | 計算攝影,提供一些計算攝影的函式 |
| stitching | 影像拼接,實作了一個自動拼接全景影像的拼接流水線 |
| shape | 形狀距離和匹配模塊,可用于形狀匹配、檢索或比較 |
| superres | 超解析度,包含一組可用于提高解析度的類和方法 |
| videostab | 視頻穩定,包含一組用于視頻穩定的類和方法 |
| viz | 三維可視化工具,用于顯示與場景互動的小部件 |
OpenCV應用場景
OpenCV 可以應用但不僅限于以下場景:二維和三維特征提取、街景影像拼接、人臉識別系統、手勢識別、人機互動、動作識別、物體識別、自動檢查和監視、分割與識別、醫學影像分析、運動跟蹤、增強現實、視頻/影像搜索與檢索、機器人與無人駕駛汽車導航與控制、駕駛員疲勞駕駛檢測等,
OpenCV影像處理基礎
影像基礎
接下來,首先從理論上介紹影像的相關概念,
影像處理中的主要問題
我們看可以把影像看作是三維世界的二維視圖,那么數字影像作為2D影像,可以使用稱為像素的有限數字集進行表示(像素的概念將在像素、顏色、通道、影像和顏色空間部分中詳細解釋),我們可以,將計算機視覺的目標定義為將這些2D資料轉換為以下內容:
- 新的資料表示(例如,新影像)
- 決策目標(例如,執行具體決策任務)
- 目標結果(例如,影像的分類)
- 資訊提取(例如,目標檢測)
在進行影像處理時,經常會遇到以下問題:
- 影像的模糊性,由于受到透視的影響,從而會導致影像視覺外觀的變化,例如,從不同的角度看同一個物體會產生不同的影像;
- 影像通常會受許多自然因素的影響,如光照、天氣、反射和運動;
- 影像中的一部分物件也可能會被其他物件遮擋,使得被遮擋的物件難以檢測或分類,隨著遮擋程度的增加,影像處理的任務(例如,影像分類)可能非常具有挑戰性,
為了更好的解釋上述問題,我們假設需要開發一個人臉檢測系統,該系統應足夠魯棒,以應對光斬訓天氣條件的變化;此外,該系統應該可以處理頭部的運動,檢測用戶的頭部在坐標系中每個軸上一定程度的旋轉(抬頭、搖頭和低頭,用戶可以離相機稍近或稍遠的情況),而許多人臉檢測演算法在人臉接近正面時表現出良好的性能,但是,如果一張臉不是正面的(例如,側面對著鏡頭),演算法就無法檢測到它,此外,演算法需要即使在用戶戴著眼鏡或太陽鏡時,也可能需要檢測面部(即使這會在眼睛區域產生遮擋),綜上所述,當開發一個計算機視覺專案時,我們必須綜合考慮到所有這些因素,一個很好的表征方法是有使用大量測驗影像來驗證演算法,我們也可以根據測驗影像的不同困難程度來對它們進行分類,以便于檢測演算法的弱點,提高演算法的魯棒性,
影像處理流程
完整的影像處理程式通常可以分為以下三個步驟:
- 讀取影像,影像的獲取可以有多種不同的來源(相機、視頻流、磁盤、在線資源),因此影像的讀取可能涉及多個函式,以便可以從不同的來源讀取影像;
- 影像處理,通過應用影像處理技術來處理影像,以實作所需的功能(例如,檢測影像中的貓);
- 顯示結果,將影像處理完成后的結果以人類可讀的方式進行呈現(例如,在影像中繪制邊界框,有時也可能需要將其保存到磁盤),
此外,上述第2步影像處理可以進一步分為三個不同的處理級別:
- 低層處理(或者在不引起歧義的情況下可以稱為預處理),通常將一個影像作為輸入,然后輸出另一個影像,可在此步驟中應用的步驟包括但不限于以下方法:噪聲消除、影像銳化、光照歸一化以及透視校正等;
- 中層處理:是將預處理后的影像提取其主要特征(例如采用 DNN 模型得到的影像特征),輸出某種形式的影像表示,它提取了用于影像進一步處理的主要特征,
- 高層處理:接受中層處理得到的影像特征并輸出最終結果,例如,處理的輸出可以是檢測到的人臉.
像素、顏色、通道、影像和顏色空間
在表示影像時,有多種不同的顏色模型,但最常見的是紅、綠、藍 (RGB) 模型,
RGB 模型是一種加法顏色模型,其中原色 (在RGB模型中,原色是紅色 R、綠色 G 和藍色 B) 混合在一起就可以用來表示廣泛的顏色范圍,
每個原色 (R, G, B) 通常表示一個通道,其取值范圍為[0, 255]內的整數值,因此,每個通道有共256個可能的離散值,其對應于用于表示顏色通道值的總位元數 (
2
8
=
256
2^8=256
28=256),此外,由于有三個不同的通道,使用 RGB 模型表示的影像稱為24位色深影像:

在上圖中,可以看到 RGB 顏色空間的“加法顏色”屬性:
- 紅色加綠色會得到黃色
- 藍色加紅色會得到品紅
- 藍色加綠色會得到青色
- 三種原色加在一起得到白色
因此,如前所述,RGB 顏色模型中,特定顏色可以由紅、綠和藍值分量合成表示,將像素值表示為 RGB 三元組 (r, g, b),典型的 RGB 顏色選擇器如下圖所示:

解析度為 800×1200 的影像是一個包含800列和1200行的網格,每個網格就是稱為一個像素,因此其中包含 800×1200=96 萬像素,應當注意,影像中有多少像素并不表示其物理尺寸(一個像素不等于一毫米),相反,像素的大小取決于為該影像設定的每英寸像素數 (Pixels Per Inch, PPI),影像的 PPI 一般設定在 [200-400] 范圍內,
計算PPI的基本公式如下:
- PPI=寬度(像素) / 影像寬度(英寸)
- PPI=高度(像素) / 影像高度(英寸)
例如,一個4×6英寸影像,影像解析度為 800×1200,則PPI是200,
影像描述
影像可以描述為2D函式 f ( x , y ) f(x, y) f(x,y),其中 ( x , y ) (x, y) (x,y) 是空間坐標,而 f ( x , y ) f(x, y) f(x,y) 是影像在點 ( x , y ) (x, y) (x,y) 處的亮度或灰度或顏色值,另外,當f(x, y)和(x, y)值都是有限離散量時,該影像也被稱為數字影像,此時:
- x ∈ [ 0 , h ? 1 ] x∈ [0, h-1] x∈[0,h?1],其中 h h h 是影像的高度
- y ∈ [ 0 , w ? 1 ] y∈ [0, w-1] y∈[0,w?1],其中 w w w 是影像的寬度
- f ( x , y ) ∈ [ 0 , L ? 1 ] f(x, y)∈ [0,L-1] f(x,y)∈[0,L?1],其中 L = 256 L=256 L=256 (對于8位灰度影像)
彩色影像也可以用同樣的方式表示,只是我們需要定義三個函式來分別表示紅色、綠色和藍色值,這三個單獨的函式中的每一個都遵循與為灰度影像定義的
f
(
x
,
y
)
f(x, y)
f(x,y) 函式相同的公式,我們將這三個函式的子索引 R、G 和 B 分別表示為
f
R
(
x
,
y
)
f_R(x, y)
fR?(x,y)、
f
G
(
x
,
y
)
f_G(x, y)
fG?(x,y) 和
f
B
(
x
,
y
)
f_B(x, y)
fB?(x,y),
同樣,黑白影像也可以表示為相同的形式,其僅需要一個函式來表示影像,且
f
(
x
,
y
)
f(x, y)
f(x,y) 只能取兩個值,通常,0 表示黑色、1 表示白色,
下圖顯示了三種不同型別的影像(彩色影像、灰度影像和黑白影像):

數字影像可以看作是真實場景的近似,因為
f
(
x
,
y
)
f(x, y)
f(x,y) 值是有限的離散量,此外,灰度和黑白影像每個點只對應有一個值,彩色影像每個點需要三個函式對應于影像的紅色、綠色和藍色分量,
影像檔案型別
盡管在 OpenCV 中處理的影像時,可以將影像看作 RGB 三元組的矩陣(在 RGB 影像模型情況下),但它們不一定是以這種格式創建、存盤或傳輸的,有許多不同的檔案格式,如GIF、PNG、位圖或JPEG,使用不同形式的壓縮(無損或有損)來更有效地表示影像,
下表列示了 OpenCV 支持的檔案格式及其關聯的檔案擴展名:
| 檔案格式 | 檔案擴展名 |
|---|---|
| Windows bitmaps | *.bmp和*.dib |
| JPEG files | *.JPEG、*.jpg 和 *.jpe |
| JPEG 2000 files | *.jp2 |
| Portable Network Graphics | *.png |
| Portable image format | *.pbm、*.pgm 和 *.ppm |
| TIFF files | *.TIFF 和 *.tif |
對影像應用無損或有損壓縮演算法,可以得到比未壓縮影像占據存盤空間小的影像,其中,在無損壓縮演算法中,得到的影像與原始影像等價,也就是說,經過反壓縮程序后,得到的影像與原始影像完全等價(相同);而在有損壓縮演算法中,得到的影像并不等同于原始影像,這意味著影像中的某些細節會丟失,在許多有損壓縮演算法中,壓縮級別是可以調整的,
OpenCV中的坐標系
為了更好的展示 OpenCV 中的坐標系以及如何訪問各個像素,查看以下低解析度影像為例:

這個圖片的尺寸是 32×41 像素,也就是說,這個影像有 1312 個像素,為了進一步說明,我們可以在每個軸上添加像素計數,如下圖所示:

現在,我們來看看
(
x
,
y
)
(x, y)
(x,y) 形式的像素索引,請注意,像素索引起始值為零,這意味著左上角位于
(
0
,
0
)
(0, 0)
(0,0),而不是
(
1
,
1
)
(1, 1)
(1,1),下面的影像,索引了 4 個單獨的像素,影像的左上角是原點的坐標:

單個像素的資訊可以從影像中提取,方法與 Python 中參考陣列的單個元素相同,
OpenCV中的通道順序
在 OpenCV 使用中,使用的顏色通道順序為 BGR 顏色格式而不是 RGB 格式,可以在下圖中看到三個通道的順序:

BGR 影像的像素結構如下圖所示,作為演示,圖示詳細說明了如何訪問pixel(y=n, x=1):

Tips:OpenCV 的最初開發人員選擇了 BGR 顏色格式(而不是 RGB 格式),是因為當時 BGR 顏色格式在軟體供應商和相機制造商中非常流行,因此選擇 BGR 是出于歷史原因,
此外,也有其他 Python 包使用的是 RGB 顏色格式(例如,Matplotlib 使用 RGB 顏色格式,Matplotlib 是最流行的 2D Python 繪圖庫,提供多種繪圖方法,可以查看 Python-Matplotlib 可視化獲取更多詳細資訊),因此,我們需要知道如何將影像從一種格式轉換為另一種格式,
當我們掌握了將影像從一種格式轉換為另一種格式的方法后,就可以選擇使用 OpenCV 進行影像處理,同時利用 Matplotlib 包提供的函式來顯示影像,接下來,讓我們看看如何處理兩個庫采用的不同顏色格式,
首先,我們使用 cv2.imread() 函式加載影像:
import cv2
img_OpenCV = cv2.imread('sigonghuiye.jpeg')
影像存盤在 img_OpenCV 變數中,因為 cv2.imread() 函式以 BGR 順序加載影像,然后,我們使用 cv2.split() 函式將加載的影像分成三個通道 (b, g, r) ,這個函式的引數就是我們要分割的影像:
b, g, r = cv2.split(img_OpenCV)
下一步是合并通道(以便根據通道提供的資訊構建新影像),但合并時順序與原影像不同,我們更改 b 和 r 通道的順序以遵循 RGB 格式,即我們所需要的 Matplotlib 格式:
img_matplotlib = cv2.merge([r, g, b])
此時,我們有兩個影像 (img_OpenCV 和 img_matplotlib),接下來,我們將分別使用 OpenCV 和 Matplotlib 繪制她們,以便我們可以對比結果,首先,我們將用 Matplotlib 顯示這兩個影像,
為了在同一個視窗中使用 Matplotlib 顯示兩個影像,我們將使用 subplot,它將多個影像放置在同一個視窗中,可以在 subplot 中使用三個引數,例如 subplot(m,n,p),此時,子圖處理
m
×
n
m \times n
m×n 網格中的圖,其中
m
m
m 確定行數,
n
n
n 確定列數,而
p
p
p 確定要在網格中放置圖的位置,要使用 Matplotlib 顯示影像,需要使用 imshow 函式,
在這種情況下,當我們水平顯示兩個影像時,
m
=
1
m = 1
m=1、
n
=
2
n = 2
n=2 ,我們將對第一個子圖 (img_OpenCV) 使用
p
=
1
p = 1
p=1,對第二個子圖(img_matplotlib) 使用
p
=
2
p = 2
p=2:
from matplotlib import pyplot as plt
plt.subplot(121)
plt.imshow(img_OpenCV)
plt.subplot(122)
plt.imshow(img_matplotlib)
plt.show()
程式輸出如下圖所示:

可以看出,第一個子圖以錯誤的顏色( BGR 順序)顯示影像,而第二個子圖以正確的顏色( RGB 順序)顯示影像,接下來,我們使用 cv2.imshow() 顯示兩個影像:
cv2.imshow('bgr image', img_OpenCV)
cv2.imshow('rgb image', img_matplotlib)
cv2.waitKey(0)
cv2.destroyAllWindows()
以下螢屏截圖顯示了執行上述代碼獲得的結果:

正如預期的那樣,螢屏截圖中,第一張圖以正確的色彩顯示影像,而第二張圖以錯誤的顏色顯示影像,
此外,如果我們想在同一個視窗中顯示兩個影像,可以構建一個包含這兩個影像的拼接影像,將兩張圖片水平連接起來,為此,我們需要使用 NumPy 的 concatenate() 方法,該方法的引數是要連接的兩個影像和要在哪個軸上進行堆疊,這里,我們令 axis = 1 (水平堆疊它們):
import numpy as np
img_concats = np.concatenate((img_OpenCV, img_matplotlib), axis=1)
cv2.imshow('bgr image and rgb image', img_concats)
cv2.waitKey(0)
cv2.destroyAllWindows()
下圖顯示了連接后的影像:

需要考慮的一個因素是 cv2.split() 是一項耗時的操作,如果確實需要劃分不同通道,應當首先考慮使用 NumPy 索引,例如,如果想獲取影像的一個通道,則可以使用 NumPy 索引獲取通道:
B = img_OpenCV[:, :, 0]
G = img_OpenCV[:, :, 1]
R = img_OpenCV[:, :, 2]
另一個需要注意的是,可以使用 NumPy 在一條陳述句中將影像從 BGR 轉換為 RGB:
img_matplotlib = img_OpenCV[:, :, ::-1]
在不同顏色空間中訪問和操作OpenCV中的像素
本節將介紹如何使用 OpenCV 訪問和讀取像素值以及如何修改它們,此外,還將學習如何訪問影像屬性,如果想一次處理多個像素,則需要創建影像區域 (Region of Image, ROI),
在 Python 中,影像表示為 NumPy 陣列,因此,示例中包含的大多數操作都與 NumPy 相關,建議需要對 NumPy 包一些了解,才能更好明白示例代碼的原理,但即使不了解也沒關系,必要時會對所用函式進行講解,
彩色影像訪問和操作OpenCV中的像素
現在,我們來看看如何在 OpenCV 中處理BGR影像,如上所述,OpenCV 加載彩色影像時,藍色通道是第一個,綠色通道是第二個,紅色通道是第三個,
首先,使用 cv2.imread() 函式讀取影像,影像應該在作業目錄中,或者應該提供圖片的完整路徑,在本例中,讀取 sigonghuiye.jpeg 影像并將其存盤在img變數中:
img = cv2.imread('sigonghuiye.jpeg')
影像加載到 img 后,可以獲得影像的一些屬性,我們要從加載的影像中提取的第一個屬性是 shape,它將告訴我們行、列和通道的數量(如果影像是彩色的),我們將此資訊存盤在 dimensions 變數中:
dimensions = img.shape
第二個屬性是影像的大小(img.size=影像高度 × 影像寬度 × 影像通道數):
total_number_of_elements= img.size
第三個屬性是影像資料型別,可以通過 img.dtype 獲得,因為像素值在 [0-255] 范圍內,所以影像資料型別是 uint8 (unsigned char):
image_dtype = img.dtype
上面示例中,我們已經使用了 cv2.imshow() 函式來在視窗中顯示影像,這里我們對其進行更詳細的介紹,使用 cv2.imshow() 函式顯示影像時,視窗會自動適應影像大小,此函式的第一個引數是視窗名,第二個引數是要顯示的影像,在這種情況下,由于加載的影像已存盤在 img 變數中,因此使用此變數作為第二個引數:
cv2.imshow("original image", img)
顯示影像后,我們來介紹下鍵盤系結函式——cv2.waitKey(),它為任何鍵盤事件等待指定的毫秒數,引數是以毫秒為單位的時間,當執行到此函式時,程式將暫停執行,當按下任何鍵后,程式將繼續執行,如果毫秒數為 0 (cv2.waitKey(0)),它將無限期地等待鍵盤敲擊事件:
cv2.waitKey(0)
要訪問(讀取)某個像素值,我們需要向 img 變數(包含加載的影像)提供所需像素的行和列,例如,要獲得 ( x = 40 , y = 6 ) (x=40, y=6) (x=40,y=6) 處的像素值 :
(b, g, r) = img[6, 40]
我們在三個變數 (b, g, r) 中存盤了三個像素值,請牢記 OpenCV 對彩色影像使用 BGR 格式,另外,我們可以一次僅訪問一個通道,在本例中,我們將使用所需通道的行、列和索引進行索引,例如,要僅獲取像素
(
x
=
40
,
y
=
6
)
(x=40, y=6)
(x=40,y=6) 處的藍色值:
b = img[6, 40, 0]
像素值也可以以相同的方式進行修改,例如,要將像素 (x=40, y=6) 處設定為紅色:
img[6, 40] = (0, 0, 255)
有時,需要處理某個區域而不是一個像素,在這種情況下,應該提供值的范圍(也稱切片),而不是單個值,例如,要獲取影像的左上角:
top_left_corner = img[0:50, 0:50]
變數 top_left_corner 可以看做是另一個影像(比img小),但是我們可以用同樣的方法處理它,
最后,如果想要關閉并釋放所有視窗,需要使用 cv2.destroyAllWindows() 函式:
cv2.destroyAllWindows()
灰度影像訪問和操作OpenCV中的像素
灰度影像只有一個通道,因此,在處理這些影像時會引入一些差異,我們將在這里重點介紹這些差異,相同的部分不再贅述,
同樣,我們將使用 cv2.imread() 函式來讀取影像,在這種情況下,需要第二個引數,因為我們希望以灰度加載影像,第二個引數是一個標志位,指定讀取影像的方式,以灰度加載影像所需的值是 cv2.IMREAD_grayscale:
gray_img = cv2.imread('logo.png', cv2.IMREAD_GRAYSCALE)
在這種情況下,我們將影像存盤在gray_img變數中,如果我們列印影像的尺寸(使用 gray_img.shape ),只能得到兩個值,即行和列,在灰度影像中,不提供通道資訊:
dimensions = gray_img.shape
shape將以元組形式回傳影像的維度 —— (828, 640),
像素值可以通過行和列坐標來訪問,在灰度影像中,只獲得一個值(通常稱為像素的強度),例如,如果我們想得到像素
(
x
=
40
,
y
=
6
)
(x=40, y=6)
(x=40,y=6) 處的像素強度:
i = gray_img[6, 40]
影像的像素值也可以以相同的方式修改,例如,如果要將像素 ( x = 40 , y = 6 ) (x=40, y=6) (x=40,y=6) 處的值更改為黑色(強度等于0):
gray_img[6, 40] = 0
OpenCV影像處理基礎小結
在本文中,首先介紹了與影像相關的關鍵概念,影像構成了構建計算機視覺專案所必需的豐富資訊,然后,我們需要了解 OpenCV 使用 BGR 顏色格式而不是 RGB,但有一些 Python 包(例如 Matplotlib )使用后一種格式,因此,需要了解如何將影像從一種顏色格式轉換為另一種顏色格式,
此外,還總結了處理影像的主要函式和引數:
- 訪問影像屬性
- OpenCV 常用函式,例如 cv2.imread() 、 cv2.split() 、 cv2.merge() 、 cv2.imshow() 、 cv2.waitKey() 和 cv2.destroyAllWindows()
- 如何在 BGR 和灰度影像中獲取和操作影像像素
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/293178.html
標籤:其他
