注意:本人尚處在opencv的入門學(xué)習(xí)階段,本博客僅為個人學(xué)習(xí)筆記見解,如有不當(dāng),歡迎指出
題目
(實驗/理論)平面標(biāo)志物的視覺跟蹤,要求:
- 選擇一個標(biāo)志物,可以是人工標(biāo)志物,也可以是自然標(biāo)志物;實現(xiàn)和實驗二相同的效果。
- 用手機或攝像頭拍攝標(biāo)志物的影像,建議讀取視頻流中的影像;
- 寫一個視覺算法獲得標(biāo)志物與相機的相對位姿;
- 測量算法的幀率;
- 添加虛擬物體;
- 算法自己完成,不得使用ARCore/easyAR等現(xiàn)成SDK。可以使用opencv中自帶的函數(shù)。
注:使用OpenCV/OpenGL來實現(xiàn)一些濾波、矩陣運算、優(yōu)化、畫圖等低層的算法。
實驗思路
步驟:識別標(biāo)志物→空間注冊→跟蹤→繪圖
識別標(biāo)志物
可以使用的方法有:模板匹配、前景分離、邊緣提取、特征點匹配、訓(xùn)練級聯(lián)分類器
模板匹配
模板匹配是一種用于在較大圖像中搜索和查找模板圖像位置的方法。它只是將模板圖??像滑動到輸入圖像上(就像在2D卷積中一樣),然后在模板圖像下比較模板和輸入圖像的拼圖。
模板匹配具有自身的局限性,主要表現(xiàn)在它只能進行平行移動,若原圖像中的匹配目標(biāo)發(fā)生旋轉(zhuǎn)或大小變化,該算法無效。
前景分離
可以用于車輛識別,就是在視頻中有動的物體和靜止的物體,通過前景分離,可以把靜的物體過濾掉,實現(xiàn)對動的車輛的識別
邊緣提取
提取圖片的邊緣,如果視頻背景是純色的,可以用這個方法提取標(biāo)志物,但是如果視頻中還有其他物體,則還需要再進行處理
特征點匹配(本實驗使用)
這是我這次實驗使用的方法。因為匹配了特征點后,物體的旋轉(zhuǎn)、平移都是可以檢測到的,方便在這個基礎(chǔ)上繪制虛擬物體
訓(xùn)練級聯(lián)分類器
這個方法嘗試過,但最后因為匹配結(jié)果比較差,就放棄了。這個方法就是對標(biāo)志物進行樣本采集,然后訓(xùn)練一個分類器,在視頻中調(diào)用該分類器對每幀進行識別,很多人臉識別、車輛識別是用的這個方法
空間注冊
將視頻第一幀的圖像與標(biāo)志物圖像進行特征匹配,找到標(biāo)志物特征點對應(yīng)在相機圖像上的坐標(biāo),跟蹤這些坐標(biāo)的移動
跟蹤
本實驗采用的是Meanshift跟蹤方法,因為它比較簡單,而且可以實現(xiàn)想要的效果
原理:
由于相鄰兩幀之間目標(biāo)的偏移量非常小,而當(dāng)前幀F(xiàn)1的目標(biāo)框B1已知,所以一種啟發(fā)式的做法是在下一幀F(xiàn)2中依舊框選已知的B1區(qū)域,根據(jù)F2中B1區(qū)域和F1中B1區(qū)域的相似性推斷出目標(biāo)框所需的偏移量,進而得到F2的目標(biāo)框B2,這就是MeanShift算法所需要解決的事
資料:MeanShift跟蹤算法
繪圖
本實驗只是繪制了一個半透明矩形在標(biāo)志物上方,因為找不到使用opencv繪制3D物體的方法,好像用openGL可以
另外,如果使用相機校準(zhǔn)棋盤格的方法,也可以繪制出一個立體圖形,參考:Docs ? 相機校準(zhǔn)和3D重建 ? 7_2_姿態(tài)估計
代碼
import numpy as np
import cv2 as cv
if __name__ == '__main__':
# 視頻的位置
cap = cv.VideoCapture(r"C:\Users\ccy\Desktop\video3.mp4")
# 拍攝第一幀
ret, first_frame = cap.read()
# 對第一幀進行特征匹配
if ret:
img1 = cv.cvtColor(first_frame, cv.COLOR_BGR2GRAY)
# 訓(xùn)練圖像
img_train = cv.imread(r'C:\Users\ccy\Desktop\1.png')
img2 = cv.cvtColor(img_train, cv.COLOR_BGR2GRAY)
# 初始化ORB檢測器
orb = cv.ORB_create()
# 基于ORB找到關(guān)鍵點和檢測器
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)
# 創(chuàng)建BF匹配器的對象
# 第一個參數(shù)normType表示距離度量方法
# 第二個參數(shù)crossCheck是布爾型變量,如果為true,表示進行雙向匹配,這樣就可以增強匹配魯棒性
bf = cv.BFMatcher(cv.NORM_HAMMING, crossCheck=True)
# 匹配描述符,返回最佳匹配
matches = bf.match(des1, des2)
leftQueryIdx = matches[0].queryIdx
bottomQueryIdx = matches[0].queryIdx
for mat in matches:
# Get the matching keypoints for each of the images
img1_idx = mat.queryIdx
img2_idx = mat.trainIdx
# x - columns
# y - rows
# Get the coordinates
(x1, y1) = kp1[img1_idx].pt
# 找到矩形最左上角的點和最右下角的點,這個可能得根據(jù)不同的標(biāo)志物稍作調(diào)整
if y1 < kp1[leftQueryIdx].pt[1]:
leftQueryIdx = img1_idx
if x1 > kp1[bottomQueryIdx].pt[0]:
bottomQueryIdx = img1_idx
# (x2, y2) = kp2[img2_idx].pt
# print("kp1[img1_idx].pt[0]:", kp1[img1_idx].pt[0])
# print("kp1[img1_idx].pt[1]:", kp1[img1_idx].pt[1])
# print("(x2,y2):", (x2, y2))
# -1是thickness參數(shù),即CV_FILL,其結(jié)果是使用與邊一樣的顏色填充圓內(nèi)部
# 下面這行代碼會標(biāo)記出匹配到的特征點
# first_frame = cv.circle(first_frame, (int(x1), int(y1)), 5, (255, 0, 0), -1)
# print("kp1[leftQueryIdx].pt", kp1[leftQueryIdx].pt)
# print("kp1[bottomQueryIdx].pt", kp1[bottomQueryIdx].pt)
leftIntCd = tuple(map(lambda x: int(x), kp1[leftQueryIdx].pt))
bottomIntCd = tuple(map(lambda x: int(x), kp1[bottomQueryIdx].pt))
# 對后面每幀進行meanshift跟蹤
(x, y) = leftIntCd
w = bottomIntCd[0] - leftIntCd[0]
h = bottomIntCd[1] - leftIntCd[1]
track_window = (x, y, w, h)
# 設(shè)置初始ROI來追蹤
roi = first_frame[y:y + h, x:x + w]
hsv_roi = cv.cvtColor(roi, cv.COLOR_BGR2HSV)
mask = cv.inRange(hsv_roi, np.array((0., 60., 32.)), np.array((180., 255., 255.)))
roi_hist = cv.calcHist([hsv_roi], [0], mask, [180], [0, 180])
cv.normalize(roi_hist, roi_hist, 0, 255, cv.NORM_MINMAX)
# 設(shè)置終止條件,可以是10次迭代,也可以至少移動1 pt
term_crit = (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 1)
while (1):
retOtherFrame, frame = cap.read()
if retOtherFrame:
# 測量幀率
loop_start = cv.getTickCount()
# 灰度處理
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
dst = cv.calcBackProject([hsv], [0], roi_hist, [0, 180], 1)
# 應(yīng)用meanshift來獲取新位置
ret, track_window = cv.meanShift(dst, track_window, term_crit)
# 在圖像上繪制
x, y, w, h = track_window
# 繪制半透明矩形
blk = np.zeros(frame.shape, np.uint8)
cv.rectangle(blk, (x, y), (x + w, y + h), (0, 255, 0), -1) # 注意在 blk的基礎(chǔ)上進行繪制;
cv.putText(blk, 'virtural', (x, y), cv.FONT_HERSHEY_COMPLEX_SMALL, 1.3, (0, 255, 0), 1)
img2 = cv.addWeighted(frame, 1.0, blk, 0.8, 1)
# 中間測幀率的代碼段
loop_time = cv.getTickCount() - loop_start
total_time = loop_time / (cv.getTickFrequency()) # 使用getTickFrequency()更加準(zhǔn)確
running_FPS = int(1 / total_time) # 幀率取整
print("running_FPS:", running_FPS)
# 顯示圖片
cv.namedWindow('img2', cv.WINDOW_FREERATIO)
cv.imshow('img2', img2)
k = cv.waitKey(1) & 0xff
if k == 27:
break
else:
break
效果
文章來源:http://www.zghlxwxcb.cn/news/detail-478728.html
參考資料
OpenCV中文官方文檔文章來源地址http://www.zghlxwxcb.cn/news/detail-478728.html
到了這里,關(guān)于山東大學(xué)增強現(xiàn)實實驗四的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!