Python计算机视觉第三章-图像到图像的映射

目录

3.1 单应性变换

3.1.1 直接线性变换算法

3.1.2 仿射变换

3.2 图像扭曲

3.2.1 图像中的图像

3.2.2 分段仿射扭曲

3.2.3 图像配准

3.3 创建全景图

3.3.1 RANSAC

3.3.2 稳健的单应性矩阵估计

3.3.3 拼接图像


3.1 单应性变换

        单应性变换是将一个平面内的点映射到另一个平面内的二维投影变换。

实验代码如下:

import cv2
import numpy as np

# 加载图像
img1 = cv2.imread('image1.jpg')
img2 = cv2.imread('image2.jpg')

# 转为灰度图像
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

# 创建SIFT特征检测器
sift = cv2.SIFT_create()

# 检测关键点和计算描述子
kp1, des1 = sift.detectAndCompute(gray1, None)
kp2, des2 = sift.detectAndCompute(gray2, None)

# 使用BFMatcher进行特征匹配
bf = cv2.BFMatcher(cv2.NORM_L2, crossCheck=False)
matches = bf.knnMatch(des1, des2, k=2)

# 应用比率测试进行过滤
good_matches = []
for m, n in matches:
    if m.distance < 0.75 * n.distance:
        good_matches.append(m)

# 提取匹配点坐标
pts1 = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)
pts2 = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)

# 计算单应性矩阵
H, mask = cv2.findHomography(pts1, pts2, cv2.RANSAC)

# 应用单应性变换
height, width, channels = img2.shape
warped_img = cv2.warpPerspective(img1, H, (width, height))

# 显示结果
cv2.imshow('Warped Image', warped_img)
cv2.imshow('Original Image', img2)
cv2.waitKey(0)
cv2.destroyAllWindows()

分析:

  1. 特征检测与描述:使用SIFT检测两幅图像中的特征点,并计算它们的描述子。这一步为后续的特征匹配做准备。

  2. 特征匹配:通过BFMatcher和比率测试筛选出最佳匹配对,确保匹配的可靠性。

  3. 单应性矩阵计算:使用RANSAC算法计算单应性矩阵(Homography Matrix),该矩阵用于描述从一幅图像到另一幅图像的透视变换。

  4. 应用单应性变换:利用cv2.warpPerspective函数对原始图像应用计算出的单应性矩阵,进行透视变换,使得图像对齐到目标图像的视角。

  5. 结果可视化:通过显示变换后的图像和目标图像,直观评估图像配准的效果。

结果:

3.1.1 直接线性变换算法

        DLT(Direct Linear Transformation,直接线性变换)是给定4个或者更多对应点对矩阵,来计算单应性矩阵 H 的算法。

import numpy as np
import cv2
import matplotlib.pyplot as plt

def dlt(points1, points2):
    """
    直接线性变换(DLT)算法计算单应性矩阵
    """
    A = []
    for p1, p2 in zip(points1, points2):
        x1, y1 = p1
        x2, y2 = p2
        A.append([-x1, -y1, -1, 0, 0, 0, x2*x1, x2*y1, x2])
        A.append([0, 0, 0, -x1, -y1, -1, y2*x1, y2*y1, y2])
    
    A = np.array(A)
    _, _, Vt = np.linalg.svd(A)
    H = Vt[-1].reshape(3, 3)
    return H

# 加载图像
img1 = cv2.imread('image1.jpg')
img2 = cv2.imread('image2.jpg')

# 转为灰度图像
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

# 创建SIFT特征检测器
sift = cv2.SIFT_create()

# 检测关键点和计算描述子
kp1, des1 = sift.detectAndCompute(gray1, None)
kp2, des2 = sift.detectAndCompute(gray2, None)

# 使用BFMatcher进行特征匹配
bf = cv2.BFMatcher(cv2.NORM_L2, crossCheck=False)
matches = bf.knnMatch(des1, des2, k=2)

# 应用比率测试进行过滤
good_matches = []
for m, n in matches:
    if m.distance < 0.75 * n.distance:
        good_matches.append(m)

# 提取匹配点坐标
pts1 = np.float32([kp1[m.queryIdx].pt for m in good_matches])
pts2 = np.float32([kp2[m.trainIdx].pt for m in good_matches])

# 计算单应性矩阵
H = dlt(pts1, pts2)

# 应用单应性变换
height, width, channels = img2.shape
warped_img = cv2.warpPerspective(img1, H, (width, height))

# 显示结果
plt.subplot(1, 2, 1)
plt.title('Warped Image')
plt.imshow(cv2.cvtColor(warped_img, cv2.COLOR_BGR2RGB))

plt.subplot(1, 2, 2)
plt.title('Original Image')
plt.imshow(cv2.cvtColor(img2, cv2.COLOR_BGR2RGB))

plt.show()

分析:

  1. 特征检测与描述:使用SIFT提取图像中的特征点及其描述子,这为匹配和后续的单应性计算提供了基础数据。

  2. 特征匹配:通过BFMatcher和比率测试筛选出最佳的特征匹配对,确保匹配的准确性和可靠性。

  3. DLT算法:通过dlt函数计算单应性矩阵,该矩阵描述了从一幅图像到另一幅图像的透视变换。算法通过构建一个线性方程组并使用SVD求解得到单应性矩阵。

  4. 应用单应性变换:利用计算得到的单应性矩阵对图像进行透视变换,使得源图像对齐到目标图像的视角。

  5. 结果可视化:通过Matplotlib显示变换后的图像与目标图像,以直观评估配准效果。

结果:

        成功应用DLT算法后,变换后的图像应与目标图像在视觉上对齐。如果对齐效果良好,说明DLT算法有效地估计了图像之间的透视关系。

3.1.2 仿射变换

        由于仿射变换具有 6 个自由度,因此我们需要三个对应点对来估计矩阵 H。通过将最后两个元素设置为 0,即 h7=h8=0,仿射变换可以用上面的 DLT 算法估计得出。

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 加载图像
img = cv2.imread('image.jpg')

# 定义原始点和变换后的点
pts1 = np.float32([[50, 50], [200, 50], [50, 200]])
pts2 = np.float32([[70, 70], [220, 50], [80, 230]])

# 计算仿射变换矩阵
matrix = cv2.getAffineTransform(pts1, pts2)

# 应用仿射变换
height, width, channels = img.shape
transformed_img = cv2.warpAffine(img, matrix, (width, height))

# 显示结果
plt.subplot(1, 2, 1)
plt.title('Original Image')
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

plt.subplot(1, 2, 2)
plt.title('Transformed Image')
plt.imshow(cv2.cvtColor(transformed_img, cv2.COLOR_BGR2RGB))

plt.show()

分析:

  1. 定义点对:选择三对点来定义仿射变换。这些点用于计算变换矩阵,将图像的某些区域映射到新的位置。

  2. 计算仿射变换矩阵:使用cv2.getAffineTransform函数,根据定义的点对计算仿射变换矩阵。该矩阵用于执行线性变换。

  3. 应用仿射变换:使用cv2.warpAffine函数应用计算得到的仿射变换矩阵对图像进行变换。

  4. 结果可视化:使用Matplotlib显示原始图像和变换后的图像,观察变换效果。

结果:

        通过仿射变换,图像的几何形状被按照指定的点对进行线性调整。变换结果应显示出图像经过旋转、缩放或剪切后的效果。仿射变换保留了直线性和点的相对平行性,适用于简单的图像变形和配准任务。

3.2 图像扭曲

        对图像块应用仿射变换,我们将其称为图像扭曲(或者仿射扭曲)。

3.2.1 图像中的图像

        仿射扭曲的一个简单例子是,将图像或者图像的一部分放置在另一幅图像中,使得它们能够和指定的区域或者标记物对齐。

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 加载源图像和目标图像
src_img = cv2.imread('1.jpg')
dst_img = cv2.imread('1_blurred.jpg')

# 定义源图像中的四个点(即需要仿射变换的区域)
src_pts = np.float32([[50, 50], [200, 50], [50, 200], [200, 200]])

# 定义目标图像中对应的四个点(即目标对齐区域)
dst_pts = np.float32([[100, 100], [300, 100], [100, 300], [300, 300]])

# 计算透视变换矩阵
matrix = cv2.getPerspectiveTransform(src_pts, dst_pts)

# 提取源图像的ROI(感兴趣区域)
src_roi = src_img[50:200, 50:200]

# 将ROI调整为156x156
target_size = (156, 156)
src_roi_resized = cv2.resize(src_roi, target_size)

# 应用透视变换
warped_roi = cv2.warpPerspective(src_roi_resized, matrix, (dst_img.shape[1], dst_img.shape[0]))

# 创建掩码
mask = np.zeros_like(dst_img, dtype=np.uint8)

# 确保掩码的目标区域大小与变换后的ROI匹配
mask[100:100+156, 100:100+156] = warped_roi[100:100+156, 100:100+156]

# 合并结果
result_img = cv2.addWeighted(dst_img, 1.0, mask, 1.0, 0)

# 显示结果
plt.subplot(1, 2, 1)
plt.title('Source Image ROI')
plt.imshow(cv2.cvtColor(src_roi_resized, cv2.COLOR_BGR2RGB))

plt.subplot(1, 2, 2)
plt.title('Result Image')
plt.imshow(cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB))

plt.show()

分析:

  1. 加载图像:从文件加载源图像和目标图像。
  2. 定义点:在源图像和目标图像中定义四个点来进行仿射变换。
  3. 计算变换矩阵:使用cv2.getPerspectiveTransform计算透视变换矩阵。
  4. 调整ROI尺寸:提取并调整源图像的ROI(感兴趣区域)大小,以适应目标图像中的区域。
  5. 应用变换:使用cv2.warpPerspective将调整后的源图像区域变换到目标图像中。
  6. 创建掩码并合成:创建掩码并将变换后的区域合成到目标图像中。
  7. 显示结果:使用Matplotlib展示源图像的ROI和最终合成后的图像。

结果:

3.2.2 分段仿射扭曲

        对应点对集合之间最常用的扭曲方式:分段仿射扭曲。给定任意图像的标记 点,通过将这些点进行三角剖分,然后使用仿射扭曲来扭曲每个三角形,我们可以将图像和另一幅图像的对应标记点扭曲对应。

import cv2
import numpy as np
import matplotlib.pyplot as plt

def apply_affine_transformation(img, src_pts, dst_pts, region):
    matrix = cv2.getAffineTransform(src_pts, dst_pts)
    transformed_img = cv2.warpAffine(img, matrix, (img.shape[1], img.shape[0]), flags=cv2.INTER_LINEAR)
    return transformed_img[region[1]:region[3], region[0]:region[2]]

def main():
    # 加载图像
    img = cv2.imread('source.jpg')
    height, width = img.shape[:2]

    # 定义分段区域
    regions = [
        (0, 0, width//2, height//2),
        (width//2, 0, width, height//2),
        (0, height//2, width//2, height),
        (width//2, height//2, width, height)
    ]

    # 定义源点和目标点
    src_pts = np.float32([[0, 0], [1, 0], [0, 1]])
    dst_pts = np.float32([[0.1, 0.2], [0.9, 0.1], [0.2, 0.9]])

    # 创建输出图像
    output_img = np.zeros_like(img)

    for region in regions:
        # 提取区域
        x1, y1, x2, y2 = region
        src_region = img[y1:y2, x1:x2]

        # 对每个区域应用仿射变换
        transformed_region = apply_affine_transformation(src_region, src_pts, dst_pts, (0, 0, src_region.shape[1], src_region.shape[0]))

        # 将变换后的区域粘贴回输出图像
        output_img[y1:y2, x1:x2] = transformed_region

    # 显示结果
    plt.subplot(1, 2, 1)
    plt.title('Original Image')
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

    plt.subplot(1, 2, 2)
    plt.title('Segmented Affine Transformed Image')
    plt.imshow(cv2.cvtColor(output_img, cv2.COLOR_BGR2RGB))

    plt.show()

if __name__ == "__main__":
    main()

分析:

  • 原始图像:展示了未经处理的原始图像。
  • 分段仿射变换后的图像:展示了应用分段仿射变换后的结果。
  • 仿射变换可能使得图像的每个区域变得不同,从而产生复杂的视觉效果。每个区域的变换独立进行,可能会在区域边界处产生接缝或视觉不连续现象。

结果:

3.2.3 图像配准

        图像配准是对图像进行变换,使变换后的图像能够在常见的坐标系中对齐。

import cv2
import numpy as np
import matplotlib.pyplot as plt

def register_images(img1, img2):
    # 转为灰度图像
    gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
    gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

    # 使用ORB特征检测器
    orb = cv2.ORB_create()
    kp1, des1 = orb.detectAndCompute(gray1, None)
    kp2, des2 = orb.detectAndCompute(gray2, None)

    # 使用BFMatcher进行特征匹配
    bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
    matches = bf.match(des1, des2)
    matches = sorted(matches, key=lambda x: x.distance)

    # 提取匹配点
    src_pts = np.float32([kp1[m.queryIdx].pt for m in matches])
    dst_pts = np.float32([kp2[m.trainIdx].pt for m in matches])

    # 计算单应性矩阵
    H, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC)

    # 应用透视变换
    h, w = img1.shape[:2]
    img2_aligned = cv2.warpPerspective(img2, H, (w, h))

    return img2_aligned, H

def main():
    # 加载图像
    img1 = cv2.imread('1.jpg')
    img2 = cv2.imread('1_blurred.jpg')

    # 图像配准
    img2_aligned, H = register_images(img1, img2)

    # 显示结果
    plt.figure(figsize=(10, 5))
    plt.subplot(1, 3, 1)
    plt.title('Image 1')
    plt.imshow(cv2.cvtColor(img1, cv2.COLOR_BGR2RGB))

    plt.subplot(1, 3, 2)
    plt.title('Image 2')
    plt.imshow(cv2.cvtColor(img2, cv2.COLOR_BGR2RGB))

    plt.subplot(1, 3, 3)
    plt.title('Registered Image 2')
    plt.imshow(cv2.cvtColor(img2_aligned, cv2.COLOR_BGR2RGB))

    plt.show()

    print("Homography Matrix:\n", H)

if __name__ == "__main__":
    main()

分析:

  • 图像 1:显示原始的参考图像。
  • 图像 2:显示需要对齐的图像。
  • 配准后的图像 2:显示通过配准后的图像。理想情况下,这张图像应该与图像 1 对齐。

结果:

实验展示了如何利用图像配准技术对齐多张图像,并分析了配准效果。

3.3 创建全景图

3.3.1 RANSAC

       RANSAC 是“RANdom SAmple Consensus”(随机一致性采样)的缩写。该方法是 用来找到正确模型来拟合带有噪声数据的迭代方法。给定一个模型,例如点集之间 的单应性矩阵,RANSAC 基本的思想是,数据中包含正确的点和噪声点,合理的模 型应该能够在描述正确数据点的同时摒弃噪声点。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import RANSACRegressor
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

# 生成数据
np.random.seed(0)
n_samples = 100
X = np.linspace(-10, 10, n_samples).reshape(-1, 1)
y = 0.5 * X.squeeze() + np.random.normal(scale=2.0, size=n_samples)

# 添加离群点
X[::10] += np.random.normal(scale=10.0, size=(n_samples // 10, 1))
y[::10] += np.random.normal(scale=10.0, size=n_samples // 10)

# 创建RANSAC回归模型
ransac = RANSACRegressor(LinearRegression(), residual_threshold=5.0, max_trials=100)
ransac.fit(X, y)

# 预测
y_ransac = ransac.predict(X)

# 计算均方误差
mse_ransac = mean_squared_error(y, y_ransac)
print(f"RANSAC Mean Squared Error: {mse_ransac}")

# 绘制结果
plt.figure(figsize=(10, 6))
plt.scatter(X, y, color='blue', label='Data with Outliers')
plt.plot(X, y_ransac, color='red', linewidth=2, label='RANSAC fit')
plt.legend()
plt.xlabel('X')
plt.ylabel('y')
plt.title('RANSAC Regression')
plt.show()

分析:

  1. 均方误差(MSE):计算RANSAC回归模型的均方误差,以评估拟合质量。较低的MSE表示模型拟合效果较好。

  2. 图示:图中蓝色点表示带有离群点的数据,红色直线表示RANSAC拟合的结果。通过观察图形,可以看到RANSAC算法在离群点存在的情况下,依然能够较好地拟合出主要的数据趋势。

结果:

        RANSAC算法在处理含有大量离群点的数据时表现出色。通过从数据中随机选择样本进行模型拟合,RANSAC能够有效地识别并排除离群点,从而得到一个更为稳健的模型。对于实际应用,RANSAC是一种强大的工具,尤其适用于数据中存在异常值的情况。

3.3.2 稳健的单应性矩阵估计

        在使用 RANSAC 模块时,我们只需 要在相应 Python 类中实现 fit() get_error() 方法,剩下就是正确地使用 ransac.py。 我们这里使用可能的对应点集来自动找到用于全景图像的单应性矩阵。

        

import cv2
import numpy as np
import matplotlib.pyplot as plt

def ransac_homography_estimation(src_pts, dst_pts, threshold=3.0):
    # 使用RANSAC来估计单应性矩阵
    H, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, threshold)
    return H, mask

def main():
    # 加载图像
    img1 = cv2.imread('image1.jpg')
    img2 = cv2.imread('image2.jpg')

    # 转为灰度图像
    gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
    gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

    # 使用ORB特征检测器
    orb = cv2.ORB_create()
    kp1, des1 = orb.detectAndCompute(gray1, None)
    kp2, des2 = orb.detectAndCompute(gray2, None)

    # 使用BFMatcher进行特征匹配
    bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
    matches = bf.match(des1, des2)
    matches = sorted(matches, key=lambda x: x.distance)

    # 提取匹配点
    src_pts = np.float32([kp1[m.queryIdx].pt for m in matches])
    dst_pts = np.float32([kp2[m.trainIdx].pt for m in matches])

    # RANSAC估计单应性矩阵
    H, mask = ransac_homography_estimation(src_pts, dst_pts)

    # 应用透视变换
    h, w = img1.shape[:2]
    img2_aligned = cv2.warpPerspective(img2, H, (w, h))

    # 绘制结果
    plt.figure(figsize=(12, 6))
    plt.subplot(1, 3, 1)
    plt.title('Image 1')
    plt.imshow(cv2.cvtColor(img1, cv2.COLOR_BGR2RGB))

    plt.subplot(1, 3, 2)
    plt.title('Image 2')
    plt.imshow(cv2.cvtColor(img2, cv2.COLOR_BGR2RGB))

    plt.subplot(1, 3, 3)
    plt.title('Registered Image 2')
    plt.imshow(cv2.cvtColor(img2_aligned, cv2.COLOR_BGR2RGB))

    plt.show()

    print("Homography Matrix:\n", H)
    print("Number of Inlier Matches:", np.sum(mask))

if __name__ == "__main__":
    main()

分析:

  • 特征检测与匹配:使用ORB特征检测器来识别和匹配图像特征点。
  • RANSAC算法:利用cv2.findHomography与RANSAC方法计算单应性矩阵,滤除离群点。
  • 透视变换:应用计算得到的单应性矩阵对图像进行变换,使其与参考图像对齐。
  • 单应性矩阵:输出的单应性矩阵描述了从图像 2 到图像 1 的变换关系。
  • 内点数量:显示RANSAC识别出的内点数量,表示配准的准确性。较多的内点表明配准效果良好。

结果:

3.3.3 拼接图像

        估计出图像间的单应性矩阵(使用 RANSAC 算法),现在我们需要将所有的图像扭曲到一个公共的图像平面上。

import cv2
import numpy as np
import matplotlib.pyplot as plt

def stitch_images(images):
    # 创建特征检测器
    orb = cv2.ORB_create()

    # 初始化图像拼接器
    stitcher = cv2.createStitcher() if hasattr(cv2, 'createStitcher') else cv2.createStitcher(False)

    # 进行拼接
    status, stitched_image = stitcher.stitch(images)

    return status, stitched_image

def main():
    # 加载图像
    img1 = cv2.imread('image1.jpg')
    img2 = cv2.imread('image2.jpg')
    img3 = cv2.imread('image3.jpg')

    # 拼接图像
    images = [img1, img2, img3]
    status, stitched_image = stitch_images(images)

    if status == cv2.Stitcher_OK:
        print("拼接成功")
        
        # 绘制结果
        plt.figure(figsize=(12, 6))
        plt.subplot(1, 2, 1)
        plt.title('拼接前的图像')
        plt.imshow(cv2.cvtColor(img1, cv2.COLOR_BGR2RGB))
        plt.axis('off')

        plt.subplot(1, 2, 2)
        plt.title('拼接后的全景图')
        plt.imshow(cv2.cvtColor(stitched_image, cv2.COLOR_BGR2RGB))
        plt.axis('off')

        plt.show()
    else:
        print("拼接失败,状态码:", status)

if __name__ == "__main__":
    main()

分析:

  • 拼接成功与否:根据status的值判断拼接是否成功。cv2.Stitcher_OK表示拼接成功。
  • 全景效果:通过查看拼接后的全景图,评估拼接效果是否符合预期。成功的拼接应展示出无缝衔接的图像。

结果:

        通过这种方式,我们可以利用现代计算机视觉工具实现高效且准确的图像拼接,生成宽广的全景图。

  • 23
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值