Author:Maddock
Date:2015-01-01
轉載請注明出處:http://www.cnblogs.com/adong7639/p/4197341.html
參考 http://blog.csdn.net/xiaowei_cqu/article/details/7616044
圖像旋轉是圖像變換中的一種常見的操作,本文將從數學原理上來給出圖像旋轉的程序代碼實現。
圖像旋轉是將圖像圍繞某點為中心,順時針或者逆時針旋轉一定的角度,構成一幅新的圖像。按照不同的點旋轉最后生成的圖像大小和形狀是一致的,唯一的差別在於空間坐標系的位置不同,本文假定按照圖像的中心旋轉。這里有一篇博客將圖像旋轉的公式介紹的非常詳細http://hi.baidu.com/wangguang246/item/972a6508d16226dfdce5b040。本文直接給出圖像旋轉的基本公式如下:
其中(X0, Y0)為原始坐標系的坐標,(x, y) 為旋轉后的像素點的坐標,θ表示旋轉角度,順時針旋轉為正,逆時針旋轉為負。
旋轉變換的矩陣:
這種旋轉公式是一個線性變換,通過圖像的四個頂點坐標進行旋轉變換后,出現新的四個頂點坐標,圖像的其他像素點坐標經過同樣的變換都會落入到四個頂點構成的平行四邊形內部。如圖所示,P1~P4為圖像的四個頂點坐標,綠色方框表示原始圖像區域, 為新的坐標, 藍色區域為旋轉后的圖像區域。
原始圖像的寬度和高度分別為W和H,通過就可以確定旋轉后的圖像寬度W'和高度H'。
上面的步驟確定了新的像素點的坐標的位置,接下來就是計算新的像素點的像素值,這里采取后向映射的方法,原理如下:
-
首先可以根據原始變換矩陣H求出逆矩陣HINV,逆矩陣表示目標圖像的坐標系映射到原始圖像坐標系的對應關系。
根據博客http://hi.baidu.com/wangguang246/item/972a6508d16226dfdce5b040中給出的圖像坐標系到數學坐標系轉換方法得出最終的HINV矩陣如下:
-
-
根據依次遍歷目標圖像的每個點,根據HINV矩陣可以將目標圖像的任意像素點位置
Pdst映射到源圖像中,顯然按照公式計算出來的像素點位置坐標為小數,可能會落到非整數像素坐標點的位置,這是可以利用PSRC周圍的幾個像素點P1~P4的像素值來插值
通過P1 和P2可以計算出Z1的像素值,P3和P4可以計算出Z2的像素值,之后通過Z1和Z2可以計算出PSRC的像素值。
P1(X1,Y1), P2(X2,Y2) , P3(X3,Y3), P4(X4,Y4), Z1(XZ1,YZ1) , Z1(XZ2,YZ2)
PSRC(DX, DY)
X1 = (int)DX;
Y1 = (int)DY;
X2 = X1 + 1;
Y2 = Y1;
X3 = X1 + 1;
Y3 = Y3 + 1;
X4 = X1;
Y4 = Y4 + 1;
設定P( )表示某一像素點的像素值大小
P(Z1) = P(P1) * K1 + (1- K1) *P(P2); K1 = DX – (int)DX;
P(Z2) = P(P3) * K1 + (1- K1) *P(P4); K1 = DX – (int)DX;
P(PSRC) = P(Z1) * K2 + (1- K2) *P(Z2); K2 = DY – (int)DY;
完整的代碼如下:
mat 版本
#include<cv.h>
#include<highgui.h>
#include <opencv2/opencv.hpp>
#include <cmath>
#include <stdlib.h>
#include <iostream>
using namespace std;
using namespace cv;
const double PI = 3.141592653;
const char *strimg = "woman.jpg"; int main() { Mat image; image = imread(strimg, 1); namedWindow("image", CV_WINDOW_AUTOSIZE); imshow("image", image); /**************************************************************************************/ //旋轉圖像 這里以逆時針為正 插值選用雙線性插值 圍繞左上角點進行旋轉 雙線性插值 Mat rotateimg; double angle = 15; double Hangle = angle*PI / 180; int Width, Height; //double r,s; double r, s; unsigned char * ptr, *dst; int temp[4]; double z1, z2; //原點會發生移動 double fx, fy; double Hcos = cos(Hangle); double Hsin = sin(Hangle); //x y 存放原圖中四個點的位置,以中心為原點 左上角開始 順時針數 int x[4]; int y[4]; int x1[4]; int y1[4]; x[0] = -(image.cols - 1) / 2; x[1] = -x[0]; x[2] = -x[0]; x[3] = x[0]; y[0] = -(image.rows - 1) / 2; y[1] = y[0]; y[2] = -y[0]; y[3] = -y[0]; int i = 0, j = 0; //x1 y1 分別存放新圖中圖像的四個角點的位置 以中心為原點 左上角點開始 順時針數 for (i = 0; i < 4; i++) { x1[i] = (int)(x[i] * Hcos - y[i] * Hsin + 0.5); y1[i] = (int)(x[i] * Hsin + y[i] * Hcos + 0.5); printf("x: %d ", x[i]); printf("y: %d ", y[i]); printf("x1: %d ", x1[i]); printf("y1: %d \n", y1[i]); } //確定新圖像的長寬 if (abs(y1[2] - y1[0]) > abs(y1[3] - y1[1])) { Height = abs(y1[2] - y1[0]); Width = abs(x1[3] - x1[1]); } else{ Height = abs(y1[3] - y1[1]); Width = abs(x1[2] - x1[0]); } rotateimg = Mat(Size(Width, Height), image.type()); //兩個偏移常量 fx = -1 * (Width - 1)* Hcos*0.5 - (Height - 1)*Hsin*0.5 + (image.cols - 1) / 2; fy = (Width - 1)*Hsin*0.5 - (Height - 1)*Hcos*0.5 + (image.rows - 1) / 2; for (i = 0; i < Height; i++) { for (j = 0; j < Width; j++) { /*[s r 1] = [j i 1] * HINV*/ s = j*Hcos + i*Hsin + fx; r = -j*Hsin + i*Hcos + fy; if (r < 1.0 || s < 1.0 || r >= image.rows || s >= image.cols) { for (int ch = 0; ch < image.channels(); ++ch) { dst = rotateimg.ptr<uchar>(i) +j * image.channels() + ch; *dst = 0; } } else{ r = r - 1; s = s - 1; for (int ch = 0; ch < image.channels(); ++ch) { ptr = image.ptr<uchar>(int(r)) + int(s) * image.channels() + ch ; temp[0] = *ptr; temp[1] = *(ptr + 1 * image.channels()); ptr = image.ptr<uchar>(int(r+1)) + int(s) * image.channels() + ch; temp[2] = *ptr; temp[3] = *(ptr + 1 * image.channels()); z1 = (temp[1] - temp[0])*(s - int(s)) + temp[0]; z2 = (temp[3] - temp[2])*(s - int(s)) + temp[2]; dst = rotateimg.ptr<uchar>(i) +j * image.channels() + ch; *dst = (int)(z1 + (z2 - z1)*(r - (int)r)); } } } } namedWindow("rotate_image", 1); imshow("rotate_image", rotateimg); imwrite("rotate.jpg", rotateimg); waitKey(0); /**************************************************************************************/ return 0; }
IplImage版本
#include<cv.h> #include<highgui.h> #include <cmath> #include <stdlib.h> #include <iostream> using namespace std; const double PI = 3.141592653; const char *strimg = "woman.jpg"; int main() { IplImage * image; image = cvLoadImage(strimg, 1); cvNamedWindow("image", CV_WINDOW_AUTOSIZE); cvShowImage("image", image); /**************************************************************************************/ //旋轉圖像 這里以逆時針為正 插值選用雙線性插值 圍繞左上角點進行旋轉 雙線性插值 IplImage* rotateimg; double angle = 15; double Hangle = angle*PI / 180; int Width, Height; //double r,s; double r, s; unsigned char * ptr, *dst; int temp[4]; double z1, z2; //原點會發生移動 double fx, fy; double Hcos = cos(Hangle); double Hsin = sin(Hangle); //x y 存放原圖中四個點的位置,以中心為原點 左上角開始 順時針數 int x[4]; int y[4]; int x1[4]; int y1[4]; x[0] = -(image->width - 1) / 2; x[1] = -x[0]; x[2] = -x[0]; x[3] = x[0]; y[0] = -(image->height - 1) / 2; y[1] = y[0]; y[2] = -y[0]; y[3] = -y[0]; int i = 0, j = 0; //x1 y1 分別存放新圖中圖像的四個角點的位置 以中心為原點 左上角點開始 順時針數 for (i = 0; i < 4; i++) { x1[i] = (int)(x[i] * Hcos - y[i] * Hsin + 0.5); y1[i] = (int)(x[i] * Hsin + y[i] * Hcos + 0.5); printf("x: %d ", x[i]); printf("y: %d ", y[i]); printf("x1: %d ", x1[i]); printf("y1: %d \n", y1[i]); } //確定新圖像的長寬 if (abs(y1[2] - y1[0]) > abs(y1[3] - y1[1])) { Height = abs(y1[2] - y1[0]); Width = abs(x1[3] - x1[1]); } else{ Height = abs(y1[3] - y1[1]); Width = abs(x1[2] - x1[0]); } rotateimg = cvCreateImage(cvSize(Width, Height), image->depth, image->nChannels); //兩個偏移常量 fx = -1 * (Width - 1)* Hcos*0.5 - (Height - 1)*Hsin*0.5 + (image->width - 1) / 2; fy = (Width - 1)*Hsin*0.5 - (Height - 1)*Hcos*0.5 + (image->height - 1) / 2; for (i = 0; i < Height; i++) { for (j = 0; j < Width; j++) { /*[s r 1] = [j i 1] * HINV*/ s = j*Hcos + i*Hsin + fx; r = -j*Hsin + i*Hcos + fy; if (r < 1.0 || s < 1.0 || r >= image->height || s >= image->width) { for (int ch = 0; ch < image->nChannels; ++ch) { dst = (unsigned char *)(rotateimg->imageData + i*rotateimg->widthStep + (j * rotateimg->nChannels + ch)); *dst = 0; } } else{ r = r - 1; s = s - 1; for (int ch = 0; ch < image->nChannels; ++ch) { ptr = (unsigned char *)(image->imageData + image->widthStep*(int)r + ((int)s * image->nChannels + ch)); temp[0] = *ptr; temp[1] = *(ptr + 1 * image->nChannels); ptr = (unsigned char*)(image->imageData + image->widthStep *(int)(r + 1) + ((int)s * image->nChannels + ch)); temp[2] = *ptr; temp[3] = *(ptr + 1 * image->nChannels); z1 = (temp[1] - temp[0])*(s - int(s)) + temp[0]; z2 = (temp[3] - temp[2])*(s - int(s)) + temp[2]; dst = (unsigned char *)(rotateimg->imageData + i*rotateimg->widthStep + (j * rotateimg->nChannels + ch)); *dst = (int)(z1 + (z2 - z1)*(r - (int)r)); } } } } cvNamedWindow("rotate_image", 1); cvShowImage("rotate_image", rotateimg); cvSaveImage("rotate.jpg", rotateimg); cvWaitKey(0); /**************************************************************************************/ return 0; }
python opencv版本
from __future__ import absolute_import from __future__ import division from __future__ import print_function import sys import os import argparse import numpy as np import random import cv2 import math def MyRotateImage(image, angle): PI = 3.141592653; #**************************************************************************************/ #旋轉圖像 這里以逆時針為正 插值選用雙線性插值 圍繞左上角點進行旋轉 雙線性插值 Hangle = angle*PI / 180; Hcos = math.cos(Hangle); Hsin = math.sin(Hangle); imgsize = image.shape print(imgsize) srcWidth = imgsize[1] srcHeight = imgsize[0] #x y 存放原圖中四個點的位置,以中心為原點 左上角開始 順時針數 x = [0,0,0,0] y = [0,0,0,0] x1 = [0,0,0,0] y1 = [0,0,0,0] x[0] = -(srcWidth - 1) / 2; x[1] = -x[0]; x[2] = -x[0]; x[3] = x[0]; y[0] = -(srcHeight - 1) / 2; y[1] = y[0]; y[2] = -y[0]; y[3] = -y[0]; #x1 y1 分別存放新圖中圖像的四個角點的位置 以中心為原點 左上角點開始 順時針數 for i in range(4): #[s r 1] = [j i 1] * HINV*/ #s = j*Hcos + i*Hsin + fx; #r = -j*Hsin + i*Hcos + fy; x1[i] = (int)(x[i] * Hcos + y[i] * Hsin + 0.5); y1[i] = (int)(-x[i] * Hsin + y[i] * Hcos + 0.5); # printf("x: %d ", x[i]); # printf("y: %d ", y[i]); # printf("x1: %d ", x1[i]); # printf("y1: %d \n", y1[i]); #確定新圖像的長寬 if (abs(y1[2] - y1[0]) > abs(y1[3] - y1[1])): Height = abs(y1[2] - y1[0]); Width = abs(x1[3] - x1[1]); else: Height = abs(y1[3] - y1[1]); Width = abs(x1[2] - x1[0]); #np.zeros(img.shape, np.uint8) rotateimg = np.zeros((Height, Width, image.ndim), dtype=np.uint8) #兩個偏移常量#原點會發生移動 fx = -1 * (Width - 1)* Hcos*0.5 - (Height - 1)*Hsin*0.5 + (srcWidth - 1) / 2; fy = (Width - 1)*Hsin*0.5 - (Height - 1)*Hcos*0.5 + (srcHeight - 1) / 2; temp = np.array([0,0,0,0], dtype=int) z1 = np.int32(0) z2 = np.int32(0) z3 = np.int32(0) imgchannel = image.ndim for i in range(Height): for j in range(Width): #[s r 1] = [j i 1] * HINV*/ s = j*Hcos + i*Hsin + fx; r = -j*Hsin + i*Hcos + fy; if r < 1.0 or s < 1.0 or r >= srcHeight or s >= srcWidth: for ch in range(imgchannel): rotateimg[i,j,ch] = 0 else: r = r - 1; s = s - 1; ty = int(r) tx = int(s) by = int(r+1) bx = int(s+1) for ch in range(imgchannel): temp[0] = image[ty,tx,ch] temp[1] = image[ty,bx,ch] temp[2] = image[by,tx,ch] temp[3] = image[by,bx,ch] # ptr = image.ptr<uchar>(int(r)) + int(s) * image.channels() + ch; # temp[0] = *ptr; # temp[1] = *(ptr + 1 * imgchannel); # ptr = image.ptr<uchar>(int(r + 1)) + int(s) * imgchannel + ch; # temp[2] = *ptr; # temp[3] = *(ptr + 1 * imgchannel); z1 = (temp[1] - temp[0])*(s - int(s)) + temp[0]; z2 = (temp[3] - temp[2])*(s - int(s)) + temp[2]; z3 = int(z1 + (z2 - z1)*(r - int(r))) rotateimg[i,j,ch] = z3 return rotateimg; if __name__ == '__main__': #圖像旋轉 img = cv2.imread("test.jpg") angle = -26.0 rotateimg = MyRotateImage(img,angle) cv2.imwrite("myrotate.jpg", rotateimg)
效果如下:
參考:
http://blog.csdn.net/renshengrumenglibing/article/details/7176790
http://hi.baidu.com/wangguang246/item/972a6508d16226dfdce5b040
http://www.cnblogs.com/tingshuo/archive/2011/05/15/2047016.html