不用PS 摳圖,Python + OpenCV 自動實現海報場景替換!

有一個問題,就下面圖片中的兩本書而言,怎樣快速讓中間邊的書本與左邊書本對齊(最終效果能實現兩張圖片重疊(最終結果在最右邊)),進行的圖像轉變可旋轉、平移、縮放、形變。

對齊.png

本文介紹 利用Opencv 如何解決這個問題,解決這個問題一般需要三步:

  • 確定至少四組對應點座標

  • 找到一個轉換矩陣;

  • 把找到的轉換矩陣應用到 Moving Image 上,實現圖像對齊;

上面提到的圖片旋轉、平移、縮放等操作的主要目的,就是要最終實現兩圖像中點對點一一映射關係,點的映射本質上就是像素點的空間轉換

Snipaste_2020-05-13_23-08-11.jpg

圖中標記了其中四組對應點,分別標爲不同的顏色,分別標爲紅、橙、黃和綠四種顏色;比如這裏的A(x1,y1)A(x_1,y_1)A1(x2,y2)A_1 (x_2,y_2) 是就是一組對應點,圖片經過轉換之後 $A_1 $ 點 必須映射到 AA 點位置。

涉及圖片中點座標變換,都需要藉助於 矩陣 運算,這裏探究的圖像維度都屬於二維,座標只需要x,yx,y 即可

面向此類轉換問題,Homography 轉換 ( 3 × 3 矩陣) 可用於解決此類轉化問題,用來解決點對點映射問題,Homography 矩陣可寫作下列方式:
H=[h00h01h02h10h11h12h20h21h22] H = \left[ \begin{matrix} h_{00}& h_{01} & h_{02}\\ h_{10} & h_{11}&h_{12}\\ h_{20} & h_{21}&h_{22}\\ \end{matrix} \right]
A(x1,y1)A(x_1 ,y_1)A1(x2,y2)A_1(x_2,y_2) 作爲對應點,則 Homography $ H $ 的的應用 如下:
[x1y11]=H[x2y21]=[h00h01h02h10h11h12h20h21h22][x2y21] \left[ \begin{matrix} x_1\\ y_1\\ 1\\ \end{matrix} \right] = H\left[ \begin{matrix} x_2\\ y_2\\ 1\\ \end{matrix} \right] = \left[ \begin{matrix} h_{00} & h_{01} & h_{02}\\ h_{10} & h_{11} & h_{12}\\ h_{20} & h_{21} & h_{22}\\ \end{matrix} \right]\left[ \begin{matrix} x_2\\ y_2\\ 1\\ \end{matrix} \right]
而矩陣 H 參數的確定至少需要 4 組對應點,因此在計算 H 時至少要找到 4 組對應點;找到的對應點組數越多,計算得到的 H 會越精確,最終的轉換效果也就會越好。

下面用 Opencv + Python 來實現上面圖片中的書籍的對齊,

import cv2
import numpy as np

if __name__ =='__main__':
    
    #圖片讀取
    img_src = cv2.imread("D:/book2.jpg")
    position_src = np.array([[141,131],[480,159],[493,630],[64,601]],dtype = float)

    img_dst = cv2.imread("D:/book1.jpg")
    position_dst = np.array([[318,256],[543,372],[316,670],[73,473]],dtype = float)
    
    #計算轉換矩陣
    h,status = cv2.findHomography(position_src,position_dst)

    #對圖片進行仿射變換
    out_img = cv2.warpPerspective(img_src,h,(img_dst.shape[1],img_dst.shape[0]))

    #Display images;
    cv2.imshow("Source image",img_src)
    cv2.imshow("Destination Image",img_dst)
    cv2.imshow("Warped Source Image",out_img)

    cv2.waitKey(0)

這裏事先已經確定好對應的四個點的座標,然後把這四個點的座標帶入 cv2.findHomography() 計算出轉換矩陣,最後把矩陣應用到兩圖像中,得到最終的轉換結果,

對齊.png

這裏提醒一點,warpPerspective 函數進行對圖像像素進行矩陣變換時,隱藏了一個參數 Interpolator ,默認爲線性插值,功能是防止像素點像素值缺失

上面小案例不方便的一點需要確定對應四個點的座標,這個步驟是比較繁瑣的,下面案例將在程序中加入交互功能,實現某個圖片的自動標記點收集、標記點點轉換:

imag11252323.gif

首先需要準備兩張圖片,其中一張爲海報,一張爲需要替換的海報;關於確定點的座標時,被替換的圖片的座標非常好確定,只需知道圖片的長寬即可;

但的海報圖像區域四個點是不好確定的, 這裏利用 Opencv 的鼠標回調函數,監視鼠標響應,根據用戶點擊來收集 PIck 得到的座標;

def mouse_handler(event,x,y,flags,data):
    if event ==cv2.EVENT_LBUTTONDOWN:
        cv2.circle(data['im'],(x,y),3,(0,0,255),5,16)
        cv2.namedWindow("Image",0)
        cv2.imshow("Image",data['im'])
        if len(data['points']) <4:
            data['points'].append([x,y])

def get_four_points(im):

    data = {}
    data['im'] = im.copy()
    data['points'] = []
    # Set the callback function for any mouse event
    cv2.namedWindow("Image", 0)
    cv2.imshow('Image',im)
    #請注意你標記點的數據,是順時針,需要與pst_src 方向一致
    cv2.setMouseCallback("Image",mouse_handler,data)
    cv2.waitKey(0)
    # Convert array to np.array
    #豎直方向堆疊起來;;;
    points = np.vstack(data['points']).astype(float)
    return points

座標確定以後,接下來就很簡單了,跟上個案例一樣,計算變換矩陣,矩陣應用到圖像旋轉,最終更換海報內容也就輕鬆完成啦,
Snipaste_2020-05-23_00-02-47.jpg
需要注意一點,座標 Pick 點的順序須與記錄替換圖像頂點順序一致,否則轉換圖會有偏差,案例完整代碼如下:

if __name__ =='__main__':

    img_src = cv2.imread("D:/first-image.jpg")
    size = img_src.shape
    # 取得四個座標
    pst_src = np.array(
        [
            [0,0],[size[1]-1,0],
            [size[1]-1,size[0]-1],
            [0,size[0]-1]
         ],dtype=float
    )

    #Read the destination image
    img_dst = cv2.imread("D:/times-square.jpg")

    print("Click on four corners of bllboard and the press ENTER")
    four_point  = get_four_points(img_dst)

    # Calculate  Homography between  source and destination points
    h,status = cv2.findHomography(pst_src,four_point)

    im_temp = cv2.warpPerspective(img_src,h,(img_dst.shape[1],img_dst.shape[0]))

    cv2.fillConvexPoly(img_dst,four_point.astype(int),0,16)

    #add wraped source image to destination image

    img_dst = img_dst + im_temp
    cv2.namedWindow("Image", 0)
    cv2.imshow("Image",img_dst)
    cv2.waitKey(0)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章