圖像編程學習筆記2――bmp位圖平移
以下文字內容copy于<<數字圖像處理編程入門>>,code為自己實現,是win32控制臺程序。
2.1 平移
平移(translation)變換大概是幾何變換中最簡單的一種了。
如圖2.1所示,初始坐標為(x0,y0)的點經過平移(tx,ty)(以向右,向下為正方向)后,坐標變為(x1,y1)。這兩點之間的關系是x1=x0+tx,y1=y0+ty。
圖2.1 平移的示意圖
以矩陣的形式表示為

我們更關心的是它的逆變換:

這是因為:我們想知道的是平移后的圖象中每個象素的顏色。例如我們想知道,新圖中左上角點的RGB值是多少?很顯然,該點是原圖的某點經過平移后得到的,這兩點的顏色肯定是一樣的,所以只要知道了原圖那點的RGB值即可。那么到底新圖中的左上角點對應原圖中的哪一點呢?將左上角點的坐標(0,0)入公式(2.2),得到x0=-tx,y0=-ty;所以新圖中的(0,0)點的顏色和原圖中(-tx, -ty)的一樣。
這樣就存在一個問題:如果新圖中有一點(x1,y1),按照公式(2.2)得到的(x0,y0)不在原圖中該怎么辦?通常的做法是,把該點的RGB值統一設成(0,0,0)或者(255,255,255)。
另一個問題是:平移后的圖象是否要放大?一種做法是不放大,移出的部分被截斷。例如,圖2.2為原圖,圖2.3為移動后的圖。這種處理,文件大小不會改變。
圖2.2 移動前的圖
圖2.3 移動后的圖
還有一種做法是:將圖象放大,使得能夠顯示下所有部分,如圖2.4所示。
圖2.4 移動后圖象被放大
這種處理,文件大小要改變。設原圖的寬和高分別是w1,h1則新圖的寬和高變為w1+|tx|和h1+|ty|,加絕對值符號是因為tx,ty有可能為負(即向左,向上移動)。
下面的函數Translation采用的是第一種做法,即移出的部分被截斷。在給出源代碼之前,先說明一個問題。
如果你用過Photoshop,CorelPhotoPaint等圖象處理軟件,可能聽說過“灰度圖”(grayscale)這個詞。灰度圖是指只含亮度信息,不含色彩信息的圖象,就象我們平時看到的黑白照片:亮度由暗到明,變化是連續的。因此,要表示灰度圖,就需要把亮度值進行量化。通常劃分成0到255共256個級別,其中0最暗(全黑),255最亮(全白)。.bmp格式的文件中,并沒有灰度圖這個概念,但是,我們可以很容易在.bmp文件中表示灰度圖。方法是用256色的調色板,只不過這個調色板有點特殊,每一項的RGB值都是相同的。也就是說RGB值從(0,0,0),(1,1,1)一直到(255,255,255)。(0,0,0)是全黑色,(255,255,255)是全白色,中間的是灰色。這樣,灰度圖就可以用256色圖來表示了。為什么會這樣呢?難道是一種巧合?其實并不是。
在表示顏色的方法中,除了RGB外,還有一種叫YUV的表示方法,應用也很多。電視信號中用的就是一種類似于YUV的顏色表示方法。
在這種表示方法中,Y分量的物理含義就是亮度,U和V分量代表了色差信號(你不必了解什么是色差,只要知道有這么一個概念就可以了)。使用這種表示方法有很多好處,最主要的有兩點:
(1) 因為Y代表了亮度,所以Y分量包含了灰度圖的所有信息,只用Y分量就能完全能夠表示出一幅灰度圖來。當同時考慮U,V分量時,就能夠表示出彩色信息來。這樣,用同一種表示方法可以很方便的在灰度和彩色圖之間切換,而RGB表示方法就做不到這一點了。
(2) 人眼對于亮度信號非常敏感,而對色差信號的敏感程度相對較弱。也就是說,圖象的主要信息包含在Y分量中。這就提示我們:如果在對YUV信號進行量化時,可以“偏心”一點,讓Y的量化級別多一些(誰讓它重要呢?)而讓UV的量化級別少一些,就可以實現圖象信息的壓縮。這一點將在第9章介紹圖象壓縮時仔細研究,這里就不深入討論了。而RGB的表示方法就做不到這一點,因為RGB三個分量同等重要,缺了誰也不行。YUV和RGB之間有著如下的對應關系
(2.3)
(2.4)
當RGB三個分量的大小一樣時,假設都是a,代入公式(2.3),得到Y=a,U=0,V=0 。你現在該明白我前面所說不是巧合的原因了吧。
使用灰度圖有一個好處,那就是方便。首先RGB的值都一樣;其次,圖象數據即調色板索引值,也就是實際的RGB值,也就是亮度值;另外,因為是256色調色板,所以圖象數據中一個字節代表一個象素,很整齊。如果是2色圖或16色圖,還要拼湊字節,很麻煩。如果是彩色的256色圖,由于圖象處理后有可能會產生不屬于這256種顏色的新顏色,就更麻煩了;這一點,今后你就會有深刻體會的。所以,做圖象處理時,一般采用灰度圖。為了將重點放在算法本身上,今后給出的程序如不做特殊說明,都是針對256級灰度圖的。其它顏色的情況,你可以自己想一想,把算法補全。
如果想得到一幅灰度圖,可以使用Sea或者PhotoShop等軟件提供的顏色轉換功能將彩色圖轉換成灰度圖。
好了,言歸正傳,下面給出Translation的源代碼。算法的思想是先將所有區域填成白色,然后找平移后顯示區域的左上角點(x0,y0)和右下角點(x1,y1) ,分幾種情況進行處理。
先看x方向(width指圖象的寬度)
(1) tx≤-width:很顯然,圖象完全移出了屏幕,不用做任何處理;
(2) -width<tx≤0:如圖2.5所示。容易看出,圖象區域的x范圍從0到width-|tx|,對應原圖的范圍從|tx|到width;
圖2.5 tx≤0,ty≤0的情況
(3) 0< tx<width:如圖2.6所示。容易看出,圖象區域的x范圍從tx到width,對應原圖的范圍從0到width - tx;
圖2.6 0< tx<width,0<ty<height的情況
(4) tx≥width:很顯然,圖象完全移出了屏幕,不用做任何處理。
y方向是對應的(height表示圖象的高度):
(1) ty≤-height,圖象完全移出了屏幕,不用做任何處理;
(2) -height<ty≤0,圖象區域的y范圍從0到height-|ty|,對應原圖的范圍從|ty|到height;
(3) 0<ty<height,圖象區域的y范圍從ty到height,對應原圖的范圍從0到height-ty;
(4) ty≥height,圖象完全移出了屏幕,不用做任何處理。
這種做法利用了位圖存儲的連續性,即同一行的象素在內存中是相鄰的。利用memcpy函數,從(x0,y0)點開始,一次可以拷貝一整行(寬度為x1-x0),然后將內存指針移到(x0,y0+1)處,拷貝下一行。這樣拷貝(y1-y0)行就完成了全部操作,避免了一個一個象素的計算,提高了效率。
CODE:(注:該程序需要一副bmp格式的灰度圖像,并放到工程目錄下,文件名為nv1.bmp)
[cpp] view plaincopy
- /**
- * 程序名: Translation.cpp
- * 功 能: 實現bmp格式灰度圖片的平移,移出部分用白色填充
- */
- #include <iostream>
- #include <cstdio>
- #include <fstream>
- #include <cstring>
- #include <windows.h>
- using namespace std;
- BITMAPFILEHEADER bmpFileHeader; //位圖文件頭
- BITMAPINFOHEADER bmpInfoHeader; //位圖信息頭
- RGBQUAD *pColorTable = new RGBQUAD[256]; //顏色表指針
- unsigned char *pBmpData; //圖像數據指針
- unsigned char *pBmpData1; //平移后圖像數據指針
- unsigned char *pTemp,*pTemp1; //臨時指針
- int width,height,imgSize; //圖像寬,高,實際大小,imgSize必須為4的倍數,bmp格式文件結構規定
- int srcX[2],srcY[2],dstX[2],dstY[2]; //平移前后位置
- /**
- * 函數名: readBmp
- * 參 數: bmpFileName--指向讀入bmp文件的文件名指針
- * 功 能: 讀入一個bmp文件,獲得相應數據
- */
- bool readBmp(char *bmpFileName)
- {
- FILE *fp = fopen(bmpFileName,"rb"); //以二進制讀方式打開指定的圖像文件
- if(NULL == fp)
- {
- printf("%s is not exist!",bmpFileName);
- return FALSE;
- }
- fread(&bmpFileHeader,sizeof(BITMAPFILEHEADER),1,fp); //讀取位圖頭信息放入bmpFileHeader,注:指針也相應移動
- fread(&bmpInfoHeader,sizeof(BITMAPINFOHEADER),1,fp); //讀取位圖信息頭放入bmpInfoHeader
- width = bmpInfoHeader.biWidth; //寬
- height = bmpInfoHeader.biHeight; //高
- fread(pColorTable,sizeof(RGBQUAD),256,fp); //讀取顏色表放入pColorTable
- // int bytePerLine = (bmpInfoHeader.biWidth * bmpInfoHeader.biBitCount + 31) / 32 * 4;
- pBmpData = new unsigned char [imgSize = bmpInfoHeader.biSizeImage];
- pBmpData1 = new unsigned char [imgSize];
- memset(pBmpData1,(BYTE)255,sizeof(char)*imgSize); //把新的圖像信息用255(白色)填充,平移后沒有圖像的區域就是白色了
- fread(pBmpData,sizeof(char),bmpInfoHeader.biSizeImage,fp); //讀取圖像信息放入pBmpData
- fclose(fp); //記住要關閉文件
- return TRUE;
- }
- /**
- * 函數名: translation
- * 參 數: tx--平移的x距離,ty--平移的y距離
- * 功 能: 實現平移,并把平移后圖像信息寫入pBmpData1
- */
- void translation(int tx,int ty)
- {
- bool xVisible = TRUE,yVisible = TRUE;
- //xVisible為FALSE時,表示x方向已經移出了可顯示的范圍
- if(tx <= -width)
- {
- xVisible = FALSE;
- }
- else if(tx <= 0)
- {
- dstX[0] = 0; //表示移動后,有圖區域的左上角點的x坐標
- dstX[1] = width + tx; //表示移動后,有圖區域的右下角點的x坐標
- }
- else if(tx < width)
- {
- dstX[0] = tx;
- dstX[1] = width;
- }
- else
- xVisible = FALSE;
- srcX[0] = dstX[0] - tx; //對應DstX0在原圖中的x坐標
- srcX[1] = dstX[1] - tx; //對應DstX1在原圖中的x坐標
- int rectWidth = srcX[1] - srcX[0]; //有圖區域的寬度
- //y的和x類似,就不加注釋了
- if(ty <= -height)
- yVisible = FALSE;
- else if(ty <= 0)
- {
- dstY[0] = 0;
- dstY[1] = height + ty;
- }
- else if(ty < height)
- {
- dstY[0] = ty;
- dstY[1] = height;
- }
- else
- yVisible = FALSE;
- srcY[0] = dstY[0] - ty;
- srcY[1] = dstY[1] - ty;
- int rectHeight = srcY[1] - srcY[0];
- int lineBytes = (width * bmpInfoHeader.biBitCount + 31) / 32 * 4; //每行所占的字節數,必須為4的倍數
- if(xVisible && yVisible)
- {
- for(int i = 0; i < rectHeight; i++ )
- {
- //pTemp指向要拷貝的那一行的最左邊的象素對應在原圖中的位
- //置。特別要注意的是,由于.bmp是上下顛倒的,
- pTemp = pBmpData + (height - 1 - (srcY[0] + i)) * lineBytes + srcX[0];
- //pTemp1指向要拷貝的那一行的最左邊的象素對應在新圖中的位置。同樣要注意上面的問題。
- pTemp1 = pBmpData1 + (height - 1 - (dstY[0] + i)) * lineBytes + dstX[0];
- memcpy(pTemp1,pTemp,rectWidth); //從pTemp中復制大小為rectWidth的數據到pTemp1,這里就是copy圖像的一行數據
- }
- }
- }
- /**
- * 函數名: writeBmp
- * 功 能: 新建一個bmp文件,把平移后的圖像信息寫入,生成一個新的bmp
- */
- void writeBmp()
- {
- char writeBmpName[] = "new.bmp";
- FILE *fp = fopen(writeBmpName,"wb"); //以二進制寫方式打開指定的圖像文件
- if(NULL == fp)
- {
- cout<<"file not exist!";
- return ;
- }
- //寫入BMP文件數據
- fwrite(&bmpFileHeader,sizeof(BITMAPFILEHEADER),1,fp);
- fwrite(&bmpInfoHeader,sizeof(BITMAPINFOHEADER),1,fp);
- fwrite(pColorTable,sizeof(RGBQUAD),256,fp);
- fwrite(pBmpData1,sizeof(char),imgSize,fp);
- fclose(fp);
- //釋放內存
- delete []pColorTable;
- delete []pBmpData1;
- delete []pBmpData;
- }
- /**
- * 函數名: work
- * 功 能: 處理
- */
- void work()
- {
- int x,y;
- char readBmpName[] = "nv1.bmp";
- if ( !readBmp(readBmpName) )
- printf("Bmp file reads faliure");
- printf("the distance of translation,cx,cy:"); //讀入平移的x和y
- scanf("%d %d",&x,&y);
- translation(x,y);
- writeBmp();
- }
- int main()
- {
- work();
- return 0;
- }
- from:http://blog.csdn.net/sun1956/article/details/8646800