コンピュータビジョン----画像のスティッチング

 1. はじめに

画像スティッチングは、現実の画像を使用してパノラマ空間を形成する技術です。複数の画像をつなぎ合わせて、大規模な画像または 360 度のパノラマを作成します。スティッチングは、シーン再構成の特殊なケースとみなすことができます。相関は平面ホモグラフィーのみを介して行われます。画像スティッチングは、動きの検出と追跡、拡張現実、解像度向上、ビデオ圧縮、画像安定化などのマシン ビジョン分野で優れた用途があります。画像ステッチングの出力は、2 つの入力画像を結合したものです。通常は 4 つのステップが使用されます。 (1) 特徴抽出: 入力画像内の特徴点を検出します。(2) 画像の位置合わせ: 画像間の幾何学的対応が確立され、共通の参照系で画像を変換、比較、分析できるようになります。(3) 画像ワーピング (Warping): 画像ワーピングとは、1 つの画像の画像を再投影し、その画像をより大きなキャンバスに配置することを指します。(4) 画像融合 (ブレンディング): 画像融合とは、境界付近の画像の階調を変更し、これらのギャップを除去して混合画像を作成することにより、画像間の滑らかな遷移を実現することです。ブレンド モードは、2 つのレイヤーをブレンドするために使用されます。

2. 実施方法

(1) SIFT を用いて画像内の特徴点を抽出し、各キーポイントの周囲の特徴ベクトルを計算します。SIFTより高速なSURFメソッドを使用することもできますが、私のopencvバージョンは最新バージョンです。特許によるものなのか、その他の理由によるものなのかはわかりません。SURF = cv2.xfeatures2D.SURF_create()でインスタンス化する場合ネットではopencvのバージョンを返せば良いと言われていますが、ここでは試していないので sift = cv2.SIFT_create() を使いました。
(2) 2 つの画像のキーポイントと特徴ベクトルをそれぞれ抽出した後、それらを使用して 2 つの画像を照合することができます。画像の結合では、Knn を使用してマッチングすることもできますが、FLANN 高速マッチング ライブラリを使用する方が高速です。画像の結合には、FLANN のホモグラフィー マッチングを使用する必要があります。
(3) ホモグラフィーが一致すると、透視変換 H 行列が得られるので、この逆行列を使用して 2 番目の画像に透視変換を実行し、1 番目の画像と同じ透視変換を実行して、次のスプライシング ステップの準備をします。 . .
(4) 視点の変更が完了した後、画像を直接結合できます。視点の変更が完了した後、numpy を介して画像の左側に画像を直接追加し、重なっている部分をカバーして結合された画像を取得します。ただし、継ぎ合わせた画像の中央に明らかな線が発生します。ギャップについては、加重平均法を使用して、境界の両側のギャップを一定の比率でブレンドできます。これは高速ですが不自然です。フェザリング法またはラプラシアン ピラミッド フュージョンのいずれかが最も効果的です。ここでは加重平均法を使用します。最初の画像を左側に重ねることもできますが、最初の画像とその重なっている領域に重み付け処理を行います。重なっている部分は左側の画像に近く、左側の画像の重みが高くなります。一部では、右に近づくほど右側の回転イメージのウェイトが高くなり、トランジションを滑らかにするために 2 つが加算され、見た目は良くなりますが、速度は遅くなります。

3. 実験写真

 

 

4. 実験

4.1 直接接続

コードは以下のように表示されます。

#导入库
import cv2
import numpy as np
import sys
from PIL import Image
#图像显示函数
def show(name,img):
    cv2.imshow(name, img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
#读取输入图片
ima = cv2.imread("p2.jpg")
imb = cv2.imread("p1.jpg")
A = ima.copy()
B = imb.copy()
imageA = cv2.resize(A,(0,0),fx=0.2,fy=0.2)
imageB = cv2.resize(B,(0,0),fx=0.2,fy=0.2)
#检测A、B图片的SIFT关键特征点,并计算特征描述子
def detectAndDescribe(image):
    # 建立SIFT生成器
    sift = cv2.SIFT_create()
    # 检测SIFT特征点,并计算描述子
    (kps, features) = sift.detectAndCompute(image, None)
    # 将结果转换成NumPy数组
    kps = np.float32([kp.pt for kp in kps])
    # 返回特征点集,及对应的描述特征
    return (kps, features)

#检测A、B图片的SIFT关键特征点,并计算特征描述子
kpsA, featuresA = detectAndDescribe(imageA)
kpsB, featuresB = detectAndDescribe(imageB)
# 建立暴力匹配器
bf = cv2.BFMatcher()
# 使用KNN检测来自A、B图的SIFT特征匹配对,K=2
matches = bf.knnMatch(featuresA, featuresB, 2)
good = []
for m in matches:
    # 当最近距离跟次近距离的比值小于ratio值时,保留此匹配对
    if len(m) == 2 and m[0].distance < m[1].distance * 0.75:
        # 存储两个点在featuresA, featuresB中的索引值
        good.append((m[0].trainIdx, m[0].queryIdx))

# 当筛选后的匹配对大于4时,计算视角变换矩阵
if len(good) > 4:
    # 获取匹配对的点坐标
    ptsA = np.float32([kpsA[i] for (_, i) in good])
    ptsB = np.float32([kpsB[i] for (i, _) in good])
    # 计算视角变换矩阵
    H, status = cv2.findHomography(ptsA, ptsB, cv2.RANSAC,4.0)

# 匹配两张图片的所有特征点,返回匹配结果
M = (matches, H, status)
# 如果返回结果为空,没有匹配成功的特征点,退出程序
if M is None:
    print("无匹配结果")
    sys.exit()
# 否则,提取匹配结果
# H是3x3视角变换矩阵
(matches, H, status) = M
# 将图片A进行视角变换,result是变换后图片
result = cv2.warpPerspective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageA.shape[0]))
# 将图片B传入result图片最左端
result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB
show('res',result)
print(result.shape)

直接得られる結果は次のとおりです。


結合された画像には明らかなギャップがあることがわかります。

4.2 マルチバンドブレンディングを実行してギャップに対処する

コードは以下のように表示されます。

import cv2
import numpy as np
from matplotlib import pyplot as plt
import time

def show(name,img):
    cv2.imshow(name, img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

MIN = 10
FLANN_INDEX_KDTREE = 0
starttime = time.time()
img1 = cv2.imread(r'D:\software\pycharm\PycharmProjects\computer-version\data\p1.jpg') #query
img2 = cv2.imread(r'D:\software\pycharm\PycharmProjects\computer-version\data\p2.jpg') #train
imageA = cv2.resize(img1,(0,0),fx=0.2,fy=0.2)
imageB = cv2.resize(img2,(0,0),fx=0.2,fy=0.2)
surf=cv2.xfeatures2d.SIFT_create()#可以改为SIFT
sift = cv2.SIFT_create()
kp1,descrip1 = sift.detectAndCompute(imageA,None)
kp2,descrip2 = sift.detectAndCompute(imageB,None)
#创建字典
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)

# 当筛选后的匹配对大于10时,计算视角变换矩阵
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(imageB, np.linalg.inv(M), (imageA.shape[1]+imageB.shape[1], imageB.shape[0]))
    direct=warpImg.copy()
    direct[0:imageA.shape[0], 0:imageB.shape[1]] =imageA
    simple=time.time()

show('res',warpImg)
rows,cols=imageA.shape[:2]
print(rows)
print(cols)
for col in range(0,cols):
    # 开始重叠的最左端
    if imageA[:, col].any() and warpImg[:, col].any():
        left = col
        print(left)
        break

for col in range(cols-1, 0, -1):
    #重叠的最右一列
    if imageA[:, col].any() and warpImg[:, col].any():
        right = col
        print(right)
        break

# Multi-band Blending算法
levels = 6
gaussian = cv2.getGaussianKernel(5, 0)
gaussian_pyramid_imageA = [imageA]
gaussian_pyramid_imageB = [warpImg]
laplacian_pyramid_imageA = [imageA]
laplacian_pyramid_imageB = [warpImg]

for i in range(levels):
    gaussian_imageA = cv2.pyrDown(gaussian_pyramid_imageA[i])
    gaussian_imageB = cv2.pyrDown(gaussian_pyramid_imageB[i])
    gaussian_pyramid_imageA.append(gaussian_imageA)
    gaussian_pyramid_imageB.append(gaussian_imageB)

for i in range(levels, 0, -1):
    laplacian_imageA = cv2.subtract(gaussian_pyramid_imageA[i-1], cv2.pyrUp(gaussian_pyramid_imageA[i], dstsize=gaussian_pyramid_imageA[i-1].shape[:2]))
    laplacian_imageB = cv2.subtract(gaussian_pyramid_imageB[i-1], cv2.pyrUp(gaussian_pyramid_imageB[i], dstsize=gaussian_pyramid_imageB[i-1].shape[:2]))
    laplacian_pyramid_imageA.append(laplacian_imageA)
    laplacian_pyramid_imageB.append(laplacian_imageB)

gaussian_pyramid_mask = [np.ones((imageA.shape[0]//(2**levels), imageA.shape[1]//(2**levels)), np.float32)]
for i in range(levels):
    gaussian_mask = cv2.pyrDown(gaussian_pyramid_mask[i])
    gaussian_pyramid_mask.append(gaussian_mask)

laplacian_pyramid = []
n = 0
for laplacian_imageA, laplacian_imageB, gaussian_mask in zip(laplacian_pyramid_imageA, laplacian_pyramid_imageB, gaussian_pyramid_mask[::-1]):
    rows, cols, dpt = laplacian_imageA.shape
    n += 1
    laplacian = np.zeros((rows, cols, dpt), np.float32)
    for row in range(rows):
        for col in range(cols):
            if gaussian_mask[row, col] == 1:
                laplacian[row, col] = laplacian_imageA[row, col]
            else:
                laplacian[row, col] = laplacian_imageB[row, col]
    laplacian_pyramid.append(laplacian)

#重建图像
image_reconstruct = laplacian_pyramid[0]
for i in range(1, levels):
    image_reconstruct = cv2.pyrUp(image_reconstruct, dstsize=laplacian_pyramid[i].shape[:2])
    image_reconstruct = cv2.add(image_reconstruct, laplacian_pyramid[i])

for row in range(0, imageA.shape[0]):
    for col in range(0, left):
        if image_reconstruct[row, col].all() == 0:
            image_reconstruct[row, col] = imageA[row, col]

cv2.imshow('result', image_reconstruct)
cv2.waitKey(0)
cv2.destroyAllWindows()

結果は次のとおりです。

 

 分析: 画像のスムージングにマルチバンド ブレンディングを使用した後、接合部分のギャップが改善されたことが図からわかります。しかし、どういうわけか、左側の画像は黒くなっています。情報を調べたところ、写真の横に黒い部分がある場合は、写真を塗りつぶせていないため、角度を変えて撮影すると良いとのこと。より良い結果を得るためにブレンドを試すことができます。しかし、テストしても解決策はありませんでした。

おすすめ

転載: blog.csdn.net/qq_44896301/article/details/130490819