《Python计算机视觉》——图像到图像的映射

文章介绍了单应性变换的概念及其在图像处理中的应用,包括图像配准、图像纠正和创建全景图。单应性变换通过8个自由度的矩阵H实现,可以使用DLT算法估计。仿射变换是单应性变换的一种特殊情况,具有6个自由度。文章还讨论了RANSAC算法在去除特征匹配中的异常点中的作用,以提高图像拼接的准确性。
摘要由CSDN通过智能技术生成

单应性变换

单应性变换是将一个平面内的点映射到另一个平面内的二维投影变换。在这里,平面是指图像或者三维中的平面表面。单应性变换具有很强的实用性,比如:图像配准、图像纠正、纹理扭曲,以及创建全景图像。我们将频繁的使用单应性变换。本质上,单应性变换H,按照下面的方程映射二维中的点(齐次坐标意义下):
[ x ′ y ′ w ′ ] = [ h 1 h 2 h 3 h 4 h 5 h 6 h 7 h 8 h 9 ] [ x y w ] (1) \left[ \begin{matrix} x'\\ y'\\ w' \end{matrix} \right]=\left[\begin{matrix} h_1 & h_2 & h_3\\ h_4 & h_5 & h_6\\ h_7 & h_8 & h_9 \end{matrix} \right]\left[ \begin{matrix} x\\ y\\ w \end{matrix} \right]\tag{1} xyw = h1h4h7h2h5h8h3h6h9 xyw (1)

x ′ = H x (2) x'=Hx\tag{2} x=Hx(2)
对于图像平面内(甚至是三维中的点)的点,齐次坐标是个非常有用的表示方式。点的齐次坐标是依赖于其尺度定义的,所以,
x = [ x , y , w ] = [ a x , a y , a w ] = [ x / w , y / w , 1 ] (3) x=\left[\begin{matrix} x, & y, & w \end{matrix} \right]=\left[\begin{matrix} ax, & ay, & aw \end{matrix} \right]=\left[\begin{matrix} x/w, & y/w, & 1 \end{matrix} \right]\tag{3} x=[x,y,w]=[ax,ay,aw]=[x/w,y/w,1](3)
都表示同一个二维点。因此,单应性矩阵 H H H也仅依赖尺度定义,所以,单应性矩阵具有8个独立的自由度。我们通常使用 w = 1 w=1 w=1来归一化点,这样,点具有唯一的图像坐标 x x x y y y。这个额外的坐标使得我们可以简单的使用同一个矩阵来表示变换。

直接线性变换算法(DLT算法)

单应性矩阵可以由两幅图像(或者平面)中对应点对计算出来。前面已经提到过,一个完全射影变换具有8个自由度。根据对应点约束,每个对应点对可以写出两个方程,分别对应于 x x x y y y坐标。因此,计算单映性矩阵 H H H 需要四个对应点对。

仿射变换

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

图像扭曲

对图像块应用仿射变换,我们将其称为图像扭曲(或者仿射扭曲)。该操作不仅经常应用在计算机图像学中,而且经常出现在计算机视觉算法中。扭曲操作可以使用SciPy工具包中的ndimage包来简单完成。命令:

transformed_im = ndimage.affine_transform(im, A, b, size)

使用如上所示的一个线性变换 A A A和一个平移向量 b b b来对图像块应用仿射变换。选项参数size可以用来指定输出图像的大小。默认输出图像设置为和原始图像同样大小。

图像中的图像

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

分段仿射扭曲

让我们看一下对应点对集合之间最常用的扭曲方式:分段仿射扭曲。给定任意图像的标记点,通过将这些点进行三角剖分,然后使用仿射扭曲来扭曲每个三角形,我么可以将图像和另一幅图像的对应标记点扭曲对应。对于任何图形和图像处理库来说,这些都是最基本的操作。下面我们来演示一下如何使用 M a t p l o t l i b Matplotlib Matplotlib S c i P y SciPy SciPy来完成该操作。

实验代码:

import numpy as np
import cv2

# 读取原始图像
img = cv2.imread('/data/data5/0.jpeg')
# 定义标记点坐标
src_pts = np.array([[50, 50], [150, 50], [100, 150]], dtype=np.float32)
dst_pts = np.array([[70, 80], [160, 50], [120, 170]], dtype=np.float32)
# 计算仿射变换矩阵
M = cv2.getAffineTransform(src_pts, dst_pts)
# 对图像进行仿射扭曲
img_warped = cv2.warpAffine(img, M, (img.shape[1], img.shape[0]))
# 显示原始图像和扭曲后的图像
cv2.imshow('Input', img)
cv2.imshow('Output', img_warped)
cv2.waitKey()

代码运行结果:
在这里插入图片描述

图像配准

图像配准是对图像进行变换,使变换后的图像能够在常见的坐标系中对齐。配准可以是严格配准,也可以是非严格配准。为了能够进行图像对比和更精细的图像分析, 图像配准是一步非常重要的操作。
配准算法的一般步骤:

  • 特征提取
    • 特征提取是指分别提取两幅图像中共有的图像特征,这种特征是出现在两幅图像中对比列、旋转、平移等变换保持一致性的特征,如线交叉点、物体边缘角点、虚圆闭区域的中心等可提取的特征。特征包括:点、线和面三类。
  • 特征匹配
    • 特征匹配分为两步: 对特征作描述;利用相似度准则进行特征匹配。现有的主要特征描述子:SIFT特征描述子,SURF特征描述子,对比度直方图(CCH),DAISY特征描述子,矩方法。常用的相似性测度准则有如欧式距离,马氏距离,Hausdorff距离等。
  • 估计变换模型
    • 空间变换模型是所有配准技术中需要考虑的一个重要因素,各种配准技术都要建立自己的变换模型,变换空间的选取与图像的变形特性有关。常用的空间变换模型有:刚体变换、仿射变换、投影变换、非线性变换。
  • 图像重采样及变换
    • 在得到两幅图像的变换参数后,要将输入图像做相应参数的变换,使之与参考图像处于同一坐标系下,则矫正后的输入图像与参考图像可用作后续的图像融合、目标变化检测处理或图像镶嵌;涉及输入图像变换后所得点坐标不一定为整像素数,则应进行插值处理。常用的插值算法有最近领域法,双线性插值法和立方卷积插值法。

创建全景图

在同一位置(即图像的照相机位置相同)拍摄的两幅或者多幅图像是单应性相关的。我们经常使用该约束将很多图像缝补起来,拼成一个大的图像来创建全景图像。

RANSAC

RANSAC算法即RANdom SAmple Consensus,随机抽样一致算法,采用迭代的方式从一组包含离群的被观测数据中估算出数学模型的参数。

RANSAC算法假设数据中包含正确数据和异常数据(或称为噪声)。正确数据记为内点(inliers),异常数据记为外点(outliers)。同时RANSAC也假设,给定一组正确的数据,存在可以计算出符合这些数据的模型参数的方法。

在利用SIFT算法进行特征点匹配时,我们会发现常常存在特征点匹配错误的现象,这些匹配错误的点将会对图像拼接的效果产生很大的影响,所以我们需要利用一定的方法剔除匹配错误的特征点,我们常会用到RANSAC算法来筛选SIFT匹配的特征点以减少误差,这个算法现在在图像配准以及拼接上得到了广泛的应用。

  • RANSAC 算法基本思想

    • 随机选择两个点
    • 根据随机选取的两个点构造方程 y = a x + b y=ax+b y=ax+b
    • 将所有的数据点套到这个模型中计算误差。
    • 给定阈值,计算inliers数量
    • 不断重复上述过程,直到达到一定迭代次数后,选择inliers数量最多的直线方程,作为问题的解。
  • RANSAC 求解单应矩阵

    • 随机选择四对匹配特征
    • 根据DLT计算单应矩阵 H H H(唯一解)
    • 对所有匹配点,计算映射误差ε
    • 根据误差阈值,确定inliers
    • 针对最大inliers集合,重新计算单应矩阵 H H H

实验代码:

# RANSAC算法图像拼接
import cv2
import numpy as np


def cv_show_image(name, img):
    print(img.shape)
    cv2.imshow(name, img)
    cv2.waitKey(0)              # 等待时间,单位是毫秒,0代表任意键终止
    cv2.destroyAllWindows()


def matchKeyPoints(bf, kptA, dpA, kptB, dpB):
    src_matches = bf.knnMatch(dpA, dpB, k=2)

    # 完了后需要做个过滤
    good_pts = []
    for res in src_matches:
        if len(res) == 2:
            m, n = res  # 单个点可以取出多个匹配结果。
            # 每一个匹配结果都是包含了三个主要信息,
            # distance: 匹配度,欧氏距离度量的
            # trainIdx: 图像A的关键点下标编号,也就是第几个关键点,数组下标
            # queryIdx: 图像B的关键点下标编号,也就是第几个关键点,数组下标
            if m.distance < 0.75 * n.distance:
                # 每个元素保存的是图像A的关键点, 和图像B的关键点
                good_pts.append((m.trainIdx, m.queryIdx))

    if len(good_pts) > 4:
        pts_src = np.float32([kptA[j] for (_, j) in good_pts])  # 获得对应的真实的关键点的位置数值
        print(pts_src.shape)
        pts_dst = np.float32([kptB[i] for (i, _) in good_pts])  # 获得对应的真实的关键点的位置数值
        print(pts_dst.shape)

        # 这个函数是将 RANSAC 算法融合在内了,拟合方式选择的是cv2.RANSAC,允许的误差阈值是4
        H, status = cv2.findHomography(pts_src, pts_dst, cv2.RANSAC, ransacReprojThreshold=4.0)
        return H

    return None


def detectKeyPoints(sift, img):
    kps, dps = sift.detectAndCompute(img, None)
    kpts = np.float32([kp.pt for kp in kps])  # 直接取所有的位置信息即可,放在list,因为这个才是我们要用的
    print(np.array(kps).shape)
    print(dps.shape)
    return kpts, dps


if __name__ == '__main__':
    imgA_SRC = cv2.imread('/data/data5/0.jpeg')
    imgB_SRC = cv2.imread('/data/data5/1.jpeg')

    # cv_show_image('imgA_SRC', imgA_SRC)
    # cv_show_image('imgB_SRC', imgB_SRC)

    # 第一步:先读取俩图片
    imgA = cv2.cvtColor(imgA_SRC, cv2.COLOR_BGR2GRAY)
    h_A, w_A = imgA.shape
    imgB = cv2.cvtColor(imgB_SRC, cv2.COLOR_BGR2GRAY)
    h_B, w_B = imgB.shape

    # 第二步:计算各个图像的关键点信息和特征描述信息
    sift = cv2.xfeatures2d.SIFT_create()
    kptA, dpA = detectKeyPoints(sift, imgA)
    kptB, dpB = detectKeyPoints(sift, imgB)

    # 第三步:进行特征匹配
    bf = cv2.BFMatcher()
    homography = matchKeyPoints(bf, kptA, dpA, kptB, dpB)
    if homography is None:
        print("not find homography Matrix..., these two images are not matched.")
        exit(1)

    # 第四步:对图像进行变换
    im_out = cv2.warpPerspective(imgA_SRC, homography, (w_A + w_B, h_A))
    cv_show_image('im_out', im_out)

    # 第五步骤,图像拼接
    im_out[0: h_B, 0: w_B, :] = imgB_SRC
    # im_out = template_img = cv2.resize(im_out, (900, 430))
    cv_show_image('im_out', im_out)

实验运行结果:
实验输入原图像:
在这里插入图片描述
实验运行后结果图:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值