文章目錄
- OpenCV的基礎應用
- 一、OpenCV簡介:
- 1.1 OpenCV 優勢
- 1.2 OpenCV-Python
- 二、環境安裝
- 2.1 環境導入
- 三、圖像表示
- 3.1 顏色空間(Color Space)
- 3.2 具體說明
- 3.3 圖像在計算機中的表示
- 四、基本圖像操作
- 4.1 創建窗口
- **1. 核心窗口行為控制**
- `cv.WINDOW_NORMAL`
- `cv.WINDOW_AUTOSIZE`(默認值)
- **2. 寬高比控制**
- `cv.WINDOW_KEEPRATIO`
- `cv.WINDOW_FREERATIO`
- **3. 顯示模式**
- `cv.WINDOW_FULLSCREEN`
- **4. 渲染與 GUI 樣式**
- `cv.WINDOW_OPENGL`
- `cv.WINDOW_GUI_EXPANDED` / `cv.WINDOW_GUI_NORMAL`
- flags 的組合使用(位運算 `|`)
- **總結:如何選擇?**
- 4.2 讀取圖像
- 參數詳解
- 關鍵補充:
- 4.3 顯示圖像
- 參數解釋:
- 4.4 保存圖像
- 參數說明
- 4.5 創建黑白圖像和隨機圖像
- 一、創建黑色圖像
- 二、創建白色圖像
- 三、創建隨機顏色圖像
- 四、通用注意事項
- 4.6 圖像的裁剪(切片)
- 4.7 圖像大小的調整(縮放)
- 一、三種圖像尺寸處理方式對比
- 二、關鍵注意點
- 三、總結
- 五、圖像繪制
- 5.1 繪制直線
- 5.2 繪制矩形
- 5.3 繪制圓形
- 5.4 繪制橢圓
- 5.5 繪制文本
- 5.6 繪制多邊形
- 綜合案例:
- 難點解析:
- 1. `cv.polylines()` 對頂點格式的要求
- 2. 為什么原始 `pts` 不符合要求?
- 3. `reshape((-1, 1, 2))` 的作用
- 4. 為什么沒有返回值?
- 六、視頻讀取與捕捉
- 6.1 `cv2.VideoCapture()` 方法
- 6.2 捕獲攝像頭實時視頻
- 6.3 讀取本地視頻文件
- 6.4 常用屬性設置與獲取
- 6.5 難點解析
- 1.為什么在讀取視頻流的時候不使用`cv2.waitKey(0)`?
- 🎯 小結:
- 2.為什么在要使用`&0XFF`來操作?
- 七、思維導圖
OpenCV的基礎應用
一、OpenCV簡介:
OpenCV(Open Source Computer Vision Library)是一個開源的計算機視覺和機器學習軟件庫。
OpenCV 由一系列 C 函數和少量 C++ 類構成,同時提供了 Python、Java、MATLAB 等語言的接口。
OpenCV 提供了大量的計算機視覺算法和圖像處理工具,廣泛應用于圖像和視頻的處理、分析以及機器學習領域。
OpenCV 的設計目標是提供一套簡單易用的計算機視覺基礎庫,幫助開發人員快速構建復雜的視覺應用。
1.1 OpenCV 優勢
-
**開源免費:**完全開源,可以自由使用,降低開發成本和技術門檻。
-
**多語言支持:**除C++原生接口外,還支持Java、Python等編程語言。
-
**跨平臺:**支持多種操作系統,Windows、Linux、ios、Android等,方便開發和部署。
-
**豐富API:**完善的傳統計算機視覺算法,涵蓋主流的機器學習算法,同時添加了對深度學習的支持。
1.2 OpenCV-Python
- C++ 高性能 + Python 簡潔
- 基于Numpy,易于科學計算、與其他庫進行集成(Scipy、Matplotlib)
二、環境安裝
pip install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple
2.1 環境導入
import cv2 as cv
三、圖像表示
在計算機視覺中,圖像是由像素(Pixel)組成的矩陣,每個像素包含了該位置的顏色或灰度信息。
- 像素:圖像的最小信息單位,通常用 8 位無符號整數表示,取值范圍 0~255
- 位深:常見圖像是 8 位圖像,即每個像素的每個通道占用 8 位
3.1 顏色空間(Color Space)
顏色空間 是表示顏色的一種方式,決定了像素的含義和顏色的表達方式不同顏色空間適用于不同的圖像處理場景。
顏色空間 | 通道含義 | 典型用途 |
---|---|---|
RGB | Red, Green, Blue | 顯示、繪圖、圖像合成 |
BGR | Blue, Green, Red | OpenCV 默認格式 |
HSV | Hue, Saturation, Value | 顏色分割、濾色、目標跟蹤 |
GRAY | 灰度 | 邊緣檢測、特征提取 |
YCrCb | Luma + 色度分量 | 視頻壓縮,膚色檢測 |
LAB | Lightness, a*, b* | 顏色校正,色彩調整 |
3.2 具體說明
-
RGB(紅綠藍)
- 最常見的顏色空間,圖像以 三通道 存儲,每通道 0~255
- 適合圖像顯示、繪制、調色板操作
- 在 OpenCV 中,讀取的彩色圖像是 BGR 順序(注意差別)
-
HSV(色調-飽和度-明度)
- 色調(H):表示顏色種類(0~179)
- 飽和度(S):顏色的純度(0~255)
- 明度(V):亮度(0~255)
- 適用于顏色分割、物體跟蹤,因為顏色分量與光照變化關系較小。
- 顏色轉換
BGR
->HSV
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
-
灰度圖(Grayscale)
-
單通道,0 表示黑色,255 表示白色
-
用于邊緣檢測、特征提取、圖像二值化
-
讀取灰度圖:
gray_img = cv2.imread('path/to/image.jpg', cv2.IMREAD_GRAYSCALE)
-
3.3 圖像在計算機中的表示
- 彩色圖像:三維 NumPy 數組,形狀
(高度, 寬度, 3)
- 灰度圖像:二維 NumPy 數組,形狀
(高度, 寬度)
- 數據類型:
unit8
(0-255) - 通道順序:
- OpenCV 默認 BGR
- 其他庫(如 Matplotlib)通常是 RGB
四、基本圖像操作
4.1 創建窗口
cv.namedWindow(winname,flags)
在 OpenCV 中,cv.namedWindow()
的 flags
參數用于控制窗口的行為(如是否可縮放、是否保持比例、顯示樣式等)。以下是常見 flags
的詳細作用和使用場景:
1. 核心窗口行為控制
cv.WINDOW_NORMAL
- 作用:允許用戶自由調整窗口大小(可拉伸、縮放),窗口尺寸不受圖像原始大小限制。
- 場景:需要手動放大 / 縮小窗口查看圖像細節時(如醫學影像、高分辨率圖像)。
cv.WINDOW_AUTOSIZE
(默認值)
- 作用:窗口大小自動適配圖像尺寸,用戶無法手動調整窗口大小。
- 場景:固定尺寸展示圖像,避免誤操作改變窗口大小(如批量展示圖像預覽)。
2. 寬高比控制
cv.WINDOW_KEEPRATIO
-
作用:調整窗口大小時,強制保持圖像的寬高比(圖像不會拉伸變形)。
-
依賴:需與
WINDOW_NORMAL
配合(因為WINDOW_AUTOSIZE
下窗口不可調整)。 -
示例
cv.namedWindow("image", cv.WINDOW_NORMAL | cv.WINDOW_KEEPRATIO)
此時拉伸窗口,圖像會按原始比例縮放,避免變形。
cv.WINDOW_FREERATIO
- 作用:允許窗口和圖像自由縮放(不保持寬高比),可能導致圖像拉伸變形。
- 場景:特殊需求(如強制填充窗口,忽略比例)。
3. 顯示模式
cv.WINDOW_FULLSCREEN
- 作用:直接以全屏模式顯示窗口(覆蓋整個屏幕)。
- 場景:需要最大化展示圖像(如演示、大屏展示)。
4. 渲染與 GUI 樣式
cv.WINDOW_OPENGL
- 作用:啟用 OpenGL 渲染支持,利用 GPU 加速繪制(適合復雜圖形渲染,如 3D 可視化結合 OpenCV 窗口)。
cv.WINDOW_GUI_EXPANDED
/ cv.WINDOW_GUI_NORMAL
-
作用
:控制窗口的 GUI 樣式:
WINDOW_GUI_EXPANDED
:顯示擴展界面(含工具欄、狀態欄,更現代)。WINDOW_GUI_NORMAL
:顯示傳統簡潔界面(無額外控件)。
flags 的組合使用(位運算 |
)
多個 flags
可通過按位或組合,例如:
# 允許自由調整窗口大小 + 保持圖像寬高比
cv.namedWindow("image", cv.WINDOW_NORMAL | cv.WINDOW_KEEPRATIO)
總結:如何選擇?
需求 | 推薦 flags 組合 | |
---|---|---|
固定尺寸展示 | cv.WINDOW_AUTOSIZE (默認) | |
可縮放且保持圖像比例 | `cv.WINDOW_NORMAL | cv.WINDOW_KEEPRATIO` |
全屏展示 | cv.WINDOW_FULLSCREEN | |
高性能渲染(如 3D) | cv.WINDOW_OPENGL |
理解這些 flags
后,可更靈活地控制 OpenCV 窗口的交互性和顯示效果 ?。
4.2 讀取圖像
cv.imread(filename='',flags=cv.IMREAD_COLOR)
參數詳解
filename ='xxx'
filename關鍵字參數為要讀取的圖片地址,可以使用絕對地址也可以是相對地址。
以下是 cv.imread()
中 flags
參數的核心說明(表格形式,按功能分類):
標志常量 | 核心作用 | 典型場景 & 補充說明 |
---|---|---|
基礎色彩模式 | ||
cv.IMREAD_GRAYSCALE | 以 灰度圖 讀取(單通道,uint8 類型) | 需黑白處理時使用;忽略顏色信息,計算更快。 |
cv.IMREAD_COLOR | 以 彩色圖 讀取(BGR 三通道,uint8 類型,默認行為) | 忽略 Alpha 通道(透明層);OpenCV 默認存儲為 BGR(而非 RGB)。 |
cv.IMREAD_UNCHANGED | 讀取 原始圖像(保留所有通道,包括 Alpha 通道,如 PNG 透明層) | 處理帶透明背景的圖像(輸出為 4 通道:BGR+Alpha)。 |
深度與格式兼容 | ||
cv.IMREAD_ANYCOLOR | 允許 任意顏色模式 讀取(不強制轉換為 BGR,保留文件原始色彩格式) | 靈活處理特殊格式圖像(如 CMYK 等非 RGB 格式)。 |
cv.IMREAD_ANYDEPTH | 允許讀取 高深度圖像(如 16 位灰度圖,普通圖像為 8 位) | 醫學影像(CT)、遙感圖像等場景;需配合格式支持。 |
降采樣(快速預覽) | ||
cv.IMREAD_REDUCED_COLOR_2 | 彩色圖 + 尺寸縮小為 1/2(降采樣,加速加載) | 后綴 _4 (1/4)、_8 (1/8)同理;適合大圖像快速預覽。 |
cv.IMREAD_REDUCED_GRAYSCALE_2 | 灰度圖 + 尺寸縮小為 1/2 | 同上,后綴數字表示縮小倍數(2? 倍)。 |
特殊處理 | ||
cv.IMREAD_IGNORE_ORIENTATION | 忽略 EXIF 方向信息(如手機拍照的旋轉標記,強制原始方向) | 避免自動旋轉圖像,保持像素原始排列。 |
cv.IMREAD_LOAD_GDAL | 使用 GDAL 庫 加載(支持 TIFF、GeoTIFF 等地理 / 遙感圖像格式) | 需提前安裝 GDAL 庫;處理專業地理數據場景。 |
關鍵補充:
-
組合使用:多個標志可通過 按位或
|
組合,例如:# 灰度圖 + 尺寸縮小1/2 img = cv.imread("img.jpg", cv.IMREAD_GRAYSCALE | cv.IMREAD_REDUCED_2)
-
默認行為:若不指定
flags
,等價于cv.IMREAD_COLOR
(但建議顯式指定,避免版本差異)。 -
返回值:若圖像讀取失敗(路徑錯誤、格式不支持),返回
None
,需提前判斷。
通過合理選擇 flags
,可靈活控制圖像的讀取方式(色彩、通道、尺寸、深度等),適配不同業務場景 ?。
4.3 顯示圖像
cv.imshow(winname,mat)
參數解釋:
winname
:顯示圖像的窗口名,以字符串類型表示,如果你的窗口之前沒有定義會自動創建一個默認的窗口屬性(cv.WINDOW_AUTOSIZE)
mat
:為你要顯示的圖像,也可以是numpy數組。
cv.imshow('win', img)
cv.waitKey(0)
cv.destroyAllWindows()
注意:在調用顯示圖像的API后,要調用cv2.waitKey(0)給圖像繪制留下時間,否則窗口會出現無響應情況,并且圖像無法顯示出來。
-
cv.waitKey(0):表示無限期地等待任何鍵盤按鍵。這種用法常見于圖像顯示窗口中,確保圖像在窗口中顯示直到用戶決定關閉它。
-
cv.waitKey(n):n>0,意味著程序將等待n毫秒。這種方式常用于視頻播放或實時攝像頭捕獲場景,以便控制每一幀停留的時間,同時允許用戶通過按鍵來中斷循環或發出命令。
cv.destroyAllWindows([winname])
cv.destroyAllWindows()
:會在當前程序執行到該語句時立即銷毀打開的窗口,并釋放與這些窗口相關的資源。winname
:窗口名,關閉指定名稱的窗口。可省略,銷毀所有已打開的窗口。- 使用任意的按鍵就可以退出程序。
4.4 保存圖像
cv.imwrite(cv.imwrite(filename='output.png', img=img1)
參數說明
filename
(必填)- 類型:字符串(
str
) - 作用:指定保存的文件路徑和文件名(需包含擴展名,如
.png
、.jpg
、.bmp
等)。 - 示例:
"output.png"
(保存在當前目錄)、"./images/result.jpg"
(保存在指定文件夾)。
- 類型:字符串(
img
(必填)- 類型:NumPy 數組(
numpy.ndarray
) - 作用:需要保存的圖像數據,必須符合 OpenCV 圖像格式要求:
- 維度:2 維(灰度圖,
(height, width)
)或 3 維(彩色圖,(height, width, 3)
,BGR 通道順序)。 - 數據類型:通常為
uint8
(像素值范圍 0-255)。
- 維度:2 維(灰度圖,
- 類型:NumPy 數組(
4.5 創建黑白圖像和隨機圖像
import cv2 as cv
import numpy as npheight = 360
width = 480
channels = 3
# 創建一個全0數組,表示黑色圖像
black = np.zeros((360, 480, 3), dtype=np.uint8)
# black = np.zeros((height,width,channels),dtype=np.uint8)
cv.imshow("black", black)# 使用np.full()創建白色圖像
white = np.full((height, width, channels), 255, dtype=np.uint8)
cv.imshow("white", white)
# 使用np.ones()創建白色圖像,利用廣播機制
white_img = np.ones((512, 512, 3), dtype=np.uint8) * 255# 索引修改像素值,表示白色圖像
black[:, :, :] = 255
# black[:,:] = 255 # 利用廣播機制
# black[:] = 255 # 利用廣播機制
cv.imshow("trans_black", black)
cv.imshow("trans_white", white_img)
# 使用np.random.randint()創建隨機顏色圖像 [low,high)
random_color = np.random.randint(0, 256, (height, width, channels), dtype=np.uint8)
cv.imshow("random_color", random_color)cv.waitKey(0)
cv.destroyAllWindows()
一、創建黑色圖像
核心原理:所有像素值為 0(BGR 三通道均為 0)
# 方法:使用np.zeros()初始化全0數組
black = np.zeros((height, width, channels), dtype=np.uint8)
- 適用場景:需要純黑色背景時
- 特點:直接創建,無需額外計算,效率最高
二、創建白色圖像
核心原理:所有像素值為 255(BGR 三通道均為 255)
-
方法 1:np.full () 直接填充
white = np.full((height, width, channels), 255, dtype=np.uint8)
- 特點:一步到位,直接指定填充值
-
方法 2:np.ones () + 廣播乘法
white_img = np.ones((height, width, channels), dtype=np.uint8) * 255
- 特點:利用廣播機制,先創建全 1 數組再放大到 255
-
方法 3:索引切片修改現有數組
# 先創建黑色圖像,再通過切片整體修改 black = np.zeros((height, width, channels), dtype=np.uint8) black[:, :, :] = 255 # 三種等價寫法 # black[:,:] = 255 # 利用廣播省略最后一維 # black[:] = 255 # 利用廣播省略后兩維
- 適用場景:需要在已有圖像基礎上修改時
三、創建隨機顏色圖像
核心原理:每個像素的 BGR 通道值隨機生成(0-255)
# 方法:np.random.randint()生成隨機整數
random_color = np.random.randint(0, 256, (height, width, channels), dtype=np.uint8)
- 特點:左閉右開區間 [0,256) 剛好覆蓋 0-255 的像素值范圍
- 適用場景:測試、隨機背景生成等
四、通用注意事項
- 數組形狀:必須是
(height, width, channels)
,符合 OpenCV 的圖像格式要求 - 數據類型:固定使用
np.uint8
(8 位無符號整數),對應像素值 0-255 的范圍 - 通道順序:OpenCV 默認使用 BGR 格式(藍、綠、紅),與 RGB 不同但不影響純色圖像創建
4.6 圖像的裁剪(切片)
-
Opencv中,圖像切片用于從圖像中提取一個子區域(矩形區域)。
-
假設你有一個圖像
img
,它的類型是numpy.ndarray
。img[y:y+h,x:x+w]
的含義如下:- x:子區域左上角的x坐標
- y:子區域左上角的y坐標
- w:子區域的寬度
- h:子區域的高度
-
切片操作
img[y:y+h,x:x+w]
提取的是從(x,y)
開始,高度為h
,寬度為w
的矩形區域
import cv2 as cv# 讀取圖像
cat = cv.imread("./images/1.jpg", cv.IMREAD_COLOR)
# 切片 arr[h1:h2, w1:w2] h2 = h1 + h, w2 = w1 + w
# (291,242) (353,288) #shape() 返回的是(h,w,c)
eye = cat[242:288,291:353]
leg = cat[99:341,82:271] #(82,99) (271,341)
# cv.imshow("cat", cat)cv.imshow("eye", eye)
cv.imshow("leg", leg)cv.waitKey(0)
cv.destroyAllWindows()
4.7 圖像大小的調整(縮放)
import cv2 as cv
import numpy as np
from PIL import Image
# 讀取圖像
pig = cv.imread('./images/pig.png', cv.IMREAD_COLOR)print(pig.shape)
cv.imshow("pig", pig)
# 調整圖像大小 cv.resize(img, (width, height))
pig2 =cv.resize(pig,(480,520))
print(pig2.shape) #(height, width, channel)pig3 = np.reshape(pig,(1080,650,3))
print(pig3.shape)pig.shape = (1080,650,3)
# 顯示圖像
cv.imshow("pig2", pig2)
cv.imshow("pig3", pig3)pig4 = Image.open('./images/pig.png')
pig4 = pig4.resize((480,520))
pig4.show()# 等待按鍵按下
cv.waitKey(0)
# 釋放窗口
cv.destroyAllWindows()
一、三種圖像尺寸處理方式對比
方法 | 原理 | 效果 / 特點 | 是否推薦用于圖像縮放 |
---|---|---|---|
cv.resize(pig, (480, 520)) | OpenCV 內置縮放,通過插值算法重新計算像素 | 生成平滑的縮放圖像,尺寸為 (520, 480, 3) (高 × 寬 × 通道),顯示正常 | ? 推薦 |
np.reshape /pig.shape = ... | 僅修改數組維度,不改變像素值和總數 | 若像素總數不匹配則報錯;若匹配則像素排列混亂,圖像嚴重撕裂、失真 | ? 不推薦 |
PIL.Image.resize((480, 520)) | PIL 庫的縮放,同樣通過插值重新計算像素 | 生成平滑的縮放圖像,尺寸為 (480, 520) (寬 × 高),顯示正常(PIL 默認 RGB 格式,但縮放邏輯正確) | ? 推薦 |
二、關鍵注意點
-
OpenCV 與 PIL 的坐標順序差異
- OpenCV 中
cv.resize(img, (width, height))
的尺寸參數是(寬, 高)
,但返回圖像的shape
是(高, 寬, 通道)
(如(520, 480, 3)
)。 - PIL 中
Image.resize((width, height))
的尺寸參數同樣是(寬, 高)
,且返回圖像的尺寸直接是(寬, 高)
(如(480, 520)
),與視覺直觀感受一致。
兩者的縮放邏輯相同(都是重采樣生成新像素),只是返回的尺寸表示方式略有差異(OpenCV 更貼近數組的
(行, 列)
邏輯)。 - OpenCV 中
-
NumPy 維度修改的問題依然存在
代碼中的pig3 = np.reshape(pig, (1080, 650, 3))
和pig.shape = (1080, 650, 3)
仍然面臨之前的問題:- 若原始圖像的總像素數(
高×寬×3
)不等于1080×650×3
,會直接報ValueError
。 - 即使像素總數恰好匹配,圖像也會因像素排列混亂而撕裂(例如原本連續的像素被強行拆分到不同行,導致線條斷裂、色彩錯位)。
- 若原始圖像的總像素數(
三、總結
- 圖像縮放的正確方式:使用
cv.resize()
(OpenCV)或Image.resize()
(PIL),兩者都通過插值算法重新計算像素,保證圖像平滑不失真。 - NumPy 維度修改的正確用途:僅用于不改變像素總數的維度重組(例如將
(h, w, 3)
展平為(h×w, 3)
),絕對不能用于改變圖像的實際寬高(縮放)。
[!NOTE]
PIL庫的Image.resize()方法的參數有哪些?
除了這三種方式,還有哪些方法可以調整圖像大小?
如何在保持圖像比例的同時調整圖像大小?
五、圖像繪制
5.1 繪制直線
cv2.line(img, start_point, end_point, color, thickness)
img
:要繪制的圖像start_point
,end_point
:起點、終點坐標(x, y)
color
:線條顏色(B, G, R)
thickness
:線寬,像素值
5.2 繪制矩形
cv2.rectangle(img, top_left, bottom_right, color, thickness)
top_left
,bottom_right
:左上角與右下角坐標color
:顏色thickness
:線寬,-1
表示填充
5.3 繪制圓形
cv2.circle(img, center, radius, color, thickness[, lineType])
center
:圓心radius
:半徑lineType
:線型(cv2.LINE_AA
抗鋸齒,推薦)
5.4 繪制橢圓
cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color, thickness)
axes
:長軸半徑,短軸半徑(長, 短)
angle
:橢圓旋轉角度startAngle
,endAngle
:起始角度,結束角度
5.5 繪制文本
cv2.putText(img, text, org, fontFace, fontScale, color, thickness[, lineType])
org
:文本左下角坐標fontFace
:字體樣式(如cv2.FONT_HERSHEY_COMPLEX
)fontScale
:字體大小縮放系數lineType
:cv2.LINE_AA
抗鋸齒
5.6 繪制多邊形
cv2.polylines(img, [pts], isClosed, color, thickness)
pts
:多邊形頂點數組,形狀必須是(-1, 1, 2)
isClosed
:是否閉合color
:顏色thickness
:線寬
綜合案例:
import cv2 as cv
import numpy as npcat = cv.imread('./images/1.jpg')
# 繪制直線 cv.line(img,start,end,color,thickness) (w,h)
cv.line(cat,(50,40),(80,40),(188,100,25),2)#繪制矩形 cv.rectangle(img,start(左上角),end(右下角),color,thickness(等于-1表示填充))
cv.rectangle(cat,(78,106),(237,306),(0,255,0),-1)#繪制圓形 cv.circle(img,center,radius,color,thickness)
# cv.LINE_AA 反走樣技術 抗鋸齒技術 平滑 默認使用的是LINE_8
cv.circle(cat,(325,269),30,(0,0,255),-1,cv.LINE_AA)#繪制橢圓形 cv.ellipse(img,center(中心點),axes(設置寬,高),angle,startAngle,endAngle,color,thickness)
cv.ellipse(cat,(550,550),(100,50),0,0,270,(255,0,0),3)#繪制文本 cv.putText(img,text,start,font,fontScale,color,thickness)
cv.putText(cat,'hello world',(10,500),cv.FONT_HERSHEY_COMPLEX,1,(255,255,255),2)#繪制多邊形 cv.polylines(img,points,isClosed,color,thickness)
pts = np.array([[10,5],[20,30],[70,20],[50,10]],np.int32)
pts = pts.reshape((-1,1,2))
cv.polylines(cat,[pts],True,(0,255,255),3)cv.imshow('cat',cat)
cv.waitKey(0)
cv.destroyAllWindows()
難點解析:
在使用 cv.polylines()
時對 pts
進行 reshape((-1, 1, 2))
轉換,是為了滿足 OpenCV 對多邊形頂點數據格式的硬性要求。具體原因如下:
1. cv.polylines()
對頂點格式的要求
cv.polylines()
的 points
參數需要傳入一個形狀為 (N, 1, 2)
的 NumPy 數組,其中:
N
表示多邊形的頂點數量(例如你的例子中有 4 個頂點,N=4
);- 中間的
1
是一個固定的維度(可理解為每個頂點被包裹在一個單元素數組中); - 最后的
2
表示每個頂點的(x, y)
坐標。
這種格式本質上是 “頂點數組的數組”,即把每個頂點作為一個獨立的子數組,整體構成一個三維數組。
2. 為什么原始 pts
不符合要求?
你的原始頂點定義是:
pts = np.array([[10,5],[20,30],[70,20],[50,10]], np.int32)
此時 pts
的形狀是 (4, 2)
(二維數組),表示 “4 個頂點,每個頂點有 x、y 兩個坐標”。但 OpenCV 不直接接受這種二維格式,必須轉換為三維格式 (4, 1, 2)
。
3. reshape((-1, 1, 2))
的作用
-1
表示 “自動計算該維度的大小”(這里會自動計算為頂點數量 4);1
和2
固定,強制將二維數組(4, 2)
轉換為三維數組(4, 1, 2)
。
4. 為什么沒有返回值?
在 OpenCV 中,繪圖函數(如 cv.line
、cv.rectangle
、cv.circle
等)的一個重要特性是:直接在原圖上進行修改,不返回新圖像。這是 OpenCV 設計上的一個核心特點,具體原因和細節如下:
-
原地操作(In-place Operation):
OpenCV 的繪圖函數會直接修改輸入的圖像數組(如代碼中的cat
),所有繪制的圖形(多邊形、線條等)都會直接 “畫” 在原圖上,因此不需要返回新圖像。
這種設計的好處是節省內存—— 如果每次繪圖都返回新圖像,會額外占用一倍的內存空間(尤其對大圖像或批量處理不友好)。 -
與 NumPy 數組的特性一致:
圖像在 OpenCV 中以 NumPy 數組的形式存儲,而 NumPy 數組的很多操作(如切片修改、數值更新)也是原地修改(不返回新數組)。繪圖函數遵循這一特性,保持了與數組操作邏輯的一致性。
六、視頻讀取與捕捉
6.1 cv2.VideoCapture()
方法
用于從攝像頭、視頻文件或視頻流中捕獲視頻
cap = cv2.VideoCapture(source[, apiPreference])
source
:輸入源0
:默認攝像頭1
、2
:外接攝像頭(編號遞增)'video.mp4'
:視頻文件路徑'rtsp://... '
:RTSP/網絡視頻流
apiPreference
(可選):指定使用的后端API(通常可省略)cv2.CAP_DSHOW
:Windows DirectShowcv2.CAP_V4L2
:Linux V4L2cv2.CAP_FFMPEG
:使用FFmpeg解碼
6.2 捕獲攝像頭實時視頻
cap = cv2.VideoCapture(0) # 打開默認攝像頭while True:ret, frame = cap.read() # 逐幀讀取if not ret:break # 讀取失敗時退出cv2.imshow('camera', frame)# 按 q 鍵退出if cv2.waitKey(10) & 0xFF == ord('q'):breakcap.release() # 釋放攝像頭
cv2.destroyAllWindows() # 關閉窗口
6.3 讀取本地視頻文件
cap = cv2.VideoCapture('video.mp4') # 替換自己本地的視頻文件while True:ret, frame = cap.read()if not ret:break # 視頻結束或讀取失敗cv2.imshow('video', frame)if cv2.waitKey(30) & 0xFF == ord('q'):breakcap.release()
cv2.destroyAllWindows()
6.4 常用屬性設置與獲取
可以使用 cap.get()
和 cap.set()
方法對視頻流參數進行讀取與修改
方法 | 說明 |
---|---|
cap.get(cv2.CAP_PROP_FRAME_WIDTH) | 獲取幀寬度 |
cap.get(cv2.CAP_PROP_FRAME_HEIGHT) | 獲取幀高度 |
cap.get(cv2.CAP_PROP_FPS) | 獲取幀率 |
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) | 設置幀寬度 |
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) | 設置幀高度 |
6.5 難點解析
1.為什么在讀取視頻流的時候不使用cv2.waitKey(0)
?
-
cv2.waitKey(0)
的作用是無限等待,直到用戶按下任意鍵
-
如果你寫在視頻播放循環里,會導致:
- 第一幀暫停等待按鍵
- 你不按鍵,程序就卡死在那里
- 按下后,繼續下一幀,再次等待,變成每一幀都要你按鍵
🛑 這顯然不符合連續播放視頻的邏輯
🎯 小結:
用法 場景 cv2.waitKey(0)
靜態圖像,等待用戶操作 cv2.waitKey(n)
(n > 0)視頻播放,循環內控制幀間隔,n越小刷新越快,越流暢
2.為什么在要使用&0XFF
來操作?
cv2.waitKey()
返回的鍵值,有些系統/后端會返回 32位整數- 其中 低8位 才是實際的按鍵 ASCII 編碼
所以:
if cv2.waitKey(10) & 0xFF == ord('q'):break
是為了 屏蔽高位影響,確保兼容性
💡 示例說明:
- 假設
cv2.waitKey()
返回了0x00000071
ord('q') = 0x71
(0x00000071) & 0xFF = 0x71
? 正常檢測
如果不加 & 0xFF
,有的環境判斷就會失敗,保證鍵值兼容,避免系統差異