Python机器视觉(3)——特征描述与图像配准

本文介绍了如何使用2D透视变换进行图像配准,通过标定原图和目标图的四个角点求解透视矩阵,比较了广义逆和最小二乘法的求解策略,并提及了RANSAC算法在参数估计中的应用。最后讨论了处理图像噪声和边界约束的方法,以及全景图的相关性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

梯度直方图

2D透视变换(图像配准)

       透视矩阵有8个未知数(右下角i恒为1),我们只需要标定原图像和新图像的四个角点,即可解出透视矩阵。解透视矩阵的步骤如下:

       化为Ax=b形式后,求解方法有很多,经查阅资料,测试了两种方法。第一种是对A求广义逆,然后求出x值,第二种方法是利用最小二乘法求解矩阵。公式如下:

       经过测试,发现最小二乘法解透视矩阵,与opencv自带函数解出的结果一致。

       随后就是针对每个像素点遍历求坐标,再将原图像坐标的像素值赋给新图像对应坐标点即可。但是,变换后的矩阵有许多噪声点,可以通过滤波器消除噪声。

       但如果目标标定点位选取不好,则会造成大量缺失点,因为原图像坐标×透视矩阵后,很可能输出的点超过新图像大小,为避免这个问题,本题将遍历每个新图像点,求对应原图像坐标,加上一些边界约束,保证新图像每个像素都能被赋值。

       如果出现了图像超出坐标范围而显示不全的情况,感觉可以扩展新图像的空间大小(如500×500的图片,扩展为以相同中心点的1500×1500图片)。

       下面是2D透视变换的代码,有三部分:调用opencv的鼠标回调函数标定原图和目标图的四个角点、求解透视矩阵、透视变换。

import cv2
import numpy as np
import time

def mark_points(img):                               #鼠标标记点位的坐标
    Points = []                               #初始化
    def on_mouse(event, x, y, flags, param):        #opencv调用函数的格式
        if event == cv2.EVENT_LBUTTONDOWN:          #如果是鼠标左键按下
            Points.append([x,y])              #添加点位坐标
    cv2.namedWindow('img', cv2.WINDOW_AUTOSIZE)
    cv2.setMouseCallback('img', on_mouse)           #调用鼠标函数
    while True:
        cv2.imshow('img', img)
        # 按 q 键退出
        if cv2.waitKey(1) & 0xFF == ord('q'):
            cv2.destroyAllWindows()
            break
    return np.float32(Points)                 #转为numpy数组格式,方便科学计算

def calculate_perspective_matrix(originPoints, targetPoints):
    # 构建线性方程组 Ax = b
    A = []
    b = []
    for i in range(4):      #一共有四组点,依次赋值
        x_origin, y_origin = originPoints[i]
        x_target, y_target = targetPoints[i]
        A.append([x_origin, y_origin, 1, 0, 0, 0, -x_target*x_origin, -x_target*y_origin])
        A.append([0, 0, 0, x_origin, y_origin, 1, -y_target*x_origin, -y_target*y_origin])
        b.append(x_target)
        b.append(y_target)

    A = np.array(A)
    b = np.array(b)
    '''A_inv = np.dot(A.T,np.linalg.inv(np.dot(A,A.T)))             #用广义逆求解矩阵
    x = np.dot(A_inv,b)
    matrix1 = np.append(x,1).reshape(3, 3)
    print('the Generalized inverse matrix is :')
    print(matrix1)'''
    matrix = np.dot(np.dot(np.linalg.inv(np.dot(A.T,A)),A.T),b)     #最小二乘法计算
    matrix = np.append(matrix,1).reshape(3, 3)                      #添加透视矩阵中右下角的常量1,然后变为3×3矩阵
    #print('the OLS matrix is :')
    #print(matrix)
    return matrix

def transformed(img,matrix):
    result = np.zeros(img.shape,dtype=np.uint8)
    for row in range(img.shape[0]):         #对每一个像素坐标进行变换
        for col in range(img.shape[1]):
            temp = np.dot(np.linalg.inv(matrix),np.append([col,row],1))
            temp /= temp[2]                 #将算出的数,转化为平面图像坐标系的数,即除以z轴数据
            temp = np.round(temp).astype(int)               #数据转为int型
            if temp[0] < 0:
                temp[0] = 0
            if temp[0] >= img.shape[1]:
                temp[0] = img.shape[1]-1
            if temp[1] < 0:
                temp[1] = 0
            if temp[1] >= img.shape[0]:
                temp[1] = img.shape[0]-1
            result[row,col] = img[temp[1]][temp[0]]
    return result

if __name__ == '__main__':
    img = cv2.imread('img.jpg')         #读取图片
    print('please mark the 4 points of origin image')
    originPoints = mark_points(img)     #标记原图片四个角点
    #targetPoints = np.float32([[0,0],[img.shape[1]/4,0],[img.shape[1]/4,img.shape[0]/4],[0,img.shape[0]/4]])
    time.sleep(1)
    print('please mark the 4 points of target image')
    targetPoints = mark_points(img)     #标记你目标图像的四个角点(如果想为完美矩形,可以用上方赋值,不用鼠标标记)
    start =time.time()                  #一个计时器start
    print('calculating the perspective matrix......')
    matrix = calculate_perspective_matrix(originPoints, targetPoints)   #计算透视矩阵
    print('the perspective matrix is:')
    print(matrix)
    print('calculating the transformed image......')
    result = transformed(img, matrix)   #根据透视矩阵求变换后的图像
    result = cv2.medianBlur(result,5)   #中值滤波去噪声点
    end = time.time()                   #一个计时器end
    print('Running time: %s Seconds'%(end-start))
    cv2.imshow('result', result)        #展示图像
    cv2.imwrite('result1.jpg',result)    #保存
    cv2.waitKey(0)
    cv2.destroyAllWindows()

参数估计

       上述广义逆矩阵可能会在计算上有偏差,最小二乘法只适用于标点精确的场合(最小二乘法没法避免离群点),可以使用RANSAC随机抽样一致性算法来进行参数估计。它的主要思想是:随机选择一组点,拟合变换参数,寻找与该变换相符的点,重新拟合参数。

全景图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值