几何变换 是指将一幅图像映射到另外一幅图像内的操作。
OpenCv中常用映射: 缩放、翻转、仿射变换、透视、重映射等。
5.1缩放
函数cv2.resize()
实现对图像的缩放,具体形式:
dst = cv2.resize( src, dsize[, fx[, fy[, interpolation]]] )
- dst 代表输出的目标图像,该图像的类型与 src 相同,其大小为 dsize(当该值非零时),或者可以通过 src.size()、fx、fy 计算得到。
- src 代表需要缩放的原始图像。
- dsize 代表输出图像大小。
- fx 代表水平方向的缩放比例。
- fy 代表垂直方向的缩放比例。
- interpolation 代表插值方式。
类型 | 说明 |
---|---|
cv2.INTER_NEAREST | 最临近插值 |
cv2.INTER_LINEAR | 双线性插值(默认方式) |
cv2.INTER_CUBIC | 三次样条插值 。 首先对源图像邻近的 4×44 \times 44×4 区域进行三次样条拟合,然后将目标像素对应的三次样条条件作为目标图像对应像素的值 |
cv2.INTER_AREA | 区域插值。 根据当前像素点周边区域的像素采样来计算像素的采样。该方法类似临近插值方式 |
cv2.INTER_LANCZOS4 | 一种使用 8×88 \times 88×8 邻域的 Lanczos 插值方法 |
cv2.INTER_LINEAR_EXACT | 位精确双线性插值 |
cv2.INTER_MAX | 插值编码掩码 |
cv2.WARP_FILL_OUTLIERS | 标志,填补目标图像中的所有像素。 如果它们中的一些对应源图像中的奇异点(离群值),则将它们设置为零 |
cv2.WARP_INVERSE_MAP | 标志:逆变换 。 例如,极坐标变换: • 如果 flag 未被设置,则进行转换:dst(φ,ρ)=src(x,y)dst(\varphi,\rho) = src(x,y)dst(φ,ρ)=src(x,y) • 如果 flag 被设置,则进行转换:dst(x,y)=src(φ,ρ)dst(x,y) = src(\varphi,\rho)dst(x,y)=src(φ,ρ) |
在 cv2.resize()函数中,目标图像的大小可以通过"参数 dsize"或者"参数 fx 和 fy"二者之一来指定
- 通过参数dsize指定
如果指定参数 dsize 的值,则无论是否指定了参数 fx 和 fy 的值,都由参数 dsize 来决定目
标图像的大小。
dsize 内第 1 个参数对应缩放后图像的宽度(width,即列数 cols,与参数 fx 相关),第 2 个参数对应缩放后图像的高度(height,即行数 rows,与参数 fy 相关)。
在 dsize 参数中,第 1 个值对应的是列数 ,第 2 个值对应的是行数。 - 通过参数fx和参数fy指定
如果参数 dsize 的值是 None,那么目标图像的大小通过参数 fx 和 fy 来决定。此时,目标
图像的大小为:
dsize=Size(round(fx*src.cols),round(fy*src.rows))
插值 是指在对图像进行几何处理时,给无法直接通过映射得到值的像素点赋值。
当缩小图像时,使用区域插值方式(INTER_AREA) 能够得到最好的效果;当放大图像时,使用三次样条插值(INTER_CUBIC) 方式和双线性插值(INTER_LINEAR) 方式都能够取得较好的效果。三次样条插值方式速度较慢,双线性插值方式速度相对较快且效果并不逊色。
使用函数 cv2.resize()对一个数组进行简单缩放。
python
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)
#在 shape 属性中,第 1 个值对应的是行数,第 2 个值对应的是列数。
#在 dsize 参数中,第 1 个值对应的是列数,第 2 个值对应的是行数。
使用函数 cv2.resize()完成一个简单的图像缩放。
python
import cv2
import numpy as np
img = cv2.imread("lena.jpg")
row,cols=img.shape[:2]
size=(int(cols*0.9),int(row*0.5))
rst=cv2.resize(img,size)
print("img.shape=\n",img.shape)
print("rst.shape=\n",rst.shape)
控制函数 cv2.resize()的 fx 参数、fy 参数,完成图像缩放。
python
import cv2
img = cv2.imread("lena.jpg")
rst=cv2.resize(img,None,fx=2,fy=0.5)
print("img.shape=",img.shape)
print("rst.shape=",rst.shape)
5.2翻转
在 OpenCV 中,图像的翻转采用函数 cv2.flip()
实现,该函数能够实现图像在水平方向翻转 、垂直方向翻转 、两个方向同时翻转,其语法结构为:
dst = cv2.flip( src, flipCode )
dst
代表和原始图像具有同样大小、类型的目标图像。src
代表要处理的原始图像。flipCode
代表旋转类型。- 参数值=0 绕着x轴翻转
- 参数值>0 绕着y轴翻转
- 参数值<0 围绕x轴、y轴翻转
python
import cv2
img = cv2.imread("lena.jpg")
x=cv2.flip(img,0)
y=cv2.flip(img,1)
xy=cv2.flip(img,-1)
cv2.imshow("img",img)
cv2.imshow("x",x)
cv2.imshow("y",y)
cv2.imshow("xy",xy)
cv2.waitKey()
cv2.destroyAllWindows()

5.3仿射
仿射变换 是指图像可以通过一系列的几何变换来实现平移、旋转等多种操作。该变换能够保持图像的平直性和平行性。
OpenCV 中的仿射函数为 cv2.warpAffine(),其通过一个变换矩阵(映射矩阵)M 实现变换

用仿射函数 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 的逆变换。的实际大小。borderMode
代表边类型, 默认为 BORDER_CONSTANT 。当该值为 BORDER_TRANSPARENT 时,意味着目标图像内的值不做改变,这些值对应原始图像内的异常值。borderValue
代表边界值,默认是 0。
通过转换矩阵 M 将原始图像 src 转换为目标图像 dst:
dst(x,y)=src(M11x+M12y+M13,M21x+M22y+M23) dst(x,y)=src(M_{11}x+M_{12}y+M_{13},M_{21}x+M_{22}y+M_{23}) 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 (x + 100, y + 200)dst(x,y)=src(x+100,y+200)
即:
dst(x,y)=src(1⋅x+0⋅y+100,0⋅x+1⋅y+200)dst (x, y) = src (1·x + 0·y + 100, 0·x + 1·y + 200)dst(x,y)=src(1⋅x+0⋅y+100,0⋅x+1⋅y+200)
转换矩阵MMM为:
M=[1010001200] M = \begin{bmatrix} 1 & 0 & 100 \\ 0 & 1 & 200 \end{bmatrix} M=[1001100200]
利用自定义转换矩阵完成图像平移。
python
import cv2
import numpy as np
img = cv2.imread("lena.jpg")
heigh,width =img.shape[:2]
x=100
y=200
M = np.float32([[1,0,x],
[0,1,y]])
move=cv2.warpAffine(img,M,(width,heigh))
cv2.imshow("original",img)
cv2.imshow("move",move)
cv2.waitKey()
cv2.destroyAllWindows()

旋转
在使用函数 cv2.warpAffine()
对图像进行旋转时,可以通过函数 cv2.getRotationMatrix2D()
获取转换矩阵M 。该函数的语法格式为:
M=cv2.getRotationMatrix2D(center, angle, scale)
- center为旋转的中心点。
- angle为旋转角度,正数 表示逆 时针旋转,负数 表示顺时针旋转。
- scale为变化尺度(缩放大小)。
图像旋转
python
import cv2
import numpy as np
img = cv2.imread("D:\openCV\lena.jpg")
heigh,width = img.shape[:2]
M = cv2.getRotationMatrix2D((heigh/2,width/2),45,0.6)
rotate = cv2.warpAffine(img,M,(width,heigh))
cv2.imshow("original",img)
cv2.imshow("rotate",rotate)
cv2.waitKey()
cv2.destroyAllWindows()

更复杂的仿射变换
函数 cv2.getAffineTransform()
来生成仿射函数 cv2.warpAffine()
所使用的转换矩阵 M
语法格式:
M=cv2.getAffineTransform(src, dst)
- src代表输入图像三个点坐标
- dst代表输出图像三个点坐标
参数通过函数cv2.getAffineTransform()
定义了两个平行四边形。src 和 dst 中的三个点分别对应平行四边形的左上角、右上角、左下角三个点。
python
import cv2
import numpy as np
img = cv2.imread("lena.jpg")
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("original",img)
cv2.imshow("dst",dst)
cv2.waitKey()
cv2.destroyAllWindows()

5.4透视
仿射变换可以将矩形映射为任意平行四边形 ,透视变换则可以将矩形映射为任意四边形。
透视变换通过函数 cv2.warpPerspective()
实现,该函数的语法是:
dst = cv2.warpPerspective( src, M, dsize[, flags[, borderMode[, borderValue]]] )
- dst 代表透视处理后的输出图像,该图像和原始图像具有相同的类型。dsize 决定输出图像的实际大小。
- src 代表要透视的图像。
- M 代表一个 3×3 的变换矩阵。
- dsize 代表输出图像的尺寸大小。
- flags 代表插值方法,默认为 INTER_LINEAR。当该值为 WARP_INVERSE_MAP 时,意味着 M 是逆变换类型,能实现从目标图像 dst 到原始图像 src 的逆变换。
- borderMode 代表边类型, 默认为 BORDER_CONSTANT 。当该值为 BORDER_TRANSPARENT 时,意味着目标图像内的值不做改变,这些值对应原始图像内的异常值。
使用函数cv2.warpPerspective()
生成转换矩阵M 。
M = cv2.getPerspectiveTransform( src, dst )
- src 代表输入图像的四个顶点的坐标。
- dst 代表输出图像的四个顶点的坐标。
5.5重映射
重映射: 把一幅图像内的像素点放置到另外一幅图像内的指定位置,通过修改像素点的位置得到一幅新图像
的重映射函数cv2.remap()
语法格式:
dst = cv2.remap( src, map1, map2, interpolation[, borderMode[, borderValue]] )
- dst 代表目标图像,它和 src 具有相同的大小和类型。
- src 代表原始图像。
- map1 参数有两种可能的值:
- 表示(x,y)点的一个映射。
- 表示 CV_16SC2 , CV_32FC1, CV_32FC2 类型(x,y)点的 x 值。
- map2 参数同样有两种可能的值:
- 当 map1 表示(x,y)时,该值为空。
- 当 map1 表示(x,y)点的 x 值时,该值是 CV_16UC1, CV_32FC1 类型(x,y)点的 y 值。
- Interpolation 代表插值方式,这里不支持 INTER_AREA 方法。
- borderMode 代表边界模式。当该值为 BORDER_TRANSPARENT 时,表示目标图像内的对应源图像内奇异点(outliers)的像素不会被修改。
- borderValue 代表边界值,该值默认为 0。
映射参数的理解
映射函数的作用是查找新图像像素在原始图像内的位置。将新图像像素映射到原始图像的过程被称为反向映射 。
需要注意的是,函数 cv2.remap()中参数 map1 指代的是像素点所在位置的列号,参数 map2指代的是像素点所在位置的行号。
使用 cv2.remap()完成数组映射
python
import cv2
import numpy as np
img = np.random.randint(0,256,size = [4,5],dtype=np.uint8)
rows,cols = img.shape[0:2]
mapx = np.ones(img.shape,np.float32)*3
mapy = np.ones(img.shape,np.float32)*0
rst=cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
print("img=\n",img)
print("mapx=\n",mapx)
print("mapy=\n",mapy)
print("rst=\n",rst)
复制
用函数 cv2.remap()实现数组复制
相当与img[mapy,mapx]
赋值给了rst
pyhton
import cv2
import numpy as np
img = np.random.randint(0,256,size=[4,5],dtype=np.uint8)
rows,cols=img.shape[0:2]
mapx = np.zeros(img.shape,np.float32)
mapy = np.zeros(img.shape,np.float32)
for i in range(rows):
for j in range(cols):
mapx[i,j]=j
mapy[i,j]=i
rst = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
print("img=\n",img)
print("mapx=\n",mapx)
print("mapy=\n",mapy)
print("rst=\n",rst)
绕x轴翻转
让图像绕着 x 轴翻转,意味着在映射过程中:
- x 坐标轴的值保持不变。
- y 坐标轴的值以 x 轴为对称轴进行交换。
用函数 cv2.remap()实现数组绕 x 轴翻转
python
import cv2
import numpy as np
img = np.random.randint(0,256,size=[4,5],dtype=np.uint8)
rows,cols=img.shape[0:2]
mapx = np.zeros(img.shape,np.float32)
mapy = np.zeros(img.shape,np.float32)
for i in range(rows):
for j in range(cols):
mapx[i,j]=j
mapy[i,j]=rows-i-1
rst = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
print("img=\n",img)
print("mapx=\n",mapx)
print("mapy=\n",mapy)
print("rst=\n",rst)
使用函数 cv2.remap()实现图像绕 x 轴的翻转
python
import cv2
import numpy as np
img = cv2.imread("lena.jpg")
rows,cols = img.shape[0:2]
mapx = np.zeros(img.shape[:2],np.float32)
mapy = np.zeros(img.shape[:2],np.float32)
for i in range(rows):
for j in range(cols):
mapx[i,j]=j
mapy[i,j]=rows-1-i
rst=cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
cv2.imshow("original",img)
cv2.imshow("result",rst)
cv2.waitKey()
cv2.destroyAllWindows()

绕y轴翻转
让图像绕着 y 轴翻转,意味着在映射过程中:
- y 坐标轴的值保持不变。
- x 坐标轴的值以 y 轴为对称轴进行交换。
用函数 cv2.remap()实现数组绕 y 轴翻转。
python
import cv2
import numpy as np
img=np.random.randint(0,256,size=[4,5],dtype=np.uint8)
rows,cols=img.shape
mapx = np.zeros(img.shape,np.float32)
mapy = np.zeros(img.shape,np.float32)
for i in range(rows):
for j in range(cols):
mapx[i,j]=cols-1-j
mapy[i,j]=i
rst=cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
print("img=\n",img)
print("mapx=\n",mapx)
print("mapy=\n",mapy)
print("rst=\n",rst)
使用函数 cv2.remap()实现图像绕 y 轴的翻转
python
import cv2
import numpy as np
img=cv2.imread("lena.bmp")
rows,cols=img.shape[:2]
mapx = np.zeros(img.shape[:2],np.float32)
mapy = np.zeros(img.shape[:2],np.float32)
for i in range(rows):
for j in range(cols):
mapx[i,j]=cols-1-j
mapy[i,j]=i
rst=cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
cv2.imshow("original",img)
cv2.imshow("result",rst)
cv2.waitKey()
cv2.destroyAllWindows()

绕x轴、y轴翻转
让图像绕着 x 轴、y 轴翻转,意味着在映射过程中:
- x 坐标轴的值以 y 轴为对称轴进行交换。
- y 坐标轴的值以 x 轴为对称轴进行交换。
用函数 cv2.remap()实现数组绕 x 轴、y 轴翻转
python
import cv2
import numpy as np
img=np.random.randint(0,256,size=[4,5],dtype=np.uint8)
rows,cols=img.shape
mapx = np.zeros(img.shape,np.float32)
mapy = np.zeros(img.shape,np.float32)
for i in range(rows):
for j in range(cols):
mapx[i,j]=cols-1-j
mapy[i,j]=rows-1-i
rst=cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
print("img=\n",img)
print("mapx=\n",mapx)
print("mapy=\n",mapy)
print("rst=\n",rst)
用函数 cv2.remap()实现图像绕 x 轴、y 轴翻转
python
import cv2
import numpy as np
img=cv2.imread("lena.jpg")
rows,cols=img.shape[:2]
mapx = np.zeros(img.shape[:2],np.float32)
mapy = np.zeros(img.shape[:2],np.float32)
for i in range(rows):
for j in range(cols):
mapx[i,j]=cols-1-j
mapy[i,j]=rows-1-i
rst=cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
cv2.imshow("original",img)
cv2.imshow("result",rst)
cv2.waitKey()
cv2.destroyAllWindows()

x轴、y轴互换
让图像的 x 轴、y 轴互换,意味着在映射过程中,对于任意一点,都需要将其 x 轴、y 轴坐标互换。
- mapx 的值调整为所在行的行号。
- mapy 的值调整为所在列的列号。
用函数 cv2.remap()实现数组的 x 轴、y 轴互换
类似于矩阵的转置,但出现行、列不同时会自动补0
使用函数 cv2.remap()实现数组的 x 轴、y 轴互换
python
import cv2
import numpy as np
img=np.random.randint(0,256,size=[4,6],dtype=np.uint8)
rows,cols=img.shape[0:2]
mapx = np.zeros(img.shape,np.float32)
mapy = np.zeros(img.shape,np.float32)
for i in range(rows):
for j in range(cols):
mapx[i,j]=i
mapy[i,j]=j
rst=cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
print("img=\n",img)
print("mapx=\n",mapx)
print("mapy=\n",mapy)
print("rst=\n",rst)
使用函数 cv2.remap()实现图像的 x 轴、y 轴互换
python
import cv2
import numpy as np
img=cv2.imread("lena.jpg")
rows,cols=img.shape[:2]
mapx = np.zeros(img.shape[:2],np.float32)
mapy = np.zeros(img.shape[:2],np.float32)
for i in range(rows):
for j in range(cols):
mapx[i,j]=i
mapy[i,j]=j
rst=cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
cv2.imshow("original",img)
cv2.imshow("result",rst)
cv2.waitKey()
cv2.destroyAllWindows()

图像缩放
python
import cv2
import numpy as np
img=cv2.imread("lena.jpg")
rows,cols=img.shape[:2]
mapx = np.zeros(img.shape[:2],np.float32)
mapy = np.zeros(img.shape[:2],np.float32)
for i in range(rows):
for j in range(cols):
if 0.25*cols<i<0.75*cols and 0.25*rows<j<0.75*rows:
mapx[i,j]=2*(j-cols*0.25)+0.5
mapy[i,j]=2*(i-rows*0.25)+0.5
else:
mapx[i,j]=0
mapy[i,j]=0
rst = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)
cv2.imshow("original",img)
cv2.imshow("result",rst)
cv2.waitKey()
cv2.destroyAllWindows()

疑问: OpenCv中明明有专门进行翻转、缩放的函数,为什么还要使用重映射函数进行翻转、缩放呢?
操作对象的抽象层级不同
函数 | 关注层级 | 原理 |
---|---|---|
cv2.flip / cv2.resize |
整幅图像或整体规则 | 内部写死了某种几何变换(翻转、缩放),直接调用即可 |
cv2.remap |
像素级别 | 你要自己告诉 OpenCV:目标图像里的每个像素,该去源图像的哪个位置采样 |
cv2.flip
、cv2.resize
、cv2.warpAffine
、cv2.warpPerspective
👉 只能做 固定的线性或简单几何变换。cv2.remap
👉 可以做 任意非线性映射。