一、算法目的
在同一位置拍攝兩張以上圖片,這些圖片是單應性相關的,即圖片之間有相同的拍攝區域。基於此將圖片進行縫補,拼成一個大的圖像來創建全景圖像。
二、基本原理
要實現兩張圖片的簡單拼接,其實只需找出兩張圖片中相似的點 (至少四個,因為 homography 矩陣的計算需要至少四個點), 計算一張圖片可以變換到另一張圖片的變換矩陣 (homography 單應性矩陣),用這個矩陣把那張圖片變換后放到另一張圖片相應的位置 ,就是相當於把兩張圖片中定好的四個相似的點重合在一起。如此,就可以實現簡單的全景拼接。
實現步驟
- 讀入連續圖片並使用SIFT特征查找匹配對應點對
import sift
#sift程序應與運行圖片在同一文件夾下
featname = ['C:/Users/Administrator/Documents/python/jmu'+str(i+1)+'.sift' for i in range(5)]
imname = ['C:/Users/Administrator/Documents/python/jmu'+str(i+1)+'.jpg' for i in range(5)]
l = {}
d = {}
for i in range(5):
sift.process_image(imname[i],featname[i])
l[i],d[i] = sift.read_features_from_file(featname[i])
matches = {}
for i in range(4):
matches[i] = sift.match(d[i+1],d[i])
sift算子存在錯誤的匹配點,因此需要用Ransac算法剔除錯誤匹配點。
- 利用RANSAC算法計算變換矩陣
2.1 RANSAC算法
RANSAC是"RANdom SAmple Consensus"(隨機一致采樣)的縮寫。該方法是用來找到正確模型來擬合帶有噪聲數據的迭代方法。給定一個模型,例如點集之間的單應性矩陣。基本的思想是:數據中包含正確的點和噪聲點,合理的模型應該能夠在描述正確數據點的同時摒棄噪聲點。
示例
一個簡單的例子是從一組觀測數據中找出合適的2維直線。假設觀測數據中包含局內點和局外點,其中局內點近似的被直線所通過,而局外點遠離於直線。簡單的最小二乘法不能找到適應於局內點的直線,原因是最小二乘法盡量去適應包括局外點在內的所有點。相反,RANSAC能得出一個僅僅用局內點計算出模型,並且概率還足夠高。但是,RANSAC並不能保證結果一定正確,為了保證算法有足夠高的合理概率,我們必須小心的選擇算法的參數。
概述
RANSAC算法的輸入是一組觀測數據,一個可以解釋或者適應於觀測數據的參數化模型,一些可信的參數。 通過反復選擇數據中的一組隨機子集來達成目標。被選取的子集被假設為局內點,並用下述方法進行驗證:
(1) 先隨機假設一小組局內點為初始值。然后用此局內點擬合一個模型,此模型適應於假設的局內點,所有的未知參數都能從假設的局內點計算得出。
(2) 用1中得到的模型去測試所有的其它數據,如果某個點適用於估計的模型,認為它也是局內點,將局內點擴充。
(3) 如果有足夠多的點被歸類為假設的局內點,那么估計的模型就足夠合理。
(4) 用所有假設的局內點去重新估計模型,因為此模型僅僅是在初始的假設的局內點估計的,后續有擴充后,需要更新。
(5)通過估計局內點與模型的錯誤率來評估模型。
整個這個過程為迭代一次,此過程被重復執行固定的次數,每次產生的模型有兩個結局:
a. 因為局內點太少,效果不如前一次的模型,而被舍棄,
b. 存在比現有的更好的模型而被選用。
求解單應性矩陣:
class RansacModel(object):
""" Class for testing homography fit with ransac.py from
http://www.scipy.org/Cookbook/RANSAC"""
def __init__(self,debug=False):
self.debug = debugdef fit(self, data):
""" 計算選取四個對應的單應性矩陣 """
# 將其轉置,來調用H_from_points()計算單應性矩陣
data = data.T
#映射的起始點
fp = data[:3,:4]
# 映射的目標點
tp = data[3:,:4]
#計算單應性矩陣然后返回
return H_from_points(fp,tp)
def get_error( self, data, H):
""" 對所有的對應計算單應性矩陣,然后對每個變換后的點,返回相應的誤差 """
data = data.T
#映射的起始點
fp = data[:3]
# 映射的目標點
tp = data[3:]
#變換fp
fp_transformed = dot(H,fp)
#歸一化齊次坐標
for i in range(3):
fp_transformed[i] /= fp_transformed[2]
return sqrt( sum((tp-fp_transformed)**2,axis=0) )
以上可看出,這個類包含fit()方法,僅接受由ransac算法選擇的4個對應點對(data中的前4個點對),然后擬合一個單應性矩陣。get_error()方法對每個對應點對使用該單應性矩陣,然后返回相應的平方距離之和。因此ransac算法能夠判定正確與錯誤的點。在實際中,還需在距離上使用一個閾值來決定合理的單應性矩陣是哪些。
3.進行圖像拼接
估計出圖像間的單應性矩陣后,需要將所有的圖像扭曲到一個公共的圖像平面上。通常,這里的公共平面為中心圖像平面(否則需要進行大量變形)。一種方法是創建一個很大的圖像,比如圖像中全部填充0,使其和中心圖像平行,然后將所有的圖像扭曲到上面。由於我所有的圖像是由照相機水平旋轉拍攝的,因此可使用一個較簡單的步驟:將中心圖像左邊或右邊的區域填充0,以便為扭曲的圖像騰出空間。
運行代碼
from pylab import *
from numpy import *
from PIL import Image
# If you have PCV installed, these imports should work
from PCV.geometry import homography, warp
from PCV.localdescriptors import sift
"""
This is the panorama example from section 3.3.
"""
# set paths to data folder
featname = ['C:/Users/Administrator/Documents/python/jmu'+str(i+1)+'.sift' for i in range(5)]
imname = ['C:/Users/Administrator/Documents/python/jmu'+str(i+1)+'.jpg' for i in range(5)]
# extract features and match
l = {}
d = {}
for i in range(5):
sift.process_image(imname[i],featname[i])
l[i],d[i] = sift.read_features_from_file(featname[i])
matches = {}
for i in range(4):
matches[i] = sift.match(d[i+1],d[i])
# visualize the matches (Figure 3-11 in the book)
for i in range(4):
im1 = array(Image.open(imname[i]))
im2 = array(Image.open(imname[i+1]))
figure()
sift.plot_matches(im2,im1,l[i+1],l[i],matches[i],show_below=True)
# function to convert the matches to hom. points
def convert_points(j):
ndx = matches[j].nonzero()[0]
fp = homography.make_homog(l[j+1][ndx,:2].T)
ndx2 = [int(matches[j][i]) for i in ndx]
tp = homography.make_homog(l[j][ndx2,:2].T)
# switch x and y - TODO this should move elsewhere
fp = vstack([fp[1],fp[0],fp[2]])
tp = vstack([fp[1],fp[0],fp[2]])
return fp,tp
# estimate the homographies
model = homography.RansacModel()
fp,tp = convert_points(1)
H_12 = homography.H_from_ransac(fp,tp,model)[0] #im 1 to 2
fp,tp = convert_points(0)
H_01 = homography.H_from_ransac(fp,tp,model)[0] #im 0 to 1
tp,fp = convert_points(2) #NB: reverse order
H_32 = homography.H_from_ransac(fp,tp,model)[0] #im 3 to 2
tp,fp = convert_points(3) #NB: reverse order
H_43 = homography.H_from_ransac(fp,tp,model)[0] #im 4 to 3
# warp the images
delta = 1200 # for padding and translation
im1 = array(Image.open(imname[1]), "uint8")
im2 = array(Image.open(imname[2]), "uint8")
im_12 = warp.panorama(H_12,im1,im2,delta,delta)
im1 = array(Image.open(imname[0]), "f")
im_02 = warp.panorama(dot(H_12,H_01),im1,im_12,delta,delta)
im1 = array(Image.open(imname[3]), "f")
im_32 = warp.panorama(H_32,im1,im_02,delta,delta)
im1 = array(Image.open(imname[4]), "f")
im_42 = warp.panorama(dot(H_32,H_43),im1,im_32,delta,2*delta)
figure()
imshow(array(im_42, "uint8"))
axis('off')
savefig("example.png",dpi=300)
show()
三、實現結果
3.1 室內場景
初始圖像
拼接后的圖像
室內圖片拼接圖片中出現問題。
分析:
1、分割明顯
2、照片歪斜嚴重
3、物體變形嚴重
原因分析:
1、建築物相似
2、角度選取不佳
3、第二張和最后一張明顯的匹配標志物
3.2 室外場景
原始圖像
拼接圖
原始圖像
拼接后的圖
可從圖片看出,合並效果並不好,畫圈處有明顯的顏色區分。
對於圖片拼接不自然問題,應進行圖片融合。理論上可使用Graph cuts方法解決。
————————————————
版權聲明:本文為CSDN博主「-Dghly」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_42399848/article/details/88902881