图像处理 - 图像平移

初始坐标为(x0,y0)的点经过平移(tx,ty)(以向右,向下为正方向)后,坐标变为(x1,y1)。这两点之间的关系是x1=x0+tx y1=y0+ty


以矩阵的形式表示为:

逆变换:


  我们想知道的是平移后的图象中每个象素的颜色。例如我们想知道,新图中左上角点的RGB值是多少?很显然,该点是原图的某点经过平移后得到的,这两点的颜色肯定是一样的,所以只要知道了原图那点的RGB值即可。那么到底新图中的左上角点对应原图中的哪一点呢?将左上角点的坐标(0,0)入公式(2.2),得到x0=-tx y0=-ty;所以新图中的(0,0)点的颜色和原图中(-tx , -ty)的一样。

如果你用过Photoshop,Corel PhotoPaint等图象处理软件,可能听说过“灰度图”(grayscale)这个词。灰度图是指只含亮度信息,不含色彩信息的图象,就象我们平时看到的黑白照片:亮度由暗到明,变化是连续的。因此,要表示灰度图,就需要把亮度值进行量化。通常划分成0255256个级别,其中0最暗(全黑)255最亮(全白).bmp格式的文件中,并没有灰度图这个概念,但是,我们可以很容易在.bmp文件中表示灰度图。方法是用256色的调色板,只不过这个调色板有点特殊,每一项的RGB值都是相同的。也就是说RGB值从(000)(111)一直到(255255255)(000)是全黑色,(255255255)是全白色,中间的是灰色。这样,灰度图就可以用256色图来表示了。为什么会这样呢?难道是一种巧合?其实并不是。

在表示颜色的方法中,除了RGB外,还有一种叫YUV的表示方法,应用也很多。电视信号中用的就是一种类似于YUV的颜色表示方法。

在这种表示方法中,Y分量的物理含义就是亮度,UV分量代表了色差信号(你不必了解什么是色差,只要知道有这么一个概念就可以了)。使用这种表示方法有很多好处,最主要的有两点:

(1)    因为Y代表了亮度,所以Y分量包含了灰度图的所有信息,只用Y分量就能完全能够表示出一幅灰度图来。当同时考虑UV分量时,就能够表示出彩色信息来。这样,用同一种表示方法可以很方便的在灰度和彩色图之间切换,而RGB表示方法就做不到这一点了。

(2)    人眼对于亮度信号非常敏感,而对色差信号的敏感程度相对较弱。也就是说,图象的主要信息包含在Y分量中。这就提示我们:如果在对YUV信号进行量化时,可以“偏心”一点,让Y的量化级别多一些(谁让它重要呢?)而让UV的量化级别少一些,就可以实现图象信息的压缩。这一点将在第9章介绍图象压缩时仔细研究,这里就不深入讨论了。而RGB的表示方法就做不到这一点,因为RGB三个分量同等重要,缺了谁也不行。YUVRGB之间有着如下的对应关系


先看x方向(width指图象的宽度)

(1)    tx-width:很显然,图象完全移出了屏幕,不用做任何处理;

(2)    -width<tx0容易看出,图象区域的x范围从0width-|tx|,对应原图的范围从|tx|width

(3)    0< tx <width:如图2.6所示。容易看出,图象区域的x范围从tx width,对应原图的范围从0width - tx

(4)    tx width:很显然,图象完全移出了屏幕,不用做任何处理。

y方向是对应的(height表示图象的高度)

(1)    ty-height,图象完全移出了屏幕,不用做任何处理;

(2)    -height<ty0,图象区域的y范围从0height-|ty|,对应原图的范围从|ty|height

(3)    0<ty<height ,图象区域的y范围从tyheight,对应原图的范围从0height-ty

(4)    tyheight,图象完全移出了屏幕,不用做任何处理。

这种做法利用了位图存储的连续性,即同一行的象素在内存中是相邻的。利用memcpy函数,从(x0,y0)点开始,一次可以拷贝一整行(宽度为x1x0),然后将内存指针移到(x0,y0+1)处,拷贝下一行。这样拷贝(y1-y0)行就完成了全部操作,避免了一个一个象素的计算,提高了效率。下面将会贴上代码,编译环境是VC 6.0 (单文档程序)。

首先还是先打开图像,这里需要加个类,表示x方向和y方向平移。

Doc.h:

class CMyDoc : public CDocument
{
public:
	BITMAPFILEHEADER m_file;
	BITMAPINFOHEADER m_info;
	BITMAPINFO* bmpinfo;

	BYTE *bmpdata;
	BOOL isOpen;
Doc.cpp:

CMyDoc::CMyDoc()
{
	// TODO: add one-time construction code here
	isOpen=FALSE;
	bmpinfo=NULL;
	bmpdata=NULL;
}
View.cpp:

void CMyView::OnDraw(CDC* pDC)
{
	CMyDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: add draw code for native data here
	BYTE* pData = NULL;
	pData = pDoc->bmpdata;
	
	if(pDoc->isOpen == TRUE)
	{
		SetDIBitsToDevice(//12个参数
			pDC->m_hDC, //设备环境句柄
			0,//绘制目标矩形区域的左上角x坐标
			0,//绘制目标矩形区域的左上角y坐标
			pDoc->bmpinfo->bmiHeader.biWidth,//绘制目标矩形区域的宽度width
			pDoc->bmpinfo->bmiHeader.biHeight,//绘制目标矩形区域的高度height
			0,//位图区域的左下角x坐标
			0,//位图区域的左下角y坐标
			0,//显示图片的起始行
			pDoc->bmpinfo->bmiHeader.biHeight,//扫描总行数
			pData,//位图数据起首地址
			pDoc->bmpinfo,//位图信息头首地址
			DIB_RGB_COLORS//颜色参数
			);
	}
}
void CMyView::OnPingyi() 
{
	// TODO: Add your command handler code here
	CMyDoc* pc=GetDocument();
	CPingyi dlg;
	dlg.SetDocument(pc);
	dlg.DoModal();
	Invalidate();
}
 CPingYi是新加的类,用来记录x方向和y方向。

Pingyi.h:

#if !defined(AFX_PINGYI_H__304A149B_64E3_4480_ACB5_D3AF19143F29__INCLUDED_)
#define AFX_PINGYI_H__304A149B_64E3_4480_ACB5_D3AF19143F29__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// Pingyi.h : header file
//
#include "平移Doc.h"
/
// CPingyi dialog

class CPingyi : public CDialog
{
public:
	int GetX();//获取对话框里的x数据
	int GetY();//获取对话框里的Y数据

	int m_x;
	int m_y;

    CMyDoc*poc;
	void SetDocument(CMyDoc *m);
	CMyDoc* GetDocument();

	void Move(BYTE *bmpdata,int width,int height,int xx,int yy);
// Construction
public:
	CPingyi(CWnd* pParent = NULL);   // standard constructor

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


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

// Implementation
protected:

	// Generated message map functions
	//{{AFX_MSG(CPingyi)
	afx_msg void OnChangeEdit1();
	afx_msg void OnChangeEdit2();
	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_PINGYI_H__304A149B_64E3_4480_ACB5_D3AF19143F29__INCLUDED_)
Pingyi.cpp:

// Pingyi.cpp : implementation file
//

#include "stdafx.h"
#include "平移.h"
#include "Pingyi.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/
// CPingyi dialog


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


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


BEGIN_MESSAGE_MAP(CPingyi, CDialog)
	//{{AFX_MSG_MAP(CPingyi)
	ON_EN_CHANGE(IDC_EDIT1, OnChangeEdit1)
	ON_EN_CHANGE(IDC_EDIT2, OnChangeEdit2)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/
// CPingyi message handlers

void CPingyi::OnChangeEdit1() 
{
	// 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_x=GetDlgItemInt(IDC_EDIT1);
	// TODO: Add your control notification handler code here	
}

void CPingyi::OnChangeEdit2() 
{
	// 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_y=GetDlgItemInt(IDC_EDIT2);
	// TODO: Add your control notification handler code here
	
}

int CPingyi::GetX()
{
	return m_x;
}

int CPingyi::GetY()
{
	return m_y;
}

void CPingyi::OnOK() 
{
	// TODO: Add extra validation here
	Move(GetDocument()->bmpdata,GetDocument()->m_info.biWidth,GetDocument()->m_info.biHeight,GetX(),GetY());
	CDialog::OnOK();
}

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

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

void CPingyi::Move(BYTE *bmpdata,int width,int height,int xx,int yy)
{
	BYTE *data;//;临时的存储空间
	BYTE *yuan;//原来的数据地址
	BYTE *bian;//改变后的图像地址

	int x1,y1;//原来的坐标

	int x2,y2;//平移后的坐标

	LONG zijie;//字节
    zijie = WIDTHBYTES(width * 8);
    data=(BYTE *)new BYTE[zijie*height];
	if(!data) delete[] data;
	
	for(x2=0;x2<height;x2++)
		for(y2=0;y2<width;y2++)
		{
			bian=data+zijie*(height-1-x2)+y2;
			
			x1= x2 - yy;
			y1= y2 - xx;
			if((x1>0)&&(x1<height)&&(y1>0)&&(y1<width))
			{
				yuan=bmpdata+zijie*(height-1-x1)+y1;

				*bian=*yuan;				
			}
			else
				* ((unsigned char*)bian) = 125;
		}
     memcpy(bmpdata,data,zijie*height);
	 delete [] data;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值