利用单应矩阵与透视变换纠正照片中的文档

Jul 17,2018   2150 words   8 min


今天因为要申请一些东西,所以要扫描护照的信息页,但我在外出差身边没有扫描仪,只能用手机拍照,拍照就会有透视畸变,所以必须要想办法把这个畸变消除掉。如下图所示,是手机拍摄的一本书,有比较明显的透视变换,我们希望的是获得正视图。这正是这篇博客需要解决的问题。 要解决这个需求其实比较简单,只要获得书的四角点的像素坐标以及对应的新的坐标,利用OpenCV即可求出单应变换矩阵,然后在用相关函数基于这个矩阵重采即可。

1.标记角点

如下所示,分别在图像中标记书的四角点坐标记录在文本文件中,并制定其对应的新的坐标。 文本文件数据采用标准CSV格式,如下。 各项含义解释如下。第一列为原图中角点的x坐标,第二列为原图中角点的y坐标,第三列为重采后图像中对应的x坐标,第四列为重采后图像中对应的y坐标。

2.计算单应矩阵

在获取了对应角点后即可通过代码读取数据,然后基于对应角点计算单应矩阵。在OpenCV中有findHomography()函数专门用于估计求解单应矩阵。

3.透视变换

在上一步计算出单应矩阵H后,下一步就可以基于找到的对应关系对原图进行透视变换和重采了。在OpenCV中也有相应的函数warpPerspective(),方便调用。这样,经过这三步,就可以实现我们的目标了。

4.实现代码

# coding=utf-8
import cv2
import numpy as np
import sys


def readPoints(file_path):
    # 打开一个文本文件
    text_file = open(file_path)
    data_item = []
    old_points = []
    new_points = []
    line = text_file.readline().strip('\n')
    data_item.append(line)
    # 逐行读取数据
    while line:
        line = text_file.readline().strip('\n')
        # 如果读取的行内容不为空,则添加到list中
        # 注意这里并不能用None,注意空字符串和空对象的区别
        if line != '':
            data_item.append(line)

    # 读取完成后对于读取的每行数据进行简单的提取和处理
    for i in range(data_item.__len__()):
        data = data_item[i].split(',')
        old_points.append([float(data[0]), float(data[1])])
        new_points.append([float(data[2]), float(data[3])])

    return old_points, new_points


if __name__ == '__main__':
    if sys.argv.__len__() == 2 and sys.argv[1] == 'help':
        print("Usage info:")
        print("Manual mode:")
        print("Description:Align with given matched points from txt file.")
        print("Paramers:D:/input.jpg D:/output.jpg points.txt")
        print("Txt file format:")
        print("points.txt:input.x input.y output.x output.y")
        print("Each txt file should have 4 points at least.")
        exit()
    elif sys.argv.__len__() == 4:
        input_img = sys.argv[1]
        output_img = sys.argv[2]
        points_txt = sys.argv[3]
        input_img = cv2.imread(input_img)
        old_points, new_poitns = readPoints(points_txt)
        # 根据选择的角点的对应新坐标计算目标影像大小
        new_w = int(abs(new_poitns[3][0] - new_poitns[2][0]))
        new_h = int(abs(new_poitns[2][1] - new_poitns[0][1]))

        # 求解单应矩阵
        homo, mask = cv2.findHomography(np.array(old_points), np.array(new_poitns))
        print(homo)
        # 进行透视变换,重采
        res = cv2.warpPerspective(input_img, homo, (new_w, new_h))
        cv2.imwrite(output_img, res)
        cv2.imshow("res", res)
        cv2.waitKey(0)

输入相关启动参数并运行上述代码,即可获得结果,如下。 圆满实现了我们想要的效果。拍摄的护照照片经过这样处理也和扫描的效果一样了。就这个功能而言,到这里就可以了,但想做的更完善一些还可以继续做,如在这里是用户手动指定四角点坐标,但其实可以使用OpenCV从而实现自动检测书籍边缘和角点,从而自动提取角点坐标,做到全流程的自动化。这也就是很多扫描类软件如OfficeLens等实现的功能。

本文作者原创,未经许可不得转载,谢谢配合

返回顶部