以下文字内容copy于<<数字图像处理编程入门>>,code为自己实现,是win32控制台程序。
镜象(mirror)分水平镜象和垂直镜象两种。图2.2的水平镜象和垂直镜象分别如图2.13和图2.14所示
图2.13 图2.2的水平镜象
图2.14 图2.2的垂直镜象
镜象的变换矩阵很简单。设原图宽为w,高为h,变换后,图的宽和高不变。
水平镜象的变化矩阵为:
(2.10)
垂直镜象的变化矩阵为:
(2.11)
镜象变换的源代码如下,因为和平移的那段程序很类似,程序中的注释就简单一些。
-
-
-
-
-
- #include <iostream>
- #include <windows.h>
- #include <fstream>
- #include <cstring>
- using namespace std;
- BITMAPFILEHEADER bmpFileHeader;
- BITMAPINFOHEADER bmpInfoHeader;
- RGBQUAD *pColorTable;
- unsigned char *pBmpData;
- unsigned char *pXBmpData;
- unsigned char *pYBmpData;
-
-
-
-
-
- bool readBmp(char *fileName)
- {
- FILE *fp = fopen(fileName,"rb");
- if (NULL == fp)
- {
- cout<<"open failure!"<<endl;
- return FALSE;
- }
-
- fread(&bmpFileHeader,sizeof(BITMAPFILEHEADER),1,fp);
- fread(&bmpInfoHeader,sizeof(BITMAPINFOHEADER),1,fp);
- pColorTable = new RGBQUAD[256];
- fread(pColorTable,sizeof(RGBQUAD),256,fp);
- int imgSize = bmpInfoHeader.biSizeImage;
- pBmpData = new unsigned char[imgSize];
-
- pXBmpData = new unsigned char[imgSize];
- pYBmpData = new unsigned char[imgSize];
- fread(pBmpData,sizeof(unsigned char),imgSize,fp);
- fclose(fp);
- return TRUE;
- }
-
-
-
-
- void mirror()
- {
- int height = bmpInfoHeader.biHeight;
- int width = bmpInfoHeader.biWidth;
- int imgSize = bmpInfoHeader.biSizeImage;
- memset(pXBmpData,0,sizeof(unsigned char )*imgSize);
- memset(pYBmpData,0,sizeof(unsigned char )*imgSize);
- int lineByte = (width * 8 + 31) / 32 * 4;
- for(int i = 0; i < height; i++ )
- {
- for(int j = 0; j < width; j++ )
- {
- *(pXBmpData + i*lineByte + width - 1 - j) = *(pBmpData + i*lineByte + j);
- *(pYBmpData + (height - i - 1)*lineByte + j) = *(pBmpData + i*lineByte + j);
- }
- }
- }
-
-
-
-
-
- bool writeBmp(char *fileName,unsigned char *bmpData)
- {
- FILE *fp = fopen(fileName,"wb");
- if (NULL == fp)
- {
- cout<<"open failure!"<<endl;
- return FALSE;
- }
- int imgSize = bmpInfoHeader.biSizeImage;
-
- fwrite(&bmpFileHeader,sizeof(BITMAPFILEHEADER),1,fp);
- fwrite(&bmpInfoHeader,sizeof(BITMAPINFOHEADER),1,fp);
- fwrite(pColorTable,sizeof(RGBQUAD),256,fp);
- fwrite(bmpData,sizeof(unsigned char),imgSize,fp);
- fclose(fp);
- return TRUE;
- }
-
-
-
-
- void work()
- {
- char readFileName[] = "test.bmp";
- if (!readBmp(readFileName))
- {
- cout<<"read failure!"<<endl;
- return ;
- }
- mirror();
- char writeFileNameX[] = "X.bmp";
- char writeFileNameY[] = "Y.bmp";
- if (!writeBmp(writeFileNameX,pXBmpData))
- {
- cout<<"X write failure!"<<endl;
- return ;
- }
- if (!writeBmp(writeFileNameY,pYBmpData))
- {
- cout<<"Y write failure!"<<endl;
- return ;
- }
-
- delete []pColorTable;
- delete []pBmpData;
- delete []pXBmpData;
- delete []pYBmpData;
- cout<<"mirror success!"<<endl;
-
- }
- int main()
- {
- work();
- return 0;
- }
转置(transpose)是指将x,y坐标对换,图2.2的转置如图2.15所示。
图2.2
图2.15 图2.2的转置
要注意的是,转置和旋转900是有区别的,不信你可以试试:怎么旋转,图2.2也转不出图2.15来。另外,转置后图的宽高对换了。转置的变换矩阵很简单:
(2.12)
镜象变换的源代码如下,因为和旋转的那段程序很类似,程序中的注释就简单一些:
由于很多代码和之前的重复,所以只给出主要功能代码(win32 控制台程序)
- void transpose()
- {
- int height = bmpInfoHeader.biHeight;
- int width = bmpInfoHeader.biWidth;
- int imgSize = bmpInfoHeader.biSizeImage;
-
- bmpInfoHeader.biHeight = width;
- bmpInfoHeader.biWidth = height;
- memset(pNewBmpData,0,sizeof(unsigned char )*imgSize);
- int lineByte = (width * 8 + 31) / 32 * 4;
- int newLineByte = (height * 8 + 31) / 32 * 4;
- for(int i = 0; i < height; i++ )
- {
- for(int j = 0; j < width; j++ )
- {
- *(pNewBmpData + (width - 1 - j)*newLineByte + i) = *(pBmpData + (height - 1 - i)*lineByte + j);
- }
- }
- }
图像的镜像变换分为两种:水平镜像和垂直镜像。水平镜像以图像垂直中线为轴,将图像的像素进行对换,也就是将图像的左半部和右半部对调。垂直镜像则是以图像的水平中线为轴,将图像的上半部分和下班部分对调。效果如下:
3.1变换原理
设图像的宽度为width,长度为height。(x,y)为变换后的坐标,(x0,y0)为原图像的坐标
- 水平镜像变换
向前映射
其逆变换为
向后映射
- 垂直镜像变换
其逆变换为
3.2基于OpenCV的实现
水平镜像的实现
void GeometricTrans::hMirrorTrans(const Mat &src, Mat &dst)
{
CV_Assert(src.depth() == CV_8U);
dst.create(src.rows, src.cols, src.type());
int rows = src.rows;
int cols = src.cols;
switch (src.channels())
{
case 1:
const uchar *origal;
uchar *p;
for (int i = 0; i < rows; i++){
origal = src.ptr<uchar>(i);
p = dst.ptr<uchar>(i);
for (int j = 0; j < cols; j++){
p[j] = origal[cols - 1 - j];
}
}
break;
case 3:
const Vec3b *origal3;
Vec3b *p3;
for (int i = 0; i < rows; i++) {
origal3 = src.ptr<Vec3b>(i);
p3 = dst.ptr<Vec3b>(i);
for(int j = 0; j < cols; j++){
p3[j] = origal3[cols - 1 - j];
}
}
break;
default:
break;
}
}
分别对三通道图像和单通道图像做了处理,由于比较类似以后的代码只处理三通道图像,不再做特别说明。
在水平镜像变换时,遍历了整个图像,然后根据映射关系对每个像素都做了处理。实际上,水平镜像变换就是将图像坐标的列换到右边,右边的列换到左边,是可以以列为单位做变换的。同样垂直镜像变换也如此,可以以行为单位进行变换。
垂直镜像变换
void GeometricTrans::vMirrorTrans(const Mat &src, Mat &dst)
{
CV_Assert(src.depth() == CV_8U);
dst.create(src.rows, src.cols, src.type());
int rows = src.rows;
for (int i = 0; i < rows; i++)
src.row(rows - i - 1).copyTo(dst.row(i));
}
src.row(rows - i - 1).copyTo(dst.row(i));
上面一行代码是变换的核心代码,从原图像中取出第i行,并将其复制到目标图像。
有这样一道题目:
10.已知有n×n的方阵A,编写程序对A进行如下运算:
(1)转置
(2)水平镜像或垂直镜像
(3)顺时针旋转90度。
这里我解决的第二小问,其它的另写文章解决。
//文章后面附程序源文件下载地址
--------------------------------------------------------------------------------------------
程序主要代码:
- #define N 3 //N表示N行N列,这里设置N为3
-
-
-
-
- void HorizontalMirror(int arr[][N],int n)
- {
- for(int i=0;i<n;i++)
- for (int j=0;j<n/2;j++)
- {
-
- int temp=arr[i][j];
- arr[i][j]=arr[i][n-j-1];
- arr[i][n-j-1]=temp;
- }
- }
-
-
-
-
- void VerticalMirror(int arr[][N],int n)
- {
- for(int i=0;i<n;i++)
- for (int j=0;j<n/2;j++)
- {
-
- int temp=arr[j][i];
- arr[j][i]=arr[n-j-1][i];
- arr[n-j-1][i]=temp;
- }
- }
----------------------------------------------------------------------------------------------
//效果截图
----------------------------------------------------------------------------------------------------------------------