登录后查看更多精彩内容~
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
本帖最后由 lysx 于 2014-9-27 09:11 编辑
在这里,枫叶感谢大家的持续关注!尤其是要感谢科大的一位同学,他不是气象专业的,我也仅和他有过一面之缘。他的QQ号还是他的老师给我的。在我对这个算法的理解,程序编写过程中,他给了我很多无私的帮助。惭愧的是,我到现在也不知道他的名字。在这里,枫叶深深地致以谢意!感谢所有好人!
这里主要讨论以图象的中心为圆心旋转。旋转之后若要保持目标区域大小不变,则整幅图像变大;若要保持整幅图像的大小不变,则旋转出去的部分需要裁剪掉。 旋转后保持原图大小,转出的部分被裁掉 以顺时针旋转为例来堆到旋转变换公式。如下图所示。
旋转前: x0=rcosb;y0=rsinb 旋转a角度后: x1=rcos(b-a)=rcosbcosa+rsinbsina=x0cosa+y0sina y1=rsin(b-a)=rsinbcosa-rcosbsina=-x0sina+y0cosa 矩阵形式为 逆变换为 上面的公式是以图像的左下角为原点旋转的。现我们要以图像的中心为原点旋转。因此需要先将坐标平移到图像中心,如下所示 设图象的宽为w,高为h,容易得到: 逆变换为 现在可以分三步来完成旋转变换: 1. 将坐标系x'o'y'平移到xoy ; 2. 在xoy坐标系下作旋转变换; 3.变换后将坐标系平移回原来位置。 用矩阵表示就是 其中R表示旋转变换矩阵。当旋转不改变图像大小时,T 与 T' 互为逆矩阵;当旋转后图像变大时,T 与 T'不是逆矩阵关系,因为图像变大了,第一次平移和第二次平移坐标系的距离不一样。因此当图像变大时,公式应该如下: 由于算法实现过程中我们需要的是逆变换的公式,因此我们只写出逆变换的表达式,如下: 其中wn ,hn 表示新图像的宽和高,wo, ho 表示原图像的宽和高。 这样,对于新图中的每一点,我们就可以根据上面逆变换公式求出对应原图中的点,得到它的灰度。如果超出原图范围,则设置为背景色。要注意的是,由于有浮点运算,计算出来点的坐标可能不是整数,采用取整处理,即找最接近的点,这样会带来一些误差(图象可能会出现锯齿)。更精确的方法是采用插值,这里暂不讨论。 C++代码如下:
/*
* rotate.cpp
* 图像旋转变换(顺时针)
* Created on: 2011-10-10
* Author: LiChanghai
*/
// 以图像中心为坐标原点,旋转后不改变图像大小
// 函数返回值为指针,指向新申请的内存区域
// 因为新图像大小改变了,需要返回新图像的尺寸
// 因此形参的高度和宽度都采用指针变量
#include<string.h>
#include<stdlib.h>
#include<cmath>
#define pi 3.1415926535
unsigned char * rotate(unsigned char *pImage, int *width, int *height, int biBitCount, float angle)
{
//定义以图像中心为原点的坐标系下原图像和新图像的四个角点坐标
float src_x1, src_y1, src_x2, src_y2, src_x3, src_y3, src_x4, src_y4;
float dst_x1, dst_y1, dst_x2, dst_y2, dst_x3, dst_y3, dst_x4, dst_y4;
//定义新图像的高度和宽度
int wnew, hnew;
//定义计算过程中需要的相关变量
float sina, cosa, temp1, temp2, alpha;
//角度转化为弧度
alpha=pi*angle/180;
cosa = float(cos(double(alpha)));
sina = float(sin(double(alpha)));
//原图像的四个角点的坐标
src_x1 = float(-0.5*(*width)); src_y1 = float(0.5*(*height));
src_x2 = float(0.5*(*width)); src_y2 = src_y1;
src_x3 = src_x1; src_y3 = float(-0.5*(*height));
src_x4 = src_x2; src_y4 = src_y3;
//计算新图像的四个角点坐标
dst_x1 = cosa*src_x1+sina*src_y1;
dst_y1 = -sina*src_x1+cosa*src_y1;
dst_x2 = cosa*src_x2+sina*src_y2;
dst_y2 = -sina*src_x2+cosa*src_y2;
dst_x3 = cosa*src_x3+sina*src_y3;
dst_y3 = -sina*src_x3+cosa*src_y3;
dst_x4 = cosa*src_x4+sina*src_y4;
dst_y4 = -sina*src_x4+cosa*src_y4;
//计算新图像的高度和宽度
float t1 = fabs(dst_x4-dst_x1), t2 = fabs(dst_x3-dst_x2);
wnew = int(t1>t2 ? t1:t2);
t1 = fabs(dst_y4-dst_y1), t2 = fabs(dst_y3-dst_y2);
hnew = int(t1>t2 ? t1:t2);
// 计算旋转变换中的两个中间变量,便于以后计算
temp1=float( -0.5*wnew*cosa+0.5*hnew*sina+0.5*(*width));
temp2=float(-0.5*wnew*sina-0.5*hnew*cosa+0.5*(*height));
//计算原图像和新图像每行像素所占的字节数(必须是4的倍数)
int lineByte = ((*width) * biBitCount/8+3)/4*4;
int lineByte2=(wnew * biBitCount/8+3)/4*4;
//申请新的位图数据存储空间
unsigned char *pImage2;
pImage2=new unsigned char[lineByte2*hnew];
//将新图像设置为背景色
memset(pImage2, 0, lineByte2*hnew);
//遍历新图像的每一个像素进行判断
int x, y, x0, y0; // x0, y0为原图像中对应坐标
for(y=0; y<hnew; y++)
for(x=0; x<wnew; x++)
{
x0= int(x*cosa-y*sina+temp1);
y0= int(x*sina+y*cosa+temp2);
//如果在原图像范围内则复制像素值
if( (x0>=0) && (x0<(*width)) && (y0>=0) && (y0<(*height)))
{
*(pImage2+lineByte2*y+x) = *(pImage+lineByte*y0+x0);
}
}
//修改原图像的高度和宽度
*width = wnew;
*height = hnew;
delete [ ] pImage; //释放原内存空间
return pImage2;
}
解决方案::如果需要图像裁剪,直接把计算新图像的宽度高度那几行注释掉,改为wnew=*width; hnew=*height即可。
|