600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > 数字图像处理(五)几何变换之图像平移 镜像 绕中心点旋转 缩放等

数字图像处理(五)几何变换之图像平移 镜像 绕中心点旋转 缩放等

时间:2020-02-06 05:16:56

相关推荐

数字图像处理(五)几何变换之图像平移 镜像 绕中心点旋转 缩放等

本文为参考这位/eastmount/article/details/46345299所做的一些笔记,文字部分复制粘贴,代码部分有所改进,增加了绕中心点旋转等

本篇博客是自己的理解,如有错误,欢迎指正,图像数据下载地址

/download/hjxu/10436834

点运算对单幅图像做处理,不改变像素的空间位置;代数运算对多幅图像做处理,也不改变像素的空间位置;几何运算对单幅图像做处理,改变像素的空间位置,几何运算包括两个独立的算法:空间变换算法和灰度级插值算法。 空间变换操作包括简单空间变换、多项式卷绕和几何校正、控制栅格插值和图像卷绕,这里主要讲述简单的空间变换,如图像平移、镜像、缩放和旋转。主要是通过线性代数中的齐次坐标变换。

图像平移坐标变换如下:

运行效果如下图所示,其中BMP图片(0,0)像素点为左下角。

第一步:在ResourceView资源视图中,添加Menu子菜单如下:(注意ID号)

第二步:设置平移对话框。将试图切换到ResourceView界面--选中Dialog,右键鼠标新建一个Dialog,并新建一个名为IDD_DIALOG_PY。编辑框(X)IDC_EDIT_PYX 和 (Y)IDC_EDIT_PYY,确定为默认按钮。设置成下图对话框:

第三步:在对话框资源模板空白区域双击鼠标—Create a new class创建一个新类--命名为CImagePYDlg。会自动生成它的.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){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++){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(),代码如下:

void CImageProcessingView::OnJhbhCzjx(){// TODO: 在此添加命令处理程序代码// 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[((m_nHeight - y)*m_nWidth + x) * 3 + (m_nHeight - y)*num];ImageSize[(y*m_nWidth + x) * 3 + y*num + 1] = m_pImage[((m_nHeight - y)*m_nWidth + x) * 3 + (m_nHeight - y)*num + 1];ImageSize[(y*m_nWidth + x) * 3 + y*num + 2] = m_pImage[((m_nHeight - y)*m_nWidth + x) * 3 + (m_nHeight - y)*num + 2];}}fwrite(ImageSize, m_nImage, 1, fpw);fclose(fpo);fclose(fpw); numPicture = 2;level = 400;Invalidate();}

三. 图像旋转-绕左下角旋转

图像饶原点旋转顺时针theta角矩阵变换如下:注意BMP图像(0,0)左下角

先来个绕左下角旋转

新建Dialog如下图所示,设置ID_DIALOG_XZ和变量:

再点击空白处创建CImageXZDlg类(旋转),它会自动生成.h和.cpp文件。打开类向导生成CImageXZDlg类的成员变量m_xzds(旋转度数),并设置其为int型(最大值360 最小值0)。

在类向导(Ctrl+W)选择类CImageProcessingView,为ID_JHBH_TXXZ(图像旋转)添加函数,同时添加头文件#include "ImageXZDlg.h"

void CImageProcessingView::OnXzzxj(){// TODO: 在此添加命令处理程序代码if (numPicture == 0){AfxMessageBox("请输入一张图像", MB_OK, 0);return;}if (m_nBitCount != 24){AfxMessageBox("输入图片不是24位", MB_OK, 0);return;}AfxMessageBox("左下角旋转!", MB_OK, 0);CImageXZ dlg;if (dlg.DoModal() == IDOK){int num;//记录每一行需要填充的字节if (m_nWidth * 3 % 4 != 0){num = 4 - m_nWidth * 3 % 4;}else{num = 0;}double PI = asin(0.5) * 6;double degree = 1.0 * dlg.m_xXZJZ * PI / 180;CString str;str.Format("转换后的角度=%d", dlg.m_xXZJZ);AfxMessageBox(str);//打开临时的图片 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];unsigned char black;int x, y;int xPlace, yPlace;for (y = 0; y < m_nHeight; y++)//高对应着行{for (x = 0; x < m_nWidth; x++)//宽对应着列{xPlace = (int)(x*cos(degree) - y*sin(degree));//代表原图上xplace的x坐标yPlace = (int)(x*sin(degree) + y*cos(degree));//代表原图上yplace的y坐标if (xPlace <= m_nWidth && yPlace <= m_nHeight && xPlace>=0 && yPlace>=0){ImageSize[(y*m_nWidth + x) * 3 + y*num] = m_pImage[(yPlace*m_nWidth+xPlace)*3 + yPlace * num];ImageSize[(y*m_nWidth + x) * 3 + y*num + 1] = m_pImage[(yPlace*m_nWidth + xPlace) * 3 + yPlace * num + 1];ImageSize[(y*m_nWidth + x) * 3 + y*num + 2] = m_pImage[(yPlace*m_nWidth + xPlace) * 3 + yPlace * num + 2];}else if (xPlace > m_nWidth || yPlace > m_nHeight || xPlace < 0 || yPlace < 0){ImageSize[(y*m_nWidth + x) * 3 + y*num] = black;ImageSize[(y*m_nWidth + x) * 3 + y*num + 1] = black;ImageSize[(y*m_nWidth + x) * 3 + y*num + 2] = black;}}}fwrite(ImageSize, m_nImage, 1, fpw);fclose(fpo);fclose(fpw);numPicture = 2;level = 400; //几何变换Invalidate();}}

运行效果如下图所示,中心旋转太难了!找到中心那个位置就不太容易,我做不下去了,fuck~同时旋转过程中,由于是饶左下角(0,0)实现,故有的角度会到界面外显示全黑。下图分别旋转15度和355度。

四. 图像旋转-绕中心点旋转(有点问题的版本)

这理论部分就有点复杂,还要找中心点坐标啥的,首先我干脆不找中心点了,默认中心点就是(width/2,height/2)

代码如下

void CImageProcessingView::OnJhzhZxxz(){// TODO: 在此添加命令处理程序代码// TODO: 在此添加命令处理程序代码if (numPicture == 0){AfxMessageBox("请输入一张图像", MB_OK, 0);return;}if (m_nBitCount != 24){AfxMessageBox("输入图片不是24位", MB_OK, 0);return;}AfxMessageBox("中心点逆时针旋转!", MB_OK, 0);CImageXZDlg dlg;if (dlg.DoModal() == IDOK){int num;//记录每一行需要填充的字节if (m_nWidth * 3 % 4 != 0){num = 4 - m_nWidth * 3 % 4;}else{num = 0;}double PI = asin(0.5) * 6;//定义pidouble degree = 1.0 * dlg.m_nZXJZ * PI / 180;//计算角度CString str;str.Format("转换后的角度=%d", dlg.m_nZXJZ);AfxMessageBox(str);//打开临时的图片 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];unsigned char black;int x, y;//旧图像点的坐标int xPlace, yPlace;//注意,这里xplace和之前的不同,这里是新图像点的坐标float a0 = (float)(m_nWidth - 1) *0.5;//计算中心点,a0是横坐标,b0是纵坐标float b0 = (float)(m_nHeight - 1)*0.5;float varx = -a0*cos(degree) + b0*sin(degree) + a0;//计算两个敞亮float vary = -a0*sin(degree) - b0*cos(degree) + b0;for (y = 0; y < m_nHeight; y++){for (x = 0; x < m_nWidth; x++){xPlace = (int)(x*cos(degree) - y*sin(degree) + varx + 0.5);//算出新图像点的坐标yPlace = (int)(x*sin(degree) + y*cos(degree) + vary + 0.5);if (xPlace <= m_nWidth && yPlace <= m_nHeight && xPlace >= 0 && yPlace >= 0){//替换ImageSize[(yPlace*m_nWidth + xPlace) * 3 + yPlace * num] = m_pImage[(y*m_nWidth + x) * 3 + y*num];ImageSize[(yPlace*m_nWidth + xPlace) * 3 + yPlace * num + 1] = m_pImage[(y*m_nWidth + x) * 3 + y*num + 1];ImageSize[(yPlace*m_nWidth + xPlace) * 3 + yPlace * num + 2] = m_pImage[(y*m_nWidth + x) * 3 + y*num + 2];}/*else if (xPlace > m_nWidth || yPlace > m_nHeight || xPlace < 0 || yPlace < 0){ImageSize[(yPlace*m_nWidth + xPlace) * 3 + yPlace * num] = black;ImageSize[(yPlace*m_nWidth + xPlace) * 3 + yPlace * num + 1] = black;ImageSize[(yPlace*m_nWidth + xPlace) * 3 + yPlace * num + 2] = black;}*/}}fwrite(ImageSize, m_nImage, 1, fpw);fclose(fpo);fclose(fpw);numPicture = 2;level = 400; //几何变换Invalidate();}}

结果很奇怪,旋转90度就不会出现这个问题。后来又写了一个版本的旋转,参考

/tingshuo/archive//05/15/2047016.html可以说是复制粘贴哈哈

五. 图像旋转-绕中心旋转(新版本)

图像旋转算法与实现

好吧,先下个定义,图像旋转是指图像以某一点为中心旋转一定的角度,形成一幅新的图像的过程。当然这个点通常就是图像的中心。既然是按照中心旋转,自然会有这样一个属性:旋转前和旋转后的点离中心的位置不变.

根据这个属性,我们可以得到旋转后的点的坐标与原坐标的对应关系。由于原图像的坐标是以左上角为原点的,所以我们先把坐标转换为以图像中心为原点。假设原图像的宽为w,高为h,(x0,y0)为原坐标内的一点,转换坐标后的点为(x1,y1)。那么不难得到:

x1= x0- w/2; y1= -y0+ h/2;

在新的坐标系下,假设点(x0,y0)距离原点的距离为r,点与原点之间的连线与x轴的夹角为b,旋转的角度为a,旋转后的点为(x1,y1), 如下图所示。

那么有以下结论:

x0=rcosb;y0=rsinb

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

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

得到了转换后的坐标,我们只需要把这些坐标再转换为原坐标系即可。这里还有一点要注意,旋转后的图像的长和宽会发生变化,因此要计算新图像的长和宽。

代码如下

void CImageProcessingView::OnJhbhGjxz(){// TODO: 在此添加命令处理程序代码if (numPicture == 0){AfxMessageBox("请输入一张图像", MB_OK, 0);return;}if (m_nBitCount != 24){AfxMessageBox("输入图片不是24位", MB_OK, 0);return;}AfxMessageBox("中心点逆时针旋转-该进版本!", MB_OK, 0);CImageXZDlg dlg;if (dlg.DoModal() == IDOK){int num;//记录每一行需要填充的字节if (m_nWidth * 3 % 4 != 0){num = 4 - m_nWidth * 3 % 4;}else{num = 0;}double PI = asin(0.5) * 6;double degree = 1.0 * dlg.m_nZXJZ * PI / 180;CString str;str.Format("转换后的角度=%d", dlg.m_nZXJZ);AfxMessageBox(str);//打开临时的图片 FILE *fpo = fopen(BmpName, "rb");FILE *fpw = fopen(BmpNameLin, "wb+");fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);fread(m_pImage, m_nImage, 1, fpo);//以图像中心为原点左上角,右上角,左下角和右下角的坐标,用于计算旋转后的图像的宽和高POINT pLT, pRT, pLB, pRB;//分别对应左上点、右上点、左下角、右下角pLT.x = -m_nWidth / 2; pLT.y = m_nHeight / 2;pRT.x = m_nWidth / 2; pRT.y = m_nHeight / 2;pLB.x = -m_nWidth / 2; pLB.y = -m_nHeight / 2;pRB.x = m_nWidth / 2; pRB.y = -m_nHeight / 2;//旋转之后的坐标POINT pLTN, pRTN, pLBN, pRBN;double sina = sin(degree);double cosa = cos(degree);pLTN.x = pLT.x*cosa + pLT.y*sina;pLTN.y = -pLT.x*sina + pLT.y*cosa;pRTN.x = pRT.x*cosa + pRT.y*sina;pRTN.y = -pRT.x*sina + pRT.y*cosa;pLBN.x = pLB.x*cosa + pLB.y*sina;pLBN.y = -pLB.x*sina + pLB.y*cosa;pRBN.x = pRB.x*cosa + pRB.y*sina;pRBN.y = -pRB.x*sina + pRB.y*cosa;//旋转后图像宽和高int desWidth = max(abs(pRBN.x - pLTN.x), abs(pRTN.x - pLBN.x));int desHeight = max(abs(pRBN.y - pLTN.y), abs(pRTN.y - pLBN.y));//分配旋转后图像的缓存int desBufSize = ((desWidth * m_nBitCount + 31) / 32) * 4 * desHeight;/*new和delete有效的进行动态内存的分配和释放*/unsigned char *ImageSize;ImageSize = new unsigned char[desBufSize];BITMAPFILEHEADER nbmfHeader;nbmfHeader.bfType = 0x4D42;nbmfHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)+ desWidth * desHeight * m_nBitCount / 8;nbmfHeader.bfReserved1 = 0;nbmfHeader.bfReserved2 = 0;nbmfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);//Bitmap头信息BITMAPINFOHEADER bmi;bmi.biSize = sizeof(BITMAPINFOHEADER);bmi.biWidth = desWidth;bmi.biHeight = desHeight;bmi.biPlanes = 1;bmi.biBitCount = m_nBitCount;bmi.biCompression = BI_RGB;bmi.biSizeImage = 0;bmi.biXPelsPerMeter = 0;bmi.biYPelsPerMeter = 0;bmi.biClrUsed = 0;bmi.biClrImportant = 0;fwrite(&nbmfHeader, sizeof(BITMAPFILEHEADER), 1, fpw);fwrite(&bmi, sizeof(BITMAPINFOHEADER), 1, fpw);int num1;//记录每一行需要填充的字节if (desWidth * 3 % 4 != 0){num1 = 4 - desWidth * 3 % 4;}else{num1 = 0;}for (int i = 0; i < desHeight; i++){for (int j = 0; j < desWidth; j++){//转换到以图像为中心的坐标系,并进行逆旋转int tX = (j - desWidth / 2)*cos(2*PI - degree) + (-i + desHeight / 2)*sin(2 * PI - degree);int tY = -(j - desWidth / 2)*sin(2 * PI - degree) + (-i + desHeight / 2)*cos(2 * PI - degree);//如果这个坐标不在原图像内,则不赋值if (tX > m_nWidth/ 2 || tX < -m_nWidth/ 2 || tY > m_nHeight / 2 || tY < -m_nHeight / 2){continue;}//再转换到原坐标系下int tXN = tX + m_nWidth/ 2; int tYN = abs(tY - m_nHeight / 2);//值拷贝ImageSize[(i*desWidth + j) * 3 + i * num1] = m_pImage[(tYN *m_nWidth + tXN) * 3 + tYN*num];ImageSize[(i*desWidth + j) * 3 + i * num1 + 1] = m_pImage[(tYN *m_nWidth + tXN) * 3 + tYN*num + 1];ImageSize[(i*desWidth + j) * 3 + i * num1 + 2] = m_pImage[(tYN *m_nWidth + tXN) * 3 + tYN*num + 2];}}fwrite(ImageSize, desBufSize, 1, fpw);fclose(fpo);fclose(fpw);numPicture = 2;level = 400; //几何变换Invalidate();}}

看新的结果

六. 图像缩放

下面是采用最近邻插值法的过程,注意BMP图缩放还需修改头文件信息。

第一步:在资源视图中添加“图像缩放”Dialog

第二步:点击空白处创建对话框的类CImageSFDlg,同时打开类向导为其添加成员变量m_sfbs(缩放倍数),其为int型在0-200之间。

第三步:打开类向导为其添加成员函数void CImageProcessingView::OnJhbhSf() 并实现缩放。同时添加头文件#include "ImageSFDlg.h"。

void CImageProcessingView::OnJhbhTxsfZjl(){// TODO: 在此添加命令处理程序代码if (numPicture == 0){AfxMessageBox("请输入一张图像", MB_OK, 0);return;}if (m_nBitCount != 24){AfxMessageBox("输入图片不是24位", MB_OK, 0);return;}AfxMessageBox("图像缩放-最近邻!", MB_OK, 0);CImageSFdlg dlg;if (dlg.DoModal() == IDOK){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);fread(m_pImage, m_nImage, 1, fpo);float SFX = dlg.SFX;float SFY = dlg.SFY;//计算缩放后的宽高int desWidth = int(m_nWidth * SFX);int desHeight = int(m_nHeight * SFY);//记录缩放后每一行需要填充的字节int num1;if (desWidth * 3 % 4 != 0){num1 = 4 - m_nWidth * 3 % 4;}else{num1 = 0;}//分配旋转后图像的缓存int desBufSize = ((desWidth * m_nBitCount + 31) / 32) * 4 * desHeight;unsigned char *ImageSize;ImageSize = new unsigned char[desBufSize];BITMAPFILEHEADER nbmfHeader;nbmfHeader.bfType = 0x4D42;nbmfHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + desWidth * desHeight * m_nBitCount / 8;nbmfHeader.bfReserved1 = 0;nbmfHeader.bfReserved2 = 0;nbmfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);//Bitmap头信息BITMAPINFOHEADER bmi;bmi.biSize = sizeof(BITMAPINFOHEADER);bmi.biWidth = desWidth;bmi.biHeight = desHeight;bmi.biPlanes = 1;bmi.biBitCount = m_nBitCount;bmi.biCompression = BI_RGB;bmi.biSizeImage = 0;bmi.biXPelsPerMeter = 0;bmi.biYPelsPerMeter = 0;bmi.biClrUsed = 0;bmi.biClrImportant = 0;fwrite(&nbmfHeader, sizeof(BITMAPFILEHEADER), 1, fpw);fwrite(&bmi, sizeof(BITMAPINFOHEADER), 1, fpw);for (int i = 0; i < desHeight; i++){for (int j = 0; j < desWidth; j++){int tXN = int(j / SFX);int tYN = int (i / SFY);//值拷贝ImageSize[(i*desWidth + j) * 3 + i * num1] = m_pImage[(tYN *m_nWidth + tXN) * 3 + tYN*num];ImageSize[(i*desWidth + j) * 3 + i * num1 + 1] = m_pImage[(tYN *m_nWidth + tXN) * 3 + tYN*num + 1];ImageSize[(i*desWidth + j) * 3 + i * num1 + 2] = m_pImage[(tYN *m_nWidth + tXN) * 3 + tYN*num + 2];}}fwrite(ImageSize, desBufSize, 1, fpw);fclose(fpo);fclose(fpw);numPicture = 2;flagSF = 1;m_nDrawWidthSF = desWidth;m_nDrawHeightSF = desHeight;level = 500; //几何变换Invalidate();}}

第四步:因为图像缩放修改BMP图片头信息,所以需要修改ShowBitmap中的显示第二张图片时的部分代码。如下所示:添加变量flagSF、m_nDrawWidthSF和m_nDrawHeightSF。

[cpp]view plaincopy/*定义显示图像缩放时的长宽与标记*/intflagSF=0;//图像几何变换缩放变换intm_nDrawWidthSF=0;//图像显示宽度缩放后intm_nDrawHeightSF=0;//图像显示高度缩放后//****************显示BMP格式图片****************//voidCImageProcessingView::ShowBitmap(CDC*pDC,CStringBmpName){......else//图像几何变换if(level=200){m_hBitmapChange=(HBITMAP)LoadImage(NULL,BmpNameLin,IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);}if(m_bitmap.m_hObject){m_bitmap.Detach();//m_bitmap为创建的位图对象}m_bitmap.Attach(m_hBitmapChange);//定义并创建一个内存设备环境CDCdcBmp;if(!dcBmp.CreateCompatibleDC(pDC))//创建兼容性的DCreturn;BITMAPm_bmp;//临时bmp图片变量m_bitmap.GetBitmap(&m_bmp);//将图片载入位图中CBitmap*pbmpOld=NULL;dcBmp.SelectObject(&m_bitmap);//将位图选入临时内存设备环境//图片显示调用函数StretchBltif(flagSF==1){CStringstr;str.Format("缩放长=%d宽%d原图长=%d宽=%d",m_nDrawWidthSF,m_nDrawHeightSF,m_nWidth,m_nHeight);AfxMessageBox(str);flagSF=0;//m_nDrawWidthSF缩放此存见函数最近邻插值法中赋值if(m_nDrawWidthSF<650&&m_nDrawHeightSF<650)pDC->StretchBlt(m_nWindowWidth-m_nDrawWidthSF,0,m_nDrawWidthSF,m_nDrawHeightSF,&dcBmp,0,0,m_bmp.bmWidth,m_bmp.bmHeight,SRCCOPY);elsepDC->StretchBlt(m_nWindowWidth-640,0,640,640,&dcBmp,0,0,m_bmp.bmWidth,m_bmp.bmHeight,SRCCOPY);//显示大小为640*640}else{//如果图片太大显示大小为固定640*640否则显示原图大小if(m_nDrawWidth<650&&m_nDrawHeight<650)pDC->StretchBlt(m_nWindowWidth-m_nDrawWidth,0,m_nDrawWidth,m_nDrawHeight,&dcBmp,0,0,m_bmp.bmWidth,m_bmp.bmHeight,SRCCOPY);elsepDC->StretchBlt(m_nWindowWidth-640,0,640,640,&dcBmp,0,0,m_bmp.bmWidth,m_bmp.bmHeight,SRCCOPY);}//恢复临时DC的位图dcBmp.SelectObject(pbmpOld);}

运行效果如下图所示,采用最近邻插值法缩放大了会出现失帧。

双线性插值有点难,后面有时间会写一下

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。