以下文字内容copy于<<数字图像处理编程入门>>,code为自己实现,是win32控制台程序。
镜象(mirror)分水平镜象和垂直镜象两种。图2.2的水平镜象和垂直镜象分别如图2.13和图2.14所示

图2.13 图2.2的水平镜象

图2.14 图2.2的垂直镜象
镜象的变换矩阵很简单。设原图宽为w,高为h,变换后,图的宽和高不变。
水平镜象的变化矩阵为:

(2.10)
垂直镜象的变化矩阵为:

(2.11)
镜象变换的源代码如下,因为和平移的那段程序很类似,程序中的注释就简单一些。
1. /**
2. * 程序名: Mirror.cpp
3. * 功 能: 实现灰度图像的水平镜像和垂直镜像
4. * 测试图片test.bmp放在工程目录下
5. */
6. #include <iostream>
7. #include <windows.h>
8. #include <fstream>
9. #include <cstring>
10. using namespace std;
11. BITMAPFILEHEADER bmpFileHeader; //bmp文件头
12. BITMAPINFOHEADER bmpInfoHeader; //bmp信息头
13. RGBQUAD *pColorTable; //bmp颜色表
14. unsigned char *pBmpData; //bmp位图数据
15. unsigned char *pXBmpData; //水平镜像bmp位图数据
16. unsigned char *pYBmpData; //垂直镜像bmp位图数据
17. /**
18. * 函数名: readBmp
19. * 参 数: fileName -- 指向文件名的指针
20. * 功 能: 读取将要处理的图片的信息,成功返回TRUE
21. */
22. bool readBmp(char *fileName)
23. {
24. FILE *fp = fopen(fileName,"rb"); //以二进制读方式打开
25. if (NULL == fp)
26. {
27. "open failure!"<<endl;
28. return FALSE;
29. }
30. //获得图片数据
31. sizeof(BITMAPFILEHEADER),1,fp);
32. sizeof(BITMAPINFOHEADER),1,fp);
33. new RGBQUAD[256];
34. sizeof(RGBQUAD),256,fp);
35. int imgSize = bmpInfoHeader.biSizeImage;
36. new unsigned char[imgSize];
37. //因为大小没有改变,所以一起处理了
38. new unsigned char[imgSize];
39. new unsigned char[imgSize];
40. sizeof(unsigned char),imgSize,fp);
41. //关闭文件
42. return TRUE;
43. }
44. /**
45. * 函数名: mirror
46. * 功 能: 对图片进行水平和垂直镜像操作
47. */
48. void mirror()
49. {
50. int height = bmpInfoHeader.biHeight;
51. int width = bmpInfoHeader.biWidth;
52. int imgSize = bmpInfoHeader.biSizeImage;
53. sizeof(unsigned char )*imgSize);
54. sizeof(unsigned char )*imgSize);
55. int lineByte = (width * 8 + 31) / 32 * 4; //每行像素的字节数
56. for(int i = 0; i < height; i++ )
57. {
58. for(int j = 0; j < width; j++ )
59. {
60. //水平镜像
61. //垂直镜像
62. }
63. }
64. }
65. /**
66. * 函数名: writeBmp
67. * 参 数: fileName -- 处理完之后的bmp图像
68. * 功 能: 写入文件数据到相应的文件中
69. */
70. bool writeBmp(char *fileName,unsigned char *bmpData)
71. {
72. FILE *fp = fopen(fileName,"wb"); //以二进制写方式打开
73. if (NULL == fp)
74. {
75. "open failure!"<<endl;
76. return FALSE;
77. }
78. int imgSize = bmpInfoHeader.biSizeImage;
79. //写入数据
80. sizeof(BITMAPFILEHEADER),1,fp);
81. sizeof(BITMAPINFOHEADER),1,fp);
82. sizeof(RGBQUAD),256,fp);
83. sizeof(unsigned char),imgSize,fp);
84. //关闭文件
85. return TRUE;
86. }
87. /**
88. * 函数名: work
89. * 功 能: 主要处理
90. */
91. void work()
92. {
93. char readFileName[] = "test.bmp";
94. if (!readBmp(readFileName))
95. {
96. "read failure!"<<endl;
97. return ;
98. }
99. mirror();
100. char writeFileNameX[] = "X.bmp";
101. char writeFileNameY[] = "Y.bmp";
102. if (!writeBmp(writeFileNameX,pXBmpData))
103. {
104. "X write failure!"<<endl;
105. return ;
106. }
107. if (!writeBmp(writeFileNameY,pYBmpData))
108. {
109. "Y write failure!"<<endl;
110. return ;
111. }
112. //释放
113. delete []pColorTable;
114. delete []pBmpData;
115. delete []pXBmpData;
116. delete []pYBmpData;
117. "mirror success!"<<endl;
118.
119. }
120. int main()
121. {
122. work();
123. return 0;
124. }
转置(transpose)是指将x,y坐标对换,图2.2的转置如图2.15所示。

图2.2

图2.15 图2.2的转置
要注意的是,转置和旋转900是有区别的,不信你可以试试:怎么旋转,图2.2也转不出图2.15来。另外,转置后图的宽高对换了。转置的变换矩阵很简单:

(2.12)
镜象变换的源代码如下,因为和旋转的那段程序很类似,程序中的注释就简单一些:
由于很多代码和之前的重复,所以只给出主要功能代码(win32 控制台程序)
1. void transpose()
2. {
3. int height = bmpInfoHeader.biHeight;
4. int width = bmpInfoHeader.biWidth;
5. int imgSize = bmpInfoHeader.biSizeImage;
6. //转置之后高宽变了
7. bmpInfoHeader.biHeight = width;
8. bmpInfoHeader.biWidth = height;
9. sizeof(unsigned char )*imgSize);
10. int lineByte = (width * 8 + 31) / 32 * 4; //每行像素的字节数
11. int newLineByte = (height * 8 + 31) / 32 * 4; //新的lineByte
12. for(int i = 0; i < height; i++ )
13. {
14. for(int j = 0; j < width; j++ )
15. {
16. //转置
17. }
18. }
19. }
图像的镜像变换分为两种:水平镜像和垂直镜像。水平镜像以图像垂直中线为轴,将图像的像素进行对换,也就是将图像的左半部和右半部对调。垂直镜像则是以图像的水平中线为轴,将图像的上半部分和下班部分对调。效果如下: 

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度。
这里我解决的第二小问,其它的另写文章解决。
//文章后面附程序源文件下载地址
--------------------------------------------------------------------------------------------
程序主要代码:
1. #define N 3 //N表示N行N列,这里设置N为3
2.
3. //对一个n行n列的数组进行水平镜像转换
4. //水平镜像操作是以矩阵垂直中轴线为中心,
5. //将矩阵分为左右两部分镜像对称变换
6. void HorizontalMirror(int arr[][N],int n)//注意二维数组作为参数时第二维大小必须有
7. {
8. for(int i=0;i<n;i++)//n行
9. for (int j=0;j<n/2;j++) //每一行转换(n/2)次
10. {
11. //将左右两个对称的元素进行交换
12. int temp=arr[i][j];
13. arr[i][j]=arr[i][n-j-1];
14. arr[i][n-j-1]=temp;
15. }
16. }
17.
18. //对一个n行n列的数组进行垂直镜像转换
19. //垂直镜像操作是以矩阵水平中轴线为中心,
20. //将矩阵分为上下两部分镜像对称变换
21. void VerticalMirror(int arr[][N],int n)//注意二维数组作为参数时第二维大小必须有
22. {
23. for(int i=0;i<n;i++)//n列
24. for (int j=0;j<n/2;j++) //每一列转换(n/2)次
25. {
26. //将上下两个对称的元素进行交换
27. int temp=arr[j][i];
28. arr[j][i]=arr[n-j-1][i];
29. arr[n-j-1][i]=temp;
30. }
31. }
----------------------------------------------------------------------------------------------
//效果截图

----------------------------------------------------------------------------------------------------------------------




















