01
引言
初学图像处理,很多人遇到的第一关就是图像旋转,图像旋转是图像几何变换中最具代表性的操作,包含了插值、背景处理、三角函数等相关知识,一个变换矩阵跟计算图像旋转之后的大小公式就让很多开发者最后直接调用函数了事,但是其实这个东西并没有这么难懂,可以说主要是之前别人写的公式太吓人,小编很久以前第一次接触的也是被吓晕了!所以决定从程序员可以接受的角度从新介绍一下图像旋转基本原理与opencv中图像旋转函数操作的基本技巧。
图像旋转基本原理
旋转涉及到两个问题,一个是图像旋转之后的大小会发生改变,会产生背景,通过背景填充方式都是填充黑色,此外旋转还是产生像素的位置迁移,新的位置像素需要通过插值计算获得,常见的插值方式有最近邻、线性插值、立方插值等。
首先看旋转之后的图像宽高变化,如下图所示:
这个是正常的平面坐标系中的旋转矩阵,可以简写为:
是一个2x3的矩阵,但是在图像中左上角是原点,要实现围绕图像的中心位置旋转,m就要重新计算,所以opencv中的图像旋转矩阵为:
其中scale是表示矩阵支持旋转+放缩,这里可以把scale=1。第三列是图像旋转之后中心位置平移量。
函数支持
opencv中支持图像旋转的函数有两个,一个是直接支持旋转的函数,但是它支持的是90,180,270这样的特殊角度旋转。
void cv::rotate ( inputarray src, outputarray dst, int rotatecode) 其中rotatecode参数必须为:
rotate_180,rotate_90_clockwiserotate_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) 代码演示
使用自定义的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) 重新计算旋转之后的图像大小,实现无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) 背景随便变化+无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
对于超声波焊接机的使用问题,我们该如何解决
LISUN力汕的LPCE-3系列LED光谱仪积分球集成系统
IIC-China 2010参展商展前专访:高通高科技
北京科研单位和企业在5G国际标准上成为了5G技术标准重要贡献者
基于SoPC的状态监测装置的嵌入式软硬件协同设计与实现
OpenCV中图像旋转函数操作原理及基本技巧
华显光学总经理陈强:用好显微镜,管理好品质
东南大学与扬子集团联合研发的智能无人巴士首发试运行
研华推出DeviceOn/iEdge工业应用程序 加速实现数据整合与边缘智能管理
PCIe中三种基本的I/O架构
限制交流电的电流原理
特斯拉将在全球范围内将汽车价格平均上调大约3%
如何构建一个3d智能月球灯?
自动数粒仪的应用范围是怎样的,它的效果怎么样
FPGA学习之安全行为和非安全行为
ArduinoUltraMega计时器的制作
考德KT6000 155M传输分析仪 SDH数据分析仪
禾赛发布Pandar40P搭载无人送货车激光雷达
影响超声波液位计测量的常见因素及问题处理
小米笔记本Air评价,狠狠打了传统厂家一个耳光