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

5.3 仿射

仿射变换是指图像可以通过一系列的几何变换来实现平移、旋转等多种操作。该变换能够保持图像的平直性和平行性。平直性是指图像经过仿射变换后,直线仍然是直线;平行性是指图像在完成仿射变换后,平行线仍然是平行线。

OpenCV中的仿射函数为cv2.warpAffine(),其通过一个变换矩阵(映射矩阵)M实现变换,具体为:

dst(x, y)=src(M11x+M12y+M13, M21x+M22y+M23

如图5-2所示,可以通过一个变换矩阵M,将原始图像O变换为仿射图像R。

图5-2 仿射变换

因此,可以采用仿射函数cv2.warpAffine()实现对图像的旋转,该函数的语法格式如下:

        dst = cv2.warpAffine( src, M, dsize[, flags[, borderMode[, borderValue]]] )

式中:

● dst代表仿射后的输出图像,该图像的类型和原始图像的类型相同。dsize决定输出图像的实际大小。

● src代表要仿射的原始图像。

● M代表一个2×3的变换矩阵。使用不同的变换矩阵,就可以实现不同的仿射变换。

● dsize代表输出图像的尺寸大小。

● flags代表插值方法,默认为INTER_LINEAR。当该值为WARP_INVERSE_MAP时,意味着M是逆变换类型,实现从目标图像dst到原始图像src的逆变换。具体可选值参见表5-1。

● borderMode代表边类型,默认为BORDER_CONSTANT。当该值为BORDER_TRANSPARENT时,意味着目标图像内的值不做改变,这些值对应原始图像内的异常值。

● borderValue代表边界值,默认是0。

通过以上分析可知,在OpenCV中使用函数cv2.warpAffine()实现仿射变换,忽略其可选参数后的语法格式为:

        dst = cv2.warpAffine( src , M , dsize )

其通过转换矩阵M将原始图像src转换为目标图像dst:

dst(x, y)=src(M11x+M12y+M13, M21x+M22y+M23

因此,进行何种形式的仿射变换完全取决于转换矩阵M。下面分别介绍通过不同的转换矩阵M实现的不同的仿射变换。

5.3.1 平移

通过转换矩阵M实现将原始图像src转换为目标图像dst:

dst(x, y)=src(M11x+M12y+M13, M21x+M22y+M23

将原始图像src向右侧移动100个像素、向下方移动200个像素,则其对应关系为:

dst (x, y)=src (x+ 100, y+ 200)

将上述表达式补充完整,即:

dst (x, y)=src (1·x+ 0·y+ 100, 0·x+ 1·y+ 200)

根据上述表达式,可以确定对应的转换矩阵M中各个元素的值为:

M11=1

M12=0

M13=100

M21=0

M22=1

M23=200

将上述值代入转换矩阵M,得到:

在已知转换矩阵M的情况下,可以直接利用转换矩阵M调用函数cv2.warpAffine()完成图像的平移。

例5.5】设计程序,利用自定义转换矩阵完成图像平移。

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

        import cv2
        import numpy as np
        img=cv2.imread("lena.bmp")
        height, width=img.shape[:2]
        x=100
        y=200
        M = np.float32([[1, 0, x], [0, 1, y]])
        move=cv2.warpAffine(img, M, (width, height))
        cv2.imshow("original", img)
        cv2.imshow("move", move)
        cv2.waitKey()
        cv2.destroyAllWindows()

运行程序,出现如图5-3所示的运行结果,其中左图是原始图像,右图是移动结果图像。

图5-3 【例5.5】程序的运行结果

5.3.2 旋转

在使用函数cv2.warpAffine()对图像进行旋转时,可以通过函数cv2.getRotationMatrix2D()获取转换矩阵。该函数的语法格式为:

        retval=cv2.getRotationMatrix2D(center, angle, scale)

式中:

● center为旋转的中心点。

● angle为旋转角度,正数表示逆时针旋转,负数表示顺时针旋转。

● scale为变换尺度(缩放大小)。

利用函数cv2.getRotationMatrix2D()可以直接生成要使用的转换矩阵M。例如,想要以图像中心为圆点,逆时针旋转45°,并将目标图像缩小为原始图像的0.6倍,则在调用函数cv2.getRotationMatrix2D()生成转换矩阵M时所使用的语句为:

        M=cv2.getRotationMatrix2D((height/2, width/2),45,0.6)

例5.6】设计程序,完成图像旋转。

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

        import cv2
        img=cv2.imread("lena.bmp")
        height, width=img.shape[:2]
        M=cv2.getRotationMatrix2D((width/2, height/2),45,0.6)
        rotate=cv2.warpAffine(img, M, (width, height))
        cv2.imshow("original", img)
        cv2.imshow("rotation", rotate)
        cv2.waitKey()
        cv2.destroyAllWindows()

运行程序,出现如图5-4所示的运行结果,其中左图是原始图像,右图是旋转结果图像。

图5-4 【例5.6】程序的运行结果

5.3.3 更复杂的仿射变换

5.3.1节和5.3.2节讲的两种仿射变换都比较简单,对于更复杂仿射变换,OpenCV提供了函数cv2.getAffineTransform()来生成仿射函数cv2.warpAffine()所使用的转换矩阵M。该函数的语法格式为:

        retval=cv2.getAffineTransform(src, dst)

式中:

● src代表输入图像的三个点坐标。

● dst代表输出图像的三个点坐标。

在该函数中,其参数值src和dst是包含三个二维数组(x, y)点的数组。上述参数通过函数cv2.getAffineTransform()定义了两个平行四边形。src和dst中的三个点分别对应平行四边形的左上角、右上角、左下角三个点。函数cv2.warpAffine()以函数cv2.getAffineTransform()获取的转换矩阵M为参数,将src中的点仿射到dst中。函数cv2.getAffineTransform()对所指定的点完成映射后,将所有其他点的映射关系按照指定点的关系计算确定。

例5.7】设计程序,完成图像仿射。

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

        import cv2
        import numpy as np
        img=cv2.imread('lena.bmp')
        rows, cols, ch=img.shape
        p1=np.float32([[0,0], [cols-1,0], [0, rows-1]])
        p2=np.float32([[0, rows*0.33], [cols*0.85, rows*0.25], [cols*0.15, rows*0.7]])
        M=cv2.getAffineTransform(p1, p2)
        dst=cv2.warpAffine(img, M, (cols, rows))
        cv2.imshow("origianl", img)
        cv2.imshow("result", dst)
        cv2.waitKey()
        cv2.destroyAllWindows()

在本例中,首先构造了两个三分量的点集合p1和p2,分别用来指代原始图像和目标图像内平行四边形的三个顶点(左上角、右上角、左下角)。然后使用M=cv2.getAffineTransform(p1, p2)获取转换矩阵M。接下来,dst=cv2.warpAffine(img, M, (cols, rows))完成了从原始图像到目标图像的仿射。

运行程序,出现如图5-5所示的运行结果,其中左图是原始图像,右图是仿射结果图像。

图5-5 【例5.7】程序的运行结果