OpenCV_python3_04

Practical Python and OpenCV,3rd Edition 04


图像转换(image transformations)

本节中,我们将介绍基本的图像转换。 这些是您可能应用于图像的常用技术,包括平移,旋转,调整大小,翻转和裁剪。

平移(Translation)

我们首先介绍的是Translation。Translatioin是沿x和y轴移动图像。 使用Translation,我们可以向上,向下,向左或向右移动图像,以及上述任意组合!请看下面的代码(translation.py):

import numpy as np 
import argparse 
import imutils 
import cv2 

ap = argparse.ArgumentParser()
ap.add_argument('-i',"--image",required=True,
    help="path to the image")
args = vars(ap.parse_args())

image = cv2.imread(args["image"])
cv2.imshow("Original",image)

M = np.float32([[1,0,25],[0,1,50]])
shifted = cv2.warpAffine(image,M,(image.shape[1],image.shape[0]))
cv2.imshow("Shifted Down and Right",shifted)

M = np.float32([[1,0,-50],[0,1,-90]])
shifted = cv2.warpAffine(image,M,(image.shape[1],image.shape[0]))
cv2.imshow("Shifted Up and Left",shifted)

解释:

主要从M——我们的的translation matrix讲起,该矩阵告诉我们的图像要进行平移多少像素(从左到右,从上到下)。该矩阵被定义为float32类型的数组,因为OpenCV希望该矩阵是一个float类型。The first row of the matrix is [1,0,tx],其中tx是the number of pixels we will shift the image left or right,而负值则表示图像将向左平移,正值表示图像将向右平移。然后我们定义the second row of the matrix as [0,1,ty],其中,ty是the number of pixels we will shift the image up or down。其中,负值表示图像向上平移,正值表示图像向下平移。

使用了上面的那个标记,我们看代码中,将tx=25,ty=50则意味着,我们将图像向右平移25个像素,向下平移50个像素。

我们定义好了平移矩阵之后,图像的实际平移是使用了cv2.warpAffine函数来执行,该函数的第一个参数是我们要进行平移的图像,第二个参数是我们的平移矩阵M,最后我们需要手动地提供图像的尺寸(width and height)作为第三个参数。

前面实现了图像的平移,但是代码太过冗余,我们新建一个.py文件来实现平移功能(imutils.py)

import numpy as np 
import cv2 

def translate(image,x,y):
    M = np.float32([[1,0,x],[0,1,y]])
    shifted = cv2.warpAffine(image,M,(image.shape[1],image.shape[0]))

    return shifted

解释:

我们的平移方法有三个参数:我们要平移的图像,我们沿x轴移动的像素数,以及我们将沿y轴移动的像素数。然后,此方法定义我们的平移矩阵M,然后再应用实际移位。最后,我们返回移位后的图像。

修改translation.py的内容

shifted = imutils.translate(image,0,100)
cv2.imshow("Shifted Down",shifted)
cv2.waitKey(0)

运行结果:

旋转(Rotation)

在这里,我们将使用θ来表示要旋转多少度。

rotate.py

import numpy as np 
import argparse 
import imutils
import cv2

ap = argparse.ArgumentParser()
ap.add_argument('-i',"--image",required=True,
    help="Path to the image")
args = vars(ap.parse_args())

image = cv2.imread(args["image"])
cv2.imshow("Original",image)

(h,w) = image.shape[:2]
center = (w // 2, h // 2)

M = cv2.getRotationMatrix2D(center,45,1.0)
rotated = cv2.warpAffine(image,M,(w,h))
cv2.imshow("Rotated by 45 Degrees",rotated)

M = cv2.getRotationMatrix2D(center,-90,1.0)
rotated = cv2.warpAffine(image,M,(w,h))
cv2.imshow("Rotated by -90 Degrees",rotated)

解释:

前面还是导入必要的包以及解析输入参数和显示原始图像。

当我们旋转图像时,我们需要指定我们想要旋转的点。 在大多数情况下,您需要围绕图像的中心旋转; 我们首先获取图像的宽度和高度,因为OpenCV将图像读取为一个numpy数组,所以和矩阵类似,矩阵的行对应着高,也就是height=image.shape[0],矩阵的列对应着图像的宽,也就是width=image.shape[1],然后我们除以2,确定图像的中心位置。这里我们使用和C语言一样的除法,//表示整除法,以确保我们得到的是整数。

就像我们定义一个矩阵来平移图像一样,我们也定义了一个矩阵来旋转图像。 不是使用NumPy手动构造矩阵,而是调用cv2.getRotationMatrix2D方法。

cv2.getRotationMatrix2D函数有三个参数:第一个是我们想要旋转图像的点,在这里是图像的中心位置,然后我们指定需要旋转的角度θ,我们第一次是旋转了45度,最后一个参数图像的比例。我们还没有讨论调整图像的大小,但是在这里你可以指定浮点值,其中1.0表示使用相同的图像尺寸。 但是,如果指定值为2.0,则图像的大小将加倍。 类似地,值0.5将图像的大小减半。

一旦我们从cv2.getRotationMatrix2D函数获得旋转矩阵M,我们就可以使用cv2.warpAffine方法将旋转应用于我们的图像。此函数的第一个参数是我们想要旋转的图像。然后,我们指定旋转矩阵M以及图像的输出尺寸(宽度和高度)。然后,显示旋转了的图像。

显示效果为:

接下来为了让代码更加的pretty and Pythonic,我们在imutils.py中添加一个rotate方法

def rotate(image,angle,center=None,scale=1.0):
    (h,w) = image.shape[:2]

    if center is None:
        center = (w // 2 , h // 2)

    M = cv2.getRotationMatrix2D(center,angle,scale)
    rotated = cv2.warpAffine(image,M,(w,h))

    return rotated

解释

我们的rotate方法有四个参数。第一个是你的image。第二个是我们想要旋转图像的角度θ。我们提供两个可选的关键字参数,center和scale。center参数是我们希望旋转图像的点。如果提供了值None,则该方法自动选图像中心为旋转点。最后,scale参数用于处理在旋转期间是否应更改图像的大小。scale参数的默认值为1.0,这意味着不应调整大小。

再次修改rotate.py文件,

rotated = imutils.rotate(image,180)
cv2.imshow("Rotated by 180 Degrees",rotated)
cv2.waitKey(0)

运行结果:

确实很pythonic!!!

Resizing

import numpy as np 
import argparse
import imutils
import cv2 

ap = argparse.ArgumentParser()
ap.add_argument('-i',"--image",required=True,help="Path to the image")
args = vars(ap.parse_args())

image = cv2.imread(args["image"])
cv2.imshow("Original",image)

解释

这个和前面一样,解析参数,读取图片并显示图片

r = 150.0 / image.shape[1]
dim = (150,int(image.shape[0] * r))

resized = cv2.resize(image,dim,interpolation=cv2.INTER_AREA)
cv2.imshow("Resized (Width)",resized)

解释

r表示the aspect ratio。这里image.shape[1]表示我们图片的宽度,上面的代码我们设置我们图片的新宽度为150个pixels,为了计算新高度与旧高度的比率,我们简单地将比率r定义为新宽度(150像素)除以旧宽度,接着为了保持宽高比,我们计算出高度(height)的变化为 height / width * 150。接下来调用resize函数,该函数的第一个参数是我们希望调整大小的图像,第二个参数是我们为新图像计算的尺寸。最后一个参数是我们的插值方法,这是在背后工作的算法 处理实际图像的大小调整方式。一般来说,我发现使用cv2.INTER_AREA在调整大小时获得最佳效果; 但是,其他适当的选择包括cv2.INTER_LINEAR,cv2.INTER_CUBIC和cv2.INTER_NEAREST。

r = 50.0 / image.shape[0]
dim = (int(image.shape[1] * r),50)

resized = cv2.resize(image,dim,interpolation=cv2.INTER_AREA)
cv2.imshow("Resized (Height)",resized)
cv2.waitKey(0)

解释

这一段代码和前一段类似,只不过这一次是固定高度为50个像素,然后保持宽高比计算出宽度,显示图像。

resized = imutils.resize(image,width = 100)
cv2.imshow("Resized via Function",resized)
cv2.waitKey(0)

解释

前面都是用了三行代码实现图像的resize,我们可以利用imutils.resize函数只需要一行即可实现一样的功能。

imutils.py函数中实现:

def resize(image,width=None,height=None,inter=cv2.INTER_AREA):
    dim = None 
    (h,w) = image.shape[:2]

    if width is None and height is None:
        return image 

    if width is None:
        r = height / float(h)
        dim = (int(w * r),height)
    else:
        r = width / float(w)
        dim = (width,int(h * r))

    resized = cv2.resize(image,dim,interpolation=inter)

    return resized

解释

第一个参数是我们想要调整大小的图像。然后,我们定义两个关键字参数,宽度和高度。这两个参数都不能为None,否则我们将不知道如何调整图像大小。我们还提供inter,这是我们的插值方法,默认为cv2.INTER_AREA。

显示效果:

Flipping

接下来我们要探索的图像转换是翻转图像。 我们可以围绕x或者翻转图像y轴,甚至两者。查看下图理解一下水平和垂直翻转的区别。

import argparse
import cv2

ap = argparse.ArgumentParser()
ap.add_argument('-i',"--image",required=True,
    help="Path to the image")
args = vars(ap.parse_args())

image = cv2.imread(args["image"])
cv2.imshow("Original",image)

解释
和前面一样,解析参数,显示原始图片

flipped = cv2.flip(image,1)
cv2.imshow("Flipped Horizontally",flipped)

flipped = cv2.flip(image,0)
cv2.imshow("Flipped Vertically",flipped)

flipped = cv2.flip(image,-1)
cv2.imshow("Flipped Horizontally & Vertically",flipped)
cv2.waitKey(0)

我们通过调用cv2.flip函数来完成图像的翻转。cv2.flip方法需要两个参数:我们要翻转的图像和a flip code,用于确定我们如何翻转图片。

使用翻转代码值1表示我们将围绕y轴水平翻转图像。指定翻转代码为0表示我们想要围绕x轴垂直翻转图像。最后,使用负翻转代码翻转两个轴图像。

显示效果:

Cropping

当我们裁剪图像时,我们想要删除我们不感兴趣的图像的外部部分。我们可以使用NumPy数组切片来完成图像裁剪。

import numpy as np 
import argparse
import cv2 

ap = argparse.ArgumentParser()
ap.add_argument('-i',"--image",required=True,
    help="Path to the image")
args = vars(ap.parse_args())

image = cv2.imread(args["image"])
cv2.imshow("Original",image)

cropped = image[30:120,240:335]
cv2.imshow("T-Rex Face",cropped)
cv2.waitKey(0)

解释

实际裁剪在一行代码上进行。我们提供NumPy数组切片以提取图像的矩形区域,从(240,30)开始到(335,120)结束。The order in which we supply the
indexes to the crop may seem counterintuitive(有悖常理); 但请记住,OpenCV将图像表示为NumPy数组,其高度优先,宽度为次之。这意味着我们需要在x轴之前提供y轴值.

为了执行我们的裁剪,NumPy需要四个索引:

  1. 开始y:起始y坐标。在这种情况下,我们从y=30开始。

  2. 结束y:结束y坐标。我们将在y=120时结束我们的裁剪。

  3. 开始x:切片的起始x坐标。我们在x=240时开始裁剪

  4. 结束x:切片的结束x轴坐标。我们的切片在x=335处结束

显示效果:

用到的函数

cv2.warpAffine

cv2.getRotationMatrix2D

cv2.resize

cv2.flip

更多的参考:

chapter-5-drawing


---------------- The End ----------------
支持一下
Fork me on GitHub ;