使用opencv对图像进行旋转的代码随手一搜即得,但是有些旋转后图像会不完整,有些只给出代码并未解释其实现原理。本文会详细介绍如何使用opencv实现图像旋转得到完整图像,以及其中的实现原理。
最终实现效果:
1. getRotationMatrix2D详解
opencv的getRotationMatrix2D函数可以获取旋转变换矩阵。输入中心点坐标(centerX,centerY),旋转角度theta,缩放比例,给出M变换矩阵
那这个矩阵到底如何计算得到的呢?
我们先对一个点基于原点进行旋转,如下图,将V1点逆时针旋转theta角度到V2点,缩放比例我们先假定为1.
V1点和原点连线与水平线夹角a,V2点和原点连线与水平线夹角b=a theta。计算旋转变换矩阵
记V1 =(x1, y1),V2 = (x2, y2)
那么
x1 = cosa
y1 = sina
x2 = cos(a theta) = costheta*cosa - sina*sintheta
y2 = sin(a b) = sina*costheta cosa*sintheta
将x1,y1带入
x2=x1costheta-y1sintheta
y2=y1costheta x1sintheta = x1sintheta y1costheta
输出矩阵形式
begin{bmatrix} x2 \ y2 end{bmatrix} = begin{bmatrix} costheta & -sintheta \ sintheta & costheta end{bmatrix} * begin{bmatrix} x1 \ y1 end{bmatrix}
但是通常我们会基于中心点进行旋转,如果是需要绕任意点(tx,ty)旋转,我们可以
1.先把旋转点平移到原点
2.然后进行以上旋转操作
3.按1的逆操作平移回去
就可以得到绕任意点旋转点变换矩阵:
以上就是旋转矩阵M的由来。
2. warpAffine操作
2.1 获取M矩阵
得到变换矩阵M,对图像每个点进行M变换就可以得到旋转后的图像,这一步可以通过opencv的warpAffine得到。但是通过以上操作,旋转后大图像会丢失信息,如下图所示:
2.2 扩大画布
画布大小不变的情况下,会有一部分图像超出,显示不全,所以我们需要将画布扩大为:
新的高由图片中两段蓝色线组合
new_H = int(w * fabs(sin(radians(angle))) h * fabs(cos(radians(angle))))
新的宽由图片中两段红色线组合
new_W = int(h * fabs(sin(radians(angle))) w * fabs(cos(radians(angle))))
新的画布扩大是基于原图左上角点扩大,显示的还是蓝色区域,同样丢失了信息。
2.3 平移图像
我们还需要将红色区域进行平移操作显示到蓝色区域
所以,在变换矩阵M上,我们可以调整平移参数:
M[0, 2] = (new_W - w) / 2
M[1, 2] = (new_H - h) / 2
通过以上操作就可以显示出文章最初到效果了。
最后附上使用opencv进行图像旋转并且不丢失信息到完整代码:
代码语言:txt复制def opencv_rotate(img, angle):
h, w = img.shape[:2]
center = (w / 2, h / 2)
scale = 1.0
# 2.1获取M矩阵
"""
M矩阵
[
cosA -sinA (1-cosA)*centerX sinA*centerY
sinA cosA -sinA*centerX (1-cosA)*centerY
]
"""
M = cv2.getRotationMatrix2D(center, angle, scale)
# 2.2 新的宽高,radians(angle) 把角度转为弧度 sin(弧度)
new_H = int(w * fabs(sin(radians(angle))) h * fabs(cos(radians(angle))))
new_W = int(h * fabs(sin(radians(angle))) w * fabs(cos(radians(angle))))
# 2.3 平移
M[0, 2] = (new_W - w) / 2
M[1, 2] = (new_H - h) / 2
rotate = cv2.warpAffine(img, M, (new_W, new_H), borderValue=(0, 0, 0))
return rotate