【數據壓縮】Exp02.BMP2YUV

實驗原理:

    本次實驗的目的是實現將不同像素深度的bmp圖像文件轉爲yuv格式,並生成一個幀數大於200yuv序列,因此在實驗之前應該先對bmp文件的數據組織格式有一定的瞭解。

   BMP文件是Windows操作系統中的標準圖像文件格式,可以分成兩類:設備相關位圖(DDB)和設備無關位圖(DIB)。它採用位映射存儲格式,除了圖像深度可選以外,在絕大多數應用中不採用其他任何壓縮,因此,BMP文件所佔用的空間很大。BMP文件的圖像深度可選 lbit4bit8bit16bit 24bitBMP文件存儲數據時,圖像的掃描方式是按從左到右、從下到上的順序。

典型的 BMP圖像文件由四部分組成:

1)位圖頭文件數據結構(BITMAPFILEHEADER),它包含 BMP 圖像文件的類型、顯示內容等信息;

2)位圖信息數據結構(BITMAPINFOHEADER),它包含有 BMP 圖像的寬、高、壓縮方法,以及定義顏色等信息;

3)調色板(Palette),這個部分是可選的,1bit\4bit\8bit位圖需要調色板,16bit\24bit不需要調色板;

4)位圖數據(ImageData),這部分的內容根據 BMP 位圖使用的位數不同而不同,在 24位圖中直接使用 RGB16位位圖用兩個字節來存儲一個像素點的RGB值,有555(高位爲0)和565G6bit表示)兩種存儲格式,而其他的小於 24 位的使用調色板中顏色索引值。

實驗步驟:

第一步:生成八張圖像深度爲24位的.bmp圖片文件,實現將單張.bmp文件轉化爲yuv文件,添加了如下的代碼:



分別定義了兩個結構體爲file_headerfile_header,用來存儲.bmp文件中表徵自身信息的數據,例如圖像的水平像素數和垂直像素數等。在調試過程中可以看到兩結構體中的數據:


再用二進制的方式打開輸入的.bmp文件:


可以看到,二進制文件中的數據是與結構體中的數據一一對應的,選中的數據即爲兩個結構體中的數據,此後即爲真正的圖像像素數據。因此證明將單張24位位圖轉爲yuv文件是正確的,效果圖如下:



第二步:實現將五張不同的24位位圖轉化爲一個200幀以上的yuv序列,每張圖像顯示的幀數可自行在命令行參數中進行設置。首先命令行參數設置如下:


利用循環結構,讀取每個輸入文件以及其顯示的幀數,循環讀取文件並將其轉爲yuv數據寫入yuv文件中:


最終得到的yuv序列爲:


第三步:實現將同一張像素深度不同的bmp格式的圖片轉爲yuv文件。對於不同像素深度的圖片,其數據存儲方式有不同。1bit4bit8bit16bit24bit是幾種常見的像素深度。對於1bit\4bit\8bit的圖像有調色板,其ImageData中存儲的是對應像素的索引值,通過該索引值到調色板中得到其對應的rgb值,而16bit\24bit的則無調色板,其ImageData中直接存儲的是素數的rgb值。因此想要將不同像素深度的圖像均能轉化爲yuv格式,構造了兩個函數:makePalette()(用來獲取文件數據中的調色板數據,若沒有調色板則不做處理);makeRGBdata()(用來將文件中的ImageData部分的數據轉爲RGB數據),關鍵代碼如下:



640x480的圖像01_1bit.bmp01_4bit.bmp01_8bit.bmp01_24bit.bmp圖像分別測試,發現都能正確生成對應的圖片,效果圖如下:


第四步:實現將ImageData中有存儲時添加的填充數據的圖像進行轉換。由於bmp文件規定每一掃描行的字節數必須是 4 的整倍數,也就是DWORD對齊的。所以當我們在將一張圖片另存爲bmp格式時,爲了保證每一掃描行都是4字節的整數倍,若選擇圖片的水平像素數不滿足這個要求,則電腦會自動添加填充數據使得每一行的字節數是4的倍數,例如對一張565x565的圖像存爲像素深度爲1bit的bmp文件,每個字節可以存儲8個像素的索引值,因此該掃描行的像素值不能被8整除,再存儲時需要先填充3bit使其能夠構成整字節,每行填充3bit後則每掃描行的數據需要71個字節,由於71不是4的倍數所以在存儲時還需要在每行的數據後加入1個字節的填充數據使得滿足每行字節數爲4 的倍數這個要求。因此,爲了使所有像素數的圖像均能正常轉換,需要在生成rgb數據時判斷哪些爲真正有用的數據,哪些是填充的數據,從而只對有用數據進行處理讀取對應rgb值,跳過存儲時添加的填充數據。因此修改makeRGBdata()函數,下面爲修改後的代碼:

由於測試時挑選了一張565x565的圖像,因此rgb2yuv.cpp中的代碼也需要進行相應的修改,否則由於寬高不能被2整除將會直接跳出不能正確生成yuv文件,因此將圖像的最後一行和最後一列去掉再對其進行處理。這一步是我之前沒有想到的,因此反覆檢查makeRGBdata中的代碼而忽略了這兒,經過修改後程序終於能跑通了,生成的yuv文件變成564x564的。


實驗結果:

將上述步驟的代碼進行整合,實現將不同像素深度的不同圖像進行轉換,生成一個yuv序列,輸入的圖像均爲565x423。最終的效果圖如下:


結論:

       這次的實驗是實現將一個數據組織形式不是很複雜的媒體文件進行轉化,但是由於對數據的存儲方式有較多細節的考慮,例如不同像素深度的圖像是否有調色板,如何將文件中存儲的數據轉爲rgb數據,如何再將ImageData中的數據轉化爲rgb數據時跳過填充數據等,實現這次的實驗還是花費了不少時間。通過這次的實驗,我瞭解到瞭如果想要自己寫出的程序能夠處理各種不同情況,真的需要把方方面面都考慮到,這樣才能提高程序的完備性。

       在實驗的過程中我也發現了一些自己需要提高的點,例如自己在編程時比較喜歡照着自己的思路從頭往下寫,但是這是一個不太好的習慣,對於一些較複雜的功能應該考慮將這個功能用一個函數封裝起來,然後在主函數中對其進行調用,這樣主函數中的代碼就不會那麼冗長且雜亂了,在看起來更簡潔易讀的同時也提高了代碼的可移植性。除此之外,在開闢緩存的時候一定要注意開闢空間的大小,再對開闢的緩存操作結束後記得關閉緩存,再對文件進行讀寫操作時也一定要注意範圍,不然很容易造成文件指針的越界。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章