图像旋转变换
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