数字图像处理(五)⼏何变换之图像平移、镜像、绕中⼼点旋
转、缩放等
本篇博客是⾃⼰的理解,如有错误,欢迎指正,图像数据下载地址
点运算对单幅图像做处理,不改变像素的空间位置;代数运算对多幅图像做处理,也不改变像素的空间位置;⼏何运算对单幅图像做处理,改变像素的空间位置,⼏何运算包括两个独⽴的算法:空间变换算法和灰度级插值算法。
空间变换操作包括简单空间变换、多项式卷绕和⼏何校正、控制栅格插值和图像卷绕,这⾥主要讲述简单的空间变换,如图像平移、镜像、缩放和旋转。主要是通过线性代数中的齐次坐标变换。
图像平移坐标变换如下:
运⾏效果如下图所⽰,其中BMP图⽚(0,0)像素点为左下⾓。
第⼀步:在ResourceView资源视图中,添加Menu⼦菜单如下:(注意ID号)
第⼆步:设置平移对话框。将试图切换到ResourceView界⾯--选中Dialog,右键⿏标新建⼀个Dialog,并新建⼀个名
为IDD_DIALOG_PY。编辑框(X)IDC_EDIT_PYX 和 (Y)IDC_EDIT_PYY,确定为默认按钮。设置成下图对话框:
第三步:在对话框资源模板空⽩区域双击⿏标—Create a new class创建⼀个新类--命名为CImagePYDl
g。会⾃动⽣成它的.h和.cpp⽂件。打开类向导(Ctrl W),选择类名:CImagePYDlg添加成员变量如下图所⽰,同时在Message Maps中⽣成ID_JHBH_PY实现函数。
第四步:在CImageProcessingView.cpp中添加头⽂件#include "ImagePYDlg.h",并实现平移。
void CImageProcessingView::OnJhbhPy()
{
// TODO: 在此添加命令处理程序代码
if (numPicture == 0)
{
AfxMessageBox("请输⼊⼀张图像", MB_OK, 0);
return;
}
if (m_nBitCount != 24)
{
AfxMessageBox("输⼊图⽚不是24位", MB_OK, 0);
return;
}
CImagePYDlg dlg;
int num;//记录每⼀⾏需要填充的字节
if (m_nWidth * 3 % 4 != 0)
{
num = 4 - m_nWidth * 3 % 4;
}
else
{
num = 0;
}
if (dlg.DoModal() == IDOK)
{
if (dlg.m_xPY > m_nWidth || dlg.m_yPY > m_nHeight)
cstring转为int{
AfxMessageBox("图像平移不能超过原始长度:", MB_OK, 0);
return;
}
AfxMessageBox("图像空间变换-平移", MB_OK, 0);
FILE *fpo = fopen(BmpName, "rb");
FILE *fpw = fopen(BmpNameLin, "wb+");
fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);
fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);
fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
fread(m_pImage, m_nImage, 1, fpo);
unsigned char *ImageSize;
ImageSize = new unsigned char[m_nImage];  //new和delete有效的进⾏动态内存的分配和释放
unsigned char black;          //填充⿊⾊='0'
int x, y;
for (y = 0; y < m_nHeight; y++)
for (y = 0; y < m_nHeight; y++)
{
if (y < dlg.m_yPY) /*第⼀部分:到平移后像素位置前⾯的所有像素点赋值为⿊⾊*/
{
for (x = 0; x < m_nWidth; x++)
{
ImageSize[(y*m_nWidth + x) * 3 + y*num] = black;
ImageSize[(y*m_nWidth + x) * 3 + 1 + y*num] = black;
ImageSize[(y*m_nWidth + x) * 3 + 2 + y*num] = black;
}
}
else if (y >= dlg.m_yPY)
{
for (x = 0; x < m_nWidth; x++)
{
if (x < dlg.m_xPY)
{
ImageSize[(y*m_nWidth + x) * 3 + y*num] = black;
ImageSize[(y*m_nWidth + x) * 3 + 1 + y*num] = black;
ImageSize[(y*m_nWidth + x) * 3 + 2 + y*num] = black;
}
else if (x >= dlg.m_xPY)
{
ImageSize[(y*m_nWidth + x) * 3 + y*num] = m_pImage[((y-dlg.m_yPY)*m_nWidth + x-dlg.m_xPY) * 3 + y*num];
ImageSize[(y*m_nWidth + x) * 3 + 1 + y*num] = m_pImage[((y - dlg.m_yPY)*m_nWidth + x - dlg.m_xPY) * 3+1 + y*num];      ImageSize[(y*m_nWidth + x) * 3 + 2 + y*num] = m_pImage[((y - dlg.m_yPY)*m_nWidth + x - dlg.m_xPY) * 3+1 + y*num];    }
}
}
}
fwrite(ImageSize, m_nImage, 1, fpw);
fclose(fpo);
fclose(fpw);
numPicture = 2;
level = 400;
Invalidate();
}
}
⼆. 图像镜像
1.⽔平镜像翻转
其设原图像宽度为lWidth,⾼度为lHeight,源图像中(x0,y0)经过⽔平镜像后坐标为
打开类向导,在CImageProcessingView中添加IDs为ID_JHBH_FZ,⽣成函数,代码如下:
void CImageProcessingView::OnJhbhSpjx()
{
// TODO: 在此添加命令处理程序代码
// TODO: 在此添加命令处理程序代码
if (numPicture == 0)
{
AfxMessageBox("请输⼊⼀张图像", MB_OK, 0);
return;
}
if (m_nBitCount != 24)
{
AfxMessageBox("输⼊图⽚不是24位", MB_OK, 0);
return;
}
AfxMessageBox("⽔平镜像!", MB_OK, 0);
int num;//记录每⼀⾏需要填充的字节
if (m_nWidth * 3 % 4 != 0)
{
num = 4 - m_nWidth * 3 % 4;
}
else
{
num = 0;
}
//打开临时的图⽚
FILE *fpo = fopen(BmpName, "rb");
FILE *fpw = fopen(BmpNameLin, "wb+");
fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);
fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);
fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
fread(m_pImage, m_nImage, 1, fpo);
/*new和delete有效的进⾏动态内存的分配和释放*/
unsigned char *ImageSize;
ImageSize = new unsigned char[m_nImage];
int x, y;
for (y = 0; y < m_nHeight; y++)
{
for (x = 0; x < m_nWidth; x++)
{
ImageSize[(y*m_nWidth + x)*3 + y*num] = m_pImage[(y*m_nWidth + m_nWidth - x)*3 + y*num];
ImageSize[(y*m_nWidth + x) * 3 + y*num + 1] = m_pImage[(y*m_nWidth + m_nWidth - x)*3 + y*num  + 1];  ImageSize[(y*m_nWidth + x) * 3 + y*num + 2] = m_pImage[(y*m_nWidth + m_nWidth - x)*3 + y*num + 2];  }
}
fwrite(ImageSize, m_nImage, 1, fpw);
fclose(fpo);
fclose(fpw);
numPicture = 2;
level = 400;
Invalidate();
}
2.垂直镜像倒转
其中变换矩阵如下:
X=X0
Y=height-Y0-1  (height为图像⾼度)
它相当于把原图的像素矩阵的最后⼀⾏像素值赋值给第⼀⾏,⾸先到(0,0)对应的(height-1,0)像素值,然后依次赋值该⾏的像素数据;最后当前⾏赋值结束,依次下⼀⾏。重点是到每⾏的第⼀个像素点即可。
代码中引⽤两个变量:Place=(m_nWidth*3)*(m_nHeight-1-1)即是(height-1,0)最后⼀⾏的第⼀个像素点;然后是循环中Place=(m_nWidth*3)*(m_nHeight-number-1)到每⾏的第⼀个像素点。
同样通过类向导⽣成函数void CImageProcessingView::OnJhbhDz(),代码如下: