基於OpenCV全景拼接(Python)SIFT/SURF


 

一.實驗內容:

利用sift算法,實現全景拼接算法,將給定的兩幅圖片拼接為一幅.

二.實驗環境:

主機配置: CPU :intel core i5-7300 2.50GHZ RAM :8.0GB

運行環境:win10 64位操作系統

開發環境:python3.7

 

三.核心算法原理:

1.SIFT算法

 

SIFT,即尺度不變特征變換(Scale-invariant feature transform,SIFT),是用於圖像處理領域的一種描述。這種描述具有尺度不變性,可在圖像中檢測出關鍵點,是一種局部特征描述子。

 

特點:

1.SIFT特征是圖像的局部特征,其對旋轉、尺度縮放、亮度變化保持不變性,對視角變化、仿射變換、噪聲也保持一定程度的穩定性;

2. 區分性(Distinctiveness)好,信息量豐富,適用於在海量特征數據庫中進行快速、准確的匹配;

3. 多量性,即使少數的幾個物體也可以產生大量的SIFT特征向量;

4.高速性,經優化的SIFT匹配算法甚至可以達到實時的要求;

5.可擴展性,可以很方便的與其他形式的特征向量進行聯合。

特征檢測基本步驟:

1.尺度空間極值檢測:

搜索所有尺度上的圖像位置。通過高斯微分函數來識別潛在的對於尺度和旋轉不變的興趣點。

2. 關鍵點定位

在每個候選的位置上,通過一個擬合精細的模型來確定位置和尺度。關鍵點的選擇依據於它們的穩定程度。

3. 方向確定

基於圖像局部的梯度方向,分配給每個關鍵點位置一個或多個方向。所有后面的對圖像數據的操作都相對於關鍵點的方向、尺度和位置進行變換,從而提供對於這些變換的不變性。

4. 關鍵點描述

在每個關鍵點周圍的鄰域內,在選定的尺度上測量圖像局部的梯度。這些梯度被變換成一種表示,這種表示允許比較大的局部形狀的變形和光照變化。

SIFT特征匹配階段: 

第一階段:SIFT特征的生成,即從多幅圖像中提取對尺度縮放、旋轉、亮度變化無關的特征向量。

第二階段:SIFT特征向量的匹配。

SIFT特征的生成一般包括以下幾個步驟:

1. 構建尺度空間,檢測極值點,獲得尺度不變性。

2. 特征點過濾並進行精確定位。

3. 為特征點分配方向值。

4. 生成特征描述子。

當兩幅圖像的SIFT特征向量生成以后,下一步就可以采用關鍵點特征向量的歐式距離來作為兩幅圖像中關鍵點的相似性判定度量。取圖1的某個關鍵點,通過遍歷找到圖像2中的距離最近的兩個關鍵點。在這兩個關鍵點中,如果最近距離除以次近距離小於某個閾值,則判定為一對匹配點。

  1. Lowe’s算法:

為了進一步篩選匹配點,來獲取優秀的匹配點,這就是所謂的“去粗取精”。一般會采用Lowe’s算法來進一步獲取優秀匹配點。

   為了排除因為圖像遮擋和背景混亂而產生的無匹配關系的關鍵點,SIFT的作者Lowe提出了比較最近鄰距離與次近鄰距離的SIFT匹配方式:取一幅圖像中的一個SIFT關鍵點,並找出其與另一幅圖像中歐式距離最近的前兩個關鍵點,在這兩個關鍵點中,如果最近的距離除以次近的距離得到的比率ratio少於某個閾值T,則接受這一對匹配點。因為對於錯誤匹配,由於特征空間的高維性,相似的距離可能有大量其他的錯誤匹配,從而它的ratio值比較高。顯然降低這個比例閾值T,SIFT匹配點數目會減少,但更加穩定,反之亦然。

   Lowe推薦ratio的閾值為0.8,但作者對大量任意存在尺度、旋轉和亮度變化的兩幅圖片進行匹配,結果表明ratio取值在0. 4~0. 6 之間最佳,小於0. 4的很少有匹配點,大於0. 6的則存在大量錯誤匹配點,所以建議ratio的取值原則如下:

ratio=0. 4:對於准確度要求高的匹配;

ratio=0. 6:對於匹配點數目要求比較多的匹配;

ratio=0. 5:一般情況下。

3.RANSAC算法

   隨機抽樣一致算法(RANdom SAmple Consensus,RANSAC),采用迭代的方式從一組包含離群的被觀測數據中估算出數學模型的參數。RANSAC算法假設數據中包含正確數據和異常數據(或稱為噪聲)。正確數據記為內點(inliers),異常數據記為外點(outliers)。同時RANSAC也假設,給定一組正確的數據,存在可以計算出符合這些數據的模型參數的方法。該算法核心思想就是隨機性和假設性,隨機性是根據正確數據出現概率去隨機選取抽樣數據,根據大數定律,隨機性模擬可以近似得到正確結果。假設性是假設選取出的抽樣數據都是正確數據,然后用這些正確數據通過問題滿足的模型,去計算其他點,然后對這次結果進行一個評分。

算法基本思想

1)要得到一個直線模型,需要兩個點唯一確定一個直線方程。所以第一步隨機選擇兩個點。

2)通過這兩個點,可以計算出這兩個點所表示的模型方程y=ax+b。

3)將所有的數據點套到這個模型中計算誤差。

4)找到所有滿足誤差閾值的點。

5)然后我們再重復(1)~(4)這個過程,直到達到一定迭代次數后,選出那個被支持的最多的模型,作為問題的解。

 

四.處理步驟:

 

Step1:從輸入的兩張圖片里檢測關鍵點、提取局部不變特征。(sift)

Step2:匹配的兩幅圖像之間的特征(Lowe’s算法)

Step3:使用RANSAC算法利用匹配特征向量估計單映矩陣(homography matrix)。

Step4:利用Step3得到的單映矩陣應用扭曲變換。

 

具體步驟分析見 . 步驟分析

 

五.核心代碼:

# -*- coding: utf-8 -*-
"""
Created on Fri Sep 27 22:29:53 2019

@author: erio
"""

import numpy as np
import imutils
import cv2  
import time

class Stitcher:
    def __init__(self):
        # determine if we are using OpenCV v3.X
        self.isv3 = imutils.is_cv3()

    def stitch(self, images, ratio=0.75, reprojThresh=4.0,
        showMatches=False):
        # unpack the images, then detect keypoints and extract
        # local invariant descriptors from them
        
        (imageB, imageA) = images
        start = time.time()
        (kpsA, featuresA) = self.detectAndDescribe(imageA)
        end = time.time()
        print('%.5f s' %(end-start))

        (kpsB, featuresB) = self.detectAndDescribe(imageB)

        # match features between the two images
        start = time.time()
        M = self.matchKeypoints(kpsA, kpsB,
            featuresA, featuresB, ratio, reprojThresh)
        end = time.time()
        print('%.5f s' %(end-start))


        # if the match is None, then there aren't enough matched
        # keypoints to create a panorama
        if M is None:
            return None

        # otherwise, apply a perspective warp to stitch the images
        # together
        (matches, H, status) = M
        start = time.time()
        result = cv2.warpPerspective(imageA, H,
            (imageA.shape[1] + imageB.shape[1], imageA.shape[0]))
        result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB
        end = time.time()
        print('%.5f s' %(end-start))


        # check to see if the keypoint matches should be visualized
        if showMatches:
            start = time.time()
            vis = self.drawMatches(imageA, imageB, kpsA, kpsB, matches,
                status)
            end = time.time()
            print('%.5f s' %(end-start))
            # return a tuple of the stitched image and the
            # visualization
            return (result, vis)

        # return the stitched image
        return result
    
    #接收照片,檢測關鍵點和提取局部不變特征
    #用到了高斯差分(Difference of Gaussian (DoG))關鍵點檢測,和SIFT特征提取
    #detectAndCompute方法用來處理提取關鍵點和特征
    #返回一系列的關鍵點
    def detectAndDescribe(self, image):
        # convert the image to grayscale
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

        # check to see if we are using OpenCV 3.X
        if self.isv3:
            # detect and extract features from the image
            descriptor = cv2.xfeatures2d.SIFT_create()
            (kps, features) = descriptor.detectAndCompute(image, None)

        # otherwise, we are using OpenCV 2.4.X
        else:
            # detect keypoints in the image
            detector = cv2.FeatureDetector_create("SIFT")
            kps = detector.detect(gray)

            # extract features from the image
            extractor = cv2.DescriptorExtractor_create("SIFT")
            (kps, features) = extractor.compute(gray, kps)

        # convert the keypoints from KeyPoint objects to NumPy
        # arrays
        kps = np.float32([kp.pt for kp in kps])

        # return a tuple of keypoints and features
        return (kps, features)
    #matchKeypoints方法需要四個參數,第一張圖片的關鍵點和特征向量,第二張圖片的關鍵點特征向量。
    #David Lowe’s ratio測試變量和RANSAC重投影門限也應該被提供。
    def matchKeypoints(self, kpsA, kpsB, featuresA, featuresB,
        ratio, reprojThresh):
        # compute the raw matches and initialize the list of actual
        # matches
        matcher = cv2.DescriptorMatcher_create("BruteForce")
        rawMatches = matcher.knnMatch(featuresA, featuresB, 2)
        matches = []

        # loop over the raw matches
        for m in rawMatches:
            # ensure the distance is within a certain ratio of each
            # other (i.e. Lowe's ratio test)
            if len(m) == 2 and m[0].distance < m[1].distance * ratio:
                matches.append((m[0].trainIdx, m[0].queryIdx))

        # computing a homography requires at least 4 matches
        if len(matches) > 4:
            # construct the two sets of points
            ptsA = np.float32([kpsA[i] for (_, i) in matches])
            ptsB = np.float32([kpsB[i] for (i, _) in matches])

            # compute the homography between the two sets of points
            (H, status) = cv2.findHomography(ptsA, ptsB, cv2.RANSAC,
                reprojThresh)

            # return the matches along with the homograpy matrix
            # and status of each matched point
            return (matches, H, status)

        # otherwise, no homograpy could be computed
        return None
    #連線畫出兩幅圖的匹配
    def drawMatches(self, imageA, imageB, kpsA, kpsB, matches, status):
        # initialize the output visualization image
        (hA, wA) = imageA.shape[:2]
        (hB, wB) = imageB.shape[:2]
        vis = np.zeros((max(hA, hB), wA + wB, 3), dtype="uint8")
        vis[0:hA, 0:wA] = imageA
        vis[0:hB, wA:] = imageB

        # loop over the matches
        for ((trainIdx, queryIdx), s) in zip(matches, status):
            # only process the match if the keypoint was successfully
            # matched
            if s == 1:
                # draw the match
                ptA = (int(kpsA[queryIdx][0]), int(kpsA[queryIdx][1]))
                ptB = (int(kpsB[trainIdx][0]) + wA, int(kpsB[trainIdx][1]))
                cv2.line(vis, ptA, ptB, (0, 255, 0), 1)

        # return the visualization
        return vis

 
if __name__ == '__main__': 
# load the two images and resize them to have a width of 400 pixels
# (for faster processing)
    imageA = cv2.imread('D:/test1.jpg')
    imageB = cv2.imread('D:/test2.jpg')
#imageA = imutils.resize(imageA, width=400)
#imageB = imutils.resize(imageB, width=400)
# stitch the images together to create a panorama
# showMatches=True 展示兩幅圖像特征的匹配,返回vis
    start = time.time()
    stitcher = Stitcher()
    (result, vis) = stitcher.stitch([imageA, imageB], showMatches=True)
# show the images
    end = time.time()
    print('%.5f s' %(end-start))
    cv2.imwrite('D:/vis1.jpg', vis)
    cv2.imwrite('D:/result.jpg', result)

 

六.步驟分析:

概述:

Sitich調用detectAndDescribe檢測兩張圖片里關鍵點、提取局部不變特征。

有了關鍵點和特征,sitich調matchKeypoints方法來匹配兩張圖片里的特征。如果返回匹配的M為None,就是因為現有的關鍵點不足以匹配生成全景圖。假設M不返回None,拆包返回元組,包含關鍵點匹配matches、從RANSAC算法中得到的最優單映射變換矩陣H以及最后的單映計算狀態列表status,用來表明那些已經成功匹配的關鍵點。

 

有了最優單映射變換矩陣H后,就可將兩張圖片“縫合起來”。stitch調用cv2.warpPerspective進行縫合。這樣,就返回一個拼接的圖片。

最后sitich調用drawMatches函數用來將兩張圖片關鍵點的匹配可視化

函數分析:

detectAndDescribe

detectAndDescribe方法用來接收照片,檢測關鍵點和提取局部不變特征。實現中用到了高斯差分(Difference of Gaussian (DoG))關鍵點檢測,和SIFT特征提取。

參數為單個的圖片。

返回值kps為keypoints,features為局部不變特征。

檢測是否用了OpenCV 3.X,如果是,就用cv2.xfeatures2d.SIFT_create方法來實現DoG關鍵點檢測和SIFT特征提取。detectAndCompute方法用來處理提取關鍵點和特征。

如果OpenCV2.4cv2.FeatureDetector_create方法來實現關鍵點的檢測(DoG)。detect方法返回一系列的關鍵點。用SIFT關鍵字來初始化cv2.DescriptorExtractor_create,設置SIFT特征提取。調用extractor的compute方法返回一組關鍵點周圍量化檢測的特征向量。

最后,關鍵點從KeyPoint對象轉換為NumPy數后返回給調用函數。

 

matchKeypoints

用於匹配兩張圖片的特征

matchKeypoints方法需要個參數,第一張圖片的關鍵點和特征向量,第二張圖片的關鍵點特征向量David Lowe’s ratio測試變量ratioRANSAC重投影門限reprojThresh。

返回值為matches, H, status。分別為匹配的關鍵點matches,最優單映射變換矩陣 H(3x3),單映計算的狀態列表status用於表示已經成功匹配的關鍵點。

匹配特征過程: 循環每張圖片的描述子,計算距離,最后找到每對描述子的最小距離。

因為這是計算機視覺中的一個非常普遍的做法,OpenCV已經內置了cv2.DescriptorMatcher_create方法,用來匹配特征。BruteForce參數表示我們能夠更詳盡計算兩張圖片直接的歐式距離,以此來尋找每對描述子的最短距離。

knnMatch方法是K=2的兩個特征向量的k-NN匹配(k-nearest neighbors algorithm,K近鄰算法),表明每個匹配的前兩名作為特征向量返回。之所以我們要的是匹配的前兩個而不是只有第一個,是因為我們需要用David Lowe’s ratio來測試假匹配然后做修剪。

之后,用第79行的rawMatches來計算每對描述子,但是這些描述子可能是錯誤的,也就是這是圖片不是真正的匹配。去修剪這些有誤的匹配,我們可以運用 Lowe’s ratio測試特別的來循環rawMatches,這是用來確定高質量的特征匹配。正常的Lowe’s ratio 值在[0.7,0.8].

我們用Lowe’s ratio 測試得到matches的值后,我們就可以計算這兩串關鍵點之間的單映性。 計算兩串關鍵點的單映性需要至少四個匹配。為了獲得更為可信的單映性,我們至少需要超過四個匹配點。

調用cv2.findHomography計算H和status。

 

 

drawMatches

drawMatches用來將兩張圖片關鍵點的匹配可視化。

參數為原始圖片A,B,關鍵點kpsA,kpsB,匹配的關鍵點matches以及單映的狀態列表status。

返回值為將兩張圖片中的匹配點用直線連接起來的圖片。

運用參數,我們可以通過將兩張圖片匹配的關鍵點N和關鍵點M畫直線連接,並返回包含這些直線的圖片實現可視化。

Stitch

參數

images。傳入圖片的列表,縫合在一起形成全景圖。注意傳入的圖像是從左到右的順序。如果提供的不是這樣的順序,程序仍然可以跑,但是輸出全景是不正確的。ratio ,用於特征匹配時David Lowe比率測試,reprojthresh 是RANSAC算法中最大像素“回旋的余地”,最后的showMatches,是一個布爾類型的值,用於表明是否調用drawMatches進行關鍵點匹配可視化

返回值,返回拼接好的圖片result已經用於可視化匹配關鍵點的圖片vis.

Sitich調用detectAndDescribe檢測兩張圖片里關鍵點、提取局部不變特征。

調matchKeypoints方法來匹配兩張圖片里的特征

調用cv2.warpPerspective進行縫合。需要三個參數:想要“縫合”上來的照片(本程序里的右邊的圖片);還有3*3的最優單映射轉換矩陣H;最后就是塑造出要輸出的照片。我們得到輸出圖像的寬是兩圖片之和,高即為第二張圖像的高度。

調用drawMatches函數用來將兩張圖片關鍵點的匹配可視化

 

  1. 結果展示:

給定圖片為老師提供的IMG_201901.jpg和IMG_201901.jpg,重命名為test1.jpg,test2.jpg。

輸出拼接好的圖片為rusult.jpg。

展示關鍵點匹配的圖片vis.jpg。

 

傳入圖片

 

 

 

 

 

 

 

test1.jpg

 

test2.jpg

 

 

 

 

 

 

拼接好的圖片:result.jpg

 

 

 

可視化關鍵點匹配:Vis.jpg

 

 

 

8.性能分析:

時間:

項目運行總時間304.79945 s

單張圖片detectAndDescribe檢測關鍵點、提取局部不變特征用時8.31044 s

matchKeypoints匹配兩張圖片里的特征用時292.67749 s

cv2.warpPerspective縫合圖像用時0.27427 s

drawMatches建立直線關鍵點的匹配可視化用時0.33913 s

可見matchKeypoints計算量最大

Lowe’s ratio循環消耗時間較長,同時cv2.findHomography計算最優單映矩陣H與status占用一定時間。

 

時間優化:

使用比SIFT快的SURF方法,

調節它的參數,減少一些關鍵點,只獲取64維而不是128維的向量等,加快速度。

 

圖像拼接質量:

拼接比較流暢,肉眼判斷為一張圖片。

缺陷在於看到有一條像折痕一樣的線條,這個就是兩個圖片的拼接線主要原因是光線的變化。

 

拼接質量優化:

對於銜接處存在的縫隙問題,有一個解決辦法是按一定權重疊加圖1和圖2的重疊部分,在重疊處圖2的比重是1,向着圖1的方向,越遠離銜接處,圖1的權重越來越大,圖2的權重越來越低,實現平穩過渡

 

優化:

 

時間優化:使用比SIFT快的SURF方法,使用Hessian算法檢測關鍵點。在使用SURF時,還可以調節它的參數,減少一些關鍵點,只獲取64維而不是128維的向量等,加快速度。

拼接質量優化:對第一張圖和它的重疊區做一些加權處理,重疊部分,離左邊圖近的,左邊圖的權重就高一些,離右邊近的,右邊旋轉圖的權重就高一些,然后兩者相加,實現平滑過渡。

思路和方法


思路
提取要拼接的兩張圖片的特征點、特征描述符;
將兩張圖片中對應的位置點找到,匹配起來;
如果找到了足夠多的匹配點,就能將兩幅圖拼接起來,拼接前,可能需要將第二幅圖透視旋轉一下,利用找到的關鍵點,將第二幅圖透視旋轉到一個與第一幅圖相同的可以拼接的角度;
進行拼接;
進行拼接后的一些處理,讓效果看上去更好。


實現方法
提取圖片的特征點、描述符,可以使用opencv創建一個SIFT對象,SIFT對象使用DoG方法檢測關鍵點,並對每個關鍵點周圍的區域計算特征向量。在實現時,可以使用比SIFT快的SURF方法,使用Hessian算法檢測關鍵點。因為只是進行全景圖拼接,在使用SURF時,還可以調節它的參數,減少一些關鍵點,只獲取64維而不是128維的向量等,加快速度。
在分別提取好了兩張圖片的關鍵點和特征向量以后,可以利用它們進行兩張圖片的匹配。在拼接圖片中,可以使用Knn進行匹配,但是使用FLANN快速匹配庫更快,圖片拼接,需要用到FLANN的單應性匹配。
單應性匹配完之后可以獲得透視變換H矩陣,用這個的逆矩陣來對第二幅圖片進行透視變換,將其轉到和第一張圖一樣的視角,為下一步拼接做准備。
透視變換完的圖片,其大小就是最后全景圖的大小,它的右邊是透視變換以后的圖片,左邊是黑色沒有信息。拼接時可以比較簡單地處理,通過numpy數組選擇直接把第一張圖加到它的左邊,覆蓋掉重疊部分,得到拼接圖片,這樣做非常快,但是最后效果不是很好,中間有一條分割痕跡非常明顯。使用opencv指南中圖像金字塔的代碼對拼接好的圖片進行處理,整個圖片平滑了,中間的縫還是特別突兀。
直接拼效果不是很好,可以把第一張圖疊在左邊,但是對第一張圖和它的重疊區做一些加權處理,重疊部分,離左邊圖近的,左邊圖的權重就高一些,離右邊近的,右邊旋轉圖的權重就高一些,然后兩者相加,使得過渡是平滑地,這樣看上去效果好一些,速度就比較慢。如果是用SURF來做,時間主要畫在平滑處理上而不是特征點提取和匹配。


python_opencv中主要使用的函數
基於python 3.7和對應的python-opencv
cv2.xfeatures2d.SURF_create ([hessianThreshold[, nOctaves[, nOctaveLayers[, extended[, upright]]]]])
  該函數用於生成一個SURF對象,在使用時,為提高速度,可以適當提高hessianThreshold,以減少檢測的關鍵點的數量,可以extended=False,只生成64維的描述符而不是128維,令upright=True,不檢測關鍵點的方向。
cv2.SURF.detectAndCompute(image, mask[, descriptors[, useProvidedKeypoints]])
  該函數用於計算圖片的關鍵點和描述符,需要對兩幅圖都進行計算。
flann=cv2.FlannBasedMatcher(indexParams,searchParams)
match=flann.knnMatch(descrip1,descrip2,k=2)
  flann快速匹配器有兩個參數,一個是indexParams,一個是searchParams,都用手冊上建議的值就可以。在創建了匹配器得到匹配數組match以后,就可以參考Lowe給出的參數,對匹配進行過濾,過濾掉不好的匹配。其中返回值match包括了兩張圖的描述符距離distance 、訓練圖(第二張)的描述符索引trainIdx 、查詢的圖(第一張)的描述符索引queryIdx 這幾個屬性。
M,mask=cv2.findHomography(srcPoints, dstPoints[, method[, ransacReprojThreshold[, mask]]])
  這個函數實現單應性匹配,返回的M是一個矩陣,即對關鍵點srcPoints做M變換能變到dstPoints的位置。
warpImg=cv2.warpPerspective(src,np.linalg.inv(M),dsize[,dst[,flags[,borderMode[,borderValue]]]])
  用這個函數進行透視變換,變換視角。src是要變換的圖片,np.linalg.inv(M)是④中M的逆矩陣,得到方向一致的圖片。
a=b.copy() 實現深度復制,Python中默認是按引用復制,a=b是a指向b的內存。
draw_params = dict(matchColor = (0,255,0),singlePointColor = (255,0,0),matchesMask = matchMask,flags = 2),img3 = cv2.drawMatches(img1,kp1,img2,kp2,good,None,**draw_params)
  使用drawMatches可以畫出匹配的好的關鍵點,matchMask是比較好的匹配點,之間用綠色線連接起來。

 

核心代碼

import cv2
import numpy as np
from matplotlib import pyplot as plt
import time
MIN = 10
starttime=time.time()
img1 = cv2.imread('1.jpg') #query
img2 = cv2.imread('2.jpg') #train

#img1gray=cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
#img2gray=cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
surf=cv2.xfeatures2d.SURF_create(10000,nOctaves=4,extended=False,upright=True)
#surf=cv2.xfeatures2d.SIFT_create()#可以改為SIFT
kp1,descrip1=surf.detectAndCompute(img1,None)
kp2,descrip2=surf.detectAndCompute(img2,None)

FLANN_INDEX_KDTREE = 0
indexParams = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
searchParams = dict(checks=50)

flann=cv2.FlannBasedMatcher(indexParams,searchParams)
match=flann.knnMatch(descrip1,descrip2,k=2)


good=[]
for i,(m,n) in enumerate(match):
        if(m.distance<0.75*n.distance):
                good.append(m)

if len(good)>MIN:
        src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2)
        ano_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,1,2)
        M,mask=cv2.findHomography(src_pts,ano_pts,cv2.RANSAC,5.0)
        warpImg = cv2.warpPerspective(img2, np.linalg.inv(M), (img1.shape[1]+img2.shape[1], img2.shape[0]))
        direct=warpImg.copy()
        direct[0:img1.shape[0], 0:img1.shape[1]] =img1
        simple=time.time()

#cv2.namedWindow("Result", cv2.WINDOW_NORMAL)
#cv2.imshow("Result",warpImg)
        rows,cols=img1.shape[:2]
        
        for col in range(0,cols):
            if img1[:, col].any() and warpImg[:, col].any():#開始重疊的最左端
                left = col
                break
        for col in range(cols-1, 0, -1):
            if img1[:, col].any() and warpImg[:, col].any():#重疊的最右一列
                right = col
                break

        res = np.zeros([rows, cols, 3], np.uint8)
        for row in range(0, rows):
            for col in range(0, cols):
                if not img1[row, col].any():#如果沒有原圖,用旋轉的填充
                    res[row, col] = warpImg[row, col]
                elif not warpImg[row, col].any():
                    res[row, col] = img1[row, col]
                else:
                    srcImgLen = float(abs(col - left))
                    testImgLen = float(abs(col - right))
                    alpha = srcImgLen / (srcImgLen + testImgLen)
                    res[row, col] = np.clip(img1[row, col] * (1-alpha) + warpImg[row, col] * alpha, 0, 255)

        warpImg[0:img1.shape[0], 0:img1.shape[1]]=res
        final=time.time()
        img3=cv2.cvtColor(direct,cv2.COLOR_BGR2RGB)
        plt.imshow(img3,),plt.show()
        img4=cv2.cvtColor(warpImg,cv2.COLOR_BGR2RGB)
        plt.imshow(img4,),plt.show()
        print("simple stich cost %f"%(simple-starttime))
        print("\ntotal cost %f"%(final-starttime))
        cv2.imwrite("simplepanorma.png",direct)
        cv2.imwrite("bestpanorma.png",warpImg)
        
else:
        print("not enough matches!")

效果圖:

 

 

 

 

 

 

參考

 

https://www.pyimagesearch.com/

https://cloud.tencent.com/developer/article/1178958

 

 https://blog.csdn.net/qq_37734256/article/details/86745451

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM