OpenCV轻松入门:面向Python
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第5章 几何变换

几何变换是指将一幅图像映射到另外一幅图像内的操作。OpenCV提供了多个与映射有关的函数,这些函数使用起来方便灵活,能够高效地完成图像的映射。

根据OpenCV函数的不同,本章将映射关系划分为缩放、翻转、仿射变换、透视、重映射等。

5.1 缩放

在OpenCV中,使用函数cv2.resize()实现对图像的缩放,该函数的具体形式为:

        dst = cv2.resize( src, dsize[, fx[, fy[, interpolation]]] )

式中:

● dst代表输出的目标图像,该图像的类型与src相同,其大小为dsize(当该值非零时),或者可以通过src.size()、fx、fy计算得到。

● src代表需要缩放的原始图像。

● dsize代表输出图像大小。

● fx代表水平方向的缩放比例。

● fy代表垂直方向的缩放比例。

● interpolation代表插值方式,具体如表5-1所示。

表5-1 插值方式

在cv2.resize()函数中,目标图像的大小可以通过“参数dsize”或者“参数fx和fy”二者之一来指定,具体介绍如下。

● 情况1:通过参数dsize指定

如果指定参数dsize的值,则无论是否指定了参数fx和fy的值,都由参数dsize来决定目标图像的大小。

此时需要注意的是,dsize内第1个参数对应缩放后图像的宽度(width,即列数cols,与参数fx相关),第2个参数对应缩放后图像的高度(height,即行数rows,与参数fy相关)。

指定参数dsize的值时,x方向的缩放大小(参数fx)为:

        (double)dsize.width/src.cols

同时,y方向的缩放大小(参数fy)为:

        (double)dsize.height/src.rows

● 情况2:通过参数fx和fy指定

如果参数dsize的值是None,那么目标图像的大小通过参数fx和fy来决定。此时,目标图像的大小为:

        dsize=Size(round(fx*src.cols), round(fy*src.rows))

插值是指在对图像进行几何处理时,给无法直接通过映射得到值的像素点赋值。例如,将图像放大为原来的2倍,必然会多出一些无法被直接映射值的像素点,对于这些像素点,插值方式决定了如何确定它们的值。除此以外,还会存在一些非整数的映射值,例如,反向映射可能会把目标图像中的像素点值映射到原始图像中的非整数值对应的位置上,当然原始图像内是不可能存在这样的非整数位置的,即目标图像上的该像素点不能对应到原始图像的某个具体位置上,此时也要对这些像素点进行插值处理,以完成映射。

函数cv2.resize()能实现对原始图像的缩放功能,需要注意的是,开始运算前,操作前的目标图像dst自身的大小、类型与最终得到的目标图像dst是没有任何关系的。目标图像dst的最终大小和类型是通过src、dsize、fx、fy指定的。如果想让原始图像调整为和目标图像一样大,则必须通过上述属性指定。

当缩小图像时,使用区域插值方式(INTER_AREA)能够得到最好的效果;当放大图像时,使用三次样条插值(INTER_CUBIC)方式和双线性插值(INTER_LINEAR)方式都能够取得较好的效果。三次样条插值方式速度较慢,双线性插值方式速度相对较快且效果并不逊色。

例5.1】设计程序,使用函数cv2.resize()对一个数组进行简单缩放。

为了方便观察,这里我们尝试直接通过函数cv2.resize()来生成一个与原始数组等大小的数组。

根据题目要求,设计程序如下:

        import cv2
        import numpy as np
        img=np.ones([2,4,3], dtype=np.uint8)
        size=img.shape[:2]
        rst=cv2.resize(img, size)
        print("img.shape=\n", img.shape)
        print("img=\n", img)
        print("rst.shape=\n", rst.shape)
        print("rst=\n", rst)

在本例中,我们期望通过函数cv2.resize()对原始图像进行缩放。为了方便观察,将目标图像设置为与原始图像等大小。

运行程序,结果如下:

        img.shape=
         (2, 4, 3)
        img=
         [[[1 1 1]
          [1 1 1]
          [1 1 1]
          [1 1 1]]

         [[1 1 1]
          [1 1 1]
          [1 1 1]
          [1 1 1]]]
        rst.shape=
         (4, 2, 3)
        rst=
         [[[1 1 1]
          [1 1 1]]

         [[1 1 1]
          [1 1 1]]

         [[1 1 1]
          [1 1 1]]

         [[1 1 1]
          [1 1 1]]]

通过程序我们观察到,我们的目的没有达到,目标图像的大小与原始图像的大小并不一致。原始图像的大小是2行4列,目标图像的大小是4行2列:

● 目标图像的行数是原始图像的列数。

● 目标图像的列数是原始图像的行数。

通过以上例题我们进一步确认:函数cv2.resize()内dsize参数与图像shape属性在行、列的顺序上是不一致的,或者说,

● 在shape属性中,第1个值对应的是行数,第2个值对应的是列数。

● 在dsize参数中,第1个值对应的是列数,第2个值对应的是行数。

我们通常使用等大小的图像进行测试,在这种情况下,可能无法发现cv2.resize()函数内dsize参数的具体使用方式。

在使用cv2.resize()函数时,要额外注意参数dsize的属性顺序问题。

例5.2】设计程序,使用函数cv2.resize()完成一个简单的图像缩放。

根据题目要求,设计程序如下:

        import cv2
        img=cv2.imread("test.bmp")
        rows, cols=img.shape[:2]
        size=(int(cols*0.9), int(rows*0.5))
        rst=cv2.resize(img, size)
        print("img.shape=", img.shape)
        print("rst.shape=", rst.shape)

运行程序,结果如下:

        img.shape= (512, 51, 3)
        rst.shape= (256, 45, 3)

从程序可以看出:

● 列数变为原来的0.9倍,计算得到51×0.9=45.9,取整得到45。

● 行数变为原来的0.5倍,计算得到512×0.5=256。

例5.3】设计程序,控制函数cv2.resize()的fx参数、fy参数,完成图像缩放。

根据题目要求,设计程序如下:

        import cv2
        img=cv2.imread("test.bmp")
        rst=cv2.resize(img, None, fx=2, fy=0.5)
        print("img.shape=", img.shape)
        print("rst.shape=", rst.shape)

运行程序,结果如下:

        img.shape= (512, 51, 3)
        rst.shape= (256, 102, 3)

从程序可以看出:

● fx进行的是水平方向的缩放,将列数变为原来的2倍,得到51×2=102。

● fy进行的是垂直方向的缩放,将行数变为原来的0.5倍,得到512×0.5=256。