[計算機視覺]100行python實現攝像機偏移、抖動告警

背景html

在實際項目中,利用深度學習在檢測道路車輛並分析車輛行爲時,須要按照事先規定的方法繪製檢測區(包含道路方向、車道區域等)。因爲各類緣由(人爲、天氣),獲取視頻數據的攝像角度容易偏移原來設定的位置,形成檢測區域和實際畫面不匹配,系統容易產生誤檢誤報等錯誤數據。所以須要在攝像機位置偏移第一時間告訴系統檢測模塊中止工做,直到攝像機歸位後再進行檢測。攝像機角度偏移告警屬於‘視頻診斷’中的一類,本文利用提取圖片特徵點實現攝像機偏移告警,demo所有python代碼不足200行。python

前面有幾篇博客文字太少,發不了首頁:算法

[AI分享]零高數理解人工智能和深度學習緩存

[AI開發]基於深度學習的卡口車型、車牌識別
app

這裏是[AI+計算機視覺]的全部文章,須要的朋友能夠點一波關注或者收藏一下。ide

 

圖像特徵點post

對於任何一張二維圖片,從像素級別上看,都存在一些咱們肉眼看不到的比較獨特的像素單元(能夠理解爲像素塊),就像咱們每一個人的臉都會不同凡響同樣,咱們稱這些具備特色的像素區域爲「圖像特徵點」。已經有很是成熟的算法來提取圖片的特徵點:學習

(1)Harris:用於檢測角點;測試

(2)SIFT:用於檢測斑點;人工智能

(3)SURF:用於檢測斑點;

(4)FAST:用於檢測角點;

(5)BRIEF:用於檢測斑點;

(6)ORB:表示帶方向的FAST算法與具備旋轉不變性的BRIEF算法;

詳細算法原理上網搜一下(我也不是很清楚:)),OpenCV中包含以上幾種算法實現。

角點:

圖像中涉及到拐角的區域,好比物體有輪廓,圖像中的物體有邊緣區分。

斑點:

一塊有特別規律的像素區域。

方向、尺寸不變性:

指特徵點不會受圖片尺寸、旋轉而改變,好比同一張圖,你縮小一倍旋轉90度後,特徵點仍是同樣的。

 

圖像匹配

提取兩張圖片的特徵點,而後將這些特徵點進行匹配關聯。若是匹配程度知足某一閾值,則認爲這兩張圖知足匹配條件。注意,對於同一個物體,拍攝角度不一樣,亮度不一樣都應該知足匹配條件。

能夠看到,對於同一個場景的不一樣拍攝角度的兩張圖片,能找到匹配到的特徵點,可是偏差很是大。咱們設置一個閾值,知足該條件才認爲兩個點匹配:

偏差少不少了,匹配到的特徵點也很是正確。

換一組攝像機的照片,前一張和後一張在拍攝時,攝像機角度往左下角有偏移,因此對應匹配到的特徵點往右上方移動了:

咱們能夠看到,雖然拍攝角度不一樣,可是因爲場景相似,仍然能匹配到特徵點(爲了減小繪圖方便看清楚,閾值設置很是嚴格,若是放寬一點還能看到更多匹配到的點),並且這些匹配到的點幾乎都正確。對於兩張徹底不一樣的場景照片,匹配到的特徵點很是少或者爲零(具體看設置的閾值)

場景不一樣,匹配到的特徵點只有視頻上的文字。

 

角度偏移告警

若是攝像機位置不變,先後拍攝兩張照片,那麼這兩張照片匹配到的特徵點的二維物理座標應該是同樣的(可能有輕微偏移,兩張照片尺寸一致)。那麼咱們能夠根據攝像機先後兩幀(或間隔時間內取得的兩幀)的匹配點物理位置是否有偏移,設置一個偏移閾值,大於該閾值時則認爲偏移,不然認爲沒偏移(或輕微偏移),固然,若是兩幀匹配到的特徵點很是少(低於一個閾值),那麼咱們認爲這倆幀徹底不同了(場景不同了),這時候攝像機徹底偏移了原來的角度。

注意點:

1)閾值很是重要;

2)先後幀匹配時,要去掉相似攝像機自動加上去的「視頻位置」、「當前時間」等等區域,由於這些區域不少時候可以匹配到特徵點,而且物理位置座標不會發生變化,形成偏差;

3)在計算特徵點物理位置偏移量時,取全部特徵點物理位置偏移的平均值。

 

最終效果

間隔時間取視頻中的幀,進行特徵點對比。根據前面的思路分爲4個等級:「無偏移」、「輕度偏移(抖動)」、「嚴重偏移」、「徹底偏移」。

 

源代碼

最重要的是代碼,很簡單,直接貼上來便可。加起來不到160行。測試不少場景,效果都不錯。

 1 '''
 2 視頻幀匹配腳本
 3 '''
 4 import numpy as np
 5 import cv2 
 6 
 7 #至少10個點匹配
 8 MIN_MATCH_COUNT = 10
 9 #徹底匹配偏移 d<4
10 BEST_DISTANCE = 4
11 #微量偏移  4<d<10
12 GOOD_DISTANCE = 10
13 
14 
15 # 特徵點提取方法,內置不少種
16 algorithms_all = {
17     "SIFT": cv2.xfeatures2d.SIFT_create(),
18     "SURF": cv2.xfeatures2d.SURF_create(8000), 
19     "ORB": cv2.ORB_create()
20 }
21 
22 '''
23 # 圖像匹配
24 # 0徹底不匹配 1場景匹配 2角度輕微偏移 3徹底匹配
25 '''
26 def match2frames(image1, image2):
27     img1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
28     img2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
29 
30     size1 = img1.shape
31     size2 = img2.shape
32 
33     img1 = cv2.resize(img1, (int(size1[1]*0.3), int(size1[0]*0.3)), cv2.INTER_LINEAR)
34     img2 = cv2.resize(img2, (int(size2[1]*0.3), int(size2[0]*0.3)), cv2.INTER_LINEAR)
35 
36     sift = algorithms_all["SIFT"]
37 
38     kp1, des1 = sift.detectAndCompute(img1, None)
39     kp2, des2 = sift.detectAndCompute(img2, None)
40     
41     FLANN_INDEX_KDTREE = 0
42     index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
43     search_params = dict(checks = 50)
44     
45     flann = cv2.FlannBasedMatcher(index_params, search_params)
46     
47     matches = flann.knnMatch(des1, des2, k=2)
48     
49     # 過濾
50     good = []
51     for m,n in matches:
52         if m.distance < 0.7*n.distance:
53             good.append(m)
54     
55     if len(good) <= MIN_MATCH_COUNT:
56         return 0  # 徹底不匹配
57     else:
58         distance_sum = 0  # 特徵點2d物理座標偏移總和
59         for m in good:
60             distance_sum += get_distance(kp1[m.queryIdx].pt, kp2[m.trainIdx].pt)
61         distance = distance_sum / len(good)  #單個特徵點2D物理位置平均偏移量
62 
63         if distance < BEST_DISTANCE:
64             return 3  #徹底匹配
65         elif distance < GOOD_DISTANCE and distance >= BEST_DISTANCE:
66             return 2  #部分偏移
67         else:
68             return 1  #場景匹配
69         
70 
71 '''
72 計算2D物理距離
73 '''
74 def get_distance(p1, p2):
75     x1,y1 = p1
76     x2,y2 = p2
77     return np.sqrt((x1-x2)**2 + (y1-y2)**2)
78 
79 
80 if __name__ == "__main__":
81     pass

測試

 1 '''
 2 攝像機角度偏移告警
 3 '''
 4 import cv2
 5 import do_match
 6 import numpy as np
 7 from PIL import Image, ImageDraw, ImageFont
 8 
 9 '''
10 告警信息
11 '''
12 def putText(frame, text):
13   cv2_im = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
14   pil_im = Image.fromarray(cv2_im)
15 
16   draw = ImageDraw.Draw(pil_im)
17   font = ImageFont.truetype("fonts/msyh.ttc", 30, encoding="utf-8")
18   draw.text((50, 50), text, (0, 255, 255), font=font)
19 
20   cv2_text_im = cv2.cvtColor(np.array(pil_im), cv2.COLOR_RGB2BGR)
21 
22   return cv2_text_im
23 
24 
25 
26 
27 texts = ["徹底偏移","嚴重偏移", "輕微偏移", "無偏移"]
28 
29 cap = cv2.VideoCapture('videos/test4_new.mp4')
30 
31 if (cap.isOpened()== False): 
32   print("Error opening video stream or file")
33 
34 first_frame = True
35 pre_frame = 0
36 
37 index = 0
38 
39 while(cap.isOpened()):
40   ret, frame = cap.read()
41   if ret == True:
42     if first_frame:
43         pre_frame = frame
44         first_frame = False
45         continue
46     
47     index += 1
48     if index % 24 == 0:
49       result = do_match.match2frames(pre_frame, frame)
50       print("檢測結果===>", texts[result])
51       
52       if result > 1:  # 緩存最近無偏移的幀
53         pre_frame = frame
54 
55       size = frame.shape
56 
57       if size[1] > 720: # 縮小顯示
58         frame = cv2.resize(frame, (int(size[1]*0.5), int(size[0]*0.5)), cv2.INTER_LINEAR)
59 
60       text_frame = putText(frame, texts[result])
61 
62       cv2.imshow('Frame', text_frame)
63     if cv2.waitKey(1) & 0xFF == ord('q'):
64       break
65   else: 
66     break
67 
68 cap.release()
69 cv2.destroyAllWindows()
相關文章
相關標籤/搜索