图像镜像与图像转置

以下文字内容copy于<<数字图像处理编程入门>>,code为自己实现,是win32控制台程序。

镜象(mirror)分水平镜象和垂直镜象两种。图2.2的水平镜象和垂直镜象分别如图2.13和图2.14所示

图2.13   图2.2的水平镜象

                                                                                 

图2.14   图2.2的垂直镜象

镜象的变换矩阵很简单。设原图宽为w,高为h,变换后,图的宽和高不变。

水平镜象的变化矩阵为:

                                                                                           

(2.10)

垂直镜象的变化矩阵为:

                                                                                           

(2.11)

镜象变换的源代码如下,因为和平移的那段程序很类似,程序中的注释就简单一些。

[cpp]  view plain copy
  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.         cout<<"open failure!"<<endl;  
  28.         return FALSE;  
  29.     }  
  30.     //获得图片数据  
  31.     fread(&bmpFileHeader,sizeof(BITMAPFILEHEADER),1,fp);  
  32.     fread(&bmpInfoHeader,sizeof(BITMAPINFOHEADER),1,fp);  
  33.     pColorTable = new RGBQUAD[256];  
  34.     fread(pColorTable,sizeof(RGBQUAD),256,fp);  
  35.     int imgSize = bmpInfoHeader.biSizeImage;  
  36.     pBmpData = new unsigned char[imgSize];  
  37.     //因为大小没有改变,所以一起处理了  
  38.     pXBmpData = new unsigned char[imgSize];  
  39.     pYBmpData = new unsigned char[imgSize];  
  40.     fread(pBmpData,sizeof(unsigned char),imgSize,fp);  
  41.     fclose(fp); //关闭文件  
  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.     memset(pXBmpData,0,sizeof(unsigned char )*imgSize);  
  54.     memset(pYBmpData,0,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.             *(pXBmpData + i*lineByte + width - 1 - j) = *(pBmpData + i*lineByte + j); //水平镜像  
  61.             *(pYBmpData + (height - i - 1)*lineByte + j) = *(pBmpData + i*lineByte + j);  //垂直镜像  
  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.         cout<<"open failure!"<<endl;  
  76.         return FALSE;  
  77.     }  
  78.     int imgSize = bmpInfoHeader.biSizeImage;  
  79.     //写入数据  
  80.     fwrite(&bmpFileHeader,sizeof(BITMAPFILEHEADER),1,fp);  
  81.     fwrite(&bmpInfoHeader,sizeof(BITMAPINFOHEADER),1,fp);  
  82.     fwrite(pColorTable,sizeof(RGBQUAD),256,fp);  
  83.     fwrite(bmpData,sizeof(unsigned char),imgSize,fp);  
  84.     fclose(fp);  //关闭文件  
  85.     return TRUE;  
  86. }  
  87. /** 
  88. * 函数名: work 
  89. * 功  能: 主要处理 
  90. */  
  91. void work()  
  92. {  
  93.     char readFileName[] = "test.bmp";  
  94.     if (!readBmp(readFileName))  
  95.     {  
  96.         cout<<"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.         cout<<"X write failure!"<<endl;  
  105.         return ;  
  106.     }  
  107.     if (!writeBmp(writeFileNameY,pYBmpData))  
  108.     {  
  109.         cout<<"Y write failure!"<<endl;  
  110.         return ;  
  111.     }  
  112.     //释放  
  113.     delete []pColorTable;  
  114.     delete []pBmpData;  
  115.     delete []pXBmpData;  
  116.     delete []pYBmpData;  
  117.     cout<<"mirror success!"<<endl;  
  118.   
  119. }  
  120. int main()  
  121. {  
  122.     work();  
  123.     return 0;  
  1. }  



转置(transpose)是指将x,y坐标对换,图2.2的转置如图2.15所示。


图2.2


图2.15   图2.2的转置

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

                                                                                           

(2.12)

镜象变换的源代码如下,因为和旋转的那段程序很类似,程序中的注释就简单一些:

由于很多代码和之前的重复,所以只给出主要功能代码(win32 控制台程序)

[cpp]  view plain copy
  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.     memset(pNewBmpData,0,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.             *(pNewBmpData + (width - 1 - j)*newLineByte + i) = *(pBmpData + (height - 1 - i)*lineByte + j); //转置  
  17.         }  
  18.     }  
  19. }  


图像的镜像变换分为两种:水平镜像和垂直镜像。水平镜像以图像垂直中线为轴,将图像的像素进行对换,也就是将图像的左半部和右半部对调。垂直镜像则是以图像的水平中线为轴,将图像的上半部分和下班部分对调。效果如下: 
imageimage

3.1变换原理

设图像的宽度为width,长度为height。(x,y)为变换后的坐标,(x0,y0)为原图像的坐标

  1. 水平镜像变换 
    image向前映射 
    其逆变换为 
    image向后映射
  2. 垂直镜像变换 
    image 
    其逆变换为 
    image

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. }  


 

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

//效果截图

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




  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值