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

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


QPainter 垂直镜像 垂直镜像英文_i++

图2.13   图2.2的水平镜象


                                                                                 

QPainter 垂直镜像 垂直镜像英文_i++_02


图2.14   图2.2的垂直镜象

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

水平镜象的变化矩阵为:


                                                                                         

QPainter 垂直镜像 垂直镜像英文_i++_03

 


(2.10)

垂直镜象的变化矩阵为:


                                                                                         

QPainter 垂直镜像 垂直镜像英文_QPainter 垂直镜像_04

 


(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所示。


    QPainter 垂直镜像 垂直镜像英文_转置_05


    图2.2


    QPainter 垂直镜像 垂直镜像英文_转置_06



    图2.15   图2.2的转置

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

                                                                                             

    QPainter 垂直镜像 垂直镜像英文_QPainter 垂直镜像_07

     


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



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

      QPainter 垂直镜像 垂直镜像英文_数据_08

      QPainter 垂直镜像 垂直镜像英文_转置_09

      3.1变换原理

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

      1. 水平镜像变换 

        QPainter 垂直镜像 垂直镜像英文_数据_10

        向前映射 
        其逆变换为 

        QPainter 垂直镜像 垂直镜像英文_QPainter 垂直镜像_11

        向后映射
      2. 垂直镜像变换 

        QPainter 垂直镜像 垂直镜像英文_QPainter 垂直镜像_12

         
        其逆变换为 

        QPainter 垂直镜像 垂直镜像英文_数据_13

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



         

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

        //效果截图


        QPainter 垂直镜像 垂直镜像英文_QPainter 垂直镜像_14

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