AI学习者 · 2020年12月18日

经验 | OpenCV图像旋转的原理与技巧

文章转载于微信公众号:OpenCV学堂
作者: gloomyfish

01引言

初学图像处理,很多人遇到的第一关就是图像旋转,图像旋转是图像几何变换中最具代表性的操作,包含了插值、背景处理、三角函数等相关知识,一个变换矩阵跟计算图像旋转之后的大小公式就让很多开发者最后直接调用函数了事,但是其实这个东西并没有这么难懂,可以说主要是之前别人写的公式太吓人,小编很久以前第一次接触的也是被吓晕了!所以决定从程序员可以接受的角度从新介绍一下图像旋转基本原理与OpenCV中图像旋转函数操作的基本技巧。

图像旋转基本原理

旋转涉及到两个问题,一个是图像旋转之后的大小会发生改变,会产生背景,通过背景填充方式都是填充黑色,此外旋转还是产生像素的位置迁移,新的位置像素需要通过插值计算获得,常见的插值方式有最近邻、线性插值、立方插值等。

首先看旋转之后的图像宽高变化,如下图所示:

image.png

image.png

函数支持

OpenCV中支持图像旋转的函数有两个,一个是直接支持旋转的函数,但是它支持的是90,180,270这样的特殊角度旋转。

void cv::rotate   (
    InputArray    src,
    OutputArray dst,
    int rotateCode
)

其中rotateCode参数必须为:

ROTATE_180,
ROTATE_90_CLOCKWISE
ROTATE_90_COUNTERCLOCKWISE 

函数warpAffine支持任意角度的旋转,通过定义M矩阵实现

void cv::warpAffine(
         InputArray      src, // 输入图像
         OutputArray dst, // 输出图像
         InputArray      M, // 旋转矩阵
         Size         dsize, // 输出图像大小
         int   flags = INTER_LINEAR, // 像素插值方式
         int   borderMode = BORDER_CONSTANT, // 背景填充默认为常量
         const Scalar &        borderValue = Scalar() // 填充颜色默认为黑色
)

但是M如何生成与获取,OpenCV中提供了一个函数根据输入的参数自动生成旋转矩阵M,该函数为:


Mat cv::getRotationMatrix2D(
         Point2f   center,
         double    angle,
         double    scale
)

代码演示

原图

image.png

使用自定义的M矩阵实现图像旋转

h, w, c = src.shape# 定义矩阵M = np.zeros((2, 3), dtype=np.float32)# 定义角度alpha = np.cos(np.pi / 4.0)beta = np.sin(np.pi / 4.0)print("alpha : ", alpha)# 初始化矩阵M[0, 0] = alphaM[1, 1] = alphaM[0, 1] = betaM[1, 0] = -betacx = w / 2cy = h / 2tx = (1-alpha)*cx - beta*cyty = beta*cx + (1-alpha)*cyM[0,2] = txM[1,2] = ty# 执行旋转dst = cv.warpAffine(src, M, (w, h))cv.imshow("rotate-center-demo", dst)

image.png

重新计算旋转之后的图像大小,实现无Crop版本的图像旋转

h, w, c = src.shapeM = np.zeros((2, 3), dtype=np.float32)alpha = np.cos(np.pi / 4.0)beta = np.sin(np.pi / 4.0)print("alpha : ", alpha)# 初始旋转矩阵M[0, 0] = alphaM[1, 1] = alphaM[0, 1] = betaM[1, 0] = -betacx = w / 2cy = h / 2tx = (1-alpha)*cx - beta*cyty = beta*cx + (1-alpha)*cyM[0,2] = txM[1,2] = ty# change with full sizebound_w = int(h * np.abs(beta) + w * np.abs(alpha))bound_h = int(h * np.abs(alpha) + w * np.abs(beta))# 添加中心位置迁移M[0, 2] += bound_w / 2 - cxM[1, 2] += bound_h / 2 - cydst = cv.warpAffine(src, M, (bound_w, bound_h))cv.imshow("rotate without cropping", dst)

image.png

背景随便变化+无Crop版本的图像旋转动态演示

degree = 1.0d1 = np.pi / 180.0while True:    alpha = np.cos(d1*degree)    beta = np.sin(d1*degree)    M[0, 0] = alpha    M[1, 1] = alpha    M[0, 1] = beta    M[1, 0] = -beta    cx = w / 2    cy = h / 2    tx = (1 - alpha) * cx - beta * cy    ty = beta * cx + (1 - alpha) * cy    M[0, 2] = tx    M[1, 2] = ty    # change with full size    bound_w = int(h * np.abs(beta) + w * np.abs(alpha))    bound_h = int(h * np.abs(alpha) + w * np.abs(beta))    M[0, 2] += bound_w / 2 - cx    M[1, 2] += bound_h / 2 - cy    red = np.random.randint(0, 255)    green = np.random.randint(0, 255)    blue = np.random.randint(0, 255)    dst = cv.warpAffine(src, M, (bound_w, bound_h), borderMode=cv.BORDER_CONSTANT, borderValue=(blue, green, red))    cv.imshow("rotate+background", dst)    c = cv.waitKey(1000)    if c == 27:        break    degree += 1    print("degree", degree)    if degree > 360:        degree = degree % 360

image.png
image.png

君子藏器于身,待时而动


推荐阅读

更多嵌入式AI技术干货请关注嵌入式AI专栏。
推荐阅读
关注数
18808
内容数
1352
嵌入式端AI,包括AI算法在推理框架Tengine,MNN,NCNN,PaddlePaddle及相关芯片上的实现。欢迎加入微信交流群,微信号:aijishu20(备注:嵌入式)
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息