<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于opencv對(duì)高空拍攝視頻消抖處理

          共 10599字,需瀏覽 22分鐘

           ·

          2021-03-14 22:30

          點(diǎn)擊下面卡片關(guān)注AI算法與圖像處理”,選擇加"星標(biāo)"或“置頂”

          重磅干貨,第一時(shí)間送達(dá)

          來(lái)源:https://luzhimin.blog.csdn.net/article/details/114523558
          編輯:AI算法與圖像處理


          一、問(wèn)題背景

          無(wú)人機(jī)在拍攝視頻時(shí),由于風(fēng)向等影響因素,不可避免會(huì)出現(xiàn)位移和旋轉(zhuǎn),導(dǎo)致拍攝出的畫面存在平移和旋轉(zhuǎn)的幀間變換, 即“抖動(dòng)” 抖動(dòng)會(huì)改變目標(biāo)物體 (車輛、行人) 的坐標(biāo),給后續(xù)的檢測(cè)、跟蹤任務(wù)引入額外誤差,造成數(shù)據(jù)集不可用。

          原效果

          目標(biāo)效果

          理想的無(wú)抖動(dòng)視頻中,對(duì)應(yīng)于真實(shí)世界同一位置的背景點(diǎn)在不同幀中的坐標(biāo)應(yīng)保持一致,從而使車輛、行人等目標(biāo)物體的坐標(biāo)變化只由物體本身的運(yùn)動(dòng)導(dǎo)致,而不包含相機(jī)的運(yùn)動(dòng) 抖動(dòng)可以由不同幀中對(duì)應(yīng)背景點(diǎn)的坐標(biāo)變換來(lái)描述


          二、量化指標(biāo)

          抖動(dòng)可以用相鄰幀之間的 x 方向平移像素 dx,y 方向平移像素 dy,旋轉(zhuǎn)角度 da,縮放比例 s 來(lái)描述,分別繪制出 4 個(gè)折線圖,根據(jù)折線圖的走勢(shì)可以判斷抖動(dòng)的程度 理想的無(wú)抖動(dòng)視頻中,dx、dy、da 幾乎始終為 0,s 幾乎始終為 1。

          三、技術(shù)思路

          我們最終實(shí)現(xiàn),將視頻的所有幀都對(duì)齊到第一幀,以達(dá)到視頻消抖問(wèn)題,實(shí)現(xiàn)邏輯如下圖所示。



          (1)首先對(duì)視頻進(jìn)行抽第一幀與最后一幀,為什么抽取兩幀?這樣做的主要目的是,我們?cè)谧鰩瑢?duì)齊時(shí),使用幀中靜態(tài)物的關(guān)鍵點(diǎn)做對(duì)齊,如果特征點(diǎn)來(lái)源于動(dòng)態(tài)物上,那么對(duì)齊后就會(huì)產(chǎn)生形變,我們選取第一幀與最后一幀,提取特征點(diǎn),留下交集部分,則可以得到靜態(tài)特征點(diǎn)我們這里稱為特征模板,然后將特征模板應(yīng)用到每一幀上,這樣可以做有效對(duì)齊。

          (2)常用特征點(diǎn)檢測(cè)器:

          SIFT: 04 年提出,廣泛應(yīng)用于各種跟蹤和識(shí)別算法,表現(xiàn)能力強(qiáng),但計(jì)算復(fù)雜度高。

          SURF: 06 年提出,是 SIFT 的演進(jìn)版本,保持強(qiáng)表現(xiàn)能力的同時(shí)大大減少了計(jì)算量。

          BRISK: BRIEF 的演進(jìn)版本,壓縮了特征的表示,提高了匹配速度。ORB: 以速度著稱,是 SURF 的演進(jìn)版本,多用于實(shí)時(shí)應(yīng)用。

          GFTT: 最早提出的 Harris 角點(diǎn)的改進(jìn)版本,經(jīng)常合稱為 Harris-Shi-Tomasi 角點(diǎn)。

          SimpleBlob: 使用 blob 的概念來(lái)抽取圖像中的特征點(diǎn),相對(duì)于角點(diǎn)的一種創(chuàng)新。FAST: 相比其他方法特征點(diǎn)數(shù)量最多,但也容易得到距離過(guò)近的點(diǎn),需要經(jīng)過(guò) NMS。

          Star: 最初用于視覺(jué)測(cè)距,后來(lái)也成為一種通用的特征點(diǎn)檢測(cè)方法。

          我們這里使用的是SURF特征點(diǎn)檢測(cè)器

          第一幀特特征點(diǎn)提取

          最后一幀特征點(diǎn)提取

          (3)在上圖中,我們發(fā)現(xiàn)所提取的特征點(diǎn)中部分來(lái)自于車身,由于車是運(yùn)動(dòng)的,所以我們不能使用,我們用第一幀與最后一幀做靜態(tài)特幀點(diǎn)匹配,生成靜態(tài)特征模板,在下圖中,我們發(fā)現(xiàn)只有所有的特征點(diǎn)只選取在靜態(tài)物上

          靜態(tài)特征點(diǎn)模板

          (4)靜態(tài)特征模板匹配 ,我們這里使用Flann算法,匹配結(jié)果如下

          特征匹配

          (5)使用匹配成功的兩組特征點(diǎn),估計(jì)兩幀之間的透視變換 (Perspective Transformation)。估計(jì)矩陣 H,其中 (x_i, y_i) 和 (x_i^′, y_i^′) 分別是兩幀的特征點(diǎn)。

          第一幀

          最后一幀對(duì)齊到第一幀

          四、實(shí)現(xiàn)代碼

          代碼基于python實(shí)現(xiàn),如下所示

          import cv2import timeimport numpy as npimport os  class Stable:    # 處理視頻文件路徑    __video_path = None     # surf 特征提取    __surf = {        # surf算法        'surf': None,        # 提取的特征點(diǎn)        'kp': None,        # 描述符        'des': None,        # 過(guò)濾后的特征模板        'template_kp': None    }     # capture    __capture = {        # 捕捉器        'cap': None,        # 視頻大小        'size': None,        # 視頻總幀        'frame_count': None,        # 視頻幀率        'fps': None,        'video': None    }     # 配置    __config = {        # 要保留的最佳特征的數(shù)量        'key_point_count': 5000,        # Flann特征匹配        'index_params': dict(algorithm=0, trees=5),        'search_params': dict(checks=50),        'ratio': 0.5,        'frame_count': 9999    }     # 當(dāng)前處理幀數(shù)    __current_frame = 0     # 需要處理幀數(shù)    __handle_count = 0     # 處理時(shí)間    __handle_timer = {        'init': 0,        'handle': 0,        'read': 0,        'key': 0,        'matrix': 0,        'flann': 0,        'perspective': 0,        'write': 0,        'other': 0,    }     # 幀隊(duì)列    __frame_queue = None     # 需要寫入的幀隊(duì)列    __write_frame_queue = None     # 特征提取列表    __surf_list = []     def __init__(self):        pass     # 初始化capture    def __init_capture(self):        self.__capture['cap'] = cv2.VideoCapture(self.__video_path)        self.__capture['size'] = (int(self.__capture['cap'].get(cv2.CAP_PROP_FRAME_WIDTH)),                                  int(self.__capture['cap'].get(cv2.CAP_PROP_FRAME_HEIGHT)))         self.__capture['fps'] = self.__capture['cap'].get(cv2.CAP_PROP_FPS)         self.__capture['video'] = cv2.VideoWriter(self.__video_path.replace('.', '_stable.'),                                                  cv2.VideoWriter_fourcc(*"mp4v"),                                                  self.__capture['fps'],                                                  self.__capture['size'])         self.__capture['frame_count'] = int(self.__capture['cap'].get(cv2.CAP_PROP_FRAME_COUNT))         self.__handle_count = min(self.__config['frame_count'], self.__capture['frame_count'])     # 初始化surf    def __init_surf(self):         st = time.time()        self.__capture['cap'].set(cv2.CAP_PROP_POS_FRAMES, 0)        state, first_frame = self.__capture['cap'].read()          self.__capture['cap'].set(cv2.CAP_PROP_POS_FRAMES, self.__capture['frame_count'] - 20)        state, last_frame = self.__capture['cap'].read()         self.__surf['surf'] = cv2.xfeatures2d.SURF_create(self.__config['key_point_count'], 1, 1, 1, 1)         # nfeatures:默認(rèn)為0,要保留的最佳特征的數(shù)量。特征按其分?jǐn)?shù)排名(在SIFT算法中按局部對(duì)比度排序)        # nOctaveLayers:默認(rèn)為3,金字塔每組(Octave)有多少層。3是D. Lowe紙中使用的值。        # contrastThreshold:默認(rèn)為0.04,對(duì)比度閾值,用于濾除半均勻(低對(duì)比度)區(qū)域中的弱特征。閾值越大,檢測(cè)器產(chǎn)生的特征越少。        # edgeThreshold:默認(rèn)為10,用來(lái)過(guò)濾邊緣特征的閾值。注意,它的意思與contrastThreshold不同,edgeThreshold越大,濾出的特征越少(保留更多特征)。        # sigma:默認(rèn)為1.6,高斯金字塔中的σ。如果使用帶有軟鏡頭的弱相機(jī)拍攝圖像,則可能需要減少數(shù)量。         self.__surf['kp'], self.__surf['des'] = self.__surf['surf'].detectAndCompute(first_frame, None)        kp, des = self.__surf['surf'].detectAndCompute(last_frame, None)         # 快速臨近匹配        flann = cv2.FlannBasedMatcher(self.__config['index_params'], self.__config['search_params'])        matches = flann.knnMatch(self.__surf['des'], des, k=2)         good_match = []        for m, n in matches:            if m.distance < self.__config['ratio'] * n.distance:                good_match.append(m)         self.__surf['template_kp'] = []        for f in good_match:            self.__surf['template_kp'].append(self.__surf['kp'][f.queryIdx])         self.__capture['cap'].set(cv2.CAP_PROP_POS_FRAMES, 0)         self.__handle_timer['init'] = int((time.time() - st) * 1000)         print("[INFO] init time:{}ms".format(self.__handle_timer['init']))     # 初始化 隊(duì)列    def __init_data(self):        pass     # 初始化    def __init(self):        self.__init_capture()        self.__init_surf()        self.__init_data()     # 處理    def __process(self):         self.__current_frame = 1         while True:             if self.__current_frame > self.__handle_count:                break             start_time = time.time()             # 抽幀            success, frame = self.__capture['cap'].read()            self.__handle_timer['read'] = int((time.time() - start_time) * 1000)             if not success: return             # 計(jì)算            frame = self.detect_compute(frame)             # 寫幀            st = time.time()            self.__capture['video'].write(frame)            self.__handle_timer['write'] = int((time.time() - st) * 1000)             self.__handle_timer['handle'] = int((time.time() - start_time) * 1000)             self.__current_frame += 1             self.print_handle_time()     # 視頻穩(wěn)像    def stable(self, path):        self.__video_path = path        self.__init()        self.__process()     # 打印耗時(shí)    def print_handle_time(self):        print(            "[INFO] handle frame:{}/{} time:{}ms(read:{}ms key:{}ms flann:{}ms matrix:{}ms perspective:{}ms write:{}ms)".                format(self.__current_frame,                       self.__handle_count,                       self.__handle_timer['handle'],                       self.__handle_timer['read'],                       self.__handle_timer['key'],                       self.__handle_timer['flann'],                       self.__handle_timer['matrix'],                       self.__handle_timer['perspective'],                       self.__handle_timer['write']))     # 特征點(diǎn)提取    def detect_compute(self, frame):         frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)         # 計(jì)算特征點(diǎn)        st = time.time()        kp, des = self.__surf['surf'].detectAndCompute(frame_gray, None)        self.__handle_timer['key'] = int((time.time() - st) * 1000)         # 快速臨近匹配        st = time.time()        flann = cv2.FlannBasedMatcher(self.__config['index_params'], self.__config['search_params'])        matches = flann.knnMatch(self.__surf['des'], des, k=2)        self.__handle_timer['flann'] = int((time.time() - st) * 1000)         # 計(jì)算單應(yīng)性矩陣        st = time.time()        good_match = []        for m, n in matches:            if m.distance < self.__config['ratio'] * n.distance:                good_match.append(m)         p1, p2 = [], []        for f in good_match:            # 存在與模板特征點(diǎn)中            if self.__surf['kp'][f.queryIdx] in self.__surf['template_kp']:                p1.append(self.__surf['kp'][f.queryIdx].pt)                p2.append(kp[f.trainIdx].pt)         H, _ = cv2.findHomography(np.float32(p2), np.float32(p1), cv2.RHO)        self.__handle_timer['matrix'] = int((time.time() - st) * 1000)         # 透視變換        st = time.time()        output_frame = cv2.warpPerspective(frame, H, self.__capture['size'], borderMode=cv2.BORDER_REPLICATE)        self.__handle_timer['perspective'] = int((time.time() - st) * 1000)         return output_frame  s = Stable() s.stable('video/test10.mov')

           五、效果展示

          我們消抖后的視頻道路完全沒(méi)有晃動(dòng),但是在邊界有馬賽克一樣的東西,那是因?yàn)閳D片對(duì)齊后后出現(xiàn)黑邊,我們采用邊緣點(diǎn)重復(fù)來(lái)彌補(bǔ)黑邊。

          消抖前

          消抖后

          六、效率優(yōu)化

          目前的處理效率(原視頻尺寸3840*2160),我們可以看出主要時(shí)間是花費(fèi)在特征點(diǎn)(key)提取上。
          可以采用異步處理+GPU提高計(jì)算效率

          處理效率

           七、存在問(wèn)題

          目前存在的問(wèn)題 還不能完全消除視頻中的所有抖動(dòng)

          (1)尤其是對(duì)于原來(lái)的抖動(dòng)比較劇烈的視頻,目前只能去除大部分明顯抖動(dòng);

          (2)由于畫面旋轉(zhuǎn)造成的邊緣畫面缺失,目前采取了復(fù)制邊緣點(diǎn) (replicate) 的操作,是否會(huì)對(duì)數(shù)據(jù)集的使用造成影響還需要進(jìn)行實(shí)驗(yàn)。

          改進(jìn)思路

          (1)對(duì)于抖動(dòng)問(wèn)題,計(jì)劃通過(guò)調(diào)整關(guān)鍵點(diǎn)檢測(cè)器參數(shù)、盡可能過(guò)濾掉運(yùn)動(dòng)物體的特征點(diǎn)、調(diào)整特征點(diǎn)匹配參數(shù)來(lái)解決;

          (2)對(duì)于邊緣畫面缺失問(wèn)題,可以使用基于 CNN 的圖像修復(fù)算法,盡可能讓缺失的邊緣表現(xiàn)得更自然 后續(xù)進(jìn)一步增加運(yùn)動(dòng)平滑等算法,實(shí)現(xiàn)對(duì)整體運(yùn)動(dòng)的進(jìn)一步平滑。

               
          個(gè)人微信(如果沒(méi)有備注不拉群!
          請(qǐng)注明:地區(qū)+學(xué)校/企業(yè)+研究方向+昵稱



          下載1:何愷明頂會(huì)分享


          AI算法與圖像處理」公眾號(hào)后臺(tái)回復(fù):何愷明,即可下載。總共有6份PDF,涉及 ResNet、Mask RCNN等經(jīng)典工作的總結(jié)分析


          下載2:終身受益的編程指南:Google編程風(fēng)格指南


          AI算法與圖像處理」公眾號(hào)后臺(tái)回復(fù):c++,即可下載。歷經(jīng)十年考驗(yàn),最權(quán)威的編程規(guī)范!



             
          下載3 CVPR2021

          AI算法與圖像處公眾號(hào)后臺(tái)回復(fù):CVPR,即可下載1467篇CVPR 2020論文 和 CVPR 2021 最新論文

          點(diǎn)亮 ,告訴大家你也在看


          瀏覽 51
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  夜夜躁很很躁日日躁麻豆 | 成人午夜爱爱视频 | 美女高潮喷水影院 | 色婷婷福利 | 91无码内射 |