Skip to content

Latest commit

 

History

History
513 lines (374 loc) · 15.9 KB

6.图像变换.md

File metadata and controls

513 lines (374 loc) · 15.9 KB

图像变换

OpenCV 提供了两个变换函数,cv2.warpAffine 和 cv2.warpPerspective,使用这两个函数你可以实现所有类型的变换。cv2.warpAffine 接收的参数是2 x 3 的变换矩阵,而 cv2.warpPerspective 接收的参数是 3 x 3 的变换矩阵。

1.尺度变换

尺度变换(缩放,拉伸)是调整图像大小,使其变大或变小。但实际操作时要比想象的复杂一些,因为调整大小带来了像素如何插值(放大)或合并(减少)的问题。

cv2.resize(src,dsize,fx=0,fy=0,interpolation=cv2.INTER_LINEAR)

作用:根据设置调整图像为指定大小,其中有两种指定方式:① 绝对尺寸:通过dsize直接设置;② 相对尺寸:dsize设置为None,然后将fx和fy设置为我们想要的比例因子即可。

插值方法

插值 含义
cv2.INTER_LINEAR 双线性插值法
cv2.INTER_NEAREST 最近邻插值
cv2.INTER_AREA 像素区域重采样(默认)
cv2.INTER_CUBIC 双三次插值
cv2.INTER_LANCZ0S4 插值(超过8×8个邻域)

例子

img1 = cv2.imread("beautiful-beauty-brunette-2147192.jpg")

# 绝对尺寸
height,width = img1.shape[:2]
res = cv2.resize(img1,(2*width,2*height),interpolation=cv2.INTER_CUBIC)

# 相对尺寸
res1 = cv2.resize(img1,None,fx=0.5,fy=0.5)

# 图像显示
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(231)
img_show('原图',img1)
plt.subplot(232)
img_show('绝对尺度(放大)',res)
plt.subplot(233)
img_show('相对尺度(缩小)',res1)

2.平移变换

平移就是将对象换一个位置。如果你要沿(x,y)方向移动,移动的距离是($t_{x}, t_{y}$),你可以以下面的方式构建移动矩阵: $$ M=\left[\begin{array}{lll}{1} & {0} & {t_{x}} \ {0} & {1} & {t_{y}}\end{array}\right] $$

cv2.warpAffine(src, M, dsize)
  • src:输入图像
  • M:移动矩阵
  • dsize:输出图像大小

注意:第三个参数的是输出图像的大小,它的格式应该是图像的(宽,高)。应该记住的是图像的宽对应的是列数,高对应的是行数。

img1 = cv2.imread("beautiful-beauty-brunette-2147192.jpg")
tx = 500
ty = 1000

M = np.float32([[1,0,tx],[0,1,ty]])
dst = cv2.warpAffine(img1,M,(img1.shape[1]+tx,img1.shape[0]+ty))

tx = 500
ty = 200
M = np.float32([[1,0,tx],[0,1,ty]])
dst1 = cv2.warpAffine(img1,M,(3000,4000))

plt.figure(figsize=(10,8),dpi=100)
plt.subplot(231)
img_show('原图',img1)
plt.subplot(232)
img_show('平移(右移500,下移1000)',dst)
plt.subplot(233)
img_show('平移(右移500,下移200)',dst1)

例子

import cv2
import matplotlib.pyplot as plt
import numpy as np


def cv_show(name,img):
    """图像显示函数
    name:字符串,窗口名称
    img:numpy.ndarray,图像
    """
    cv2.namedWindow(name,cv2.WINDOW_NORMAL)
    cv2.imshow(name,img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    
if __name__=="__main__":

    #img1 = cv2.imread("lenaNoise.png")
    img1 = cv2.imread("attractive-beautiful-beauty-2267088.jpg")
    
    
    tx = 100
    ty = 150
     
    M = np.float32([[1,0,tx],[0,1,ty]])
     
    dst = cv2.warpAffine(img1,M,(img1.shape[0],img1.shape[1]))
       
    cv_show('translation',dst)

3.旋转变换

对一个图像旋转角度 θ, 需要使用到下面形式的旋转矩阵。 $$ M=\left[\begin{array}{cc}{\cos \theta} & {-\sin \theta} \ {\sin \theta} & {\cos \theta}\end{array}\right] $$ 但是 OpenCV 允许你在任意地方进行旋转,但是旋转矩阵的形式应该修改为 $$ M=\left[\begin{array}{ccc}{\alpha} & {\beta} & {(1-\alpha) \cdot \text { center } x-\beta \cdot \text { centery }} \ {-\beta} & {\alpha} & {\beta \cdot \text { centerx }+(1-\alpha) \cdot \text { centery }}\end{array}\right] $$ 其中: $$ \begin{array}{c}{\alpha=s c a l e \cdot \cos \theta} \ {\beta=s c a l e \cdot \sin \theta}\end{array} $$ 为了构建这个旋转矩阵,OpenCV 提供了一个函数:cv2.getRotationMatrix2D。

cv2.getRotationMatrix2D(center, angle, scale)
cv2.warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) → dst

参数说明:

  • getRotationMatrix2D:
  • center–表示旋转的中心点
  • angle–表示旋转的角度degrees
  • scale–图像缩放尺度

warpAffine:

  • src – 输入的图像
  • M – 2 X 3 的变换矩阵.
  • dsize – 输出的图像的size大小
  • dst – 输出的图像
  • flags – 输出图像的插值方法(参考尺度变换)
  • borderMode – 图像边界的处理方式

旋转任意角度的步骤 1.读取图像

2.使用getRotationMatrix2D函数求取旋转矩阵M,使用warpAffine函数对图像进行旋转

3.显示图像

例子:在不缩放的情况下将图像旋转 90 度。

import cv2
import matplotlib.pyplot as plt
import numpy as np


def cv_show(name,img):
    """图像显示函数
    name:字符串,窗口名称
    img:numpy.ndarray,图像
    """
    cv2.namedWindow(name,cv2.WINDOW_NORMAL)
    cv2.imshow(name,img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


if __name__=="__main__":

    #img1 = cv2.imread("lenaNoise.png")
    img1 = cv2.imread("cute-dog.jpg")
    
    rows,cols = img1.shape[:2]
    
    # 这里的第一个参数为旋转中心,第二个为旋转角度,第三个为旋转后的缩放因子
    # 可以通过设置旋转中心,缩放因子,以及窗口大小来防止旋转后超出边界的问题
    M = cv2.getRotationMatrix2D((cols/2,rows/2),90,1)
   
    dst = cv2.warpAffine(img1,M,(cols,rows))
       
    cv_show('translation',dst)

例子

import cv2
import matplotlib.pyplot as plt
import numpy as np

def img_show(name,img):
    """matplotlib图像显示函数
    name:字符串,图像标题
    img:numpy.ndarray,图像
    """
    if len(img.shape) == 3:
        img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    plt.imshow(img,'gray')
    #plt.xticks([])
    #plt.yticks([])
    plt.xlabel(name,fontproperties='FangSong',fontsize=12)
    
if __name__=="__main__":

    #img1 = cv2.imread("lenaNoise.png")
    img1 = cv2.imread("cute-dog.jpg")
    
    rows,cols = img1.shape[:2]
    
    #将图像放到图像大小的两倍画布中,将图像移动的画布中心
    maxBorder =int(max(cols*2, rows*2))
    tx = (maxBorder-cols)/2
    ty = (maxBorder-rows)/2 
    M = np.float32([[1,0,tx],[0,1,ty]])
    img2 = cv2.warpAffine(img1,M,(maxBorder,maxBorder))
    
    # 这里的第一个参数为旋转中心,第二个为旋转角度,第三个为旋转后的缩放因子
    # 可以通过设置旋转中心,缩放因子,以及窗口大小来防止旋转后超出边界的问题
    M = cv2.getRotationMatrix2D((maxBorder/2,maxBorder/2),45,1)
    dst = cv2.warpAffine(img2,M,(maxBorder,maxBorder))
    
    
    plt.figure(figsize=(10,8),dpi=80)
    plt.subplot(231)
    img_show('原图',img1)
    plt.subplot(232)
    img_show('平移到画布中心',img2)
    plt.subplot(233)
    img_show('旋转45度',dst)

补充:使用OpenCV和Python旋转图像(正确)

import cv2
import matplotlib.pyplot as plt
import numpy as np

def cv_show(name,img):
    """图像显示函数
    name:字符串,窗口名称
    img:numpy.ndarray,图像
    """
    cv2.namedWindow(name,cv2.WINDOW_NORMAL)
    cv2.imshow(name,img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

def img_show(name,img):
    """matplotlib图像显示函数
    name:字符串,图像标题
    img:numpy.ndarray,图像
    """
    if len(img.shape) == 3:
        img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    plt.imshow(img,'gray')
    #plt.xticks([])
    #plt.yticks([])
    plt.xlabel(name,fontproperties='FangSong',fontsize=12)
    
def rotate_bound(image,angle):
    #获取图像的尺寸
    #旋转中心
    (h,w) = image.shape[:2]
    (cx,cy) = (w/2,h/2)
    
    #设置旋转矩阵
    M = cv2.getRotationMatrix2D((cx,cy),-angle,1.0)
    cos = np.abs(M[0,0])
    sin = np.abs(M[0,1])
    
    # 计算图像旋转后的新边界
    nW = int((h*sin)+(w*cos))
    nH = int((h*cos)+(w*sin))
    
    # 调整旋转矩阵的移动距离(t_{x}, t_{y})
    M[0,2] += (nW/2) - cx
    M[1,2] += (nH/2) - cy
    
    return cv2.warpAffine(image,M,(nW,nH))



if __name__=="__main__":

    img1 = cv2.imread("gooey.jpg")
    img2 = rotate_bound(img1,20)
    #cv_show('rotate15',img2)
    plt.figure(figsize=(12,8),dpi=80)
    plt.subplot(121)
    img_show('原图',img1)
    plt.subplot(122)
    img_show('顺时针旋转20°',img2)

$$ M=\left[\begin{array}{ccc}{\cos \theta} & {\sin \theta} & {(1-\cos \theta) \cdot \text { center } x-\sin \theta \cdot \text { centery }} \ {-\sin \theta} & {\cos \theta} & {\sin \theta \cdot \text { centerx }+(1-\cos \theta) \cdot \text { centery }}\end{array}\right] $$ 我们先定义了 rotate_bound 函数。

  • 此方法接受输入图像image并以角度angle旋转它。

  • 获取图像宽w、高h,确定旋转中心,将围绕其中心(x,y) 。

  • 调用 cv2 .getRotationMatrix2D 获取我们的旋转矩阵 M。但是,要调整任何图像边框截断问题,我们需要自己的手动计算旋转后的图像的宽高。我们首先从旋转矩阵M中获取余弦值和正弦值 。这使我们能够计算旋转图像的新宽度和高度,确保图像的任何部分都不会被切断。

  • 一旦我们知道了新的宽度和高度,我们就可以通过再次修改旋转矩阵来调整图像的位置 。

  • 最后,调用 cv2.warpAffine函数旋转实际图像,同时确保没有图像被截断。

4.翻转

翻转通过函数flip实现:

函数说明:

cv2.flip(src, flipCode[, dst]) 

参数说明:

  • src – 输入的图像

  • dst – 输出的图像

  • flipCode – 翻转模式:

    flipCode==0垂直翻转(沿X轴翻转)

    flipCode>0水平翻转(沿Y轴翻转)

    flipCode<0水平垂直翻转(先沿X轴翻转,再沿Y轴翻转,等价于旋转180°)

例子

import cv2
import matplotlib.pyplot as plt
import numpy as np

def img_show(name,img):
    """matplotlib图像显示函数
    name:字符串,图像标题
    img:numpy.ndarray,图像
    """
    if len(img.shape) == 3:
        img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    plt.imshow(img,'gray')
    plt.xticks([])
    plt.yticks([])
    plt.xlabel(name,fontproperties='FangSong',fontsize=12)
    

if __name__=="__main__":

    #img1 = cv2.imread("lenaNoise.png")
    img1 = cv2.imread("cute-dog.jpg")
    
    # 水平翻转
    flip_horiz_img = cv2.flip(img1, 1)
    # 垂直翻转
    flip_verti_img = cv2.flip(img1, 0)
    # 水平垂直翻转
    flip_horandver_img = cv2.flip(img1, -1)

    plt.figure(figsize=(8,12),dpi=80)
    plt.subplot(221)
    img_show('原图',img1)
    plt.subplot(222)
    img_show('水平翻转',flip_horiz_img)
    plt.subplot(223)
    img_show('垂直翻转',flip_verti_img)
    plt.subplot(224)
    img_show('水平垂直翻转',flip_horandver_img)

5.仿射变换

图像的仿射变换涉及到图像的形状位置角度的变化,是深度学习预处理中常到的功能,在此简单回顾一下。仿射变换具体到图像中的应用,主要是对图像的缩放scale,旋转rotate,剪切shear,翻转flip和平移translate的组合。在OpenCV中,仿射变换的矩阵是一个2×3的矩阵,其中左边的2×2子矩阵是线性变换矩阵,右边的2×1的两项是平移项: $$ \begin{array}{l}{A=\left[\begin{array}{ll}{a_{00}} & {a_{01}} \ {a_{10}} & {a_{11}}\end{array}\right], B=\left[\begin{array}{l}{b_{0}} \ {b_{1}}\end{array}\right]} \ {M=\left[\begin{array}{ll}{A} & {B}\end{array}\right]=\left[\begin{array}{lll}{a_{00}} & {a_{01}} & {b_{0}} \ {a_{10}} & {a_{11}} & {b_{1}}\end{array}\right]}\end{array} $$ 对于图像上的任一位置(x,y),仿射变换执行的是如下的操作:

$$ T_{a f f i n e}=A\left[\begin{array}{l}{x} \ {y}\end{array}\right]+B=M\left[\begin{array}{l}{x} \ {y} \ {1}\end{array}\right] $$ 需要注意的是,对于图像而言,宽度方向是x,高度方向是y,坐标的顺序和图像像素对应下标一致。所以原点的位置不是左下角而是右上角,y的方向也不是向上,而是向下。

在仿射变换中,原图中所有的平行线在结果图像中同样平行。为了创建这个矩阵我们需要从原图像中找到三个点以及他们在输出图像中的位置。然后cv2.getAffineTransform 会创建一个 2x3 的矩阵,最后这个矩阵会被传给函数 cv2.warpAffine。

例子

import cv2
import matplotlib.pyplot as plt
import numpy as np

def img_show(name,img):
    """matplotlib图像显示函数
    name:字符串,图像标题
    img:numpy.ndarray,图像
    """
    if len(img.shape) == 3:
        img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    plt.imshow(img,'gray')
    plt.xticks([])
    plt.yticks([])
    plt.xlabel(name,fontproperties='FangSong',fontsize=12)
    

if __name__=="__main__":

    #img1 = cv2.imread("lenaNoise.png")
    img1 = cv2.imread("cute-dog.jpg")
    rows,cols = img1.shape[:2]
    
    pts1 = np.float32([[500,500],[2000,500],[500,2000]])
    pts2 = np.float32([[100,1000],[2000,500],[1000,2500]])
    M = cv2.getAffineTransform(pts1,pts2)
    img2 = cv2.warpAffine(img1,M,(rows,cols))
    
    plt.figure(figsize=(10,8),dpi=80)
    plt.subplot(221)
    img_show('原图',img1)
    plt.subplot(222)
    img_show('仿射变换',img2)

6.透视变换

对于视角变换,我们需要一个 3x3 变换矩阵。在变换前后直线还是直线。要构建这个变换矩阵,你需要在输入图像上找 4 个点,以及他们在输出图像上对应的位置。这四个点中的任意三个都不能共线。这个变换矩阵可以有 函数 cv2.getPerspectiveTransform() 构建。然后把这个矩阵传给函数cv2.warpPerspective。

例子

import cv2
import matplotlib.pyplot as plt
import numpy as np

def img_show(name,img):
    """matplotlib图像显示函数
    name:字符串,图像标题
    img:numpy.ndarray,图像
    """
    if len(img.shape) == 3:
        img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    plt.imshow(img,'gray')
    plt.xticks([])
    plt.yticks([])
    plt.xlabel(name,fontproperties='FangSong',fontsize=12)
    

if __name__=="__main__":

    #img1 = cv2.imread("lenaNoise.png")
    img1 = cv2.imread("cute-dog.jpg")

    rows,cols = img1.shape[:2]
    
    pts1 = np.float32([[200,200],[200,3000],[4800,200],[4800,3000]])
    pts2 = np.float32([[200,200],[200,3000],[4800,600],[4800,2000]])
    M = cv2.getPerspectiveTransform(pts1,pts2)
    dst=cv2.warpPerspective(img1,M,(cols,rows),0.6)
        
    plt.figure(figsize=(10,8),dpi=80)
    plt.subplot(121)
    img_show('原图',img1)
    plt.subplot(122)
    img_show('透视变换',dst)