日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

MFC空间几何变换之图像平移、镜像、旋转、缩放

發(fā)布時間:2024/7/23 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MFC空间几何变换之图像平移、镜像、旋转、缩放 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本文主要講述基于VC++6.0 MFC圖像處理的應(yīng)用知識,主要結(jié)合自己大三所學(xué)課程《數(shù)字圖像處理》及課件進(jìn)行講解,主要通過MFC單文檔視圖實(shí)現(xiàn)顯示BMP圖片空間幾何變換,包括圖像平移、圖形旋轉(zhuǎn)、圖像反轉(zhuǎn)倒置鏡像和圖像縮放的知識。同時文章比較詳細(xì)基礎(chǔ),沒有采用GDI+獲取矩陣,而是通過讀取BMP圖片信息頭和矩陣像素實(shí)現(xiàn)變換,希望該篇文章對你有所幫助,尤其是初學(xué)者和學(xué)習(xí)圖像處理的學(xué)生。
? ? ? ?【數(shù)字圖像處理】一.MFC詳解顯示BMP格式圖片
? ? ? ?【數(shù)字圖像處理】二.MFC單文檔分割窗口顯示圖片
? ? ? ?【數(shù)字圖像處理】三.MFC實(shí)現(xiàn)圖像灰度、采樣和量化功能詳解
? ? ? ?【數(shù)字圖像處理】四.MFC對話框繪制灰度直方圖
? ? ? ?【數(shù)字圖像處理】五.MFC圖像點(diǎn)運(yùn)算之灰度線性變化、灰度非線性變化、閾值化和均衡化處理詳解
? ? ? ??免費(fèi)資源下載地址:
? ? ? ??http://download.csdn.net/detail/eastmount/8772951

?

一. 圖像平移

?

? ? ? ?前一篇文章講述了圖像點(diǎn)運(yùn)算(基于像素的圖像變換),這篇文章講述的是圖像幾何變換:在不改變圖像內(nèi)容的情況下對圖像像素進(jìn)行空間幾何變換的處理方式。
? ? ? ??點(diǎn)運(yùn)算對單幅圖像做處理,不改變像素的空間位置;代數(shù)運(yùn)算對多幅圖像做處理,也不改變像素的空間位置;幾何運(yùn)算對單幅圖像做處理,改變像素的空間位置,幾何運(yùn)算包括兩個獨(dú)立的算法:空間變換算法和灰度級插值算法。

? ? ? ? 空間變換操作包括簡單空間變換、多項(xiàng)式卷繞和幾何校正、控制柵格插值和圖像卷繞,這里主要講述簡單的空間變換,如圖像平移、鏡像、縮放和旋轉(zhuǎn)。主要是通過線性代數(shù)中的齊次坐標(biāo)變換。
? ? ? ? 圖像平移坐標(biāo)變換如下:

? ? ? ? 運(yùn)行效果如下圖所示,其中BMP圖片(0,0)像素點(diǎn)為左下角。


? ? ? ? 其代碼核心算法:
? ? ? ? 1.在對話框中輸入平移坐標(biāo)(x,y) m_xPY=x,m_yPY=y
? ? ? ? 2.定義Place=dlg.m_yPY*m_nWidth*3 表示當(dāng)前m_yPY行需要填充為黑色
? ? ? ? 3.新建一個像素矩陣?ImageSize=new unsigned char[m_nImage]
? ? ? ? 4.循環(huán)整個像素矩陣處理?
? ? ? ? ? ? ?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) {//圖像像素平移區(qū)域
? ? ? ? ? ? ? ? ? ? ? ??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號)

? ? ? ? 第二步:設(shè)置平移對話框。將試圖切換到ResourceView界面--選中Dialog,右鍵鼠標(biāo)新建一個Dialog,并新建一個名為IDD_DIALOG_PY。編輯框(X)IDC_EDIT_PYX 和 (Y)IDC_EDIT_PYY,確定為默認(rèn)按鈕。設(shè)置成下圖對話框:

? ? ? ? 第三步:在對話框資源模板空白區(qū)域雙擊鼠標(biāo)—Create a new class創(chuàng)建一個新類--命名為CImagePYDlg。會自動生成它的.h和.cpp文件。打開類向?qū)?Ctrl W),選擇類名:CImagePYDlg添加成員變量如下圖所示,同時在Message Maps中生成ID_JHBH_PY實(shí)現(xiàn)函數(shù)。

?
? ? ? ? 第四步:在CImageProcessingView.cpp中添加頭文件#include "ImagePYDlg.h",并實(shí)現(xiàn)平移。

  • /********************************************************/

  • /* 圖像空間幾何變換:圖像平移 ID_JHBH_PY(幾何變換-平移)

  • /* 使用平移對話框:CImagePYDlg dlg

  • /* 算法:f(x,y)=f(x+x0,y+y0)圖像所有點(diǎn)平移,空的補(bǔ)黑'0'

  • /* 注意該圖像平移方法只是從左上角(0,0)處開始平移

  • /* 其他方向原理相同 自己去實(shí)現(xiàn)

  • /********************************************************/

  • ?
  • void CImageProcessingView::OnJhbhPy()

  • {

  • if(numPicture==0) {

  • AfxMessageBox("載入圖片后才能空間平移!",MB_OK,0);

  • return;

  • }

  • //定義采樣對話框也是用來空間變換平移的坐標(biāo)

  • CImagePYDlg dlg;

  • if( dlg.DoModal()==IDOK ) //顯示對話框

  • {

  • //采樣坐標(biāo)最初為圖片的自身像素

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

  • AfxMessageBox("圖片平移不能為超過原圖長寬!",MB_OK,0);

  • return;

  • }

  • AfxMessageBox("圖片空間變換-平移!",MB_OK,0);

  • ?
  • //打開臨時的圖片 讀寫文件

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

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

  • fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);

  • fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);

  • fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);

  • fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);

  • fread(m_pImage,m_nImage,1,fpo);

  • ?
  • /************************************************************/

  • /* 圖片空間變換-平移

  • /* 坐標(biāo)(dlg.m_xPY,dlg.m_yPY)表示圖像平移的坐標(biāo)

  • /* 先用Plave計(jì)算出平移后的起始坐標(biāo),其他的坐標(biāo)賦值為'0'黑色

  • /* 然后依次平移坐標(biāo),空的賦為黑色,否則填充

  • /************************************************************/

  • ?
  • /******************************************************************/

  • /* 嚴(yán)重錯誤1:數(shù)組變量賦值相等

  • /* 在View.h中定義變量 BYTE *m_pImage 讀入圖片數(shù)據(jù)后的指針

  • /* 建立臨時變量數(shù)組,讓它平移變換 unsigned char *ImageSize

  • /* ImageSize=m_pImage(錯誤)

  • /* 會導(dǎo)致ImageSize賦值變換時m_pImage也產(chǎn)生了變換,所以輸出全為黑色

  • /* 因?yàn)樗鼈z指向了相同的數(shù)組地址

  • /* 解決方法:使用下面C++的new方法動態(tài)分配或for循環(huán)i=m_nImage賦值

  • /******************************************************************/

  • ?
  • /*臨時變量存儲的像素與m_pImage相同,便于處理圖像*/

  • unsigned char *ImageSize;

  • ImageSize=new unsigned char[m_nImage]; //new和delete有效的進(jìn)行動態(tài)內(nèi)存的分配和釋放

  • ?
  • int Place; //建立臨時坐標(biāo) 記錄起始坐標(biāo)(0,0)平移過來的位置

  • int m_pImagePlace; //原始圖像平移為(0,0) 圖像把它平移到Place位置

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

  • ?
  • /************************************************************/

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

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

  • /* 不能使用的上面的因?yàn)榭赡軋D像的最后一行沒有完整的一行像素

  • /* 這樣會出現(xiàn)exe報(bào)錯,使用m_nImage讀寫所有像素比較正確

  • /************************************************************/

  • ?
  • Place=dlg.m_yPY*m_nWidth*3; //前m_yPY行都要填充為黑色

  • black=0; //顏色為黑色

  • m_pImagePlace=0; //圖像處事位置為(0,0),把該點(diǎn)像素平移過去

  • int countWidth=0; //記錄每行的像素個數(shù),滿行時變回0

  • int number=0; //數(shù)字記錄使用的像素行數(shù),平移時使用

  • ?
  • for(int i=0 ; i<m_nImage ; i++ )

  • {

  • /*如果每行的像素填滿時清為0*/

  • if(countWidth==m_nWidth*3) {

  • countWidth=0;

  • }

  • ?
  • /*第一部分:到平移后像素位置前面的所有像素點(diǎn)賦值為黑色*/

  • if(i<Place) {

  • ImageSize[i]=black; //賦值為黑色

  • continue;

  • }

  • ?
  • /*第二部分:平移區(qū)域的左邊部分賦值為黑色*/

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

  • ImageSize[i]=black; //賦值為黑色

  • countWidth++;

  • continue;

  • }

  • ?
  • /****************************/

  • /* 各部分如圖所示:

  • /* 000000000000000000000000

  • /* 000000000000000000000000

  • /* 0000000.................

  • /* 0000000.................

  • /* 0000000.................

  • /* 0000000.................

  • /* 點(diǎn)表示像素部分,0為黑色

  • /****************************/

  • ?
  • /* 重點(diǎn)錯誤提示:由于bmp圖像顯示是從左下角開始存儲(0,0)點(diǎn)所以輸出圖像為 */

  • /* bmp圖像是從左下角到右上角排列的 */

  • ?
  • /****************************/

  • /* 各部分如圖所示:

  • /* 0000000.................

  • /* 0000000.................

  • /* 0000000.................

  • /* 0000000.................

  • /* 000000000000000000000000

  • /* 000000000000000000000000

  • /* 點(diǎn)表示像素部分,0為黑色

  • /****************************/

  • ?
  • /*第三部分:圖像像素平移區(qū)域*/

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

  • {

  • ImageSize[i]=m_pImage[m_pImagePlace];

  • m_pImagePlace++;

  • countWidth++;

  • if(countWidth==m_nWidth*3)

  • {

  • number++;

  • m_pImagePlace=number*m_nWidth*3;

  • }

  • }

  • }

  • ?
  • fwrite(ImageSize,m_nImage,1,fpw);

  • fclose(fpo);

  • fclose(fpw);

  • numPicture = 2;

  • level=200; //200表示幾何變換

  • Invalidate();

  • }

  • }

  • ? ? ? ? 同時在ShowBitmap中添加level標(biāo)記重新繪制圖片,代碼如下:

  • else //圖像幾何變換

  • if(level=200)

  • {

  • m_hBitmapChange = (HBITMAP) LoadImage(NULL,BmpNameLin,IMAGE_BITMAP,0,0,

  • LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);

  • }

  • ? ? ? ?運(yùn)行時需要注意一點(diǎn):BMP圖像在處理過程中可能會出現(xiàn)一些斜線,而平移(40,60)位移量時可能出現(xiàn)如下。他是因?yàn)锽MP格式有個非常重要的規(guī)定,要求每一掃描的字節(jié)數(shù)據(jù)必須能被4整除,也就是Dword對齊(長度4字節(jié)),如果圖像的一行字節(jié)數(shù)不能被4整除,就需要在每行末尾不起0達(dá)到標(biāo)準(zhǔn)。
    ? ? ? ? 例如一行像素為97字節(jié),我們就需要補(bǔ)3個字節(jié)嗎,數(shù)值可以是0,但是我們在BMP格式的信息頭里說明了其寬度,所以補(bǔ)齊后對我們沒有影響,所以后面補(bǔ)若干個字節(jié)的0即可直到被4整除。
    ?
    ? ? ? ? 通過后面的圖像縮放后,我從學(xué)做了一遍這個補(bǔ)齊的縮放。代碼如下,能夠?qū)崿F(xiàn)完美平移。nice啊~

  • void CImageProcessingView::OnJhbhPy()

  • {

  • if(numPicture==0) {

  • AfxMessageBox("載入圖片后才能空間平移!",MB_OK,0);

  • return;

  • }

  • //定義采樣對話框也是用來空間變換平移的坐標(biāo)

  • CImagePYDlg dlg;

  • if( dlg.DoModal()==IDOK ) //顯示對話框

  • {

  • //采樣坐標(biāo)最初為圖片的自身像素

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

  • AfxMessageBox("圖片平移不能為超過原圖長寬!",MB_OK,0);

  • return;

  • }

  • AfxMessageBox("圖片空間變換-平移!",MB_OK,0);

  • ?
  • //打開臨時的圖片 讀寫文件

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

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

  • fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);

  • fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);

  • ?
  • int num; //記錄每行多余的圖像素?cái)?shù)個數(shù)

  • int sfSize; //補(bǔ)齊后的圖像大小

  • //重點(diǎn):圖像的每行像素都必須是4的倍數(shù):1*1的圖像為 r g b 00H

  • if(m_nWidth*3%4!=0)

  • {

  • num=(4-m_nWidth*3%4);

  • sfSize=(m_nWidth*3+num)*m_nHeight; //每行多number個

  • }

  • else

  • {

  • num=0;

  • sfSize=m_nWidth*m_nHeight*3;

  • }

  • //注意:假如最后一行像素不足,我默認(rèn)處理為完整的一行,不足補(bǔ)00H

  • //總之處理后的圖像總是m*n且為4倍數(shù),每行都完整存在

  • ?
  • /*更改文件頭信息 定義臨時文件頭結(jié)構(gòu)變量*/

  • BITMAPFILEHEADER bfhsf;

  • BITMAPINFOHEADER bihsf;

  • bfhsf=bfh;

  • bihsf=bih;

  • bfhsf.bfSize=sfSize+54;

  • fwrite(&bfhsf,sizeof(BITMAPFILEHEADER),1,fpw);

  • fwrite(&bihsf,sizeof(BITMAPINFOHEADER),1,fpw);

  • fread(m_pImage,m_nImage,1,fpo);

  • ?
  • CString str;

  • str.Format("補(bǔ)齊=%d",num);

  • AfxMessageBox(str);

  • ?
  • /*臨時變量存儲的像素與sfSize相同 new和delete有效的進(jìn)行動態(tài)內(nèi)存的分配和釋放*/

  • unsigned char *ImageSize;

  • ImageSize=new unsigned char[sfSize];

  • ?
  • int Place; //建立臨時坐標(biāo) 記錄起始坐標(biāo)(0,0)平移過來的位置

  • int m_pImagePlace; //原始圖像平移為(0,0) 圖像把它平移到Place位置

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

  • unsigned char other=0; //補(bǔ)碼00H='\0'

  • ?
  • Place=dlg.m_yPY*(m_nWidth*3+num); //前m_yPY行都要填充為黑色

  • m_pImagePlace=0; //圖像處事位置為(0,0),把該點(diǎn)像素平移過去

  • int countWidth=0; //記錄每行的像素個數(shù),滿行時變回0

  • int number=0; //數(shù)字記錄使用的像素行數(shù),平移時使用

  • ?
  • for(int i=0 ; i<sfSize ; i++ )

  • {

  • /*第一部分:到平移后像素位置前面的所有像素點(diǎn)賦值為黑色*/

  • if(i<Place)

  • {

  • ImageSize[i]=black; //賦值為黑色

  • continue;

  • }

  • ?
  • /*第二部分:平移區(qū)域的左邊部分賦值為黑色*/

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

  • {

  • ImageSize[i]=black; //賦值為黑色

  • countWidth++;

  • continue;

  • }

  • ?
  • /*第三部分:圖像像素平移區(qū)域*/

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

  • {

  • ImageSize[i]=m_pImage[m_pImagePlace];

  • m_pImagePlace++;

  • countWidth++;

  • if(countWidth==m_nWidth*3)

  • {

  • if(num==0)

  • {

  • countWidth=0;

  • number++;

  • m_pImagePlace=number*m_nWidth*3;

  • }

  • else //num為補(bǔ)0

  • {

  • for(int j=0;j<num;j++)

  • {

  • i++;

  • ImageSize[i]=other;

  • }

  • countWidth=0;

  • number++;

  • m_pImagePlace=number*(m_nWidth*3+num); //重點(diǎn):添加Num

  • }

  • }

  • }

  • }

  • ?
  • fwrite(ImageSize,sfSize,1,fpw);

  • fclose(fpo);

  • fclose(fpw);

  • numPicture = 2;

  • level=200; //200表示幾何變換

  • Invalidate();

  • }

  • }

  • ? ? ? ? 運(yùn)行效果如下圖所示,完美平移,其他算法遇到斜線問題類似補(bǔ)齊即可。




    ?

    ?

    二. 圖像鏡像

    1.水平鏡像翻轉(zhuǎn)
    ? ? ? ? 其變換矩陣如下:
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?X=width-X0-1 ? (width為圖像寬度)
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Y=Y0
    ? ? ? ? 打開類向?qū)?#xff0c;在CImageProcessingView中添加IDs為ID_JHBH_FZ,生成函數(shù),代碼如下:

  • /* 幾何變換 圖像翻轉(zhuǎn):自己對這個功能比較感興趣,做個圖像反轉(zhuǎn) */

  • void CImageProcessingView::OnJhbhFz()

  • {

  • if(numPicture==0) {

  • AfxMessageBox("載入圖片后才能空間反轉(zhuǎn)!",MB_OK,0);

  • return;

  • }

  • AfxMessageBox("圖片空間變換-反轉(zhuǎn)圖像!",MB_OK,0);

  • ?
  • //打開臨時的圖片

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

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

  • fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);

  • fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);

  • fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);

  • fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);

  • fread(m_pImage,m_nImage,1,fpo);

  • ?
  • /*new和delete有效的進(jìn)行動態(tài)內(nèi)存的分配和釋放*/

  • unsigned char *ImageSize;

  • ImageSize=new unsigned char[m_nImage];

  • int countWidth=0; //記錄每行的像素個數(shù),滿行時變回0

  • int Place; //記錄圖像每行的位置,便于圖像反轉(zhuǎn)

  • int number=0; //數(shù)字記錄使用的像素行數(shù)

  • Place=m_nWidth*3-1;

  • ?
  • //翻轉(zhuǎn)矩陣: y=y0 x=width-x0-1

  • for(int i=0 ; i<m_nImage ; i++ )

  • {

  • if(countWidth==m_nWidth*3)

  • {

  • countWidth=0;

  • }

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

  • Place--;

  • countWidth++;

  • if(countWidth==m_nWidth*3)

  • {

  • number++;

  • Place=number*m_nWidth*3-1;

  • }

  • }

  • ?
  • fwrite(ImageSize,m_nImage,1,fpw);

  • fclose(fpo);

  • fclose(fpw);

  • numPicture = 2;

  • level=200;

  • Invalidate();

  • }

  • ? ? ? ? 運(yùn)行效果如下圖所示,其中還是存在一些小BUG,如前面的BMP圖補(bǔ)0湊齊4整數(shù)倍寬度或顏色失幀。

    ?




    2.垂直鏡像倒轉(zhuǎn)
    ? ? ? ? 其中變換矩陣如下:
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??X=X0
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Y=height-Y0-1 ? (height為圖像高度)
    ? ? ? ? 它相當(dāng)于把原圖的像素矩陣的最后一行像素值賦值給第一行,首先找到(0,0)對應(yīng)的(height-1,0)像素值,然后依次賦值該行的像素?cái)?shù)據(jù);最后當(dāng)前行賦值結(jié)束,依次下一行。重點(diǎn)是找到每行的第一個像素點(diǎn)即可。
    ? ? ? ? 代碼中引用兩個變量:Place=(m_nWidth*3)*(m_nHeight-1-1)即是(height-1,0)最后一行的第一個像素點(diǎn);然后是循環(huán)中Place=(m_nWidth*3)*(m_nHeight-number-1)找到每行的第一個像素點(diǎn)。

    ? ? ? ? 同樣通過類向?qū)珊瘮?shù)void CImageProcessingView::OnJhbhDz(),代碼如下:

  • /* 幾何變換 圖像倒轉(zhuǎn) */

  • void CImageProcessingView::OnJhbhDz()

  • {

  • if(numPicture==0) {

  • AfxMessageBox("載入圖片后才能空間反轉(zhuǎn)!",MB_OK,0);

  • return;

  • }

  • AfxMessageBox("圖片空間變換-反轉(zhuǎn)圖像!",MB_OK,0);

  • ?
  • //打開臨時的圖片

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

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

  • fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);

  • fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);

  • fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);

  • fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);

  • fread(m_pImage,m_nImage,1,fpo);

  • ?
  • /*new和delete有效的進(jìn)行動態(tài)內(nèi)存的分配和釋放*/

  • unsigned char *ImageSize;

  • ImageSize=new unsigned char[m_nImage];

  • int countWidth=0; //記錄每行像素個數(shù),滿行時變回0

  • int Place; //每列位置

  • int number=0; //像素行數(shù)

  • Place=(m_nWidth*3)*(m_nHeight-1-1); //0行存儲

  • ?
  • //翻轉(zhuǎn)矩陣: x=x0 y=height-y0-1

  • for(int i=0 ; i<m_nImage ; i++ )

  • {

  • ImageSize[i]=m_pImage[Place]; //(0,0)賦值(0,0)像素

  • Place++;

  • countWidth++;

  • if(countWidth==m_nWidth*3)

  • {

  • countWidth=0;

  • number++;

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

  • }

  • }

  • ?
  • fwrite(ImageSize,m_nImage,1,fpw);

  • fclose(fpo);

  • fclose(fpw);

  • numPicture = 2;

  • level=200;

  • Invalidate();

  • }

  • ? ? ? ? 運(yùn)行結(jié)果如下圖所示,第二張圖顏色沒有失幀或變灰,這完全可以懷疑在翻轉(zhuǎn)過程中RGB像素編程BGR后導(dǎo)致的結(jié)果,最終實(shí)現(xiàn)了翻轉(zhuǎn)圖像,但灰度存在一定;所以如果改為RBG順序不變化即可原圖顏色顯示。




    ?


    ?

    ?

    三. 圖像旋轉(zhuǎn)

    ? ? ? ? 圖像饒?jiān)c(diǎn)旋轉(zhuǎn)順時針theta角矩陣變換如下:注意BMP圖像(0,0)左下角


    ? ? ? ? 寫到這里真心覺得寫底層的代碼非常困難啊!尤其是以為像素轉(zhuǎn)換二維像素,同時也覺得當(dāng)時的自己算法部分還是很強(qiáng)大的,也感覺到如果采用GDI+操作像素矩陣Matrix或ColorMatrix是多么的方便,因?yàn)樗x好了X和Y向量,這就是為什么Android前面寫的圖像處理要容易得多。但是效率高~
    ? ? ? ? 好像利用GDI+旋轉(zhuǎn)通過幾句代碼即可:
    ? ? ? ? matrix.Rotate(15); //矩陣旋轉(zhuǎn)15度
    ? ? ? ? graph.SetTransform(&matrix);
    ? ? ? ? graph.DrawImage(&image,points,3);
    ? ? ? ? 下面這部分代碼是實(shí)現(xiàn)Android旋轉(zhuǎn)的:參考我的博客

  • //旋轉(zhuǎn)圖片

  • private void TurnPicture() {

  • Matrix matrix = new Matrix();

  • turnRotate=turnRotate+15;

  • //選擇角度 饒(0,0)點(diǎn)選擇 正數(shù)順時針 負(fù)數(shù)逆時針 中心旋轉(zhuǎn)

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

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

  • Canvas canvas = new Canvas(createBmp);

  • Paint paint = new Paint();

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

  • imageCreate.setBackgroundColor(Color.RED);

  • imageCreate.setImageBitmap(createBmp);

  • textview2.setVisibility(View.VISIBLE);

  • }

  • ? ? ? ? 實(shí)現(xiàn)效果如下圖所示:


    ? ? ? ? 言歸正傳,新建Dialog如下圖所示,設(shè)置ID_DIALOG_XZ和變量:

    ? ? ? ? 再點(diǎn)擊空白處創(chuàng)建CImageXZDlg類(旋轉(zhuǎn)),它會自動生成.h和.cpp文件。打開類向?qū)蒀ImageXZDlg類的成員變量m_xzds(旋轉(zhuǎn)度數(shù)),并設(shè)置其為int型(最大值360 最小值0)。
    ? ? ? ? 在類向?qū)?Ctrl+W)選擇類CImageProcessingView,為ID_JHBH_TXXZ(圖像旋轉(zhuǎn))添加函數(shù),同時添加頭文件#include "ImageXZDlg.h"

  • /**********************************************************/

  • /* 幾何變換:圖片旋轉(zhuǎn)

  • /* 先添加對話框:IDD_JHBH_TXXZ(圖像旋轉(zhuǎn)),創(chuàng)建新類CImageXZDlg

  • /* 創(chuàng)建輸入度數(shù)的:m_xzds Member variables 為int 0-360間

  • /**********************************************************/

  • ?
  • void CImageProcessingView::OnJhbhTxxz()

  • {

  • if(numPicture==0) {

  • AfxMessageBox("載入圖片后才能空間旋轉(zhuǎn)!",MB_OK,0);

  • return;

  • }

  • ?
  • //定義對話框并調(diào)用對話框

  • CImageXZDlg dlg;

  • if( dlg.DoModal()==IDOK ) //顯示對話框

  • {

  • AfxMessageBox("圖片空間變換-旋轉(zhuǎn)圖像!",MB_OK,0);

  • //讀寫文件

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

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

  • fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);

  • fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);

  • fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw);

  • fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw);

  • fread(m_pImage,m_nImage,1,fpo);

  • ?
  • /*new和delete有效的進(jìn)行動態(tài)內(nèi)存的分配和釋放*/

  • unsigned char *ImageSize;

  • ImageSize=new unsigned char[m_nImage];

  • int Place; //記錄圖像每行的位置,便于圖像旋轉(zhuǎn)

  • ?
  • /*定義PA=3.14時使用的方法是arcsin(1.0/2)*6即為π*/

  • double PA;

  • PA=asin(0.5)*6;

  • ?
  • /*把輸入的0-360的正整數(shù)度數(shù)轉(zhuǎn)換為角度,30度=π/6*/

  • double degree;

  • degree=PA*dlg.m_xzds/180; //調(diào)用dlg.m_xzds(旋轉(zhuǎn)度數(shù))

  • ?
  • //對應(yīng)的二維矩陣 注意圖像矩陣從左下角開始處理 它最終要轉(zhuǎn)換成一維存儲

  • int X,Y; //圖像變換前通過一維矩陣轉(zhuǎn)換為二維

  • int XPlace,YPlace;

  • ?
  • //輸出轉(zhuǎn)換為的角度

  • CString str;

  • str.Format("轉(zhuǎn)換后的角度=%f",degree);

  • AfxMessageBox(str);

  • ?
  • //圖像旋轉(zhuǎn)處理

  • for(int i=0 ; i<m_nImage ; i++ )

  • {

  • //原圖:一維矩陣轉(zhuǎn)換為二維矩陣

  • X=(i/3)%m_nWidth;

  • Y=(i/3)/m_nWidth;

  • //注意錯誤:X=i/m_nHeight Y=i%m_nWidth; 只輸出最后1/3

  • ?
  • //圖像旋轉(zhuǎn)為:a(x,y)=x*cos-y*sin b(x,y)=x*sin+y*cos

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

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

  • ?
  • //在轉(zhuǎn)換為一維圖想輸出

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

  • {

  • Place=YPlace*m_nWidth*3+XPlace*3;

  • //在圖像范圍內(nèi)賦值為該像素

  • if(Place+2<m_nImage)

  • {

  • ImageSize[i]=m_pImage[Place];

  • i++;

  • ImageSize[i]=m_pImage[Place+1];

  • i++;

  • ImageSize[i]=m_pImage[Place+2];

  • }

  • //否則賦值為黑色

  • else

  • {

  • ImageSize[i]=0;

  • i++;

  • ImageSize[i]=0;

  • i++;

  • ImageSize[i]=0;

  • }

  • }

  • //否則賦值為黑色

  • else

  • {

  • ImageSize[i]=0;

  • i++;

  • ImageSize[i]=0;

  • i++;

  • ImageSize[i]=0;

  • }

  • }

  • ?
  • fwrite(ImageSize,m_nImage,1,fpw);

  • fclose(fpo);

  • fclose(fpw);

  • numPicture = 2;

  • level=200; //幾何變換

  • Invalidate();

  • }

  • }

  • ? ? ? ? 運(yùn)行效果如下圖所示,中心旋轉(zhuǎn)太難了!找到中心那個位置就不太容易,我做不下去了,fuck~同時旋轉(zhuǎn)過程中,由于是饒左下角(0,0)實(shí)現(xiàn),故有的角度會到界面外顯示全黑。下圖分別旋轉(zhuǎn)15度和355度。




    ?


    ?

    ?

    四. 圖像縮放

    ? ? ? ? 圖像縮放主要有兩種方法:
    ? ? ? ? 1.最近鄰插值:向后映射時,輸出圖像的灰度等于離它所映射位置最近的輸入圖像的灰度值。其中向前映射和向后映射如下:

    ?

    ? ? ? ? 對于向前映射每個輸出圖像的灰度要經(jīng)過多次運(yùn)算,對于向后映射,每個輸出圖像的灰度只經(jīng)過一次運(yùn)算。在實(shí)際應(yīng)用中,更多的是采用向后映射法,其中根據(jù)四個相鄰像素灰度值計(jì)算某個位置的像素灰度值即為灰度級插值。
    ? ? ? ? 2.雙線性插值:四點(diǎn)確定一個平面函數(shù),屬于過約束問題。即單位正方形頂點(diǎn)已知,求正方形內(nèi)任一點(diǎn)的f(x,y)值。


    ? ? ? ? 換個通熟的說法,如下圖所示。采用最近鄰插值法就是P(x,y)像素值采用四舍五入等于離它最近的輸入圖像像素值。分別計(jì)算它到四個頂點(diǎn)之間的距離,但是這樣會造成圖像的馬賽克、鋸齒等現(xiàn)象。而采用雙線性插值法,主要通過該坐標(biāo)周圍的四個像素值,按照比例混合計(jì)算器近似值。比例混合的依據(jù)是離哪個像素近,哪個像素的比例越大。



    ? ? ? ? 下面是采用最近鄰插值法的過程,注意BMP圖縮放還需修改頭文件信息。
    ? ? ? ? 第一步:在資源視圖中添加“圖像縮放”Dialog

    ? ? ? ? 第二步:點(diǎn)擊空白處創(chuàng)建對話框的類CImageSFDlg,同時打開類向?qū)槠涮砑映蓡T變量m_sfbs(縮放倍數(shù)),其為int型在0-200之間。



    ? ? ? ? 第三步:打開類向?qū)槠涮砑映蓡T函數(shù)void CImageProcessingView::OnJhbhSf() 并實(shí)現(xiàn)縮放。同時添加頭文件#include "ImageSFDlg.h"。

  • /*******************************************************************/

  • /* ID_JHBH_SF: 幾何運(yùn)算-縮放-最近鄰插值算法

  • /* 算法思想:輸出圖像的灰度等于離它所映射位置最近的輸入圖像的灰度值

  • /* 先計(jì)算出放大縮小后的長寬,根據(jù)它計(jì)算找原圖中的點(diǎn)灰度,四舍五入

  • /*******************************************************************/

  • ?
  • void CImageProcessingView::OnJhbhSf()

  • {

  • if(numPicture==0) {

  • AfxMessageBox("載入圖片后才能幾何縮放圖像!",MB_OK,0);

  • return;

  • }

  • ?
  • CImageSFDlg dlg; //定義縮放對話框

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

  • {

  • //采樣坐標(biāo)最初為圖片的自身像素 m_sfbs(縮放倍數(shù))

  • if( dlg.m_sfbs==0 ) {

  • AfxMessageBox("輸入圖片縮放倍數(shù)不能為0!",MB_OK,0);

  • return;

  • }

  • ?
  • FILE *fpo = fopen(BmpName,"rb");

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

  • fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);

  • fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);

  • ?
  • /*先求縮放后的長寬*/

  • int sfWidth,sfHeight; //縮放后的長寬

  • int sfSize; //縮放后的圖像大小

  • sfWidth=(int)(m_nWidth*(dlg.m_sfbs*1.0)/100); //24位圖像RGB必須是3倍數(shù) 循環(huán)讀取時為RGB

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

  • int number; //記錄每行多余的圖像素?cái)?shù)個數(shù)

  • ?
  • //重點(diǎn):圖像的每行像素都必須是4的倍數(shù):1*1的圖像為 r g b 00H

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

  • number=(4-sfWidth*3%4);

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

  • }

  • else {

  • number=0;

  • sfSize=sfWidth*sfHeight*3;

  • }

  • //注意:假如最后一行像素不足,我默認(rèn)處理為完整的一行,不足補(bǔ)00H

  • //總之處理后的圖像總是m*n且為4倍數(shù),每行都完整存在

  • ?
  • /*更改文件頭信息 定義臨時文件頭結(jié)構(gòu)變量*/

  • BITMAPFILEHEADER bfhsf;

  • BITMAPINFOHEADER bihsf; //縮放(sf)

  • bfhsf=bfh;

  • bihsf=bih;

  • ?
  • bfhsf.bfSize=sfSize+54;

  • bihsf.biWidth=sfWidth;

  • bihsf.biHeight=sfHeight;

  • ?
  • //顯示部分m_nDrawWidth<650顯示原圖,否則顯示

  • flagSF=1; //圖像縮放為1標(biāo)識變量

  • m_nDrawWidthSF=sfWidth;

  • m_nDrawHeightSF=sfHeight;

  • ?
  • fwrite(&bfhsf,sizeof(BITMAPFILEHEADER),1,fpw);

  • fwrite(&bihsf,sizeof(BITMAPINFOHEADER),1,fpw);

  • ?
  • fread(m_pImage,m_nImage,1,fpo);

  • ?
  • unsigned char red,green,blue;

  • unsigned char other=0; //補(bǔ)碼00H='\0'

  • int placeX; //記錄在原圖中的第幾行的位置

  • int placeY; //記錄在原圖中的位置(x,y)

  • int placeBH; //記錄變換后在變換圖中的位置

  • ?
  • /*new和delete有效的進(jìn)行動態(tài)內(nèi)存的分配和釋放*/

  • unsigned char *ImageSize;

  • ImageSize=new unsigned char[sfSize];

  • ?
  • /*讀取文件像素信息 縮放注意:1.找最近灰度 2.四舍五入法(算法+0.5)*/

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

  • {

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

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

  • {

  • red=green=blue=0;

  • //放大倍數(shù)為(dlg.m_sfbs*1.0/100)

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

  • //重點(diǎn)是:number*i補(bǔ)充00H,如果是numer圖像會被切成2塊

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

  • if(placeY+2<m_nImage)

  • {

  • ImageSize[placeBH]=m_pImage[placeY];

  • ImageSize[placeBH+1]=m_pImage[placeY+1];

  • ImageSize[placeBH+2]=m_pImage[placeY+2];

  • }

  • else

  • {

  • ImageSize[placeBH]=0;

  • ImageSize[placeBH+1]=0;

  • ImageSize[placeBH+2]=0;

  • }

  • }

  • }

  • ?
  • fwrite(ImageSize,sfSize,1,fpw);

  • fclose(fpo);

  • fclose(fpw);

  • numPicture = 2;

  • level=200;

  • Invalidate();

  • }

  • }

  • ? ? ? ? 第四步:因?yàn)閳D像縮放修改BMP圖片頭信息,所以需要修改ShowBitmap中的顯示第二張圖片時的部分代碼。如下所示:添加變量flagSF、m_nDrawWidthSF和m_nDrawHeightSF。

  • /*定義顯示圖像縮放時的長寬與標(biāo)記*/

  • int flagSF=0; //圖像幾何變換縮放變換

  • int m_nDrawWidthSF=0; //圖像顯示寬度縮放后

  • int m_nDrawHeightSF=0; //圖像顯示高度縮放后

  • ?
  • //****************顯示BMP格式圖片****************//

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

  • {

  • ......

  • else //圖像幾何變換

  • if(level=200)

  • {

  • m_hBitmapChange = (HBITMAP) LoadImage(NULL,BmpNameLin,IMAGE_BITMAP,0,0,

  • LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION);

  • }

  • ?
  • ?
  • if( m_bitmap.m_hObject ) {

  • m_bitmap.Detach(); //m_bitmap為創(chuàng)建的位圖對象

  • }

  • m_bitmap.Attach(m_hBitmapChange);

  • //定義并創(chuàng)建一個內(nèi)存設(shè)備環(huán)境

  • CDC dcBmp;

  • if( !dcBmp.CreateCompatibleDC(pDC) ) //創(chuàng)建兼容性的DC

  • return;

  • BITMAP m_bmp; //臨時bmp圖片變量

  • m_bitmap.GetBitmap(&m_bmp); //將圖片載入位圖中

  • CBitmap *pbmpOld = NULL;

  • dcBmp.SelectObject(&m_bitmap); //將位圖選入臨時內(nèi)存設(shè)備環(huán)境

  • ?
  • //圖片顯示調(diào)用函數(shù)StretchBlt

  • if(flagSF==1)

  • {

  • CString str;

  • str.Format("縮放長=%d 寬%d 原圖長=%d 寬=%d",m_nDrawWidthSF,

  • m_nDrawHeightSF,m_nWidth,m_nHeight);

  • AfxMessageBox(str);

  • flagSF=0;

  • //m_nDrawWidthSF縮放此存見函數(shù)最近鄰插值法中賦值

  • if(m_nDrawWidthSF<650 && m_nDrawHeightSF<650)

  • pDC->StretchBlt(m_nWindowWidth-m_nDrawWidthSF,0,

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

  • else

  • pDC->StretchBlt(m_nWindowWidth-640,0,640,640,&dcBmp,0,0,

  • m_bmp.bmWidth,m_bmp.bmHeight,SRCCOPY); //顯示大小為640*640

  • }

  • else {

  • //如果圖片太大顯示大小為固定640*640 否則顯示原圖大小

  • if(m_nDrawWidth<650 && m_nDrawHeight<650)

  • pDC->StretchBlt(m_nWindowWidth-m_nDrawWidth,0,

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

  • else

  • pDC->StretchBlt(m_nWindowWidth-640,0,640,640,&dcBmp,0,0,

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

  • }

  • //恢復(fù)臨時DC的位圖

  • dcBmp.SelectObject(pbmpOld);

  • }

  • ? ? ? ? 運(yùn)行效果如下圖所示,采用最近鄰插值法縮放大了會出現(xiàn)失幀。

    ?


    ?


    ? ? ? ? 但是同時當(dāng)圖片縮小是總是報(bào)錯,圖片縮放確實(shí)有點(diǎn)難,因?yàn)橄袼匦枰a(bǔ)齊4整數(shù)倍,同時需要修改消息頭,同時像素矩陣的變換都非常復(fù)雜。

    ?

    ? ? ? ??

    //

    轉(zhuǎn)載:https://blog.csdn.net/eastmount/article/details/46345299

    總結(jié)

    以上是生活随笔為你收集整理的MFC空间几何变换之图像平移、镜像、旋转、缩放的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。