單應性變換
單應性變換是指一個平面上的點通過一個矩陣變換映射到另一個平面上的點,這個變換矩陣是一個 3 × 3 3 \times 3 3×3 的矩陣,稱為單應性矩陣。單應性變換可以分為仿射變換和投影變換兩種類型。
仿射變換
在單應性變換中,仿射變換是其中一種特殊的變換。仿射變換是指在變換前后,保持原來的平行線還是平行線,并且保持原來的比例關系不變。一個二維平面上的仿射變換可以表示為:
[ x ′ y ′ 1 ] = [ a b c d e f 0 0 1 ] [ x y 1 ] \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} = \begin{bmatrix} a & b & c \\ d & e & f \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} ?x′y′1? ?= ?ad0?be0?cf1? ? ?xy1? ?
其中, ( x , y ) (x,y) (x,y) 是原來平面上的一個點, ( x ′ , y ′ ) (x',y') (x′,y′) 是變換后平面上的對應點。矩陣中的參數(shù) a , b , c , d , e , f a,b,c,d,e,f a,b,c,d,e,f 決定了變換的效果。
具體地,仿射變換包括平移、旋轉、縮放和錯切四種基本變換。其中,平移可以通過將矩陣中的 c c c 和 f f f 分別設置為平移的距離 t x t_x tx? 和 t y t_y ty? 來實現(xiàn);旋轉可以通過將矩陣中的 a a a 和 d d d 設置為 cos ? θ \cos \theta cosθ 和 sin ? θ \sin \theta sinθ, b b b 和 e e e 設置為 ? sin ? θ -\sin \theta ?sinθ 和 cos ? θ \cos \theta cosθ 來實現(xiàn);縮放可以通過將矩陣中的 a a a 和 e e e 分別設置為 s x s_x sx? 和 s y s_y sy? 來實現(xiàn);錯切可以通過將矩陣中的 b b b 設置為 k x k_x kx? 或 d d d 設置為 k y k_y ky? 來實現(xiàn)。其中, t x t_x tx? 和 t y t_y ty? 表示平移的距離, θ \theta θ 表示旋轉的角度, s x s_x sx? 和 s y s_y sy? 表示縮放的比例, k x k_x kx? 和 k y k_y ky? 表示錯切的程度。
圖像扭曲實現(xiàn)
以下是原圖:
import cv2
import numpy as np
# 讀取圖片
img = cv2.imread('background/background.jpg')
# 圖像的高和寬
rows, cols = img.shape[:2]
# 設置扭曲前后的三個點的坐標
pts1 = np.float32([[0, 0], [cols - 1, 0], [0, rows - 1]])
pts2 = np.float32([[cols * 0.2, rows * 0.1], [cols * 0.9, rows * 0.2], [cols * 0.1, rows * 0.9]])
# 生成變換矩陣
M = cv2.getAffineTransform(pts1, pts2)
# 進行仿射變換
dst = cv2.warpAffine(img, M, (cols, rows))
# 顯示原圖和扭曲后的圖像
cv2.imshow('image', img)
cv2.imshow('affine', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
扭轉后的圖片:
圖像嵌入(圖中圖)
被嵌入圖像:
嵌入至以下圖像中:
# -*- coding: utf-8 -*-
import cv2
from numpy import array
from PCV.geometry import warp
from PIL import Image
from pylab import *
from scipy import ndimage
# 讀取兩張灰度圖像
im1 = cv2.imread(r'background/in.jpg', cv2.IMREAD_GRAYSCALE)
im2 = cv2.imread(r'background/background.jpg', cv2.IMREAD_GRAYSCALE)
# 設置仿射變換的四個點
tp = array([[164,538,540,264],[40,36,405,405],[1,1,1,1]])
# 進行仿射變換
im3 = warp.image_in_image(im1,im2,tp)
# 顯示三張圖像
figure()
gray()
# 顯示第一張圖像
imshow(im1)
show()
# 顯示第二張圖像
imshow(im2)
show()
# 顯示第三張圖像
imshow(im3)
show()
嵌入后的結果:
RANSAC算法
算法介紹
RANSAC(Random Sample Consensus)是一種基于隨機采樣的迭代算法,用于估計數(shù)據(jù)集中的模型參數(shù)。它主要用于從一組有噪聲的數(shù)據(jù)中估計出一個最優(yōu)的數(shù)學模型。
下面是 RANSAC 算法的基本流程:
-
隨機采樣:從原始數(shù)據(jù)中隨機選擇一定數(shù)量的樣本來構造一個初始模型,這個初始模型可以用來進行后續(xù)的計算。
-
模型擬合:使用所選樣本來擬合一個數(shù)學模型。
-
內點選擇:計算每個數(shù)據(jù)點到所估計的模型的距離,如果距離小于給定的閾值,則將該數(shù)據(jù)點視為“內點”,否則將其視為“外點”。
-
判斷收斂:判斷是否有足夠的內點,如果內點數(shù)目達到了一定的閾值,就認為模型已經收斂。
-
重復以上步驟:重復以上步驟,直到滿足收斂條件或達到預先設定的最大迭代次數(shù)。
圖片收集
圖片就地取材,在宿舍后方連續(xù)拍攝2張可拼接的圖片:
無RANSAC優(yōu)化和有RANSAC優(yōu)化的代碼實現(xiàn)
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
def blend_images(s, t):
# 給圖片加邊框
s = cv.copyMakeBorder(s, 50, 50, 0, 250, cv.BORDER_CONSTANT, value=(0,0,0))
t = cv.copyMakeBorder(t, 50, 50, 0, 250, cv.BORDER_CONSTANT, value=(0,0,0))
# 獲取圖片大小
w,h = s.shape[:2]
# 轉換為灰度圖
g1 = cv.cvtColor(s, cv.COLOR_BGR2GRAY)
g2 = cv.cvtColor(t, cv.COLOR_BGR2GRAY)
# 創(chuàng)建SIFT對象
sift = cv.SIFT_create()
# 檢測關鍵點和計算描述符
k1, d1 = sift.detectAndCompute(g1, None)
k2, d2 = sift.detectAndCompute(g2, None)
# 創(chuàng)建FLANN匹配器
F = cv.FlannBasedMatcher(dict(algorithm=1, trees=5), dict(checks=50))
# 匹配關鍵點
m = F.knnMatch(d1, d2, k=2)
mask = [[0, 0] for _ in range(len(m))]
# 根據(jù)閾值篩選匹配點
l1 = [j for i, (j, k) in enumerate(m) if j.distance < 0.7 * k.distance]
mask = [[1, 0] if j.distance < 0.7 * k.distance else [0, 0] for i, (j, k) in enumerate(m)]
# 獲取圖片大小
r, c = s.shape[:2]
# 如果匹配點大于10個,計算單應性矩陣
if len(l1) > 10:
sc = np.float32([k1[m.queryIdx].pt for m in l1]).reshape(-1, 1, 2)
ds = np.float32([k2[m.trainIdx].pt for m in l1]).reshape(-1, 1, 2)
M, mask = cv.findHomography(sc, ds, cv.RANSAC, 4.0)
MM = np.array(M)
# 透視變換
wimg = cv.warpPerspective(t, np.array(MM), (t.shape[1], t.shape[0]), flags=cv.WARP_INVERSE_MAP)
# 獲取左右邊界
left = next(col for col in range(0, c) if s[:, col].any() and wimg[:, col].any())
right = next(col for col in range(c-1, 0, -1) if s[:, col].any() and wimg[:, col].any())
# 創(chuàng)建結果圖像
res = np.zeros([r, c, 3], np.uint8)
for row in range(0, r):
for col in range(0, c):
if not s[row, col].any():
res[row, col] = wimg[row, col]
elif not wimg[row, col].any():
res[row, col] = s[row, col]
else:
slen = float(abs(col - left))
tlen = float(abs(col - right))
alpha = slen / (slen + tlen)
# 混合圖像
res[row, col] = np.clip(s[row, col] * (1-alpha) + wimg[row, col] * alpha, 0, 255)
# 以下為不做ransac處理的結果結果
M0, mask0 = cv.findHomography(sc, ds, cv.RANSAC, 0)
MM0 = np.array(M0)
wimg0 = cv.warpPerspective(t, np.array(MM0), (t.shape[1], t.shape[0]), flags=cv.WARP_INVERSE_MAP)
res0 = np.zeros([r, c, 3], np.uint8)
for row in range(0, r):
for col in range(0, c):
if not s[row, col].any():
res0[row, col] = wimg0[row, col]
elif not wimg0[row, col].any():
res0[row, col] = s[row, col]
else:
slen = float(abs(col - left))
tlen = float(abs(col - right))
alpha = slen / (slen + tlen)
# 混合圖像
res0[row, col] = np.clip(s[row, col] * (1-alpha) + wimg0[row, col] * alpha, 0, 255)
return res, res0
# 讀取左右兩張圖片
i1 = cv.imread(r'left.jpg')
i2 = cv.imread(r'right.jpg')
# 調整圖片大小
i1 = cv.resize(i1,(756,1008))
i2 = cv.resize(i2,(756,1008))
res, res0 = blend_images(i1, i2)
# 展示結果
plt.subplot(1, 2, 1)
plt.imshow(cv.cvtColor(res0, cv.COLOR_BGR2RGB))
plt.title('res0')
plt.subplot(1, 2, 2)
plt.imshow(cv.cvtColor(res, cv.COLOR_BGR2RGB))
plt.title('res')
plt.show()
# 保存結果
cv.imwrite('res0.jpg', res0)
cv.imwrite('res.jpg', res)
生成的全景結果:
差別
理論上這兩種方法應該會存在些許差別,但是通過這次實驗我們很難看出,當然我在本機上也運行過其他圖片,拼接的結果都幾乎相似。文章來源:http://www.zghlxwxcb.cn/news/detail-444463.html
總結
本次實驗主要涉及到計算機視覺領域中圖像特征提取、匹配、單應性矩陣估計以及圖像融合等方面的內容。這一過程中,我們使用了一些常用的庫和工具,例如numpy、cv2和matplotlib等。通過學習本實驗,我對圖像拼接的原理、方法和實現(xiàn)方式有了更深入的了解和認識,同時也掌握了使用OpenCV進行圖像拼接的基本技能。文章來源地址http://www.zghlxwxcb.cn/news/detail-444463.html
到了這里,關于計算機視覺--圖像拼接的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!