以下文字内容copy于<<数字图像处理编程入门>>,code为自己实现,是win32控制台程序。
镜象(mirror)分水平镜象和垂直镜象两种。图2.2的水平镜象和垂直镜象分别如图2.13和图2.14所示
图2.13 图2.2的水平镜象
图2.14 图2.2的垂直镜象
镜象的变换矩阵很简单。设原图宽为w,高为h,变换后,图的宽和高不变。
水平镜象的变化矩阵为:
(2.10)
垂直镜象的变化矩阵为:
(2.11)
镜象变换的源代码如下,因为和平移的那段程序很类似,程序中的注释就简单一些。
- /**
- * 程序名: Mirror.cpp
- * 功 能: 实现灰度图像的水平镜像和垂直镜像
- * 测试图片test.bmp放在工程目录下
- */
- #include <iostream>
- #include <windows.h>
- #include <fstream>
- #include <cstring>
- using namespace std;
- BITMAPFILEHEADER bmpFileHeader; //bmp文件头
- BITMAPINFOHEADER bmpInfoHeader; //bmp信息头
- RGBQUAD *pColorTable; //bmp颜色表
- unsigned char *pBmpData; //bmp位图数据
- unsigned char *pXBmpData; //水平镜像bmp位图数据
- unsigned char *pYBmpData; //垂直镜像bmp位图数据
- /**
- * 函数名: readBmp
- * 参 数: fileName -- 指向文件名的指针
- * 功 能: 读取将要处理的图片的信息,成功返回TRUE
- */
- 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;
- }
- /**
- * 函数名: mirror
- * 功 能: 对图片进行水平和垂直镜像操作
- */
- 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); //垂直镜像
- }
- }
- }
- /**
- * 函数名: writeBmp
- * 参 数: fileName -- 处理完之后的bmp图像
- * 功 能: 写入文件数据到相应的文件中
- */
- 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;
- }
- /**
- * 函数名: work
- * 功 能: 主要处理
- */
- 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; //新的lineByte
- 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
- //对一个n行n列的数组进行水平镜像转换
- //水平镜像操作是以矩阵垂直中轴线为中心,
- //将矩阵分为左右两部分镜像对称变换
- void HorizontalMirror(int arr[][N],int n)//注意二维数组作为参数时第二维大小必须有
- {
- for(int i=0;i<n;i++)//n行
- for (int j=0;j<n/2;j++) //每一行转换(n/2)次
- {
- //将左右两个对称的元素进行交换
- int temp=arr[i][j];
- arr[i][j]=arr[i][n-j-1];
- arr[i][n-j-1]=temp;
- }
- }
- //对一个n行n列的数组进行垂直镜像转换
- //垂直镜像操作是以矩阵水平中轴线为中心,
- //将矩阵分为上下两部分镜像对称变换
- void VerticalMirror(int arr[][N],int n)//注意二维数组作为参数时第二维大小必须有
- {
- for(int i=0;i<n;i++)//n列
- for (int j=0;j<n/2;j++) //每一列转换(n/2)次
- {
- //将上下两个对称的元素进行交换
- int temp=arr[j][i];
- arr[j][i]=arr[n-j-1][i];
- arr[n-j-1][i]=temp;
- }
- }
----------------------------------------------------------------------------------------------
//效果截图
----------------------------------------------------------------------------------------------------------------------