精华内容
下载资源
问答
  • 正割线平移法求非线性方程根,邢振中,,传统正割线收敛较慢,迭代法则无法用计算机自动建立满足收敛的迭代函数。从传统正割线出发,提出了新的非线性方程求根方法—
  • 本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程《数字图像处理》及课件进行讲解,主要通过MFC单文档视图实现显示BMP图片空间几何变换,包括图像平移、图形旋转、图像反转倒置镜像和图像...

    本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程《数字图像处理》及课件进行讲解,主要通过MFC单文档视图实现显示BMP图片空间几何变换,包括图像平移、图形旋转、图像反转倒置镜像和图像缩放的知识。同时文章比较详细基础,没有采用GDI+获取矩阵,而是通过读取BMP图片信息头和矩阵像素实现变换,希望该篇文章对你有所帮助,尤其是初学者和学习图像处理的学生。
           【数字图像处理】一.MFC详解显示BMP格式图片
           【数字图像处理】二.MFC单文档分割窗口显示图片
           【数字图像处理】三.MFC实现图像灰度、采样和量化功能详解
           【数字图像处理】四.MFC对话框绘制灰度直方图
           【数字图像处理】五.MFC图像点运算之灰度线性变化、灰度非线性变化、阈值化和均衡化处理详解
            免费资源下载地址:
            http://download.csdn.net/detail/eastmount/8772951

     

    一. 图像平移

     

           前一篇文章讲述了图像点运算(基于像素的图像变换),这篇文章讲述的是图像几何变换:在不改变图像内容的情况下对图像像素进行空间几何变换的处理方式。
            点运算对单幅图像做处理,不改变像素的空间位置;代数运算对多幅图像做处理,也不改变像素的空间位置;几何运算对单幅图像做处理,改变像素的空间位置,几何运算包括两个独立的算法:空间变换算法和灰度级插值算法。

            空间变换操作包括简单空间变换、多项式卷绕和几何校正、控制栅格插值和图像卷绕,这里主要讲述简单的空间变换,如图像平移、镜像、缩放和旋转。主要是通过线性代数中的齐次坐标变换。
            图像平移坐标变换如下:

            运行效果如下图所示,其中BMP图片(0,0)像素点为左下角。


            其代码核心算法:
            1.在对话框中输入平移坐标(x,y) m_xPY=x,m_yPY=y
            2.定义Place=dlg.m_yPY*m_nWidth*3 表示当前m_yPY行需要填充为黑色
            3.新建一个像素矩阵 ImageSize=new unsigned char[m_nImage]
            4.循环整个像素矩阵处理 
                 for(int i=0 ; i<m_nImage ; i++ ){
                       if(i<Place) {ImageSize[i]=black; continue;} //黑色填充底部 从小往上绘图
                       else if(i>=Place && countWidth<dlg.m_xPY*3) {//黑色填充左部分
                             ImageSize[i]=black; countWidth++;  continue;
                       }
                       else if(i>=Place && countWidth>=dlg.m_xPY*3) {//图像像素平移区域
                            ImageSize[i]=m_pImage[m_pImagePlace];//原(0,0)像素赋值过去
                            m_pImagePlace++; countWidth++;
                            if(countWidth==m_nWidth*3) { //一行填满 m_pImagePlace走到(0,1)
                                  number++; m_pImagePlace=number*m_nWidth*3;
                            }
                       }
                 }
             5.写文件绘图fwrite(ImageSize,m_nImage,1,fpw)

            第一步:在ResourceView资源视图中,添加Menu子菜单如下:(注意ID号)

            第二步:设置平移对话框。将试图切换到ResourceView界面--选中Dialog,右键鼠标新建一个Dialog,并新建一个名为IDD_DIALOG_PY。编辑框(X)IDC_EDIT_PYX 和 (Y)IDC_EDIT_PYY,确定为默认按钮。设置成下图对话框:

            第三步:在对话框资源模板空白区域双击鼠标—Create a new class创建一个新类--命名为CImagePYDlg。会自动生成它的.h和.cpp文件。打开类向导(Ctrl W),选择类名:CImagePYDlg添加成员变量如下图所示,同时在Message Maps中生成ID_JHBH_PY实现函数。

     
            第四步:在CImageProcessingView.cpp中添加头文件#include "ImagePYDlg.h",并实现平移。

     
    1. /********************************************************/

    2. /* 图像空间几何变换:图像平移 ID_JHBH_PY(几何变换-平移)

    3. /* 使用平移对话框:CImagePYDlg dlg

    4. /* 算法:f(x,y)=f(x+x0,y+y0)图像所有点平移,空的补黑'0'

    5. /* 注意该图像平移方法只是从左上角(0,0)处开始平移

    6. /* 其他方向原理相同 自己去实现

    7. /********************************************************/

    8.  
    9. void CImageProcessingView::OnJhbhPy()

    10. {

    11. if(numPicture==0) {

    12. AfxMessageBox("载入图片后才能空间平移!",MB_OK,0);

    13. return;

    14. }

    15. //定义采样对话框也是用来空间变换平移的坐标

    16. CImagePYDlg dlg;

    17. if( dlg.DoModal()==IDOK ) //显示对话框

    18. {

    19. //采样坐标最初为图片的自身像素

    20. if( dlg.m_xPY>m_nWidth || dlg.m_yPY>m_nHeight ) {

    21. AfxMessageBox("图片平移不能为超过原图长宽!",MB_OK,0);

    22. return;

    23. }

    24. AfxMessageBox("图片空间变换-平移!",MB_OK,0);

    25.  
    26. //打开临时的图片 读写文件

    27. FILE *fpo = fopen(BmpName,"rb");

    28. FILE *fpw = fopen(BmpNameLin,"wb+");

    29. fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);

    30. fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);

    31. fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);

    32. fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);

    33. fread(m_pImage,m_nImage,1,fpo);

    34.  
    35. /************************************************************/

    36. /* 图片空间变换-平移

    37. /* 坐标(dlg.m_xPY,dlg.m_yPY)表示图像平移的坐标

    38. /* 先用Plave计算出平移后的起始坐标,其他的坐标赋值为'0'黑色

    39. /* 然后依次平移坐标,空的赋为黑色,否则填充

    40. /************************************************************/

    41.  
    42. /******************************************************************/

    43. /* 严重错误1:数组变量赋值相等

    44. /* 在View.h中定义变量 BYTE *m_pImage 读入图片数据后的指针

    45. /* 建立临时变量数组,让它平移变换 unsigned char *ImageSize

    46. /* ImageSize=m_pImage(错误)

    47. /* 会导致ImageSize赋值变换时m_pImage也产生了变换,所以输出全为黑色

    48. /* 因为它俩指向了相同的数组地址

    49. /* 解决方法:使用下面C++的new方法动态分配或for循环i=m_nImage赋值

    50. /******************************************************************/

    51.  
    52. /*临时变量存储的像素与m_pImage相同,便于处理图像*/

    53. unsigned char *ImageSize;

    54. ImageSize=new unsigned char[m_nImage]; //new和delete有效的进行动态内存的分配和释放

    55.  
    56. int Place; //建立临时坐标 记录起始坐标(0,0)平移过来的位置

    57. int m_pImagePlace; //原始图像平移为(0,0) 图像把它平移到Place位置

    58. unsigned char black; //填充黑色='0'

    59.  
    60. /************************************************************/

    61. /* for(int i=0 ; i<m_nHeight ; i++ )

    62. /* for(int j=0 ; j<m_nWidth ; j++ )

    63. /* 不能使用的上面的因为可能图像的最后一行没有完整的一行像素

    64. /* 这样会出现exe报错,使用m_nImage读写所有像素比较正确

    65. /************************************************************/

    66.  
    67. Place=dlg.m_yPY*m_nWidth*3; //前m_yPY行都要填充为黑色

    68. black=0; //颜色为黑色

    69. m_pImagePlace=0; //图像处事位置为(0,0),把该点像素平移过去

    70. int countWidth=0; //记录每行的像素个数,满行时变回0

    71. int number=0; //数字记录使用的像素行数,平移时使用

    72.  
    73. for(int i=0 ; i<m_nImage ; i++ )

    74. {

    75. /*如果每行的像素填满时清为0*/

    76. if(countWidth==m_nWidth*3) {

    77. countWidth=0;

    78. }

    79.  
    80. /*第一部分:到平移后像素位置前面的所有像素点赋值为黑色*/

    81. if(i<Place) {

    82. ImageSize[i]=black; //赋值为黑色

    83. continue;

    84. }

    85.  
    86. /*第二部分:平移区域的左边部分赋值为黑色*/

    87. else if(i>=Place && countWidth<dlg.m_xPY*3) { //RGB乘3

    88. ImageSize[i]=black; //赋值为黑色

    89. countWidth++;

    90. continue;

    91. }

    92.  
    93. /****************************/

    94. /* 各部分如图所示:

    95. /* 000000000000000000000000

    96. /* 000000000000000000000000

    97. /* 0000000.................

    98. /* 0000000.................

    99. /* 0000000.................

    100. /* 0000000.................

    101. /* 点表示像素部分,0为黑色

    102. /****************************/

    103.  
    104. /* 重点错误提示:由于bmp图像显示是从左下角开始存储(0,0)点所以输出图像为 */

    105. /* bmp图像是从左下角到右上角排列的 */

    106.  
    107. /****************************/

    108. /* 各部分如图所示:

    109. /* 0000000.................

    110. /* 0000000.................

    111. /* 0000000.................

    112. /* 0000000.................

    113. /* 000000000000000000000000

    114. /* 000000000000000000000000

    115. /* 点表示像素部分,0为黑色

    116. /****************************/

    117.  
    118. /*第三部分:图像像素平移区域*/

    119. else if(i>=Place && countWidth>=dlg.m_xPY*3)

    120. {

    121. ImageSize[i]=m_pImage[m_pImagePlace];

    122. m_pImagePlace++;

    123. countWidth++;

    124. if(countWidth==m_nWidth*3)

    125. {

    126. number++;

    127. m_pImagePlace=number*m_nWidth*3;

    128. }

    129. }

    130. }

    131.  
    132. fwrite(ImageSize,m_nImage,1,fpw);

    133. fclose(fpo);

    134. fclose(fpw);

    135. numPicture = 2;

    136. level=200; //200表示几何变换

    137. Invalidate();

    138. }

    139. }

            同时在ShowBitmap中添加level标记重新绘制图片,代码如下:

     
    1. else //图像几何变换

    2. if(level=200)

    3. {

    4. m_hBitmapChange = (HBITMAP) LoadImage(NULL,BmpNameLin,IMAGE_BITMAP,0,0,

    5. LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);

    6. }

           运行时需要注意一点:BMP图像在处理过程中可能会出现一些斜线,而平移(40,60)位移量时可能出现如下。他是因为BMP格式有个非常重要的规定,要求每一扫描的字节数据必须能被4整除,也就是Dword对齐(长度4字节),如果图像的一行字节数不能被4整除,就需要在每行末尾不起0达到标准。
            例如一行像素为97字节,我们就需要补3个字节吗,数值可以是0,但是我们在BMP格式的信息头里说明了其宽度,所以补齐后对我们没有影响,所以后面补若干个字节的0即可直到被4整除。
     
            通过后面的图像缩放后,我从学做了一遍这个补齐的缩放。代码如下,能够实现完美平移。nice啊~

     
    1. void CImageProcessingView::OnJhbhPy()

    2. {

    3. if(numPicture==0) {

    4. AfxMessageBox("载入图片后才能空间平移!",MB_OK,0);

    5. return;

    6. }

    7. //定义采样对话框也是用来空间变换平移的坐标

    8. CImagePYDlg dlg;

    9. if( dlg.DoModal()==IDOK ) //显示对话框

    10. {

    11. //采样坐标最初为图片的自身像素

    12. if( dlg.m_xPY>m_nWidth || dlg.m_yPY>m_nHeight ) {

    13. AfxMessageBox("图片平移不能为超过原图长宽!",MB_OK,0);

    14. return;

    15. }

    16. AfxMessageBox("图片空间变换-平移!",MB_OK,0);

    17.  
    18. //打开临时的图片 读写文件

    19. FILE *fpo = fopen(BmpName,"rb");

    20. FILE *fpw = fopen(BmpNameLin,"wb+");

    21. fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);

    22. fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);

    23.  
    24. int num; //记录每行多余的图像素数个数

    25. int sfSize; //补齐后的图像大小

    26. //重点:图像的每行像素都必须是4的倍数:1*1的图像为 r g b 00H

    27. if(m_nWidth*3%4!=0)

    28. {

    29. num=(4-m_nWidth*3%4);

    30. sfSize=(m_nWidth*3+num)*m_nHeight; //每行多number个

    31. }

    32. else

    33. {

    34. num=0;

    35. sfSize=m_nWidth*m_nHeight*3;

    36. }

    37. //注意:假如最后一行像素不足,我默认处理为完整的一行,不足补00H

    38. //总之处理后的图像总是m*n且为4倍数,每行都完整存在

    39.  
    40. /*更改文件头信息 定义临时文件头结构变量*/

    41. BITMAPFILEHEADER bfhsf;

    42. BITMAPINFOHEADER bihsf;

    43. bfhsf=bfh;

    44. bihsf=bih;

    45. bfhsf.bfSize=sfSize+54;

    46. fwrite(&bfhsf,sizeof(BITMAPFILEHEADER),1,fpw);

    47. fwrite(&bihsf,sizeof(BITMAPINFOHEADER),1,fpw);

    48. fread(m_pImage,m_nImage,1,fpo);

    49.  
    50. CString str;

    51. str.Format("补齐=%d",num);

    52. AfxMessageBox(str);

    53.  
    54. /*临时变量存储的像素与sfSize相同 new和delete有效的进行动态内存的分配和释放*/

    55. unsigned char *ImageSize;

    56. ImageSize=new unsigned char[sfSize];

    57.  
    58. int Place; //建立临时坐标 记录起始坐标(0,0)平移过来的位置

    59. int m_pImagePlace; //原始图像平移为(0,0) 图像把它平移到Place位置

    60. unsigned char black=0; //填充黑色='0'

    61. unsigned char other=0; //补码00H='\0'

    62.  
    63. Place=dlg.m_yPY*(m_nWidth*3+num); //前m_yPY行都要填充为黑色

    64. m_pImagePlace=0; //图像处事位置为(0,0),把该点像素平移过去

    65. int countWidth=0; //记录每行的像素个数,满行时变回0

    66. int number=0; //数字记录使用的像素行数,平移时使用

    67.  
    68. for(int i=0 ; i<sfSize ; i++ )

    69. {

    70. /*第一部分:到平移后像素位置前面的所有像素点赋值为黑色*/

    71. if(i<Place)

    72. {

    73. ImageSize[i]=black; //赋值为黑色

    74. continue;

    75. }

    76.  
    77. /*第二部分:平移区域的左边部分赋值为黑色*/

    78. else if(i>=Place && countWidth<dlg.m_xPY*3) //RGB乘3

    79. {

    80. ImageSize[i]=black; //赋值为黑色

    81. countWidth++;

    82. continue;

    83. }

    84.  
    85. /*第三部分:图像像素平移区域*/

    86. else if(i>=Place && countWidth>=dlg.m_xPY*3)

    87. {

    88. ImageSize[i]=m_pImage[m_pImagePlace];

    89. m_pImagePlace++;

    90. countWidth++;

    91. if(countWidth==m_nWidth*3)

    92. {

    93. if(num==0)

    94. {

    95. countWidth=0;

    96. number++;

    97. m_pImagePlace=number*m_nWidth*3;

    98. }

    99. else //num为补0

    100. {

    101. for(int j=0;j<num;j++)

    102. {

    103. i++;

    104. ImageSize[i]=other;

    105. }

    106. countWidth=0;

    107. number++;

    108. m_pImagePlace=number*(m_nWidth*3+num); //重点:添加Num

    109. }

    110. }

    111. }

    112. }

    113.  
    114. fwrite(ImageSize,sfSize,1,fpw);

    115. fclose(fpo);

    116. fclose(fpw);

    117. numPicture = 2;

    118. level=200; //200表示几何变换

    119. Invalidate();

    120. }

    121. }

            运行效果如下图所示,完美平移,其他算法遇到斜线问题类似补齐即可。




     

     

    二. 图像镜像

    1.水平镜像翻转
            其变换矩阵如下:
                                     X=width-X0-1   (width为图像宽度)
                                     Y=Y0
            打开类向导,在CImageProcessingView中添加IDs为ID_JHBH_FZ,生成函数,代码如下:

     
    1. /* 几何变换 图像翻转:自己对这个功能比较感兴趣,做个图像反转 */

    2. void CImageProcessingView::OnJhbhFz()

    3. {

    4. if(numPicture==0) {

    5. AfxMessageBox("载入图片后才能空间反转!",MB_OK,0);

    6. return;

    7. }

    8. AfxMessageBox("图片空间变换-反转图像!",MB_OK,0);

    9.  
    10. //打开临时的图片

    11. FILE *fpo = fopen(BmpName,"rb");

    12. FILE *fpw = fopen(BmpNameLin,"wb+");

    13. fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);

    14. fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);

    15. fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);

    16. fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);

    17. fread(m_pImage,m_nImage,1,fpo);

    18.  
    19. /*new和delete有效的进行动态内存的分配和释放*/

    20. unsigned char *ImageSize;

    21. ImageSize=new unsigned char[m_nImage];

    22. int countWidth=0; //记录每行的像素个数,满行时变回0

    23. int Place; //记录图像每行的位置,便于图像反转

    24. int number=0; //数字记录使用的像素行数

    25. Place=m_nWidth*3-1;

    26.  
    27. //翻转矩阵: y=y0 x=width-x0-1

    28. for(int i=0 ; i<m_nImage ; i++ )

    29. {

    30. if(countWidth==m_nWidth*3)

    31. {

    32. countWidth=0;

    33. }

    34. ImageSize[i]=m_pImage[Place]; //(0,0)赋值(0,width*3-1)像素

    35. Place--;

    36. countWidth++;

    37. if(countWidth==m_nWidth*3)

    38. {

    39. number++;

    40. Place=number*m_nWidth*3-1;

    41. }

    42. }

    43.  
    44. fwrite(ImageSize,m_nImage,1,fpw);

    45. fclose(fpo);

    46. fclose(fpw);

    47. numPicture = 2;

    48. level=200;

    49. Invalidate();

    50. }

            运行效果如下图所示,其中还是存在一些小BUG,如前面的BMP图补0凑齐4整数倍宽度或颜色失帧。

     




    2.垂直镜像倒转
            其中变换矩阵如下:
                                          X=X0
                                          Y=height-Y0-1   (height为图像高度)
            它相当于把原图的像素矩阵的最后一行像素值赋值给第一行,首先找到(0,0)对应的(height-1,0)像素值,然后依次赋值该行的像素数据;最后当前行赋值结束,依次下一行。重点是找到每行的第一个像素点即可。
            代码中引用两个变量:Place=(m_nWidth*3)*(m_nHeight-1-1)即是(height-1,0)最后一行的第一个像素点;然后是循环中Place=(m_nWidth*3)*(m_nHeight-number-1)找到每行的第一个像素点。

            同样通过类向导生成函数void CImageProcessingView::OnJhbhDz(),代码如下:

     
    1. /* 几何变换 图像倒转 */

    2. void CImageProcessingView::OnJhbhDz()

    3. {

    4. if(numPicture==0) {

    5. AfxMessageBox("载入图片后才能空间反转!",MB_OK,0);

    6. return;

    7. }

    8. AfxMessageBox("图片空间变换-反转图像!",MB_OK,0);

    9.  
    10. //打开临时的图片

    11. FILE *fpo = fopen(BmpName,"rb");

    12. FILE *fpw = fopen(BmpNameLin,"wb+");

    13. fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);

    14. fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);

    15. fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);

    16. fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);

    17. fread(m_pImage,m_nImage,1,fpo);

    18.  
    19. /*new和delete有效的进行动态内存的分配和释放*/

    20. unsigned char *ImageSize;

    21. ImageSize=new unsigned char[m_nImage];

    22. int countWidth=0; //记录每行像素个数,满行时变回0

    23. int Place; //每列位置

    24. int number=0; //像素行数

    25. Place=(m_nWidth*3)*(m_nHeight-1-1); //0行存储

    26.  
    27. //翻转矩阵: x=x0 y=height-y0-1

    28. for(int i=0 ; i<m_nImage ; i++ )

    29. {

    30. ImageSize[i]=m_pImage[Place]; //(0,0)赋值(0,0)像素

    31. Place++;

    32. countWidth++;

    33. if(countWidth==m_nWidth*3)

    34. {

    35. countWidth=0;

    36. number++;

    37. Place=(m_nWidth*3)*(m_nHeight-number-1);

    38. }

    39. }

    40.  
    41. fwrite(ImageSize,m_nImage,1,fpw);

    42. fclose(fpo);

    43. fclose(fpw);

    44. numPicture = 2;

    45. level=200;

    46. Invalidate();

    47. }

            运行结果如下图所示,第二张图颜色没有失帧或变灰,这完全可以怀疑在翻转过程中RGB像素编程BGR后导致的结果,最终实现了翻转图像,但灰度存在一定;所以如果改为RBG顺序不变化即可原图颜色显示。




     


     

     

    三. 图像旋转

            图像饶原点旋转顺时针theta角矩阵变换如下:注意BMP图像(0,0)左下角


            写到这里真心觉得写底层的代码非常困难啊!尤其是以为像素转换二维像素,同时也觉得当时的自己算法部分还是很强大的,也感觉到如果采用GDI+操作像素矩阵Matrix或ColorMatrix是多么的方便,因为它定义好了X和Y向量,这就是为什么Android前面写的图像处理要容易得多。但是效率高~
            好像利用GDI+旋转通过几句代码即可:
            matrix.Rotate(15); //矩阵旋转15度
            graph.SetTransform(&matrix);
            graph.DrawImage(&image,points,3);
            下面这部分代码是实现Android旋转的:参考我的博客

     
    1. //旋转图片

    2. private void TurnPicture() {

    3. Matrix matrix = new Matrix();

    4. turnRotate=turnRotate+15;

    5. //选择角度 饶(0,0)点选择 正数顺时针 负数逆时针 中心旋转

    6. matrix.setRotate(turnRotate,bmp.getWidth()/2,bmp.getHeight()/2);

    7. Bitmap createBmp = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig());

    8. Canvas canvas = new Canvas(createBmp);

    9. Paint paint = new Paint();

    10. canvas.drawBitmap(bmp, matrix, paint);

    11. imageCreate.setBackgroundColor(Color.RED);

    12. imageCreate.setImageBitmap(createBmp);

    13. textview2.setVisibility(View.VISIBLE);

    14. }

            实现效果如下图所示:


            言归正传,新建Dialog如下图所示,设置ID_DIALOG_XZ和变量:

            再点击空白处创建CImageXZDlg类(旋转),它会自动生成.h和.cpp文件。打开类向导生成CImageXZDlg类的成员变量m_xzds(旋转度数),并设置其为int型(最大值360 最小值0)。
            在类向导(Ctrl+W)选择类CImageProcessingView,为ID_JHBH_TXXZ(图像旋转)添加函数,同时添加头文件#include "ImageXZDlg.h"

     
    1. /**********************************************************/

    2. /* 几何变换:图片旋转

    3. /* 先添加对话框:IDD_JHBH_TXXZ(图像旋转),创建新类CImageXZDlg

    4. /* 创建输入度数的:m_xzds Member variables 为int 0-360间

    5. /**********************************************************/

    6.  
    7. void CImageProcessingView::OnJhbhTxxz()

    8. {

    9. if(numPicture==0) {

    10. AfxMessageBox("载入图片后才能空间旋转!",MB_OK,0);

    11. return;

    12. }

    13.  
    14. //定义对话框并调用对话框

    15. CImageXZDlg dlg;

    16. if( dlg.DoModal()==IDOK ) //显示对话框

    17. {

    18. AfxMessageBox("图片空间变换-旋转图像!",MB_OK,0);

    19. //读写文件

    20. FILE *fpo = fopen(BmpName,"rb");

    21. FILE *fpw = fopen(BmpNameLin,"wb+");

    22. fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);

    23. fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);

    24. fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);

    25. fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);

    26. fread(m_pImage,m_nImage,1,fpo);

    27.  
    28. /*new和delete有效的进行动态内存的分配和释放*/

    29. unsigned char *ImageSize;

    30. ImageSize=new unsigned char[m_nImage];

    31. int Place; //记录图像每行的位置,便于图像旋转

    32.  
    33. /*定义PA=3.14时使用的方法是arcsin(1.0/2)*6即为π*/

    34. double PA;

    35. PA=asin(0.5)*6;

    36.  
    37. /*把输入的0-360的正整数度数转换为角度,30度=π/6*/

    38. double degree;

    39. degree=PA*dlg.m_xzds/180; //调用dlg.m_xzds(旋转度数)

    40.  
    41. //对应的二维矩阵 注意图像矩阵从左下角开始处理 它最终要转换成一维存储

    42. int X,Y; //图像变换前通过一维矩阵转换为二维

    43. int XPlace,YPlace;

    44.  
    45. //输出转换为的角度

    46. CString str;

    47. str.Format("转换后的角度=%f",degree);

    48. AfxMessageBox(str);

    49.  
    50. //图像旋转处理

    51. for(int i=0 ; i<m_nImage ; i++ )

    52. {

    53. //原图:一维矩阵转换为二维矩阵

    54. X=(i/3)%m_nWidth;

    55. Y=(i/3)/m_nWidth;

    56. //注意错误:X=i/m_nHeight Y=i%m_nWidth; 只输出最后1/3

    57.  
    58. //图像旋转为:a(x,y)=x*cos-y*sin b(x,y)=x*sin+y*cos

    59. XPlace=(int)(X*cos(degree)-Y*sin(degree));

    60. YPlace=(int)(X*sin(degree)+Y*cos(degree));

    61.  
    62. //在转换为一维图想输出

    63. if( (XPlace>=0 && XPlace<=m_nWidth) && (YPlace>=0 && YPlace<=m_nHeight) )

    64. {

    65. Place=YPlace*m_nWidth*3+XPlace*3;

    66. //在图像范围内赋值为该像素

    67. if(Place+2<m_nImage)

    68. {

    69. ImageSize[i]=m_pImage[Place];

    70. i++;

    71. ImageSize[i]=m_pImage[Place+1];

    72. i++;

    73. ImageSize[i]=m_pImage[Place+2];

    74. }

    75. //否则赋值为黑色

    76. else

    77. {

    78. ImageSize[i]=0;

    79. i++;

    80. ImageSize[i]=0;

    81. i++;

    82. ImageSize[i]=0;

    83. }

    84. }

    85. //否则赋值为黑色

    86. else

    87. {

    88. ImageSize[i]=0;

    89. i++;

    90. ImageSize[i]=0;

    91. i++;

    92. ImageSize[i]=0;

    93. }

    94. }

    95.  
    96. fwrite(ImageSize,m_nImage,1,fpw);

    97. fclose(fpo);

    98. fclose(fpw);

    99. numPicture = 2;

    100. level=200; //几何变换

    101. Invalidate();

    102. }

    103. }

            运行效果如下图所示,中心旋转太难了!找到中心那个位置就不太容易,我做不下去了,fuck~同时旋转过程中,由于是饶左下角(0,0)实现,故有的角度会到界面外显示全黑。下图分别旋转15度和355度。




     


     

     

    四. 图像缩放

            图像缩放主要有两种方法:
            1.最近邻插值:向后映射时,输出图像的灰度等于离它所映射位置最近的输入图像的灰度值。其中向前映射和向后映射如下:

     

            对于向前映射每个输出图像的灰度要经过多次运算,对于向后映射,每个输出图像的灰度只经过一次运算。在实际应用中,更多的是采用向后映射法,其中根据四个相邻像素灰度值计算某个位置的像素灰度值即为灰度级插值。
            2.双线性插值:四点确定一个平面函数,属于过约束问题。即单位正方形顶点已知,求正方形内任一点的f(x,y)值。


            换个通熟的说法,如下图所示。采用最近邻插值法就是P(x,y)像素值采用四舍五入等于离它最近的输入图像像素值。分别计算它到四个顶点之间的距离,但是这样会造成图像的马赛克、锯齿等现象。而采用双线性插值法,主要通过该坐标周围的四个像素值,按照比例混合计算器近似值。比例混合的依据是离哪个像素近,哪个像素的比例越大。



            下面是采用最近邻插值法的过程,注意BMP图缩放还需修改头文件信息。
            第一步:在资源视图中添加“图像缩放”Dialog

            第二步:点击空白处创建对话框的类CImageSFDlg,同时打开类向导为其添加成员变量m_sfbs(缩放倍数),其为int型在0-200之间。



            第三步:打开类向导为其添加成员函数void CImageProcessingView::OnJhbhSf() 并实现缩放。同时添加头文件#include "ImageSFDlg.h"。

     
    1. /*******************************************************************/

    2. /* ID_JHBH_SF: 几何运算-缩放-最近邻插值算法

    3. /* 算法思想:输出图像的灰度等于离它所映射位置最近的输入图像的灰度值

    4. /* 先计算出放大缩小后的长宽,根据它计算找原图中的点灰度,四舍五入

    5. /*******************************************************************/

    6.  
    7. void CImageProcessingView::OnJhbhSf()

    8. {

    9. if(numPicture==0) {

    10. AfxMessageBox("载入图片后才能几何缩放图像!",MB_OK,0);

    11. return;

    12. }

    13.  
    14. CImageSFDlg dlg; //定义缩放对话框

    15. if( dlg.DoModal()==IDOK )

    16. {

    17. //采样坐标最初为图片的自身像素 m_sfbs(缩放倍数)

    18. if( dlg.m_sfbs==0 ) {

    19. AfxMessageBox("输入图片缩放倍数不能为0!",MB_OK,0);

    20. return;

    21. }

    22.  
    23. FILE *fpo = fopen(BmpName,"rb");

    24. FILE *fpw = fopen(BmpNameLin,"wb+");

    25. fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);

    26. fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);

    27.  
    28. /*先求缩放后的长宽*/

    29. int sfWidth,sfHeight; //缩放后的长宽

    30. int sfSize; //缩放后的图像大小

    31. sfWidth=(int)(m_nWidth*(dlg.m_sfbs*1.0)/100); //24位图像RGB必须是3倍数 循环读取时为RGB

    32. sfHeight=(int)(m_nHeight*(dlg.m_sfbs*1.0)/100);

    33. int number; //记录每行多余的图像素数个数

    34.  
    35. //重点:图像的每行像素都必须是4的倍数:1*1的图像为 r g b 00H

    36. if(sfWidth*3%4!=0) {

    37. number=(4-sfWidth*3%4);

    38. sfSize=(sfWidth*3+(4-sfWidth*3%4))*sfHeight;

    39. }

    40. else {

    41. number=0;

    42. sfSize=sfWidth*sfHeight*3;

    43. }

    44. //注意:假如最后一行像素不足,我默认处理为完整的一行,不足补00H

    45. //总之处理后的图像总是m*n且为4倍数,每行都完整存在

    46.  
    47. /*更改文件头信息 定义临时文件头结构变量*/

    48. BITMAPFILEHEADER bfhsf;

    49. BITMAPINFOHEADER bihsf; //缩放(sf)

    50. bfhsf=bfh;

    51. bihsf=bih;

    52.  
    53. bfhsf.bfSize=sfSize+54;

    54. bihsf.biWidth=sfWidth;

    55. bihsf.biHeight=sfHeight;

    56.  
    57. //显示部分m_nDrawWidth<650显示原图,否则显示

    58. flagSF=1; //图像缩放为1标识变量

    59. m_nDrawWidthSF=sfWidth;

    60. m_nDrawHeightSF=sfHeight;

    61.  
    62. fwrite(&bfhsf,sizeof(BITMAPFILEHEADER),1,fpw);

    63. fwrite(&bihsf,sizeof(BITMAPINFOHEADER),1,fpw);

    64.  
    65. fread(m_pImage,m_nImage,1,fpo);

    66.  
    67. unsigned char red,green,blue;

    68. unsigned char other=0; //补码00H='\0'

    69. int placeX; //记录在原图中的第几行的位置

    70. int placeY; //记录在原图中的位置(x,y)

    71. int placeBH; //记录变换后在变换图中的位置

    72.  
    73. /*new和delete有效的进行动态内存的分配和释放*/

    74. unsigned char *ImageSize;

    75. ImageSize=new unsigned char[sfSize];

    76.  
    77. /*读取文件像素信息 缩放注意:1.找最近灰度 2.四舍五入法(算法+0.5)*/

    78. for(int i=0; i<sfHeight ; i++ ) //行

    79. {

    80. placeX=(int)(i/(dlg.m_sfbs*1.0/100)+0.5)*bih.biWidth*3;

    81. for(int j=0; j<sfWidth ; j++ ) //列

    82. {

    83. red=green=blue=0;

    84. //放大倍数为(dlg.m_sfbs*1.0/100)

    85. placeY=placeX+(int)(j/(dlg.m_sfbs*1.0/100)+0.5)*3;

    86. //重点是:number*i补充00H,如果是numer图像会被切成2块

    87. placeBH=(i*sfWidth*3+number*i)+j*3;

    88. if(placeY+2<m_nImage)

    89. {

    90. ImageSize[placeBH]=m_pImage[placeY];

    91. ImageSize[placeBH+1]=m_pImage[placeY+1];

    92. ImageSize[placeBH+2]=m_pImage[placeY+2];

    93. }

    94. else

    95. {

    96. ImageSize[placeBH]=0;

    97. ImageSize[placeBH+1]=0;

    98. ImageSize[placeBH+2]=0;

    99. }

    100. }

    101. }

    102.  
    103. fwrite(ImageSize,sfSize,1,fpw);

    104. fclose(fpo);

    105. fclose(fpw);

    106. numPicture = 2;

    107. level=200;

    108. Invalidate();

    109. }

    110. }

            第四步:因为图像缩放修改BMP图片头信息,所以需要修改ShowBitmap中的显示第二张图片时的部分代码。如下所示:添加变量flagSF、m_nDrawWidthSF和m_nDrawHeightSF。

     
    1. /*定义显示图像缩放时的长宽与标记*/

    2. int flagSF=0; //图像几何变换缩放变换

    3. int m_nDrawWidthSF=0; //图像显示宽度缩放后

    4. int m_nDrawHeightSF=0; //图像显示高度缩放后

    5.  
    6. //****************显示BMP格式图片****************//

    7. void CImageProcessingView::ShowBitmap(CDC *pDC, CString BmpName)

    8. {

    9. ......

    10. else //图像几何变换

    11. if(level=200)

    12. {

    13. m_hBitmapChange = (HBITMAP) LoadImage(NULL,BmpNameLin,IMAGE_BITMAP,0,0,

    14. LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);

    15. }

    16.  
    17.  
    18. if( m_bitmap.m_hObject ) {

    19. m_bitmap.Detach(); //m_bitmap为创建的位图对象

    20. }

    21. m_bitmap.Attach(m_hBitmapChange);

    22. //定义并创建一个内存设备环境

    23. CDC dcBmp;

    24. if( !dcBmp.CreateCompatibleDC(pDC) ) //创建兼容性的DC

    25. return;

    26. BITMAP m_bmp; //临时bmp图片变量

    27. m_bitmap.GetBitmap(&m_bmp); //将图片载入位图中

    28. CBitmap *pbmpOld = NULL;

    29. dcBmp.SelectObject(&m_bitmap); //将位图选入临时内存设备环境

    30.  
    31. //图片显示调用函数StretchBlt

    32. if(flagSF==1)

    33. {

    34. CString str;

    35. str.Format("缩放长=%d 宽%d 原图长=%d 宽=%d",m_nDrawWidthSF,

    36. m_nDrawHeightSF,m_nWidth,m_nHeight);

    37. AfxMessageBox(str);

    38. flagSF=0;

    39. //m_nDrawWidthSF缩放此存见函数最近邻插值法中赋值

    40. if(m_nDrawWidthSF<650 && m_nDrawHeightSF<650)

    41. pDC->StretchBlt(m_nWindowWidth-m_nDrawWidthSF,0,

    42. m_nDrawWidthSF,m_nDrawHeightSF,&dcBmp,0,0,m_bmp.bmWidth,m_bmp.bmHeight,SRCCOPY);

    43. else

    44. pDC->StretchBlt(m_nWindowWidth-640,0,640,640,&dcBmp,0,0,

    45. m_bmp.bmWidth,m_bmp.bmHeight,SRCCOPY); //显示大小为640*640

    46. }

    47. else {

    48. //如果图片太大显示大小为固定640*640 否则显示原图大小

    49. if(m_nDrawWidth<650 && m_nDrawHeight<650)

    50. pDC->StretchBlt(m_nWindowWidth-m_nDrawWidth,0,

    51. m_nDrawWidth,m_nDrawHeight,&dcBmp,0,0,m_bmp.bmWidth,m_bmp.bmHeight,SRCCOPY);

    52. else

    53. pDC->StretchBlt(m_nWindowWidth-640,0,640,640,&dcBmp,0,0,

    54. m_bmp.bmWidth,m_bmp.bmHeight,SRCCOPY);

    55. }

    56. //恢复临时DC的位图

    57. dcBmp.SelectObject(pbmpOld);

    58. }

            运行效果如下图所示,采用最近邻插值法缩放大了会出现失帧。

     


     


            但是同时当图片缩小是总是报错,图片缩放确实有点难,因为像素需要补齐4整数倍,同时需要修改消息头,同时像素矩阵的变换都非常复杂。

     

            

    //

    转载:https://blog.csdn.net/eastmount/article/details/46345299

    展开全文
  • 这是我在做计算机图形学是做的软件,用的是C#语言。...实现了图形的橡皮拉伸和图形平移,旋转,缩放功能。能记录绘制图形起点和终点!用的是基础的算法,如:中点画线,中点画圆,bresenham等!界面美观!使用方便
  • 把相移法与光学彩色信息图像处理技术相结合,提出全场数字化确定主方向角的光弹性五步彩色相移法。其主要优点是采用最简单的白光平面偏振仪光学系统,由数码照相机采集五幅等色线与等倾线相互耦合的彩色条纹图,经...
  • 接触有一项目,相机在下面,被定位物体在上面,对产品要进行旋转和平移校正,机械手吸取产品时的位置不定,属于盲吸,这时机械手的吸盘和被定位物体的中心不一定在同一个位置,此时要对产品进行旋转和平移校正。...

    接触有一项目,相机在下面,被定位物体在上面,对产品要进行旋转和平移校正,机械手吸取产品时的位置不定,属于盲吸,这时机械手的吸盘和被定位物体的中心不一定在同一个位置,此时要对产品进行旋转和平移校正。取产品的一个角点进行定位。

    要注意此时,机械手的吸盘中心在相机拍照位的位置是固定的,也即被定位物体在相机视野里的旋转中心是确定的,即吸盘的位置,也就是要先要标定出吸盘的坐标(相机坐标系或机器人坐标系),然后在图像里即可知道其旋转中心的坐标(吸盘在相机坐标系或机器人坐标系,统一到一个坐标系即可)。

    然后利用

    hom_mat2d_identity (HomMat2DIdentity)
    *其中207  139为假定的旋转中心即吸盘的在相机坐标系的位置(也可以统一到机器人坐标系下) -Angle2为要旋转回去的角度标准位置 207,139为假定的旋转中心
    *吸盘在拍照位的位置即为旋转中心,可利用九点标定确定该点在相机坐标系的位置
    hom_mat2d_rotate (HomMat2DIdentity, -Angle2, 207, 139, HomMat2DRotate)
    *测试旋转图像看一下效果
    affine_trans_image (GrayImage2, ImageAffineTrans1, HomMat2DRotate, 'constant', 'false')
    *假定捕捉到的定位的角点241,111 经过旋转矩阵之后的位置Qx,Qy。根据该位置再标准位置进行XY平移补偿
    affine_trans_point_2d (HomMat2DRotate, 241, 111, Qx, Qy)
    
    *也可以使用vector_angle_to_rigid()函数进行相应的上面运算 
    *其中207, 139为假定的旋转中心,Angle2为当前角点处的角度,后面的Angle1为要旋转到的标准产品的角度
    vector_angle_to_rigid (207, 139, Angle2, 207, 139, Angle1, HomMat2D1)
    *假定捕捉到的定位的角点241,111 经过旋转矩阵之后的位置Qx,Qy。根据该位置再标准位置进行XY平移补偿
    affine_trans_point_2d (HomMat2D1, 241, 111, Qx, Qy)

    进行求得旋转之后角点的位置,再进行平移补偿位置即可

    测试代码下载地址https://download.csdn.net/download/yue1453544229/11190995

    以上内容需要求解旋转中心,旋转中心的求法如下:

    方法1:可将物体旋转两次,求得某点旋转的三个点位,根据三个点位求解其外接圆,求得的圆心即是旋转中心在相机坐标系的位置。

    方法2:可能受视野限制,只能旋转一个角度的情况,已知某点的旋转前后的坐标,旋转的角度,求旋转中心。其求解公式如下图

    该方法的计算方法是使用旋转矩阵进行求解的,根据旋转矩阵求解方程组

    这个图的Px,Py为待求的旋转中心

    这个图的Px,Py,Qx,Qy为已知的旋转前后的点坐标值。

     

     

    展开全文
  • 字符串左旋右旋——三步旋转!(K个字符)

    题目:实现一个函数,可以左旋字符串中的k个字符。
    AABCD左旋一个字符得到ABCDA
    AABCD左旋两个字符得到BCDAA

    方法一:三步旋转法

    左旋程序思路:首先根据画图得知左旋后的结果,然后在分析实现的简约步骤;
    第一步:字符串全逆序;
    第二步:左边的字符串逆序;
    第三步:右边的字符串逆序;

    举例:左旋K=1个字符:

    首先是头文件和主函数部分:

    #define _CRT_SECURE_NO_WARNINGS 1
    #include<stdio.h>
    #include<windows.h>
    #include<assert.h>
    #include<string.h>
    
    
    int main()
    {
    	char arr[] = "aabcd";/*未定义的数组内容不能用{}括起来,只能采用“”的形式。*/
    	printf("%s\n", my_left_move(arr, 1));/*表示左旋一个字符*/
    	system("pause");
    	return 0;
    }
    

    接下来是左旋函数实现部分:

    char* my_left_move(  char *src, int count)/* 函数的实现,此处的src是指针变量,不能加coonst常属性,是不可修改的左值*/
    {
    	int i = 0;
    	int len = strlen(src);
    	char tmp = 0;
    	assert(src);//断言指针的有效性
    	for (i = 0; i < len/2; i++)//第一步:整体逆序
    	{
    		采用了数组的方式:并且循环条件除以2是为了优化算法;
    		/*tmp = src[i];
    		src[i] = src[i + len-1];
    		src[i + len-1] = tmp;*/
    		采用指针的形式符合函数定义
    	     tmp=*(src+i);
    		 *(src + i) = *(src  + len - 1-i);//此处的下标要自己去分析的src变成了src+len-1,+i变成了-i;
    		 *(src + len - 1 - i) = tmp;
    	}
    	for (i = 0; i <(len-count)/2 ; i++)//第二步:左边的字符串逆序
    	{
    		tmp = *(src + i);
    		*(src + i) = *(src + len -count- 1 - i);
    		*(src + len - count-1 - i) = tmp;
    	}
    	for (i=0; i <count/2; i++)//第三步:右边的字符串逆序
    	{
    		tmp =*(src + len -count+i) ;
    		*(src + len -count  + i) = *(src+len-1-i);
    		*(src + len - 1 - i) = tmp;
    	}
    	return src;
    }


    然后是整个程序:(左旋一个字符)

    左旋一个字符:
    #define _CRT_SECURE_NO_WARNINGS 1
    #include<stdio.h>
    #include<windows.h>
    #include<assert.h>
    #include<string.h>
    
    char* my_left_move(  char *src, int count)/* 函数的实现,此处的src是指针变量,不能加coonst常属性,是不可修改的左值*/
    {
    	int i = 0;
    	int len = strlen(src);
    	char tmp = 0;
    	assert(src);//断言指针的有效性
    	for (i = 0; i < len/2; i++)//第一步:整体逆序
    	{
    		采用了数组的方式:并且循环条件除以2是为了优化算法;
    		/*tmp = src[i];
    		src[i] = src[i + len-1];
    		src[i + len-1] = tmp;*/
    		采用指针的形式符合函数定义
    	     tmp=*(src+i);
    		 *(src + i) = *(src  + len - 1-i);//此处的下标要自己去分析的src变成了src+len-1,+i变成了-i;
    		 *(src + len - 1 - i) = tmp;
    	}
    	for (i = 0; i <(len-count)/2 ; i++)//第二步:左边的字符串逆序
    	{
    		tmp = *(src + i);
    		*(src + i) = *(src + len -count- 1 - i);
    		*(src + len - count-1 - i) = tmp;
    	}
    	for (i=0; i <count/2; i++)//第三步:右边的字符串逆序
    	{
    		tmp =*(src + len -count+i) ;
    		*(src + len -count  + i) = *(src+len-1-i);
    		*(src + len - 1 - i) = tmp;
    	}
    	return src;
    }
    int main()
    {
    	char arr[] = "aabcd";/*未定义的数组内容不能用{}括起来,只能采用“”的形式。*/
    	printf("%s\n", my_left_move(arr, 1));/*表示左旋一个字符*/
    	system("pause");
    	return 0;
    }
    


    举例:左旋K=2个字符:

    无论左旋多少个字符,方法一样,只是在区分左串和右串的时候把要移动的字符串看做一个整体,其内部顺序不变;

    同样的先是头文件和主函数:

    #define _CRT_SECURE_NO_WARNINGS 1
    #include<stdio.h>
    #include<windows.h>
    #include<assert.h>
    #include<string.h>
    
    int main()
    {
    	char arr[] = "aabcd";/*未定义的数组内容不能用{}括起来,只能采用“”的形式。*/
    	printf("%s\n", my_left_move(arr, 2));/*表示左旋两个字符*/
    	system("pause");
    	return 0;
    }


    接下来是左旋函数实现部分:

    char* my_left_move(char *src, int count)/* 函数的实现,此处的src是指针变量,不能加coonst常属性,是不可修改的左值*/
    {
    	int i = 0;
    	int len = strlen(src);
    	char tmp = 0;
    	assert(src);//断言指针的有效性
    	for (i = 0; i < len / 2; i++)//第一步:整体逆序
    	{
    		采用了数组的方式:并且循环条件除以2是为了优化算法;
    		/*tmp = src[i];
    		src[i] = src[i + len-1];
    		src[i + len-1] = tmp;*/
    		采用指针的形式符合函数定义
    		tmp = *(src + i);
    		*(src + i) = *(src + len - 1 - i);//此处的下标要自己去分析的src变成了src+len-1,+i变成了-i;
    		*(src + len - 1 - i) = tmp;
    	}
    	for (i = 0; i <(len - count) / 2; i++)//第二步:左边的字符串逆序
    	{
    		tmp = *(src + i);
    		*(src + i) = *(src + len - count - 1 - i);
    		*(src + len - count - 1- i) = tmp;
    	}
    	for (i = 0; i <count / 2; i++)//第三步:右边的字符串逆序
    	{
    		tmp = *(src + len - count + i);
    		*(src + len - count + i) = *(src + len - 2 - i);
    		*(src + len - 2- i) = tmp;
    	}
    	return src;
    }



    然后是整个程序:(左旋两个字符)

     

    #define _CRT_SECURE_NO_WARNINGS 1
    #include<stdio.h>
    #include<windows.h>
    #include<assert.h>
    #include<string.h>
    char* my_left_move(char *src, int count)/* 函数的实现,此处的src是指针变量,不能加coonst常属性,是不可修改的左值*/
    {
    	int i = 0;
    	int len = strlen(src);
    	char tmp = 0;
    	assert(src);//断言指针的有效性
    	for (i = 0; i < len / 2; i++)//第一步:整体逆序
    	{
    		采用了数组的方式:并且循环条件除以2是为了优化算法;
    		/*tmp = src[i];
    		src[i] = src[i + len-1];
    		src[i + len-1] = tmp;*/
    		采用指针的形式符合函数定义
    		tmp = *(src + i);
    		*(src + i) = *(src + len - 1 - i);//此处的下标要自己去分析的src变成了src+len-1,+i变成了-i;
    		*(src + len - 1 - i) = tmp;
    	}
    	for (i = 0; i <(len - count) / 2; i++)//第二步:左边的字符串逆序
    	{
    		tmp = *(src + i);
    		*(src + i) = *(src + len - count - 1 - i);
    		*(src + len - count - 1- i) = tmp;
    	}
    	for (i = 0; i <count / 2; i++)//第三步:右边的字符串逆序
    	{
    		tmp = *(src + len - count + i);
    		*(src + len - count + i) = *(src + len - 2 - i);
    		*(src + len - 2- i) = tmp;
    	}
    	return src;
    }
    int main()
    {
    	char arr[] = "aabcd";/*未定义的数组内容不能用{}括起来,只能采用“”的形式。*/
    	printf("%s\n", my_left_move(arr, 2));/*表示左旋两个字符*/
    	system("pause");
    	return 0;
    }

    方法二:移相法

    我们可以通过画图的方式来实现移相左旋:

    我们直接来做左旋K=2个字符的:

    01.<span style="font-size:18px;color:#ff0000;"><strong>  
    02.</strong></span>#define _CRT_SECURE_NO_WARNINGS 1  
    03. #include<assert.h>  
    04. #include<windows.h>  
    05. #include<stdio.h>  
    06. #include<string.h>  
    07. void  my_left_move(char* str, int count)  
    08. {  
    09.  assert(str);  
    10.  int i = 0;  
    11.  int len = strlen(str);  
    12.  if (len <= 1)  
    13.  {  
    14.   return ;  
    15.  }  
    16.  for (i = 0; i <count; i++)  
    17.  {  
    18.   int j = 0;  
    19.   char  tmp = str[0];  <span style="font-size:18px;color:#ff0000;"><strong>//把第一个字符保存起来</strong></span>  
    20.  for (j = 0; j < len - 1; j++)  
    21.   {  
    22.      
    23.    *(str + j) = *(str + j + 1);  <span style="font-size:18px;"><span style="color:#ff0000;"><strong>//然后依次进行平移</strong>  
    24.</span></span>  }  
    25.   *(str + len - 1) = tmp;  <span style="color:#ff0000;"><strong><span style="font-size:18px;">//再把tmp保存的内容赋给最后一个空位</span></strong>  
    26.</span> }  
    27. }  
    28.int main()  
    29. {  
    30.  char arr[] = "AABCD";  
    31.  int k= 2;  
    32.  my_left_move(arr, k);  
    33.  printf("%s\n", arr);  
    34.  system("pause");  
    35.  return 0;  
    36. }    
    



    总结:左旋一个字符或者多个字符的唯一差别就是在最后一步右边字符串的逆序个数上,循环条件K即count变了呢!

    那么做完左旋的你是不是也想做一做右旋呢?其实你只要画画图就知道,右旋和左旋其实是一样的只不过你在区分左串和右串的起始位置有所不同而已!

    那么这里给出一个右旋K=count字符的右旋实现程序:(采用三步旋转法)

    (传参合适的K)

    这里也可以采用移相法!!!

    <strong>char* my_right_move(char *src, int count)/* 函数的实现,此处的src是指针变量,不能加coonst常属性,是不可修改的左值*/
    {
    	int i = 0;
    	int len = strlen(src);
    	char tmp = 0;
    	assert(src);//断言指针的有效性
    	for (i = 0; i < len / 2; i++)//第一步:整体逆序
    	{
    		采用了数组的方式:并且循环条件除以2是为了优化算法;
    		/*tmp = src[i];
    		src[i] = src[i + len-1];
    		src[i + len-1] = tmp;*/
    		采用指针的形式符合函数定义
    		tmp = *(src + i);
    		*(src + i) = *(src + len - 1 - i);//此处的下标要自己去分析的src变成了src+len-1,+i变成了-i;
    		*(src + len - 1 - i) = tmp;
    	}
    	for (i = 0; i <(len - count) / 2; i++)//第二步:左边的字符串逆序
    	{
    		tmp = *(src + i);
    		*(src + i) = *(src + len - count - 1 - i);
    		*(src + len - count - 1 - i) = tmp;
    	}
    	for (i = 0; i <count / 2; i++)//第三步:右边的字符串逆序
    	{
    		tmp = *(src + len - count + i);
    		*(src + len - count + i) = *(src + len - 1 - i);
    		*(src + len - 1 - i) = tmp;
    	}
    	return src;
    }</strong>

    题目:判断一个字符串是否为另外一个字符串旋转之后的字符串。
    例如:给定s1 = AABCD和s2 = BCDAA,返回1,给定s1 = abcd和s2 = ACBD,返回0.


    AABCD左旋一个字符得到ABCDA
    AABCD左旋两个字符得到BCDAA

    AABCD右旋一个字符得到DAABC
    AABCD右旋两个字符得到CDAAB

    同样先把头文件和主函数编好:

    16.#define _CRT_SECURE_NO_WARNINGS 1  
    17.#include<assert.h>  
    18.#include<windows.h>  
    19.#include<stdio.h>  
    20.#include<string.h>  
    21.  
    22.int main()  
    23.{  
    24.    char arr[] = "AABCD";  
    25.    int ret = my_move(arr, "ABCDA");  
    26.    if (ret == 1)  
    27.    {  
    28.        printf("yes\n");  
    29.    }  
    30.    else  
    31.    {  
    32.        printf("no\n");  
    33.    }  
    34.    system("pause");  
    35.    return 0;  
    36.}  
    37.  
    

    然后是函数实现部分:

    char my_move(char* str1, char* str2)  
    42.{  
    43.    assert(str1);  
    44.    assert(str2);  
    45.    int len = strlen(str1);  
    46.    strncat(str1, str1, len);  
    47.    if (strstr(str1, str2))  
    48.    {  
    49.        return 1;  
    50.    }  
    51.    else  
    52.    {  
    53.        return 0;  
    54.    }  
    55.}  
    

    最后是整个程序:

    01.#define _CRT_SECURE_NO_WARNINGS 1  
    02.#include<assert.h>  
    03.#include<windows.h>  
    04.#include<stdio.h>  
    05.#include<string.h>  
    06.char my_move(char* str1, char* str2)  
    07.{  
    08.    assert(str1);  
    09.    assert(str2);  
    10.    int len = strlen(str1);  
    11.    strncat(str1, str1, len);  
    12.    if (strstr(str1, str2))  
    13.    {  
    14.        return 1;  
    15.    }  
    16.    else  
    17.    {  
    18.        return 0;  
    19.    }  
    20.}  
    21.int main()  
    22.{  
    23.    char arr[] = "AABCD";  
    24.    int ret = my_move(arr, "ABCDA");  
    25.    if (ret == 1)  
    26.    {  
    27.        printf("yes\n");  
    28.    }  
    29.    else  
    30.    {  
    31.        printf("no\n");  
    32.    }  
    33.    system("pause");  
    34.    return 0;  
    35.}  
    
    
     
          .
    <span style="color:#cc0000;">  </span>

    哈哈,到这里,整个左旋右旋相信已经一目了然了,继续加油吧!


     


     

     

    展开全文
  •  同样的,因为二维平移只有xy轴两个方向,我们建立仿射坐标系观察,同时扩充“平移维度”,建立矩阵T_translate和线性方程组,就能得到我们需要的平移矩阵T_translate。  前面我们推了二维情况下仿射坐标系旋转,...

            紧接上一篇:http://blog.csdn.net/yinhun2012/article/details/79566148

            前面我们从理论上理解了图形学中图形的变换过程,具体可以理解为如果要对一个图形A进行变换,那么存在图形A所处的相同原点的仿射空间SpaceA(或者说仿射空间SpaceA中原点处存在一个图形A),这时候我们暂时忽略图形A,只考虑SpaceA经过各种变换最终变换成另一个仿射空间SpaceB(或者说变换后的仿射空间SpaceA与另一个仿射空间SpaceB重合),因为仿射空间SpaceA变换后可能被伸缩或者被旋转同时又移动了,那么仿射空间SpaceA中的图形A同时跟着被变换成了另外的样子,如下图:

            

            想像一下,假设把你自己当作一个“图形”,这时候出现一个任务,就是让躺在床上的你从家里出发去公司坐在办公椅上上班写代码,你应该怎么做呢,首先你要起床(从平躺的向量变成竖着的向量,这就经历了一个人体旋转的过程),然后出发,经过一条长长的街道xxx路(从xxx路的起点路口到xxx路的终点路口,这就经历了一个人体平移的过程),然后拐弯进入yyy路并且继续行走(这就经历了人体旋转加人体平移两个过程),然后进入公司走到办公椅前坐下(从竖着站立状态变成坐下的坐立状态,这可以人为人体进行了形变缩放)。

            这么一看,我们对于变换的理解就可以分解为旋转、平移、缩放三个过程。

            前面我们对齐次坐标的理解,知道了旋转和缩放这两个过程可以通过矩阵T进行线性变换处理,但是平移却是需要将矩阵T扩充“平移维度”得到矩阵T'才能进行线性变换处理,所以数学上我们处理n维空间的图形变换就要使用n+1维齐次坐标以及n+1行n+1列矩阵来得到。接下来我们就来具体推导二维仿射坐标系下的旋转/平移/缩放。

            1.矩阵操作仿射坐标系旋转,还是老办法,建立单位圆进行逆时针旋转θ角(三角函数中规定逆时针旋转为正角)的旋转矩阵辅助推导,如下图:

                

            我们通过矩阵T_rotate旋转变换仿射坐标系xoy到x1oy1,矩阵T_rotate的推导也是通过建立线性方程组解方程得到T_rotate的未知数。那么仿射坐标系所“容纳”的原点图形也就跟着变换了。因为旋转变换不需要额外扩充成齐次坐标表示也能进行变换,所以我们忽略“平移维度”。 

            2.矩阵操作仿射坐标系缩放,建立辅助单位圆,如下图:

            

            上面我们首先推导了标准缩放,也就是xy缩放比例K相同,接下来缩放比例不同x缩放m/y缩放n,依旧是通过建立矩阵T_scale以及转换成线性方程组就很容易得到矩阵T_scale了。

            3.矩阵操作仿射坐标系平移,特殊情况来了!前面我们讲过仿射坐标系平移是不能直接进行线性变换的,而需要扩充平移维度进行辅助计算,如下图:

            

            同样的,因为二维平移只有xy轴两个方向,我们建立仿射坐标系观察,同时扩充“平移维度”,建立矩阵T_translate和线性方程组,就能得到我们需要的平移矩阵T_translate。

            前面我们推了二维情况下仿射坐标系旋转,缩放,平移的变换矩阵T_rotate,T_scale,T_translate,但是三个矩阵太不规范了,有的是2x2有的是3x3,有的使用了齐次坐标,有的又只使用了普通坐标,这对我们程序中进行计算是非常不友好的。之前我们说了平移变换是通过扩充“平移维度”来处理的,并不影响“旋转维度”和“缩放维度”,同时GPU设计的重要指标之一就是极快速处理4x4及以下的矩阵计算,所以我们通过将T_rotate和T_scale扩充维度到T_translate的行列数,用来统一传入GPU进行处理。

            扩充方式如下图:

            

            这里我们只需要将“平移维度”填入“旋转维度”和“缩放维度”就行了,如果不放心的话我们就验证一下扩充后的矩阵的计算结果,如下图:

            

    缩放矩阵同理也可以计算出来结果。

            这么一来我们就把旋转平移缩放的变换全部使用齐次坐标和3x3矩阵给统一化了,但是我们还是觉得麻烦,因为我处理一个图形变换居然还需要使用三个矩阵去分别计算,我们能不能直接把这三个矩阵通过什么方法变成一个呢,接下来我们来观察矩阵乘法的性质,继续由简入难。

            假设我们有两个2x2的矩阵T1,T2来变换一个2x1的矩阵V(2x1的矩阵也就是一个二维向量),按照平常方法我们肯定就是先使用T1*V变换得到V1,然后再使用T2*V1变换得到V2,整个过程我们记为T2*(T1*V)。我们希望优化中间环节,尽量少的直接将V带入运算,希望变换成(T2*T1)*V,先将T2*T1得到矩阵T3,然后将T3*V变换得到V2,此时我们的问题就变成T2*T1得到的矩阵T3是否起到了T1、T2分别变换V相同的作用。

            我们知道矩阵A*矩阵B的规则就是矩阵A的行m与矩阵B的列n的元素分别相乘再相加最终得到的元素放置于结果矩阵C的m行n列中,那么上面的问题矩阵T1*V得到的V1再使用T2*V1得到的结果和T2*T1得到T3*V的结果相同,因为这种矩阵乘法的计算规律在独立出每个行列计算后得到单个元素的分解式符合乘法分配律(c*(a+b) = c*a + c*b)运算然后拆分组合成另外的乘法分配律的组合体,这时候我们必须通过实际运算来观察,因为红色语言的描叙都过于复杂到无法直接心算,如下图:

           

            最后的乘法分配组合经过分解拆分继续组合成乘法分配律组合体。

           这时候我们可以将原本的旋转平移缩放三次矩阵操作T_rotate*(T_translate*(T_scale*Vector))变化成T_all(T_all = T_rotate*(T_translate*T_scale)) *Vector,保证Vector经过一个矩阵的一次变换达到目标。

            



            

            

            

            

            

                            

            

            

    展开全文
  • 小白谈计算机图形学(二)如何画圆引言如何画圆基本思想中点画圆中点画圆基本思路中点画圆改进Bresenham画圆算法Bresenham基本思路数值微分改进中点画线中点画线引言中点画线改进Bresenham画线Bresenham基本...
  • 1)平移 2)放大和缩小 3)旋转 4)计算仿射变换矩阵 5)插值算法 6)Python实现 2.图像的投影变换 3.极坐标转换 总结 首先要了解OpenCV的坐标原点(0,0)是在坐标的左上角,实现集合变换需要两个独立的算法: 1...
  • 程序员可以用最适合他们手中问题的坐标系来描述对象,并且可以自动的缩放和平移图形,使得其能正确地在屏幕窗口中显示。 这个描述对象的空间被称为世界坐标系,即场景中物体在实际世界中的坐标。 世界坐标系是...
  • (假设起始点为坐标原点,后面将通过坐标平移来处理非圆心在圆点) 2,计算决策关键参数的初始值,P = 5/4 - r(pk = (xk+1)2+(yk-0.5)2-r2, 当 x=0, y=r可以推出P0); 3,在每个Xk的位置,从k =0 开始,更具决策值P来...
  • 在网上搜索关于非曲线的双圆弧拟合问题时,大多是建立在复杂的数学分析基础上,却未看到一篇讨论是采用绘图来解决问题。下面,我将结合实际讨论一下,在Mastercam中是如何通过绘图,由给出的一段非曲线,到...
  • 先解释一下相移法中相机所捕获的条纹图案的数学表达式的含义 这里需要解释的是,表示第n张相移图上(x,y)点处的灰度值,为条纹图背景值,为调制强度函数。与上图中的变量解释不一样,查看了将近十几个人的文章,...
  • 的生成

    2013-06-07 15:03:01
    这里仅讨论圆心位于坐标原点的的扫描转换算法,对于圆心不在原点的,可先用平移变换,将它的圆心平移到原点,然后进行扫描转换,最后再平移到原来的位置。  有几种较容易的方法可以得到的扫描转换,但是效率...
  • 最小二乘法椭圆拟合

    千次阅读 2015-07-02 14:13:21
    现在考虑一种特殊情况,假设二维点云拟合出的椭圆方程交点不在X轴或Y轴,即在上面的标准方程基础上发生“偏转+平移”,此时的椭圆方程形式该是什么样子? 先考虑旋转: 平面上一点P(X,Y)旋转θ角度,旋转后的坐标...
  • 针对空间扩展目标成像纹理特征少,背景单一,以及目标旋转、平移、形变等运动特征所导致的目标定位跟踪困难的问题,研究了基于惯量椭圆位姿补偿的目标定位跟踪方法(TTBPC)。通过惯量椭圆测姿获取目标准确俯仰角...
  • 视觉SLAM笔记(30) 特征点

    万次阅读 2019-10-08 18:46:15
    特征点、特征点、ORB 特征(FAST 关键点、BRIEF 描述子)、特征匹配
  • 题目:用Besenham实现的绘制 基本要求: (1)数据输入项为:圆心坐标与半径 (2)直线与输出在CDC设备中 附加要求: (1)通过用户输入可改变线型(实线、虚线与点划线) (2)通过用户输入可改变线宽(用方...
  • 地球是的:《世界是的》

    千次阅读 2006-12-11 10:29:00
    在各大书店里充栋的无数本有关全球化的著作中,《世界是的》(The World Is Flat: A Brief History of the Twenty-first Century)并不只是“又一本”这样的书。在过去的一年里,它被包括比尔•盖茨在内的商界领袖...
  • 椭圆的扫描转换-中点Bresenham算法

    千次阅读 2018-06-18 15:40:49
    椭圆的扫描转换是在屏幕像素点阵中选取最佳逼近于理想椭圆像素点集的过程。椭圆是长半轴和短半轴不相等的,椭圆的扫描...需要进行圆心平移或使用自定义坐标系可以绘制椭圆。 如果“x方向上每次加1,y方向上减不减...
  • 算法系列之十一:生成算法

    万次阅读 多人点赞 2012-02-12 21:45:51
    输入您的搜索字词 提交搜索表单 在平面解析几何中,的方程可以描述为(x – x0)2 + (y – y0)2 = R2,其中(x0, y0)是圆心坐标,R是的半径,特别的,当(x0, y0)就是坐标中心点时,方程可以简化为x2 + y2 = R2。...
  • 的扫描转换-中点Bresenham算法

    千次阅读 2020-04-17 09:01:43
    的扫描转换是在屏幕像素点阵中确定最佳逼近于理想的像素点集的过程。的绘制可以使用简单方程画圆算法或极...屏幕设备坐标系的原点位于左上角,绘制结果为1/4,需要进行圆心平移或使用自定义坐系可以绘制整...
  • 算法系列之十三:椭圆的生成算法

    万次阅读 多人点赞 2012-04-24 23:38:18
    椭圆和直线、一样,是图形学领域中的一种常见图元,椭圆的生成算法(光栅转换算法)也是图形学软件中最常见的生成算法之一。在平面解析几何中,椭圆的方程可以描述为(x – x0)2 / a2+ (y – y0)2 / b2 = 1,其中...
  • 中点画圆

    千次阅读 2013-10-23 11:23:10
    第一点,我们可以确认画任意位置的,都可以先在坐标系原点画一个半径相同的,然后通过平移来得到这个原因。 第二点,y=x,y=-x,x=0,y=0这四条线是圆心在原点上的的对称线。所以我们只需画出横坐标X在区间【0,...
  • 内容有:Hermite曲线,四点Hermite曲线,旋转变换,比例变换,平移变换,错切变换,复合变换,对称变换,八分画圆,画椭圆,扫描线填充,简单种子填充,递归填充,图案填充,Bresenham画直线,直线裁剪,...
  • (包括平移,放缩,旋转,错切以及复合变换) 4)运用所学的区域填充算法实现区域填充。 5)运用所学算法实现线段裁剪以及多边形裁剪(多边形裁剪为可选)。 6)自己实现其它附加以及需要完善的功能。
  • 目标:先人为的将原始图像进行平移,再使用相位相关计算两个图像之间的变换(即计算出人为平移的值) 思路为:       1.读取图像,并人为的设定平移参数,利用仿射变换将图像进行...
  • 计算机图形学实习代码,在MFC中实现包括DDA直线,中点直线,Bresenham ,正负法圆,Bezier 曲线... 以及图像平移旋转缩放对称变换算法,扫描线填充算法,种子填充,边缘填充,二维裁剪,线型,颜色以及填充颜色设置。
  • 实现了中点画圆和椭圆以及Bresenham画圆算法。考虑坐标不在中心原点的情况,椭圆可以横竖绘制不能倾斜,C++graphics.h图形库实现。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 14,218
精华内容 5,687
关键字:

平移圆法