利用OpenCV光流算法實現視頻特征點跟蹤

光流簡介

????????光流(optical?flow)是運動物體在觀察成像平面上的像素運動的瞬時速度。光流法是利用圖像序列中像素在時間域上的變化以及相鄰幀之間的相關性來找到上一幀跟當前幀之間存在的對應關系,從而計算出相鄰幀之間物體的運動信息的一種方法。通常將二維圖像平面特定坐標點上的灰度瞬時變化率定義為光流矢量。光流是由物體或相機的運動引起的圖像物體在連續兩幀之間的明顯運動的模式。它是 2D 矢量場,其中每個矢量是一個位移矢量,顯示點從第一幀到第二幀的移動。

? ? ? ? 以下圖片顯示了計算出的光流示意圖,顏色表示光流方向,顏色飽和度表示大小:

?

? ? ? ? 參考博文:

計算機視覺大型攻略 —— 光流(1)基本原理和經典算法_光流算法_linusyue的博客-CSDN博客

光流法(optical flow)簡介_Fm鐨的博客-CSDN博客

opencv光流實現

????????光流追蹤的前提是:

1. 對象的像素強度在連續幀之間不會改變;

2. 相鄰像素具有相似的運動。

?OpenCV提供了兩種算法計算光流:

cv::calcOpticalFlowPyrLK()---稀疏光流: 通過 Lucas-Kanade 方法計算稀疏特征集的光流(使用 Shi-Tomasi 算法檢測到的角點

cv::calcOpticalFlowFarneback--密集光流: 通過 Gunner Farneback 來尋找密集光流。它計算幀中所有點的光流。

p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, winSize=(15, 15), maxLevel=2, criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))

- old_gray: 上一幀單通道灰度圖
- frame_gray: 下一幀單通道灰度圖
- prePts:p0上一幀坐標pts
- nextPts: None
- winSize: 每個金字塔級別上搜索窗口的大小
- maxLevel: 最大金字塔層數
- criteria:指定迭代搜索算法的終止條件,在指定的最大迭代次數 10 之后或搜索窗口移動小于 0.03

flow = cv2.calcOpticalFlowFarneback(prvs, next, None, 0.5, 3, 15, 3, 5, 1.2, 0)

- prvs: 上一幀單通道灰度圖
- next: 下一幀單通道灰度圖
- flow: 流 None
- pyr_scale: 0.5經典金字塔,構建金字塔縮放scale
- level:3 初始圖像的金字塔層數
- winsize:3 平均窗口大小,數值越大,算法對圖像的魯棒性越強
- iterations:15 迭代次數
- poly_n:5 像素鄰域的參數多邊形大小,用于在每個像素中找到多項式展開式;較大的值意味著圖像將使用更平滑的曲面進行近似,從而產生更高的分辨率、魯棒算法和更模糊的運動場;通常多邊形n=5或7。
- poly_sigma:1.2 高斯標準差,用于平滑導數
- flags: 可以是以下操作標志的組合:OPTFLOW_USE_INITIAL_FLOW:使用輸入流作為初始流近似值。OPTFLOW_FARNEBACK_GAUSSIAN: 使用GAUSSIAN過濾器而不是相同尺寸的盒過濾器;

源碼實例

稀疏光流追蹤

# 光流追蹤
# 光流追蹤的前提是:1. 對象的像素強度在連續幀之間不會改變;2. 相鄰像素具有相似的運動。
# - cv2.goodFeaturesToTrack() 確定要追蹤的特征點
# - cv2.calcOpticalFlowPyrLK() 追蹤視頻中的特征點# 取第一幀,檢測其中的一些 Shi-Tomasi 角點,使用 Lucas-Kanade 光流迭代跟蹤這些點。
# 對于函數 cv2.calcOpticalFlowPyrLK() 傳遞前一幀、前一個點和下一幀。它返回下一個點以及一些狀態編號,如果找到下一個點,則值為 1,否則為零。
# 然后在下一步中迭代地將這些下一個點作為前一個點傳遞。# USAGE
# python video_optical_flow.pyimport imutils
import numpy as np
import cv2cap = cv2.VideoCapture('images/slow_traffic_small.mp4')# ShiTomasi角點檢測的參數
feature_params = dict(maxCorners=100,qualityLevel=0.3,minDistance=7,blockSize=7)# Lucas Kanada光流檢測的參數
lk_params = dict(winSize=(15, 15),maxLevel=2,criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))# 構建隨機顏色
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)num = 0
while (1):ret, frame = cap.read()if not ret:breakframe_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)# 使用迭代Lucas Kanade方法計算稀疏特征集的光流# - old_gray: 上一幀單通道灰度圖# - frame_gray: 下一幀單通道灰度圖# - prePts:p0上一幀坐標pts# - nextPts: None# - winSize: 每個金字塔級別上搜索窗口的大小# - maxLevel: 最大金字塔層數# - criteria:指定迭代搜索算法的終止條件,在指定的最大迭代次數criteria.maxCount之后或搜索窗口移動小于criteria.epsilonp1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)# 選擇軌跡點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, (a, b), (c, d), color[i].tolist(), 2)frame = cv2.circle(frame, (a, b), 5, color[i].tolist(), -1)img = cv2.add(frame, mask)cv2.imshow('frame', img)cv2.imwrite('videoof-imgs/' + str(num) + '.jpg', imutils.resize(img, 500))print(str(num))num = num + 1k = cv2.waitKey(30) & 0xffif k == 27:break# 更新之前的幀和點old_gray = frame_gray.copy()p0 = good_new.reshape(-1, 1, 2)cv2.destroyAllWindows()
cap.release()

改進版稀疏光流追蹤

# 優化后的光流追蹤—Lucas-Kanade tracker
# (當不見檢查下一個關鍵點的正確程度時,即使圖像中的任何特征點消失,光流也有可能找到下一個看起來可能靠近它的點。實際上對于穩健的跟蹤,角點應該在特定的時間間隔內檢測點。
# 找到特征點后,每 30 幀對光流點的向后檢查,只選擇好的。)
# Lucas Kanade稀疏光流演示。使用GoodFeatures跟蹤用于跟蹤初始化和匹配驗證的回溯幀之間。
# Lucas-Kanade sparse optical flow demo. Uses goodFeaturesToTrack for track initialization and back-tracking for match verification between frames.# Usage
# pyhton lk_track.py images/slow_traffic_small.mp4
# 按 ESC鍵退出from __future__ import print_functionimport imutils
import numpy as np
import cv2def draw_str(dst, target, s):x, y = targetcv2.putText(dst, s, (x + 1, y + 1), cv2.FONT_HERSHEY_PLAIN, 1.0, (0, 0, 0), thickness=2, lineType=cv2.LINE_AA)cv2.putText(dst, s, (x, y), cv2.FONT_HERSHEY_PLAIN, 1.0, (255, 255, 255), lineType=cv2.LINE_AA)lk_params = dict(winSize=(15, 15),maxLevel=2,criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))feature_params = dict(maxCorners=500,qualityLevel=0.3,minDistance=7,blockSize=7)class App:def __init__(self, video_src):self.track_len = 10self.detect_interval = 30self.tracks = []self.cam = cv2.VideoCapture(video_src)self.frame_idx = 0def run(self):while True:_ret, frame = self.cam.read()if not _ret:breakframe_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)vis = frame.copy()if len(self.tracks) > 0:img0, img1 = self.prev_gray, frame_grayp0 = np.float32([tr[-1] for tr in self.tracks]).reshape(-1, 1, 2)p1, _st, _err = cv2.calcOpticalFlowPyrLK(img0, img1, p0, None, **lk_params)p0r, _st, _err = cv2.calcOpticalFlowPyrLK(img1, img0, p1, None, **lk_params)d = abs(p0 - p0r).reshape(-1, 2).max(-1)good = d < 1new_tracks = []for tr, (x, y), good_flag in zip(self.tracks, p1.reshape(-1, 2), good):if not good_flag:continuetr.append((x, y))if len(tr) > self.track_len:del tr[0]new_tracks.append(tr)cv2.circle(vis, (x, y), 2, (0, 255, 0), -1)self.tracks = new_trackscv2.polylines(vis, [np.int32(tr) for tr in self.tracks], False, (0, 255, 0))draw_str(vis, (20, 20), 'track count: %d' % len(self.tracks))if self.frame_idx % self.detect_interval == 0:mask = np.zeros_like(frame_gray)mask[:] = 255for x, y in [np.int32(tr[-1]) for tr in self.tracks]:cv2.circle(mask, (x, y), 5, 0, -1)p = cv2.goodFeaturesToTrack(frame_gray, mask=mask, **feature_params)if p is not None:for x, y in np.float32(p).reshape(-1, 2):self.tracks.append([(x, y)])self.prev_gray = frame_graycv2.imshow('lk_track', vis)print(self.frame_idx)cv2.imwrite('videoOof-imgs/' + str(self.frame_idx) + '.jpg', imutils.resize(vis, 500))self.frame_idx += 1ch = cv2.waitKey(1)if ch == 27:breakdef main():import systry:video_src = sys.argv[1]except:video_src = 0App(video_src).run()print('Done')if __name__ == '__main__':print(__doc__)main()cv2.destroyAllWindows()

密集光流追蹤

# OpenCV中的密集光流
# Lucas-Kanade 方法計算稀疏特征集的光流(使用 Shi-Tomasi 算法檢測到的角點)。
# OpenCV 提供了另一種算法: Gunner Farneback 來尋找密集光流。它計算幀中所有點的光流。
# 通過cv2.calcOpticalFlowFarneback() 將得到一個帶有光流向量 (u,v) 的 2 通道陣列。可以找到它們的大小和方向,然后對結果進行顏色編碼以實現更好的可視化。
# 在HSV圖像中,方向對應于圖像的色調,幅度對應于價值平面。import cv2
import imutils
import numpy as npcap = cv2.VideoCapture('images/slow_traffic_small.mp4')ret, frame1 = cap.read()
prvs = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)
hsv[..., 1] = 255num = 0
while (1):ret, frame2 = cap.read()if not ret:breaknext = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)# 使用迭代Gunner Farneback 方法計算密集特征的光流# - prvs: 上一幀單通道灰度圖# - next: 下一幀單通道灰度圖# - flow: 流 None# - pyr_scale: 0.5經典金字塔,構建金字塔縮放scale# - level:3 初始圖像的金字塔層數# - winsize:3 平均窗口大小,數值越大,算法對圖像的魯棒性越強# - iterations:15 迭代次數# - poly_n:5 像素鄰域的參數多邊形大小,用于在每個像素中找到多項式展開式;較大的值意味著圖像將使用更平滑的曲面進行近似,從而產生更高的分辨率、魯棒算法和更模糊的運動場;通常多邊形n=5或7。# - poly_sigma:1.2 高斯標準差,用于平滑導數# - flags: 可以是以下操作標志的組合:OPTFLOW_USE_INITIAL_FLOW:使用輸入流作為初始流近似值。OPTFLOW_FARNEBACK_GAUSSIAN: 使用GAUSSIAN過濾器而不是相同尺寸的盒過濾器;flow = cv2.calcOpticalFlowFarneback(prvs, next, None, 0.5, 3, 15, 3, 5, 1.2, 0)mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1])hsv[..., 0] = ang * 180 / np.pi / 2hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX)rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)cv2.imshow('Origin VS frame2', np.hstack([frame2, rgb]))cv2.imwrite('dof-imgs/' + str(num) + '.jpg', imutils.resize(np.hstack([frame2, rgb]), 600))k = cv2.waitKey(30) & 0xffnum = num + 1if k == 27:breakelif k == ord('s'):cv2.imwrite('dof-imgs/origin VS dense optical flow HSVres' + str(num) + ".jpg",imutils.resize(np.hstack([frame2, rgb]), width=800))prvs = nextcap.release()
cv2.destroyAllWindows()

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/38510.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/38510.shtml
英文地址,請注明出處:http://en.pswp.cn/news/38510.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

大模型PEFT技術原理(二):P-Tuning、P-Tuning v2

隨著預訓練模型的參數越來越大&#xff0c;尤其是175B參數大小的GPT3發布以來&#xff0c;讓很多中小公司和個人研究員對于大模型的全量微調望而卻步&#xff0c;近年來研究者們提出了各種各樣的參數高效遷移學習方法&#xff08;Parameter-efficient Transfer Learning&#x…

css鼠標樣式 cursor: pointer

cursor: none; cursor:not-allowed; 禁止選擇 user-select: none; pointer-events:none;禁止觸發事件, 該樣式會阻止默認事件的發生&#xff0c;但鼠標樣式會變成箭頭

Hlang社區-前端社區宣傳首頁實現

文章目錄 前言頁面結構固定釘頭部輪播JS特效完整代碼總結前言 這里的話,博主其實也是今年參與考研的大軍之一,所以的話,是抽空去完成這個項目的,當然這個項目的肯定是可以在較短的時間內完成的。 那么廢話不多說,昨天也是干到1點多,把這個首頁寫出來了。先看看看效果吧:…

Linux中 socket編程中多進程/多線程TCP并發服務器模型

一、循環服務器(while)【不常用】 一次只能處理一個客戶端的請求&#xff0c;等這個客戶端退出后&#xff0c;才能處理下一個客戶端。缺點&#xff1a;循環服務器所處理的客戶端不能有耗時操作。 模型 sfd socket(); bind(); listen(); while(1) {newfd accept();while(1){r…

分別在linux和windows上設置socket為阻塞模式

在 Linux 和 Windows 系統中&#xff0c;都可以將 socket 設置為非阻塞模式。 Linux平臺 在 Linux 系統中&#xff0c;可以使用 fcntl 函數來設置 socket 為非阻塞模式。例如&#xff1a; int flags fcntl(socket_fd, F_GETFL, 0); fcntl(socket_fd, F_SETFL, flags | O_NO…

【問心篇】渴望、熱情和選擇

加班太嚴重完全沒有時間學習&#xff0c;怎么辦&#xff1f; 我真的不算聰明的人&#xff0c;但是&#xff0c;我對學習真的是有渴望的。說得好聽一點&#xff0c;我希望自己在不停地成長&#xff0c;不辜負生活在這個信息化大變革的時代。說得不好的一點&#xff0c;就是我從…

斷點續傳的未來發展趨勢與前景展望

斷點續傳是一種在網絡傳輸中斷后&#xff0c;能夠從中斷的位置繼續傳輸的技術。它可以有效地避免因為網絡不穩定、服務器故障、用戶操作等原因導致的傳輸失敗&#xff0c;節省了用戶的時間和流量&#xff0c;提高了傳輸的效率和可靠性。斷點續傳在很多場景中都有廣泛的應用&…

AI 繪畫Stable Diffusion 研究(八)sd采樣方法詳解

大家好&#xff0c;我是風雨無阻。 本文適合人群&#xff1a; 希望了解stable Diffusion WebUI中提供的Sampler究竟有什么不同&#xff0c;想知道如何選用合適采樣器以進一步提高出圖質量的朋友。 想要進一步了解AI繪圖基本原理的朋友。 對stable diffusion AI繪圖感興趣的朋…

【洛谷 P5736】【深基7.例2】質數篩 題解(埃氏篩法)

【深基7.例2】質數篩 題目描述 輸入 n n n 個不大于 1 0 5 10^5 105 的正整數。要求全部儲存在數組中&#xff0c;去除掉不是質數的數字&#xff0c;依次輸出剩余的質數。 輸入格式 第一行輸入一個正整數 n n n&#xff0c;表示整數個數。 第二行輸入 n n n 個正整數 …

jquery如何修改選中狀態

jquery修改選中狀態的方法&#xff1a;1、使用addClass和removeClass方法&#xff0c;可以向選中的元素添加一個多個類名&#xff0c;從而改變其樣式或狀態&#xff1b;2、使用toggleClass方法&#xff0c;可以在選中元素上添加或移除一個類名&#xff0c;如果該類名已經存在&a…

208. 實現 Trie (前綴樹)

Trie&#xff08;發音類似 "try"&#xff09;或者說 前綴樹 是一種樹形數據結構&#xff0c;用于高效地存儲和檢索字符串數據集中的鍵。這一數據結構有相當多的應用情景&#xff0c;例如自動補完和拼寫檢查。 請你實現 Trie 類&#xff1a; Trie() 初始化前綴樹對象…

手撕LFU緩存

手撕LRU緩存_右大臣的博客-CSDN博客 是LRU的升級&#xff0c;多了一個訪問次數的維度 實現 LFUCache 類&#xff1a; LFUCache(int capacity) - 用數據結構的容量 capacity 初始化對象int get(int key) - 如果鍵 key 存在于緩存中&#xff0c;則獲取鍵的值&#xff0c;否則返…

C# Lamda到底是什么?!

lamda作為匿名函數&#xff0c;現在已經能夠出現子啊C#程序的任何可能位置&#xff0c;它可能作為參數為委托或其他函數復制&#xff0c;或者單獨作為表達式&#xff0c;或者承擔一些類似C中內聯函數的一些作用承擔一些簡單計算。熟練的使用Lamda表達式能夠讓減少代碼的冗余&am…

Django圖書商城系統實戰開發-總結經驗之后端開發

Django圖書商城系統實戰開發-總結經驗之后端開發 簡介 在這篇博客中&#xff0c;我將總結經驗分享后端開發Django圖書商城系統的過程。在開發過程中&#xff0c;我遇到了各種挑戰和問題&#xff0c;并且通過實踐獲得了寶貴的經驗和教訓。通過本文&#xff0c;我希望能幫助讀者…

vue3+vite配置vantUI主題

?在項目中統一配置UI主題色&#xff0c;各個組件配色統一修改 vantUI按需安裝 參考vantUI文檔 創建vantVar.less文件夾進行樣式編寫 vantVar.less :root:root{//導航--van-nav-bar-height: 44px;//按鈕--van-button-primary-color: #ffffff;--van-button-primary-backgr…

linux——mysql的高可用MHA

目錄 一、概述 一、概念 二、組成 三、特點 四、工作原理 二、案例 三、構建MHA 一、基礎環境 二、ssh免密登錄 三、主從復制 master slave1 四、MHA安裝 一、環境 二、安裝node 三、安裝manager 一、概述 一、概念 MHA&#xff08;MasterHigh Availability&a…

力扣 198. 打家劫舍

題目來源&#xff1a;https://leetcode.cn/problems/house-robber/description/ C題解&#xff1a;因為是間接偷竊&#xff0c;所以偷nums[i]家前&#xff0c;一定偷過第i-2或者i-3家&#xff0c;因為i-1不能偷。 例如12345共5家&#xff0c;先偷第1家&#xff0c;那么2不能偷…

(三)Unity開發Vision Pro——入門

3.入門 1.入門 本節涵蓋了幾個重要主題&#xff0c;可幫助您加快visionOS 平臺開發速度。在這里&#xff0c;您將找到構建第一個 Unity PolySpatial XR 應用程序的分步指南的鏈接&#xff0c;以及 PolySpatial XR 開發時的一些開發最佳實踐。 2.開發與迭代 有關先決條件、開…

顯卡nvidia-smi后 提示Faild 解決過程,包含卸載重裝NVIDIA驅動步驟

顯卡異常: 顯卡nvidia-smi后 提示Faild 解決過程&#xff0c;卸載重裝nvidia驅動步驟 文章目錄 顯卡異常: 顯卡nvidia-smi后 提示Faild 解決過程&#xff0c;卸載重裝nvidia驅動步驟 [toc]1 緣由2 解決過程3 過程所需命令4 解決4.1 把該顯卡重新拔插一下卸載NVIDIA驅動的方法&a…

單元測試優化:為什么要對程序進行測試?測試有什么好處?

單元測試&#xff08;Unit Testing&#xff09;又稱為模塊測試, 是針對程序模塊&#xff08;軟件設計的最小單位&#xff09;來進行正確性檢驗的測試工作。 程序單元是應用的最小可測試部件。簡單來說&#xff0c;就是測試數據的穩定性是否達到程序的預期。 我們日常開發時可能…