天天看點

計算機視覺第三章 圖像到圖像的映射一、單應性變換二、圖像扭曲

文章目錄

  • 一、單應性變換
    • 直接線性變換
    • 仿射變換
  • 二、圖像扭曲
    • 圖像中的圖像
    • 使用三角形仿射扭曲效果

一、單應性變換

單應性變換時将一個平面内的點映射到另一個平面内的二維碼投影變換。本質上,單應性變換H,按照下面的方程映射二維中的點:

計算機視覺第三章 圖像到圖像的映射一、單應性變換二、圖像扭曲

或者

計算機視覺第三章 圖像到圖像的映射一、單應性變換二、圖像扭曲

單應性矩陣H僅以來尺度定義,是以它有8個獨立的自由度。通常使用w=1來歸一化點,使點具有唯一的圖像坐标x和y。這個額外的坐标使得我們可以簡單地使用一個矩陣來表示變換。

直接線性變換

單應性矩陣可以由兩幅圖像(或者平面)中對對應對計算出來。已知一個完全射影變換具有8個自由度。根據對應點限制,每個對應點對可以寫出兩個方程,分别對應于x和y坐标。是以,計算單應性矩陣H需要4個對應點對。

DLT,直線線性變換使給定4個或者橫奪對應點對矩陣,來計算單應性矩陣H的算法。将單應性矩陣作用在對應點對上,重新寫出改方程,可以得到下面的方程:

計算機視覺第三章 圖像到圖像的映射一、單應性變換二、圖像扭曲

或者Ah=0,其中A是一個具有對應點對二倍數量行數的矩陣。将這些對應點對方程的系數堆疊到一個矩陣中,我們可以使用SVD算法找到H的最小二乘解。

def H_from_points(fp, tp):
    """使用線性DLT方法,計算單應性矩陣H,使fp映射到tp。點自動進行歸一化"""

    if fp.shape != tp.shape:
        raise RuntimeError('number of points do not match')

    # 對點進行歸一化(對數值計算很重要)
    # --- 映射起始點 ---
    m = mean(fp[:2], axis=1)
    maxstd = max(std(fp[:2], axis=1)) + 1e-9
    C1 = diag([1 / maxstd, 1 / maxstd, 1])
    C1[0][2] = -m[0] / maxstd
    C1[1][2] = -m[1] / maxstd
    fp = dot(C1, fp)

    # --- 映射對應點 ---
    m = mean(tp[:2], axis=1)
    maxstd = max(std(tp[:2], axis=1)) + 1e-9
    C2 = diag([1 / maxstd, 1 / maxstd, 1])
    C2[0][2] = -m[0] / maxstd
    C2[1][2] = -m[1] / maxstd
    tp = dot(C2, tp)

    # 建立用于線性方法的矩陣,對于每個對應對,在矩陣中會出現兩行數值
    nbr_correspondences = fp.shape[1]
    A = zeros((2 * nbr_correspondences, 9))
    for i in range(nbr_correspondences):
        A[2 * i] = [-fp[0][i], -fp[1][i], -1, 0, 0, 0,
                    tp[0][i] * fp[0][i], tp[0][i] * fp[1][i], tp[0][i]]
        A[2 * i + 1] = [0, 0, 0, -fp[0][i], -fp[1][i], -1,
                        tp[1][i] * fp[0][i], tp[1][i] * fp[1][i], tp[1][i]]

    U, S, V = linalg.svd(A)
    H = V[8].reshape((3, 3))

    # 反歸一化
    H = dot(linalg.inv(C2), dot(H, C1))

    # 歸一化,然後傳回
    return H / H[2, 2]
           

仿射變換

由于仿射變換有6個自由度,是以我們需要三個對應點對來估計矩陣H。通過将最後兩個元素設定為0,仿射變換可以用上面的DLT算法估計得出。

下面是計算仿射矩陣H的方法。其中tp是變換後的坐标,fp是變換前的坐标,通過計算H,使得tp是fp通過仿射變換矩陣H得到的,然後傳回H。

def Haffine_from_points(fp, tp):
    """計算H仿射變換,使得tp是fp經過仿射變換H得到的"""

    if fp.shape != tp.shape:
        raise RuntimeError('number of points do not match')

    # 對點進行歸一化(對數值計算很重要)
    # --- 映射起始點 ---
    m = mean(fp[:2], axis=1)
    maxstd = max(std(fp[:2], axis=1)) + 1e-9
    C1 = diag([1 / maxstd, 1 / maxstd, 1])
    C1[0][2] = -m[0] / maxstd
    C1[1][2] = -m[1] / maxstd
    fp_cond = dot(C1, fp)

    # --- 映射對應點 ---
    m = mean(tp[:2], axis=1)
    C2 = C1.copy()  # 兩個點集,必須都進行相同的縮放
    C2[0][2] = -m[0] / maxstd
    C2[1][2] = -m[1] / maxstd
    tp_cond = dot(C2, tp)

    # 因為歸一化後點的均值為0,是以平移量為0
    A = concatenate((fp_cond[:2], tp_cond[:2]), axis=0)
    U, S, V = linalg.svd(A.T)

    # 如Hartley和Zisserman著的Multiplr View Geometry In Computer,Scond Edition所示,
    # 建立矩陣B和C
    tmp = V[:2].T
    B = tmp[:2]
    C = tmp[2:4]

    tmp2 = concatenate((dot(C, linalg.pinv(B)), zeros((2, 1))), axis=1)
    H = vstack((tmp2, [0, 0, 1]))

    # 反歸一化
    H = dot(linalg.inv(C2), dot(H, C1))

    return H / H[2, 2]
           

二、圖像扭曲

對圖像塊應用仿射變換,我們将其稱為圖像扭曲。

from array import array
from numpy import *
from matplotlib.pyplot import *
from scipy import ndimage
from PIL import Image

im = array(Image.open('image1.jpg').convert('L'))
H = array([[1.4,0.05,-100],[0.05,1.5,-100],[0,0,1]])
im2 = ndimage.affine_transform(im, H[:2,:2],(H[0,2],H[1,2]))

gray()
subplot(121)
imshow(im)
axis('off')
subplot(122)
imshow(im2)
axis('off')
show()
           
計算機視覺第三章 圖像到圖像的映射一、單應性變換二、圖像扭曲

輸出圖像結果中丢失的像素用0來填充。

圖像中的圖像

仿射扭曲的一個簡單的例子是,将圖像或者圖像的一部分放置在另一幅圖像中,使得它們能夠和指定的區域或者标記物對齊。

from numpy import *
from matplotlib.pyplot import *
from numpy import array
from scipy import ndimage
from PIL import Image
import warp

im1 = array(Image.open('cup.jpg').convert('L'))
im2 = array(Image.open('image1.jpg').convert('L'))

gray()
subplot(131)
imshow(im1)
axis('equal')
axis('off')
subplot(132)
imshow(im2)
axis('equal')
axis('off')

# 4個點的y,x坐标
tp = array([[500, 680, 680, 500], [80, 80, 200, 200], [1, 1, 1, 1]])

im3 = warp.image_in_image(im1, im2, tp)
subplot(133)
imshow(im3)
axis('equal')
axis('off')
show()
           

測試将杯子放到桌子上

計算機視覺第三章 圖像到圖像的映射一、單應性變換二、圖像扭曲

通過標明的四個點來确定杯子圖檔在桌子圖檔中的坐标,在調整時可以通過輸出坐标系來确定大概的坐标範圍。

使用三角形仿射扭曲效果

對于三個點,仿射變換可以将一幅圖像進行扭曲,使這三對對應點可以完美地比對上。這是因為仿射變換有6個自由度,三個對應點對可以給出6個限制條件。是以我們也可以嘗試将圖像分成兩個三角形,然後對他們分别進行圖像扭曲操作。

from array import array

from numpy import *
from matplotlib.pyplot import *
from scipy import ndimage
from PIL import Image
import warp
import homography

im1 = array(Image.open('McDonald.jpg').convert('L'))
im2 = array(Image.open('advertise.jpg').convert('L'))

# 標明im1角上的一些點
print(im1.shape)
m, n = im1.shape[:2]

# 麥當勞的4個頂點坐标
fp = array([[0, m, m, 0], [0, 0, n, n], [1, 1, 1, 1]])
# 廣告牌的坐标
tp = array([[135, 305, 345, 175], [75, 70, 470, 460], [1, 1, 1, 1]])

# 第一個三角形
tp2 = tp[:, :3]  # 前三個點
fp2 = fp[:, :3]

# 計算H
H = homography.Haffine_from_points(tp2, fp2)
im1_t = ndimage.affine_transform(im1, H[:2, :2],
                                 (H[0, 2], H[1, 2]), im2.shape[:2])

# 三角形的alpha
alpha = warp.alpha_for_triangle(tp2, im2.shape[0], im2.shape[1])
im3 = (1 - alpha) * im2 + alpha * im1_t

# 第二個三角形
tp2 = tp[:, [0, 2, 3]]   # 第1、3、4個坐标
fp2 = fp[:, [0, 2, 3]]

# 計算H
H = homography.Haffine_from_points(tp2, fp2)
im1_t = ndimage.affine_transform(im1, H[:2, :2],
                                 (H[0, 2], H[0, 2]), im2.shape[:2])

# 三角形的alpha圖像
alpha = warp.alpha_for_triangle(tp2, im2.shape[0], im2.shape[1])
im4 = (1 - alpha) * im3 + alpha * im1_t

figure()
gray()
imshow(im4)
axis("equal")
axis("off")
show()

           

下面要講麥當勞的圖檔放到廣告牌上

計算機視覺第三章 圖像到圖像的映射一、單應性變換二、圖像扭曲
計算機視覺第三章 圖像到圖像的映射一、單應性變換二、圖像扭曲
計算機視覺第三章 圖像到圖像的映射一、單應性變換二、圖像扭曲

對于整幅圖像,使用放射扭曲效果沒有使用三角形仿射變換的好。簡單為每個三角形建立alpha圖像,然後将所有的圖像合并起來。該三角形的alpha圖像可以簡單地通過檢查像素的坐标是否能夠寫成三角形頂點坐标的凸組合來計算得出。通過将圖檔分成兩個三角形來仿射扭曲成功将圖檔帖滿廣告牌。

繼續閱讀