获取源工程可访问gitee可在此工程的基础上进行学习。
A实验:
(1)使用VC++设计程序:实现图像平移变换,图像缩放、图像裁剪、图像对角线镜像。
(2)使用VC++设计程序:对一幅高度与宽度均相等的图像,实现逆时针90度旋转。—这个直接使用B实验的代码也可以完成(B–任意大小图片旋转任意角度)
图像的平移:通过直角坐标系的平移变换公式:
x’ = x +dx
y’ = y + dy
注:
(x,y)为源图像的坐标,(x’, y’)为新图像的坐标,dx对应x的偏移量,dy对应y的偏移量。即:平移之后新图像上坐标为(x’, y’)的像素点的颜色值,应该等于原图像上坐标为(x, y)的像素点的颜色值。
void CImageProcessingView::OnGeoTranslation()
{
// 实验 图像平移
//MessageBox("请在这里添加图像平移的代码");
// 获得当前文档对象
CImageProcessingDoc* pDoc = GetDocument();
// 判断图像是否已被加载
if( pDoc->m_pDibInit->IsEmpty() )
{
MessageBox("图像未加载");
return;
}
int width = pDoc->m_pDibInit->GetWidth();
int height = pDoc->m_pDibInit->GetHeight();
int bitCount = pDoc->m_pDibInit->GetBitCount();
// 将 m_pDibInit 拷贝至 m_pDibTest
pDoc->m_pDibTest->CloneDib(pDoc->m_pDibInit);
// 设置 pDoc->m_pDibTest 为全白图像
int i, j;
RGBQUAD rgbQuad1;
rgbQuad1.rgbBlue = 255;
rgbQuad1.rgbGreen = 255;
rgbQuad1.rgbRed = 255;
rgbQuad1.rgbReserved = 0;
for(i=0; i<width; i++)
{
for(j=0; j<height; j++)
{
pDoc->m_pDibTest->SetPixelColor(i,j,&rgbQuad1);
}
}
//************************图像平移****************************//
RGBQUAD Quad;//新的像素点
float translatepixel = 2 ;
int newwidth=width*translatepixel;//移动后的新的画布的大小
int newheight=height*translatepixel;
pDoc->m_pDibTest->SetWidthHeight(newwidth,newheight);//重设画布大小
//将像素点的值进行移动
for(i=0;i<width;i++)
for(j=0;j<height;j++)
{
Quad =pDoc->m_pDibInit->GetPixelColor(i,j);//获取原图像的像素点的值
pDoc->m_pDibTest->SetPixelColor(i+256,j,&Quad);
}
//*********************************************************************//
// 交换 m_pDibInit 与 m_pDibTest 指针
CDib* pTmp = pDoc->m_pDibInit;
pDoc->m_pDibInit = pDoc->m_pDibTest;
pDoc->m_pDibTest = pTmp;
// 设置脏标记
pDoc->SetModifiedFlag(TRUE);
// 更新视图
pDoc->UpdateAllViews(NULL);
}
设原图像大小为宽度M、高度N,调整后宽度为k1×M、高度为k2×N,则:
Img.New(x,y) = Img.Old(x/k1, y/k2)
这样就可以实现图像的缩放
void CImageProcessingView::OnGeoResizing()
{
// 实验 图像缩放
//MessageBox("请在这里添加图像缩放的代码");
// 获得当前文档对象
CImageProcessingDoc* pDoc = GetDocument();
// 判断图像是否已被加载
if( pDoc->m_pDibInit->IsEmpty() )
{
MessageBox("图像未加载");
return;
}
int width = pDoc->m_pDibInit->GetWidth();
int height = pDoc->m_pDibInit->GetHeight();
int bitCount = pDoc->m_pDibInit->GetBitCount();
// 将 m_pDibInit 拷贝至 m_pDibTest
pDoc->m_pDibTest->CloneDib(pDoc->m_pDibInit);
// 考虑将图像放大两倍的情况
float nResizing = 0.6;//大于1则为放大,小于1则为缩小
// 获得新的图像高度
int newWidth = width*nResizing;
int newHeight = height*nResizing;
pDoc->m_pDibTest->SetWidthHeight(newWidth, newHeight);
//***************************图像的放大与缩小***********//
//间隔采样
int i=0;
int j=0;
RGBQUAD Quad1;
for(i=0;i<newWidth;i++)
for(j=0;j<newHeight;j++)
{
Quad1=pDoc->m_pDibInit->GetPixelColor(i/nResizing,j/nResizing);
pDoc->m_pDibTest->SetPixelColor(i,j,&Quad1);
}
// 交换 m_pDibInit 与 m_pDibTest 指针
CDib* pTmp = pDoc->m_pDibInit;
pDoc->m_pDibInit = pDoc->m_pDibTest;
pDoc->m_pDibTest = pTmp;
// 设置脏标记
pDoc->SetModifiedFlag(TRUE);
// 更新视图
pDoc->UpdateAllViews(NULL);
}
图像裁剪是从原始图像中选择或提取感兴趣的区域,以产生新的图像。这个过程可以通过调整图像的像素坐标来实现。以下是图像裁剪的基本原理:
选择裁剪区域: 首先,确定你希望保留的图像区域。这可以通过指定裁剪区域的坐标、宽度和高度来完成。裁剪区域通常以左上角和右下角的坐标、宽度和高度来定义。
调整坐标: 对于裁剪区域内的每个像素,调整其坐标以匹配裁剪后的图像。如果裁剪区域的左上角坐标是 ( x start , y start ) (x_{\text{start}}, y_{\text{start}}) (xstart,ystart),那么裁剪后的图像中的像素 ( i , j ) (i, j) (i,j)的新坐标是 ( i − x start , j − y start ) (i-x_{\text{start}}, j - y_{\text{start}}) (i−xstart,j−ystart)。
创建新图像: 使用裁剪后的坐标,从原始图像中提取像素值,并将它们组合成新的图像。新图像的宽度和高度将是裁剪区域的宽度和高度。
void CImageProcessingView::OnGeoCut()
{
// 实验 图像裁剪
// 获得当前文档对象
CImageProcessingDoc* pDoc = GetDocument();
// 判断图像是否已被加载
if( pDoc->m_pDibInit->IsEmpty() )
{
MessageBox("图像未加载");
return;
}
// 将 m_pDibInit 拷贝至 m_pDibTest
pDoc->m_pDibTest->CloneDib(pDoc->m_pDibInit);
// 设置裁剪的起始坐标点
int x1 = pDoc->m_pDibInit->m_lpBMIH->biWidth/4;
int y1 = pDoc->m_pDibInit->m_lpBMIH->biHeight/3;
// 设置裁剪的宽度与高度
int cx = pDoc->m_pDibInit->m_lpBMIH->biWidth/2;
int cy = pDoc->m_pDibInit->m_lpBMIH->biHeight/3;
// 将 m_pDibTest 的宽度与高度 按照裁剪的要求进行设置
pDoc->m_pDibTest->SetWidthHeight(cx, cy);
// 复制像素点
int i,j;
RGBQUAD rgbQuad;
for(i=0; i<cx; i++)
for(j=0; j<cy; j++)
{
rgbQuad = pDoc->m_pDibInit->GetPixelColor(i+x1, j+y1);
pDoc->m_pDibTest->SetPixelColor(i, j, &rgbQuad);
}
// 交换 m_pDibInit 与 m_pDibTest 指针
CDib* pTmp = pDoc->m_pDibInit;
pDoc->m_pDibInit = pDoc->m_pDibTest;
pDoc->m_pDibTest = pTmp;
// 设置脏标记
pDoc->SetModifiedFlag(TRUE);
// 更新视图
pDoc->UpdateAllViews(NULL);
}
图像的对角镜像是指通过图像的对角线进行翻转,从而得到镜像效果。具体来说,对角镜像的原理是将图像中的每个像素与其对角位置上的像素进行交换。
对于一个二维图像,坐标为 ( i , j ) (i, j) (i,j) 的像素与坐标为 ( j , i ) (j, i) (j,i) 的像素进行交换。这个操作实际上是关于对角线翻转。如果图像的宽度和高度相等,对角镜像就是将图像沿着其主对角线翻转。
以下是一个简单的示例说明对角镜像的原理:
假设有一个3x3的图像:
1 2 3
4 5 6
7 8 9
对角线上的元素是 (1, 5, 9),将它们与对应的对角位置上的元素进行交换:
1 4 7
2 5 8
3 6 9
这就是对角线翻转后的图像。对于更大的图像,同样的原理适用。
在计算机图像处理中,对角线镜像通常涉及到图像的像素交换或者矩阵操作。这可以通过遍历图像的每个像素并进行交换操作来实现。
void CImageProcessingView::OnDiagonalMirror()
{
// 实验 对角线镜像显示图像
//MessageBox("请在这里添加图像对角线镜像的代码");
// 可以参考:CImageProcessingView::OnGeoVerticalMirror()
// 获得当前文档对象
CImageProcessingDoc* pDoc = GetDocument();
// 判断图像是否已被加载
if( pDoc->m_pDibInit->IsEmpty() )
{
MessageBox("图像未加载");
return;
}
int width = pDoc->m_pDibInit->GetWidth();
int height = pDoc->m_pDibInit->GetHeight();
int bitCount = pDoc->m_pDibInit->GetBitCount();
if( width!=height )
{
MessageBox("图像长度与宽度不一致");
return;
}
// 将 m_pDibInit 拷贝至 m_pDibTest
pDoc->m_pDibTest->CloneDib(pDoc->m_pDibInit);
//*************************图像的对角镜像******************//
int i,j;
RGBQUAD quad;
for(i=0;i<width;i++)
for(j=0;j<height;j++)
{
quad = pDoc->m_pDibInit->GetPixelColor(i, j);
pDoc->m_pDibTest->SetPixelColor(j, i, &quad); //将横竖坐标对换即可
}
// 交换 m_pDibInit 与 m_pDibTest 指针
CDib* pTmp = pDoc->m_pDibInit;
pDoc->m_pDibInit = pDoc->m_pDibTest;
pDoc->m_pDibTest = pTmp;
// 设置脏标记
pDoc->SetModifiedFlag(TRUE);
// 更新视图
pDoc->UpdateAllViews(NULL);
}
原先的实验要求是实现宽和高相等的图片的旋转,但是后来B实验的要求是任意角度,就改写了代码,升级了一下
图像的旋转是通过应用旋转矩阵来实现的。旋转矩阵对应于二维平面上的坐标变换,其中旋转角度 (\theta) 决定了变换的角度。对于给定的坐标 ((X, Y)),应用旋转矩阵可以得到旋转后的坐标 ((X’, Y’))。
旋转矩阵如下所示:
[ X ′ Y ′ ] = [ cos ( θ ) − sin ( θ ) sin ( θ ) cos ( θ ) ] [ X Y ] \begin{bmatrix}X' \\Y'\end{bmatrix}=\begin{bmatrix}\cos(\theta) & -\sin(\theta) \\\sin(\theta) & \cos(\theta)\end{bmatrix}\begin{bmatrix}X \\Y\end{bmatrix} [X′Y′]=[cos(θ)sin(θ)−sin(θ)cos(θ)][XY]
对应的坐标变换公式是:
X ′ = X ⋅ cos ( θ ) − Y ⋅ sin ( θ ) X' = X \cdot \cos(\theta) - Y \cdot \sin(\theta) X′=X⋅cos(θ)−Y⋅sin(θ)
Y ′ = X ⋅ sin ( θ ) + Y ⋅ cos ( θ ) Y' = X \cdot \sin(\theta) + Y \cdot \cos(\theta) Y′=X⋅sin(θ)+Y⋅cos(θ)
这个公式描述了绕原点进行旋转的情况。如果要围绕其他点旋转,则需要先将坐标平移到该点,执行旋转,然后再将坐标平移到原来的位置。
在计算机图像处理中,旋转通常涉及对图像的每个像素应用坐标变换,以实现整体图像的旋转效果。
void CImageProcessingView::OnGeoRotation()
{
// 实验 图像旋转
//MessageBox("请在这里添加图像旋转的代码");
// 获得当前文档对象
CImageProcessingDoc* pDoc = GetDocument();
// 判断图像是否已被加载
if( pDoc->m_pDibInit->IsEmpty() )
{
MessageBox("图像未加载");
return;
}
int width = pDoc->m_pDibInit->GetWidth();
int height = pDoc->m_pDibInit->GetHeight();
int bitCount = pDoc->m_pDibInit->GetBitCount();
pDoc->m_pDibTest->CloneDib(pDoc->m_pDibInit);
int newwidth = width * (sqrt(2) + 1);
int newheight = height * (sqrt(2) + 1);
pDoc->m_pDibTest->SetWidthHeight(newwidth,newheight); //重设大小为对角线长度
double angle = M_PI / 2; //任意角度
RGBQUAD quad;
// 设置 pDoc->m_pDibTest 为全白图像
int i, j;
RGBQUAD rgbQuad1;
rgbQuad1.rgbBlue = 255;
rgbQuad1.rgbGreen = 255;
rgbQuad1.rgbRed = 255;
rgbQuad1.rgbReserved = 0;
for (i = 0; i < newwidth; i++)
{
for (j = 0; j < newheight; j++)
{
pDoc->m_pDibTest->SetPixelColor(i, j, &rgbQuad1);
}
}
for ( i = 0; i < width; i++)
for ( j = 0; j < height; j++)
{
quad = pDoc->m_pDibInit->GetPixelColor(i, j);
int x = 0;
x = -(i * cos(angle) + j * sin(angle));
int y = 0;
y = (i * sin(angle) + j * cos(angle));
pDoc->m_pDibTest->SetPixelColor(x, y, &quad);
}
// 交换 m_pDibInit 与 m_pDibTest 指针
CDib* pTmp = pDoc->m_pDibInit;
pDoc->m_pDibInit = pDoc->m_pDibTest;
pDoc->m_pDibTest = pTmp;
// 设置脏标记
pDoc->SetModifiedFlag(TRUE);
// 更新视图
pDoc->UpdateAllViews(NULL);
}