几何变换

2022/11/12 openCV

# 思维导图

image-20221112094548009

# 缩放

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

dst = cv2.resize(src,dsize[,fx[,fy[,interpolation]]])
  • dst 输出的图像
  • src 原始图像
  • dsize 输出图像大小
  • fx 水平方向缩放比例
  • fy 垂直方向缩放比例
  • interpolation代表插值方式

图像的大小可以通过dsizefx和fy二者之一来指定

示例一

dsize中第一个值是列数,第二个值是行数

# 通过 dsize改变
import cv2
import numpy as np
img=cv2.imread("cat.jpg")
rows,cols=img.shape[:2]
size=(int(cols*0.9),int(rows*0.5))

rst = cv2.resize(img,size)
print(img.shape)
print(rst.shape)

结果

(484, 650, 3)
(242, 585, 3)

分析

列数 650*0.9 = 585
行数 484*0.5 = 242

示例二

# 通过 fx和fy
import cv2
import numpy as np
img=cv2.imread("cat.jpg")

rst = cv2.resize(img,None,fx=2,fy=0.5)
print(img.shape)
print(rst.shape)

结果

(484, 650, 3)
(242, 1300, 3)

分析

fx 水平方向变成2650*2 = 1300
fy 垂直方向变成0.6484*0.5 = 242

# 翻转

在OpenCV中,图像的翻转采用函数cv2.flip()实现,该函数能够实现图像在水平方向翻转、垂直方向翻转、两个方向同时翻转

dst = cv2.flip( src, flipCode )
  • dst 目标图像
  • src 原始图像
  • flipCode代表旋转类型

image.png

import cv2
import numpy as np
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)

image.png

# 仿射

仿射变换是指图像可以通过一系列的几何变换来实现平移、旋转等多种操作。

image.png

5.3.1 平移

将原始图像src向右移动100个像素、向下移动200个像素

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

将公式补充完整

image.png

矩阵M对应的值为

  • M11 = 1
  • M12 = 0
  • M13 = 100
  • M21 = 0
  • M22 = 1
  • M23 = 200

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

image.png

程序实现

import cv2
import numpy as np
img=cv2.imread("cat.jpg")
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))

5.3.2旋转

可以通过函数cv2.getRotationMatrix2D()获取转换矩阵

retval = cv2.getRotationMatrix2D(center,angle,scale)
  • center 旋转的中心点
  • angle 旋转的角度(正数表示逆时针旋转,负数表示顺时针旋转)
  • scale 为变换尺度(缩放大小)

程序实现

import cv2
import numpy as np
img=cv2.imread("cat.jpg")
height,width=img.shape[:2]
# 中心为宽度1/2,高度1/2
# 旋转45度
# 缩小0.6倍
M = cv2.getRotationMatrix2D((width/2,height/2),45,0.6)

rotate=cv2.warpAffine(img,M,(width,height))

5.3.3 更复杂的仿射变换

OpenCV提供了函数cv2.getAffineTransform()来生成仿射函数

retval = cv2.getAffineTransform(src,dst)
  • src 代表输入图像的三个点的坐标
  • dst 代表输出图像的三个点的坐标

程序实现

import cv2
import numpy as np
img=cv2.imread("cat.jpg")
rows,cols,ch=img.shape
# 输入图像3个点
p1=np.float32([[0,0],[cols-1,0],[0,rows-1]])
# 输出图像3个点
p2=np.float32([[0,rows*0.33],[cols*0.85,rows*0.25],[cols*0.15,rows*0.17]])
# 转换矩阵
M = cv2.getAffineTransform(p1,p2)
# 转换
dst=cv2.warpAffine(img,M,(width,height))

# 透视

仿射可以将矩形映射为任意平行四边形,透视变换则可以将矩形映射为任意四边形

dst = cv2.warpPerspective(src,M,dsize[,flags[,borderMode[,borderValue]]])
  • dst 透视处理后的图像。dsize决定输出图像的实际大小
  • src 原始图像
  • M代表一个3x3的变换矩阵
  • dsize 图像尺寸大小
  • flags 代表插值方法
  • borderMode 代表边类型
  • borderValue 代表边界值,默认0

生成转换矩阵

retval = cv2.getPerspectiveTransform( src,dst )
  • src 表示输入图像的四个顶点坐标
  • dst 表示输出图像的四个顶点坐标

程序设计

import cv2
import numpy as np
img=cv2.imread("cat.jpg")
rows,cols,ch=img.shape
# 输入图像3个点
pts1=np.float32([[150,59],[400,50],[60,450],[310,450]])
# 输出图像3个点
pts2=np.float32([[50,50],[rows-50,50],[50,cols-50],[rows-50,cols-50]])
# 转换矩阵
M = cv2.getPerspectiveTransform(pts1,pts2)
# 转换
dst=cv2.warpPerspective(img,M,(cols,rows))

image.png

# 重映射

把一幅图内的像素点放置到另外一副图像内的指定位置,这个过程称为重映射。

dst = cv2.remap(src,map1,map2,interpolation[,borderModel,[,borderValue])
  • dst 代表目标图像,它和src具有相同的大小和类型

  • src 原始图像

  • map参数有两种可能的值:

    • 表示(x,y)点的一个映射。
    • 表示CV_16SC2,CV_32FC1,CV_32FC2类型(x,y)点的值
  • Interpolation代表插值方式。

  • borderModel代表边界模式。当该值为BORDER_TRANSPARENT时,表示目标图像内的对应源图像内奇异点(outliers)的像素不会被修改。

  • borderValue代表边界值,该值默认为0

5.5.2 复制

将对应的x,y点进行复制

import cv2
import numpy as np
img=np.random.randint(0,256,size=[4,5],dtype=np.uint8)

rows,cols = img.shape

# cols
mapx = np.zeros(img.shape,np.float32)
# row
mapy = np.zeros(img.shape,np.float32)

for i in range(rows):
    for j in range(cols):
        mapx.itemset((i,j),j)
        mapy.itemset((i,j),i)
# 复制
rst = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)

print(img)
print(mapx)
print(mapy)
print(rst)

image.png image.png image.png image.png

分析

y的0,x的[0-9]   [0][0] [0][1] ....

5.5.3 绕x轴旋转

  • x轴坐标保持不变(map1保持不变)
  • y轴坐标的值以x轴为对称交换(map2 = 总行数-1-当前行数)
import cv2
import numpy as np
img=np.random.randint(0,256,size=[4,5],dtype=np.uint8)

rows,cols = img.shape

# cols
mapx = np.zeros(img.shape,np.float32)
# row
mapy = np.zeros(img.shape,np.float32)

for i in range(rows):
    for j in range(cols):
        mapx.itemset((i,j),j)
        mapy.itemset((i,j),rows-1-i)
# 复制
rst = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)

print(img)
print(mapx)
print(mapy)
print(rst)

image.png image.png image.png image.png

关键代码

mapy.itemset((i,j),rows-1-i)

5.5.4 绕y轴旋转

  • y轴坐标轴的值保持不变(map2保持不变)
  • x坐标轴的值以y轴为对称轴进行交换(map1的值调整为 总列数-1-当前列号)
import cv2
import numpy as np
img=np.random.randint(0,256,size=[4,5],dtype=np.uint8)

rows,cols = img.shape

# cols
mapx = np.zeros(img.shape,np.float32)
# row
mapy = np.zeros(img.shape,np.float32)

for i in range(rows):
    for j in range(cols):
        mapx.itemset((i,j),cols-1-j)
        mapy.itemset((i,j),i)
# 复制
rst = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)

print(img)
print(mapx)
print(mapy)
print(rst)

image.png image.png image.png image.png

关键代码

mapx.itemset((i,j),cols-1-j)

5.5.7 图像缩放

  • 在目标图像的x轴(0.25×X轴长度,0.75×X轴长度)区间内生成缩小图像;x轴其余区域的点取样自x轴上任意一点的值。 解释
  • 在目标图像的x轴(0.25×Y轴长度,0.75×Y轴长度)区间内生成缩小图像;y轴其余区域的点取样自x轴上任意一点的值。
import cv2
import numpy as np

img = cv2.imread("cat.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.itemset((i,j),2*(j-cols*0.25)+0.5)
            mapx.itemset((i,j),2*(i-rows*0.25)+0.5)
        else:
                mapx.itemset((i,j),0)
                mapy.itemset((i,j),0)
rst = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
cv2.imshow("original",img)
cv2.imshow("result",rst)

image-20220530230914-3gmzgpc