旋转(rotation)有一个绕着什么转的问题,通常的做法是以图象的中心为圆心旋转,类似下面这种情况:

GPUImageTransformFilter 图片旋转90 图像处理图像旋转_图像处理

可以看出,旋转后图象变大了。另一种做法是不让图象变大,转出的部分被裁剪掉如图2.9所示。

我们采用第一种做法,首先给出变换矩阵。在我们熟悉的坐标系中,将一个点顺时针旋转a角后的坐标变换公式,r为该点到原点的距离,在旋转过程中,r保持不变;b为r与x轴之间的夹角。

GPUImageTransformFilter 图片旋转90 图像处理图像旋转_VC_02


旋转前:x0=rcosb;y0=rsinb

旋转a角度后:

x1=rcos(b-a)=rcosbcosa+rsinbsina=x0cosa+y0sina;

y1=rsin(b-a)=rsinbcosa-rcosbsina=-x0sina+y0cosa;

以矩阵的形式表示:


GPUImageTransformFilter 图片旋转90 图像处理图像旋转_#include_03


上面的公式中,坐标系xoy是以图象的中心为原点,向右为x轴正方向,向上为y轴正方向。它和以图象左上角点为原点o’,向右为x’轴正方向,向下为y’轴正方向的坐标系x’o’y’之间的转换关系如何呢?

GPUImageTransformFilter 图片旋转90 图像处理图像旋转_VC_04

设图象的宽为w,高为h,容易得到:


GPUImageTransformFilter 图片旋转90 图像处理图像旋转_VC_05


逆变换为:


GPUImageTransformFilter 图片旋转90 图像处理图像旋转_图像处理_06


有了上面的公式,我们可以把变换分成三步:

1.将坐标系o’变成o;

2.将该点顺时针旋转a角;

3.将坐标系o变回o’,这样,我们就得到了变换矩阵,是上面三个矩阵的级联。

这样,对于新图中的每一点,我们就可以根据公式求出对应原图中的点,得到它的灰度。如果超出原图范围,则填成白色。要注意的是,由于有浮点运算,计算出来点的坐标可能不是整数,采用取整处理,即找最接近的点,这样会带来一些误差(图象可能会出现锯齿)。

这里的打开bmp格式图像代码就不再贴出来了,就贴出来旋转类(这个类是新加的,对话框里面可以输入旋转角度)里面的代码和调用代码。

View.h

void CMyView::OnXuanzhuan() 
{
	// TODO: Add your command handler code here
	CMyDoc* pc=GetDocument();
	CXuanzhuan dlg;
	dlg.SetDocument(pc);
	dlg.DoModal();
	Invalidate();
}

Xuanzhuan.h

#if !defined(AFX_XUANZHUAN_H__B7192E70_1EA8_4EEF_BC0E_A30D3785DC93__INCLUDED_)
#define AFX_XUANZHUAN_H__B7192E70_1EA8_4EEF_BC0E_A30D3785DC93__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// Xuanzhuan.h : header file
//
#include "图像旋转Doc.h"


/
// CXuanzhuan dialog

class CXuanzhuan : public CDialog
{
public:
	int m_jiaodu;
	void xuanzhuan(BYTE *&shuju,int width,int height,int du);

	
    CMyDoc*poc;
	void SetDocument(CMyDoc *m);
	CMyDoc* GetDocument();
	int GetData();
// Construction
public:
	CXuanzhuan(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
	//{{AFX_DATA(CXuanzhuan)
	enum { IDD = IDD_DIALOG1 };
		// NOTE: the ClassWizard will add data members here
	//}}AFX_DATA


// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CXuanzhuan)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:

	// Generated message map functions
	//{{AFX_MSG(CXuanzhuan)
	afx_msg void OnChangeJiaodu();
	virtual void OnOK();
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_XUANZHUAN_H__B7192E70_1EA8_4EEF_BC0E_A30D3785DC93__INCLUDED_)

Xuanzhuan.cpp:

// Xuanzhuan.cpp : implementation file
//

#include "stdafx.h"
#include "图像旋转.h"
#include "Xuanzhuan.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#include "图像旋转Doc.h"
/
// CXuanzhuan dialog


CXuanzhuan::CXuanzhuan(CWnd* pParent /*=NULL*/)
	: CDialog(CXuanzhuan::IDD, pParent)
{
	//{{AFX_DATA_INIT(CXuanzhuan)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
	poc=NULL;
}


void CXuanzhuan::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CXuanzhuan)
		// NOTE: the ClassWizard will add DDX and DDV calls here
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CXuanzhuan, CDialog)
	//{{AFX_MSG_MAP(CXuanzhuan)
	ON_EN_CHANGE(IDC_JIAODU, OnChangeJiaodu)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/
// CXuanzhuan message handlers

void CXuanzhuan::OnChangeJiaodu() 
{
	// TODO: If this is a RICHEDIT control, the control will not
	// send this notification unless you override the CDialog::OnInitDialog()
	// function and call CRichEditCtrl().SetEventMask()
	// with the ENM_CHANGE flag ORed into the mask.
	m_jiaodu=GetDlgItemInt(IDC_JIAODU);
	// TODO: Add your control notification handler code here
	
}

void CXuanzhuan::OnOK() 
{
	// TODO: Add extra validation here
	xuanzhuan(GetDocument()->bmpdata,GetDocument()->bmpinfo->bmiHeader.biWidth,GetDocument()->bmpinfo->bmiHeader.biHeight,GetData());

	CDialog::OnOK();
}


void CXuanzhuan::xuanzhuan(BYTE *&shuju,int width,int height,int du)
{
	float cosa;//余弦值
	float sina;//正弦值

	BYTE *data;//临时存储
	BYTE *yuan;
	BYTE *bian;
	
	float srcx1,srcx2,srcx3,srcx4,srcy1,srcy2,srcy3,srcy4;//定义原图像的四个角的坐标
	float dstx1,dstx2,dstx3,dstx4,dsty1,dsty2,dsty3,dsty4;//定义新图像的四个角的坐标
	
	int newW;//新的宽度
	int newH;//新的高度

	int x0,y0;//原来的图像坐标
	int x1,y1;//旋转后的图像坐标

	float num1,num2;//常用值
	float du1;
	du1=(float)RADIAN(du);//角度到弧度的转化

	cosa=(float)cos((double)du1);//计算cosa的值
	sina=(float)sin((double)du1);//计算sina的值
计算出原来的坐标  以图像中心为原点  向右为x的正半轴 向下为y的正半轴
	srcx1=(float)(-0.5*width);  srcy1=(float)(0.5*height);
	srcx2=(float)(0.5*width);   srcy2=(float)(0.5*height);
	srcx3=(float)(-0.5*width);  srcy3=(float)(-0.5*height);
	srcx4=(float)(0.5*width);   srcy4=(float)(-0.5*height);
//计算出新图像的四个角的坐标  还是以图像中心为原点
	dstx1=(float)(cosa*srcx1+sina*srcy1);  dsty1=(float)(-sina*srcx1+cosa*srcy1);
	dstx2=(float)(cosa*srcx2+sina*srcy2);  dsty2=(float)(-sina*srcx2+cosa*srcy2);
	dstx3=(float)(cosa*srcx3+sina*srcy3);  dsty3=(float)(-sina*srcx3+cosa*srcy3);
	dstx4=(float)(cosa*srcx4+sina*srcy4);  dsty4=(float)(-sina*srcx4+cosa*srcy4);
///计算出新的宽度和高度/
	newW=(int)(max(fabs(dstx4-dstx1),fabs(dstx3-dstx2))+0.5);
	newH=(int)(max(fabs(dsty4-dsty1),fabs(dsty3-dsty2))+0.5);
//计算出常用值//
	num1 = (float) (-0.5 * (newW - 1) * cosa - 0.5 * (newH - 1) * sina + 0.5 * (width  - 1));
	num2 = (float) ( 0.5 * (newW - 1) * sina - 0.5 * (newH - 1) * cosa + 0.5 * (height - 1));
//
	LONG zijie; 
	LONG newzijie;
	zijie = WIDTHBYTES(width * 8);//原来的一行字节数
	newzijie=WIDTHBYTES(newW*8);//新的一行的字节数

	GetDocument()->bmpinfo->bmiHeader.biWidth=newW;
	GetDocument()->bmpinfo->bmiHeader.biHeight=newH;//把新的宽度和高度传给bmpinfo

	data=(BYTE *)new BYTE[newzijie*newH];//给临时变量分配数据

	for(x0=0;x0<newH;x0++)
	{
		for(y0=0;y0<newW;y0++)
		{
			bian = data + newzijie * (newH - 1 - x0) + y0;
			//计算在原来图像的坐标
			x1 = (LONG) (-(y0)*sina+(x0)*cosa+num2+0.5);
			y1 = (LONG) ( (y0)*cosa+(x0)*sina+num1+0.5);
			if( (y1 >= 0) && (y1 < width) && (x1 >= 0) && (x1 < height))//判断坐标是否在原图像内
			{
				yuan = shuju + zijie * (height - 1 - x1) + y1;

				*bian = *yuan;
			}
			else
			{
				// 对于源图中没有的像素,直接赋值为0
				* ((unsigned char*)bian) = 0;
			}
		}
	}
	if(shuju)  delete [] shuju;
	shuju=data;
	Invalidate();	
}

void CXuanzhuan::SetDocument(CMyDoc *m)
{
	poc=m;
}

CMyDoc* CXuanzhuan::GetDocument()
{
	return poc;
}

int CXuanzhuan::GetData()
{
	return m_jiaodu;

}