關於圖像旋轉以及旋轉後對應像素的位置
參考鏈接: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
關於圖像旋轉以及旋轉後對應像素的位置