参考链接:http://www.echojb.com/image/2016/11/14/258268.html
一、首先来说一下关于像素旋转一定角度后的对应位置:
(1)旋转中心为左上角原点:
旋转有一个绕什么转的问题。我们先来看最简单的,绕第一个像素转,则旋转的情况会像这样:
令旋转前有
旋转a角度后有
以矩阵形式表示为
(2)旋转中心为图像中心:
当图片较大时,计算会很慢。主要是判断和计算太多了这里只讨论图像处理,程序的优化暂时放一边运行结果如下:
我们能看到,旋转后的图像有很多“蜂窝煤”。主要是点转换后要取整。导致原图中有些点映射到同一个点,而生成的图中有些点在原图中没有点映射到它。所以出现了很多“蜂窝煤”。果然理论还只是理论啊 下面我们来看看更通常一点的做法:以图像的中心为圆心进行旋转。这里涉及到一个坐标系的转换问题。看下图:
在矩阵中我们的坐标系通常是AB和AC方向的,而传统的笛卡尔直角坐标系是DE和DF方向的。令图像表示为M×N的矩阵,对于点A而言,两坐标系中的坐标分别是(0,0)和(-N/2,M/2)矩阵中点(x‘,y‘)转换为笛卡尔坐标系(x,y)的转换关系为:
逆变换为
于是我们得到图像以中心旋转的思路
- 将矩阵坐标上点(原谅我这样称呼它)转换为笛卡尔坐标系
- 将该点旋转a度。旋转公式前面已经给出了
- 将旋转后的点再转换为矩阵坐标
于是得到最后结果
python中numpy有矩阵运算能力,但这里我们直接进行数值计算就可以了。用方程表示如下:
二、关于图像旋转:
M = cv2.getRotationMatrix2D(crop_center, (a * (-1)), 1.0) #顺时针旋转 tar_img = cv2.warpAffine(crop_img, M, (crop_width, crop_height)) #旋转后的图片
如上,首先使用getRotationMatrix2D获取旋转矩阵,然后再使用warpAffine利用这个矩阵进行旋转;
总结:计算像素旋转的核心算法其实就是像平面坐标系和笛卡尔坐标系之间的转换,因为直接旋转像平面坐标系不太好计算旋转之后的像素的位置,但是基于笛卡尔坐标系下计算像素旋转后的位置却很容易,因此这里引入了笛卡尔坐标系作为一个辅助坐标系,这种思想很重要的!比如在摄影测量学中,为了建立像平面上的点和对应的大地坐标下的点的对应关系,引入了像空坐标系,像辅坐标系,这样一来通过像平面坐标系-》像空坐标系-》像辅坐标系-》大地坐标系,就建立了对应关系。所以通过构建辅助坐标系在计算空间关系时特别有用!!!
注意:像素旋转中使用到的公式针对的是逆时针旋转,而图像旋转中使用到的是顺时针旋转!!!所以在同时使用的时候要特别注意这一点(可能要乘一个-1)!
放上一段我自己写的代码,用到了这两个旋转:
def rotation(src_img_size , crop_img , src_box_info , src_pts , angle): #返回旋转后的pts坐标和boc框 src_height = int(src_img_size[0]) #原始大图片的尺寸 src_width = int(src_img_size[1]) crop_height = crop_img.shape[0] #截取的图片尺寸 crop_width = crop_img.shape[1] tar_pts = np.zeros((4 , 2) , dtype = int) tar_box_info = np.zeros((2 , 2) , dtype = int) #存储缩放后的box框 a = random.randint(0 , angle) angle_pi = a * math.pi / 180.0 #crop_img进行旋转============================================================================ crop_center = (crop_width / 2 , crop_height / 2) #旋转中心设为图片中心 M = cv2.getRotationMatrix2D(crop_center, (a * (-1)), 1.0) #顺时针旋转 tar_img = cv2.warpAffine(crop_img, M, (crop_width, crop_height)) #旋转后的图片 #============================================================================================ for i in range(len(src_pts) / 2): tar_pts[i][0] = int(int(src_pts[2 * i]) * math.cos(angle_pi) - int(src_pts[2 * i + 1]) * math.sin(angle_pi) - 0.5 * src_width * math.cos(angle_pi) + 0.5 * src_height * math.sin(angle_pi) + 0.5 * src_width) tar_pts[i][1] = int(int(src_pts[2 * i]) * math.sin(angle_pi) + int(src_pts[2 * i + 1]) * math.cos(angle_pi) - 0.5 * src_width * math.sin(angle_pi) - 0.5 * src_height * math.cos(angle_pi) + 0.5 * src_height) for i in range(len(src_box_info) / 2): tar_box_info[i][0] = int(int(src_box_info[2 * i]) * math.cos(angle_pi) - int(src_box_info[2 * i + 1]) * math.sin(angle_pi) - 0.5 * src_width * math.cos(angle_pi) + 0.5 * src_height * math.sin(angle_pi) + 0.5 * src_width) tar_box_info[i][1] = int(int(src_box_info[2 * i]) * math.sin(angle_pi) + int(src_box_info[2 * i + 1]) * math.cos(angle_pi) - 0.5 * src_width * math.sin(angle_pi) - 0.5 * src_height * math.cos(angle_pi) + 0.5 * src_height) return tar_img , tar_box_info , tar_pts
原文地址:https://www.cnblogs.com/zf-blog/p/8947800.html