matlab 通过矩阵变换使图像旋转平移_【OpenCV+Python】图像缩放旋转平移与几何变换...

图像缩放旋转平移

在OpenCV中我们经常会用到图像的缩放,旋转以及平移,比如有的时候我们想对一类图片进行操作,这个时候我们就需要对这一类的图片进行尺寸统一,在以后深度学习的学习中,我们也会发现,对于数据集的训练,它们的数据集样本通常都是尺寸统一的。

▼ 缩放

在OpenCV中,使用函数cv2.resize()实现对图像的缩放。

语法格式为:

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

  • src:原图片

  • dsize:缩放图片尺寸,如果dsize=0,默认计算方式如下dsize=Size(round(fx∗src.cols),round(fy∗src.rows))

  • fx fy和dsize不能同时为0 fx,fy分别是在x,y轴上的缩放系数,默认取0时,fx=(double)dsize.width/src.cols ,fy=(double)dsize.height/src.cols,默认参数为0可以不写如里面。

  • interpolation:差值方式使用默认即可。默认的是INTER_LINEAR - a bilinear interpolation (used by default)现行差值,如下图所示。

4a6b1d1a060c10123559ded4502af586.png

我们习惯的坐标表示是:先x横坐标,再y纵坐标。在图像处理中,这种惯性思维经常情况下是错误的。

因为在计算机中,图像是以矩阵的形式保存的,先行后列。所以,一张 宽×高×颜色通道=480×256×3 的图片会保存在一个 256×480×3 的三维张量中。图像处理时也是按照这种思想进行计算的(其中就包括 OpenCV 下的图像处理),即 高×宽×颜色通道。

但是问题来了,cv2.resize这个API却是个小例外。因为它的参数输入却是 宽×高×颜色通道。

我们来看代码:

import cv2pic = cv2.imread("cat.jpg")cv2.imshow("org",pic)pic = cv2.resize(pic, (400, 400), interpolation=cv2.INTER_CUBIC)cv2.imshow('res', pic)cv2.waitKey(0)cv2.destroyAllWindows()

先看原图效果:

f0c200a20dac4f3f7ef53ebf3cfbfe29.png

再看缩放之后的效果:

b9aae9b9cb064a1cce7fce216c6d5585.png

我们发现,用这种方法缩小之后的宽高比跟原图并非一样,现在我们可以进行代码的改进:

import cv2pic = cv2.imread("cat.jpg")cv2.imshow("org",pic)pic = cv2.resize(pic, (0, 0), fx=0.5, fy=0.5,interpolation=cv2.INTER_NEAREST)cv2.imshow('res', pic)cv2.waitKey(0)cv2.destroyAllWindows()

我们直接来看缩放之后的图像:

3ff58fca80ecb1a7e053aeec391f58a9.png

在这里,如果我们不直接指定缩放后大小,则通过fx和fy直接指定缩放比例,0.5则长宽都为原来一半。

▼ 旋转

OpenCV中对图像的旋转主要是先通过getRotationMatrix2D函数得到图像的旋转矩阵,然后再通过仿射变换函数warpAffine得到旋转后的图像。

我们看一下函数:

cv2.getRotationMatrix2D(center, angle, scale)

  • center:旋转中心点 (cx, cy) ,你可以随意指定。

  • angle:旋转的角度 单位是角度 逆时针方向为正方向,角度为正值代表逆时针。

  • scale:缩放倍数,值等于1.0代表尺寸不变。

该函数返回的就是仿射变换矩阵M,之后我们将这个仿射变换的矩阵传入到下一个函数:

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

  • src:输入图像。

  • M:变换矩阵。

  • dsize:输出图像的大小。

  • flags:插值方法的组合(int 类型!)

  • borderMode:边界像素模式(int 类型!)

  • borderValue:边界填充值; 默认情况下,它为0。

现在我们来讲述一下图像旋转的原理。

我们根据公式:

c66aadf99e93c6e0381b913d86e331ab.png

可以得出:

df7bcfa6fa92101e0328b9a4bc3b76b3.png

所以对应的变换矩阵为:

13b42300b93584a318402476a58c5b27.png

我们来看代码:

import cv2img = cv2.imread("cat.jpg")rows,cols = img.shape[:2]M = cv2.getRotationMatrix2D((cols/2,rows/2),90,1)dst = cv2.warpAffine(img,M,(cols,rows))cv2.imshow('res', dst)cv2.waitKey(0)cv2.destroyAllWindows()

旋转之后的效果:

b3fb0edf9829154f4ad4e93659e3fc11.png

▼ 图像平移

图像的平移实际上就是矩阵的移动,它是最简单的一种空间变换,其表达式为:

其中(b0,b1) 是偏移量。

举个例子,如果是向右平移10个像素, 向下平移30个像素的话, 那么变换矩阵M:

d9c3493b3e937d90aadd5e2c637cefe3.png

我们来看源码:

import cv2import numpy as npimg = cv2.imread('cat.jpg')height,width,channel = img.shape# 声明变换矩阵 向右平移10个像素, 向下平移30个像素M = np.float32([[1, 0, 10], [0, 1, 30]])# 进行2D 仿射变换shifted = cv2.warpAffine(img, M, (width, height))cv2.imshow('res', shifted)cv2.waitKey(0)cv2.destroyAllWindows()

效果演示:

c58525c910f11d5f1393d713a38739ec.png

如若需要修改平移的方位,只需要改动矩阵的值就可以。

图像-几何转换

上次教程中我们谈及到OpenCV的缩放旋转以及平移,它们本质上都属于简单的几何变换,本次我们将讨论稍微复杂的一下几何变换。

▼ 图像翻转

图像翻转有多种方式,包括使用固定的API以及使用numpy进行操作等等。

(1)使用flip函数实现翻转

函数原型:

flip(src, flipCode[, dst]) -> dst

  • src:输入图片

  • flipCode:翻转代码

  • 1:水平翻转 Horizontally (图片第二维度是column)

  • 0:垂直翻转 *Vertically * (图片第一维是row)

  • -1:同时水平翻转与垂直反转 Horizontally & Vertically

我们来看代码:

import numpy as npimport cv2from matplotlib import pyplot as pltimg = cv2.imread('cat.jpg')def bgr2rbg(img):'''将颜色空间从BGR转换为RBG'''return img[:,:,::-1]# 水平翻转flip_h = cv2.flip(img, 1)# 垂直翻转flip_v = cv2.flip(img, 0)# 同时水平翻转与垂直翻转flip_hv = cv2.flip(img, -1)plt.subplot(221)plt.title('SRC')plt.imshow(bgr2rbg(img))plt.subplot(222)plt.title('Horizontally')plt.imshow(bgr2rbg(flip_h))plt.subplot(223)plt.title('Vertically')plt.imshow(bgr2rbg(flip_v))plt.subplot(224)plt.title('Horizontally & Vertically')plt.imshow(bgr2rbg(flip_hv))plt.show()

代码实现了三种翻转的方式:水平翻转、垂直翻转以及水平垂直翻转。我们来看效果:

8ed53f490b8aab3f929734338a2095ae.png

(2)使用numpy的索引进行图像反转

我们刚刚使用了OpenCV的API进行了图像翻转的操作,实际上仅仅使用numpy也是相当的方便。在numpy中使用img[:,::-1]则代表为矩阵水平翻转,使用img[::-1]则为矩阵垂直翻转,所以我们不难看出,水平与垂直同时翻转则为img[::-1, ::-1]。

我们来看代码:

import cv2import numpy as npfrom matplotlib import pyplot as pltimg = cv2.imread('cat.jpg')height,width,channel = img.shape# 水平翻转flip_h =  img[:,::-1]# 垂直翻转flip_v =  img[::-1]# 水平垂直同时翻转flip_hv =  img[::-1, ::-1]def bgr2rbg(img):'''将颜色空间从BGR转换为RBG'''return img[:,:,::-1]plt.subplot(221)plt.title('SRC')plt.imshow(bgr2rbg(img))plt.subplot(222)plt.title('Horizontally')plt.imshow(bgr2rbg(flip_h))plt.subplot(223)plt.title('Vertically')plt.imshow(bgr2rbg(flip_v))plt.subplot(224)plt.title('Horizontally & Vertically')plt.imshow(bgr2rbg(flip_hv))plt.show()

效果仍然相同:

599826186c15bf6774004151da619f9c.png

(3)利用wrapAffine实现翻转

事实上,利用OpenCV的wrapAffine也可以进行图像的翻转,在上一节的教程中,我们已经讲述了利用相关函数实现旋转以及平移的操作,从本质上来讲,它们的原理基本上相同,现在我们来看一下用它实现翻转的原理:

我们假设width 代表图像的宽度 height代表图像的高度:

水平翻转的变换矩阵为:

434e698d5bf2f6a4c7f4d7265eed804f.png

垂直翻转的变换矩阵则为:

2eb2f57211e8bff63c1e782f570051e8.png

如若同时进行水平翻转与垂直翻转,那么有:

5d2475565dee056d3f1086f21a68d138.png

据此我们来看实现的代码:

import cv2import numpy as npfrom matplotlib import pyplot as pltimg = cv2.imread('cat.jpg')height,width,channel = img.shape# 水平翻转M1 = np.float32([[-1, 0, width], [0, 1, 0]])flip_h =  cv2.warpAffine(img, M1, (width, height))# 垂直翻转M2 = np.float32([[1, 0, 0], [0, -1, height]])flip_v =  cv2.warpAffine(img, M2, (width, height))# 水平垂直同时翻转M3 = np.float32([[-1, 0, width], [0, -1, height]])flip_hv =  cv2.warpAffine(img, M3, (width, height))def bgr2rbg(img):'''将颜色空间从BGR转换为RBG'''return img[:,:,::-1]plt.subplot(221)plt.title('SRC')plt.imshow(bgr2rbg(img))plt.subplot(222)plt.title('Horizontally')plt.imshow(bgr2rbg(flip_h))plt.subplot(223)plt.title('Vertically')plt.imshow(bgr2rbg(flip_v))plt.subplot(224)plt.title('Horizontally & Vertically')plt.imshow(bgr2rbg(flip_hv))plt.show()

实验效果仍然跟之前两种方法的一样,在这里就不一一演示了。

▼ 仿射变换

实际上,之前我们讲过的缩放旋转平移以及刚刚讲述的翻转它们本质上都属于图像的仿射变换,现在我们讨论一个函数,算是对仿射变换的一个总结:

M=cv2.GetAffineTransform(src, dst)

  • src:原始图像中的三个点的坐标

  • dst:变换后的这三个点对应的坐标

  • M:根据三个对应点求出的仿射变换矩阵

在前面原理的讲述中,我们也知道了仿射变换通常是对于一个2*3的矩阵:

2dd7b3472323a60951852945ecd2d080.png

考虑到我们要使用矩阵614bcca726666253ecf1e78704fccb54.pngc981331f64d8708cd0bdb2e6a8a6192b.png对二维向量88bb221637262f465c47fd0b3d421848.png做变换,所以也能表示为下列形式:

267f6e344df3d45fc45dd42c106e05ee.png

044550e9306ffc8113105450286b79fc.png

现在我们来大致看一下代码:

import cv2 as cvimport matplotlib.pyplot as pltimport numpy as npimg = cv.imread('cat.jpg')rows,cols,ch = img.shapepts1 = np.float32([[50,50],[200,50],[50,200]])pts2 = np.float32([[10,100],[200,50],[100,250]])M = cv.getAffineTransform(pts1,pts2)dst = cv.warpAffine(img,M,(cols,rows))plt.subplot(121),plt.imshow(img),plt.title('Input')plt.subplot(122),plt.imshow(dst),plt.title('Output')plt.show()

91de616c3e0fad8e939bc3ca5ecf3839.png

我们可以看到一只扭曲的猫。

▼ Perspective Transformation

我们平时可能用过各种扫描的软件,比如像扫描全能王这种软件,我们在使用的过程中会发现,即便是你图片拍的比较斜,软件后期仍然可以对你的图片进行校正,然后从而进行图像增强处理,这种图像矫正操作,实际上就是图像的Perspective变换(关键字屏蔽,大家自行了解)。现在的很多案例比如车牌识别,答题卡判卷以及文字识别等等,它们都是以图像的Perspective变换将图像矫正之后才进行下一步的操作的。

之前我们在 仿射变换简介 里面讲解的是仿射变换AffineTransform。那么Perspective变换与仿射变换之间最大的区别是什么呢?仿射变换可以用三个点确定一个变换,而Perspective换则不一定,所以可以将仿射变换作为投影变换的一个特例。需要四个点,才能确定Perspective变换。

OpenCV提供了warpPerspective( )函数来实现图片的Perspective变换,只需要输入梯形四个顶点的坐标和目标画布四个角的坐标,即可自动完成转换。核心代码只有两行:首先读取两个坐标数组,计算变换矩阵;然后根据变换矩阵对原图进行Perspective变换,并输出到目标画布。

函数原型:

cv2.getPerspectiveTransform(src, dst) → retval

  • src:源图像中待测矩形的四点坐标

  • sdt:目标图像中矩形的四点坐标

返回由源图像中矩形到目标图像矩形变换的矩阵,接下来引出下面这个函数:

cv2.warpPerspective(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) → dst

  • src:输入图像

  • M:变换矩阵

  • dsize:目标图像shape

  • flags:插值方式,interpolation方法INTER_LINEAR或INTER_NEAREST

  • borderMode:边界补偿方式,BORDER_CONSTANT or BORDER_REPLICATE

  • borderValue:边界补偿大小,常值,默认为0

或者我们可以使用另一个函数:

cv2.perspectiveTransform(src, m[, dst]) → dst

  • src:输入的2通道或者3通道的图片

  • m:变换矩阵

这两个函数返回的相同的规格的图片。

对于Perspective变换,我们需要3x3变换矩阵。即使在转换后,直线也将保持直线。要找到此变换矩阵,在输入图像上有4个点,在输出图像上需要相应的点。在这4个点中,其中3个不应共线。然后可以通过函数cv.getPerspectiveTransform找到转换矩阵。然后将cv.warpPerspective应用于此3x3转换矩阵,我们来看代码:

import cv2 as cvimport matplotlib.pyplot as pltimport numpy as npimg = cv.imread('sudoku.png')rows,cols,ch = img.shapepts1 = np.float32([[56,65],[368,52],[28,387],[389,390]])pts2 = np.float32([[0,0],[300,0],[0,300],[300,300]])M = cv.getPerspectiveTransform(pts1,pts2)dst = cv.warpPerspective(img,M,(300,300))plt.subplot(121),plt.imshow(img),plt.title('Input')plt.subplot(122),plt.imshow(dst),plt.title('Output')plt.show()

87ad5082f1d8de2f016eefcd6e12add3.png

可以看到,图像对于给定的坐标已经进行了矫正,这对于之后的形态学的操作是相当方便的。

1b548f090b02c91a6b11f39f6d2e6afd.gif 462e63f6e287f70b9cb958fe1b15be5a.png 扫码入群 扫码添加管理员微信

加入“电子产品世界”粉丝交流群

↓↓↓↓点击,查看更多新闻

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值