Visión por computadora ---- Unión de imágenes

 1. Introducción

Image Stitching es una tecnología que utiliza imágenes de la vida real para formar un espacio panorámico. Une múltiples imágenes en una imagen a gran escala o un panorama de 360 ​​grados. Stitching puede considerarse como un caso especial de reconstrucción de escenas, en el que la imagen La correlación se realiza únicamente mediante homografía plana. La unión de imágenes tiene excelentes aplicaciones en los campos de la visión artificial, como la detección y seguimiento de movimiento, la realidad aumentada, la mejora de la resolución, la compresión de video y la estabilización de imagen. La salida de la unión de imágenes es la unión de las dos imágenes de entrada. Generalmente se utilizan cuatro pasos: (1) Extracción de características: detecta puntos característicos en la imagen de entrada. (2) Registro de Imágenes: Se establece la correspondencia geométrica entre imágenes para que puedan ser transformadas, comparadas y analizadas en un sistema de referencia común. (3) Deformación de la imagen (Warping): La deformación de la imagen se refiere a reproyectar la imagen de una de las imágenes y colocar la imagen en un lienzo más grande. (4) Fusión de imágenes (fusión): la fusión de imágenes consiste en lograr una transición suave entre imágenes cambiando el nivel de gris de la imagen cerca del límite, eliminando estos espacios y creando una imagen mixta. Los modos de fusión se utilizan para fusionar dos capas.

2. Método de implementación

(1) Utilice SIFT para extraer puntos característicos en la imagen y calcule vectores característicos para el área alrededor de cada punto clave. Puedes usar el método SURF, que es más rápido que SIFT, pero mi versión de opencv es la última versión. No sé si es por patentes o alguna otra razón. Al crear una instancia con SURF = cv2.xfeatures2D.SURF_create () , se informará un error. Se dice en línea que puede devolver la versión opencv. , pero no lo probé aquí, así que usé sift = cv2.SIFT_create().
(2) Después de extraer los puntos clave y los vectores de características de las dos imágenes respectivamente, se pueden utilizar para hacer coincidir las dos imágenes. Al empalmar imágenes, puede usar Knn para hacer coincidir, pero es más rápido usar la biblioteca de coincidencia rápida de FLANN. Para empalmar imágenes, debe usar la coincidencia de homografía de FLANN.
(3) Una vez coincidente la homografía, se puede obtener la matriz H de transformación de perspectiva. Utilice esta matriz inversa para realizar la transformación de perspectiva en la segunda imagen y transfiérala a la misma perspectiva que la primera imagen para prepararse para el siguiente paso de empalme. . .
(4) Una vez completado el cambio de perspectiva, las imágenes se pueden empalmar directamente. Después de completar el cambio de perspectiva, la imagen se agrega directamente al lado izquierdo de la imagen a través de numpy, cubriendo las partes superpuestas para obtener el empalme. imagen. Sin embargo, habrá una línea obvia en el medio de la imagen empalmada. Para los espacios, puede usar el método de promedio ponderado para combinar los espacios con una cierta proporción en ambos lados del límite. Esto es rápido pero poco natural. . O el método de difuminado o la fusión de la pirámide laplaciana funcionan mejor. Aquí se utiliza el método de promedio ponderado. Puede apilar la primera imagen a la izquierda, pero realice un procesamiento de ponderación en la primera imagen y su área superpuesta. La parte superpuesta está más cerca de la imagen izquierda y la imagen izquierda tiene un peso mayor. Algunos, más cerca de la derecha, el peso de la imagen de rotación de la derecha es mayor, y luego las dos se suman para hacer la transición suave, que se ve mejor pero es más lenta.

3. Imágenes experimentales

 

 

4. Experimentar

4.1 Empalme directo

El código se muestra a continuación:

#导入库
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)

Los resultados obtenidos directamente son los siguientes:


Podemos ver que hay lagunas obvias en las imágenes unidas:

4.2 Realizar una combinación multibanda para solucionar los huecos

El código se muestra a continuación:

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()

El resultado es el siguiente:

 

 Análisis: Se puede ver en la figura que después de usar la fusión multibanda para suavizar la imagen, se han mejorado los espacios en el empalme. Pero por alguna razón, la imagen de la izquierda es negra. Después de consultar la información, descubrí que si hay una parte negra al lado de la imagen es porque la imagen no se puede llenar por completo, puedes intentar cambiar el ángulo al disparar. Puedes intentar mezclar para lograr mejores resultados. Pero después de las pruebas no hubo solución.

Supongo que te gusta

Origin blog.csdn.net/qq_44896301/article/details/130490819
Recomendado
Clasificación