使用C++实现多张BMP图片转换为YUV动画----附加淡入淡出转场(逐渐变明变暗),及垂直滑像转场(逐行渐变)
使用C++實現多張BMP圖片轉換為YUV動畫----附加淡入淡出轉場(逐漸變明變暗),及垂直滑像轉場(逐行漸變)
- 一、BMP圖像簡介
- 1、BMP圖像是什么?
- 2、BMP圖像文件結構
- 1)圖象文件頭
- 2)圖象信息頭
- 3)調色板數據
- 4)圖象數據
- 二、利用命令參數實現讀取文件的簡單化安全化
- 三、使用C++實現多張BMP轉換為YUV動畫
- 1、BMP文件讀入
- 2、BMP(RGB)轉YUV
- 3、多張BMP圖片轉換為YUV動畫
- 四、淡入淡出轉場的實現(逐漸變明變暗)
- 1、淡入(變明)
- 2、淡出(變暗)
- 3、原始亮度圖片
- 4、效果演示
- 五、垂直滑像轉場的實現(逐行漸變)
- 1、代碼實現
- 2、效果演示
- 六、整體代碼演示
- 七、歸納總結
一、BMP圖像簡介
1、BMP圖像是什么?
BMP是英文Bitmap(位圖)的簡寫,它是Windows操作系統中的標準圖像文件格式,能夠被多種Windows應用程序所支持。隨著Windows操作系統的流行與豐富的Windows應用程序的開發,BMP位圖格式理所當然地被廣泛應用。這種格式的特點是包含的圖像信息較豐富,幾乎不進行壓縮,但由此導致了它與生俱來的缺點–占用磁盤空間過大。所以,目前BMP在單機上比較流行。BMP位圖文件默認的文件擴展名是BMP或者bmp(有時它也會以.DIB或.RLE作擴展名)。
2、BMP圖像文件結構
位圖文件可看成由4個部分組成:位圖文件頭(bitmap-file header)、位圖信息頭(bitmap-information header)、彩色表(color table)和定義位圖的字節(位圖數據,即圖像數據,Data Bits 或Data Body)陣列,它具有如下所示的形式。
1)圖象文件頭
0000h 文件標識 2 bytes 兩字節的內容用來識別位圖的類型:
‘BM’ :Windows 3.1x,95,NT,…
‘BA’ :OS/2 Bitmap Array
‘CI’ :OS/2 Color Icon
‘CP’ :OS/2 Color Pointer
‘IC’ :OS/2 Icon
‘PT’ :OS/2 Pointer
注:因為OS/2系統并沒有被普及開,所以在編程時,你只需判斷第一個標識“BM”就行。
0002h File Size 1 dword 用字節表示的整個文件的大小
0006h Reserved 1 dword 保留,必須設置為0
000Ah Bitmap Data Offset 1 dword 從文件開始到位圖數據開始之間的數據(bitmap data)之間的偏移量
2)圖象信息頭
000Eh Bitmap Header Size 1 dword位圖信息頭(Bitmap Info Header)的長度,用來描述位圖的顏色、壓縮方法等。下面的長度表示:
28h - Windows 3.1x,95,NT,…
0Ch - OS/2 1.x
F0h - OS/2 2.x
注:在Windows95、98、2000等操作系統中,位圖信息頭的長度并不一定是28h,因為微軟已經制定出了新的BMP文件格式,其中的信息頭結構變化比較大,長度加長。所以最好不要直接使用常數28h,而是應該從具體的文件中讀取這個值。這樣才能確保程序的兼容性。
0012h Width 1 dword位圖的寬度,以象素為單位
0016h Height 1 dword位圖的高度,以象素為單位
001Ah Planes 1 word位圖的位面數(注:該值將總是1)
001Ch Bits Per Pixel 1 word 每個象素的位數
1 - 單色位圖(實際上可有兩種顏色,缺省情況下是黑色和白色。你可以自己定義這兩種顏色)
4 - 16 色位圖
8 - 256 色位圖
16 - 16bit 高彩色位圖
24 - 24bit真彩色位圖
32 - 32bit 增強型真彩色位圖
001Eh Compression 1 dword 壓縮說明:
0 - 不壓縮 (使用BI_RGB表示)
1 - RLE 8-使用8位RLE壓縮方式(用BI_RLE8表示)
2 - RLE 4-使用4位RLE壓縮方式(用BI_RLE4表示)
3 - Bitfields-位域存放方式(用BI_BITFIELDS表示)
0022h Bitmap Data Size 1 dword 用字節數表示的位圖數據的大小。該數必須是4的倍數
0026h HResolution 1 dword 用象素/米表示的水平分辨率
002Ah VResolution 1 dword 用象素/米表示的垂直分辨率
002Eh Colors 1 dword位圖使用的顏色數。如8-比特/象素表示為100h或者 256.
0032h Important Colors 1 dword 指定重要的顏色數。當該域的值等于顏色數時(或者等于0時),表示所有顏色都一樣重要
3)調色板數據
根據BMP版本的不同而不同 Palette N * 4 byte 調色板規范。對于調色板中的每個表項,這4個字節用下述方法來描述RGB的值:1字節用于藍色分量
1字節用于綠色分量
1字節用于紅色分量
1字節用于填充符(設置為0)
4)圖象數據
根據BMP版本及調色板尺寸的不同而不同 Bitmap Data xxx bytes 該域的大小取決于壓縮方法及圖像的尺寸和圖像的位深度,它包含所有的位圖數據字節,這些數據可能是彩色調色板的索引號,也可能是實際的RGB值,這將根據圖像信息頭中的位深度值來決定。
二、利用命令參數實現讀取文件的簡單化安全化
(與本人之前所寫的博客內容相同,詳解可看“使用C++實現YUV格式圖像與RGB格式圖像之間相互轉換”)
通過選擇“項目”-“yuv2rgb屬性”進入yuv2rgb屬性頁
在屬性頁中選擇“配置屬性”-“調試”進入調試器界面
在命令參數中按順序依次輸入值(文件名稱、參數等)并以“ ”(空格)隔開,這些值會依次排放在argv[1]-argv[n]之中,需要調用的時候直接賦值即可:
并在工作目錄選擇文件所在位置即可并通過已下代碼即可使文件讀取更加安全,文件路徑不會再在代碼中展現:
yuvFile = fopen(yuvFileName, "rb");rgbFile = fopen(rgbFileName, "wb");三、使用C++實現多張BMP轉換為YUV動畫
本次的BMP圖像采用寬度1024像素和高度為576像素,位深度為24位真彩色,不涉及調色板。
1、BMP文件讀入
因為本次實驗采用的圖像不涉及調色板所以只需讀取三部份文件----文件頭、信息頭及圖像數據。因此,打開BMP文件后,先根據FileHeader判斷圖像屬性,然后僅需從InfoHeader中讀取出寬和高,開出相應的緩存區即可。下面為代碼實現:
BITMAPFILEHEADER File_header;//文件頭BITMAPINFOHEADER Info_header;//信息頭FILE* bmp;int width,height;//圖像寬高信息bmp = fopen(bmpFileName, "rb");fread(&File_header,sizeof(BITMAPFILEHEADER),1,bmp);//圖區文件頭fread(&Info_header,sizeof(BITMAPINFOHEADER),1,bmp);//讀取信息頭width = (int)Info_header.biWidth;//獲取寬度信息height = (int)Info_header.biHeight;//獲取高度信息//開辟空間yBuf = (u_int8_t*)malloc(width * height);uBuf = (u_int8_t*)malloc((width * height) /4);vBuf = (u_int8_t*)malloc((width * height) 4);fread(rgbBuf, 1, width * height * 3, bmp);//讀取圖像數據2、BMP(RGB)轉YUV
因為本次實驗從BMP文件中讀出的圖像數據部分為RGB格式,所以可以直接引用本人以往博客點擊此處進入,中的RGB2YUV函數進行轉化,在這里不再做講解。
3、多張BMP圖片轉換為YUV動畫
在上面,介紹了如何將一張BMP圖片轉換為YUV圖片,接下來就要說明如何將多張BMP圖片轉換為YUV動畫。
要將多張BMP圖片轉換為YUV動畫在大體上只需兩部分:一是將多張BMP圖片依次輸入,二是將每張BMP圖片序列分別輸入30次,這樣得到的就是一個YUV動畫。
其實,要用代碼實現這個轉換非常簡單,只需將上面介紹的步驟添加幾個簡單的“for”循環嵌套即可:
首先是外層的大循環,是為了實現第一部分(將多張BMP圖片依次輸入)因為這里采用4張圖片,所以z=4;
其次便是內層循環,是為了實現第二部分(將每張BMP圖片序列分別輸入30次):
整體代碼會在講完轉場的實現后一并給出
四、淡入淡出轉場的實現(逐漸變明變暗)
淡入淡出的意思就是逐漸變明變暗,所以只需要將Y的值增大減小即可達成,在這里經過實驗發現依次乘(除)1.08效果最好。
1、淡入(變明)
因為是變亮,所以需將初始較暗的圖片存入新的緩存中,因為這里是10張圖依次變量所以只需要將原來的亮度Y除以1.08的10次方(2.16)即可,再將暗圖片緩存依次成1.08即可恢復成原圖,并實現漸變明亮的效果。以下是代碼實現:
for (i = 0; i < width*height; i++)//獲取初始較暗緩存{yBuf_high[i] = yBuf[i]/2.16;}for(int x=0;x<10;x++)//依次亮度提升{for (int j = 0; j < width*height; j++){yBuf_high[j] = yBuf_high[j]*1.08;}for (int m = 0; m < width*height; m++){if (yBuf_high[m] < 16) yBuf_high[m] = 16;if (yBuf_high[m] > 235) yBuf_high[m] = 235;}fwrite(yBuf_high, 1, width * height, yuvFile);fwrite(uBuf, 1, (width * height) / 4, yuvFile);fwrite(vBuf, 1, (width * height) / 4, yuvFile);}2、淡出(變暗)
與變明的思想相同,只需先將原圖的亮度Y存入新的緩存當中,再將亮度Y依次除以1.08即可,以下為代碼實現:
for (i = 0; i < width*height; i++)//獲取初始緩存{yBuf_low[i] = yBuf[i];}for(int x=0;x<10;x++)//依次變暗{for (int j = 0; j < width*height; j++){yBuf_low[j] = yBuf_low[j]/1.08;}for (int m = 0; m < width*height; m++){if (yBuf_low[m] < 16) yBuf_low[m] = 16;if (yBuf_low[m] > 235) yBuf_low[m] = 235;}fwrite(yBuf_low, 1, width * height, yuvFile);fwrite(uBuf, 1, (width * height) / 4, yuvFile);fwrite(vBuf, 1, (width * height) / 4, yuvFile);}3、原始亮度圖片
只需在淡入和淡出之間插入10張原始圖片即可實現一個附帶淡入淡出轉場的動畫,以下是插入10張原始圖片的代碼:
for(i=0;i<10;i++){fwrite(yBuf, 1, width * height, yuvFile);fwrite(uBuf, 1, (width * height) / 4, yuvFile);fwrite(vBuf, 1, (width * height) / 4, yuvFile);}完整代碼在垂直滑像轉場講解完之后給出
4、效果演示
這里只選擇了一張圖片進行演示,多張圖片演示在垂直滑像轉場中有演示,因為CSDN只支持上傳GIF圖片所以效果不是很好。
五、垂直滑像轉場的實現(逐行漸變)
1、代碼實現
垂直滑像轉場的實現分為兩步,第一步將第一張圖像全部存入y、u、v緩存中,第二步將后一張圖片依次存入y、u、v緩存中即可實現,以下是代碼實現:
//換行for(int b=0;b<width*height;b++)//將第一張圖片y存入緩存{yBuf_huanhang[b] = yBuf[b];}for(int b=0;b<width*height/4;b++)//將第一張圖片u、v存入緩存{uBuf_huanhang[b] = uBuf[b];vBuf_huanhang[b] = vBuf[b];}for(int a=30;a>0;a--)//依次將后一張圖片y、u、v存入緩存中{for(int b=0;b<width*height/a;b++){yBuf_huanhang[b] = yBuf_2[b];}for(int b=0;b<width*height/a/4;b++){uBuf_huanhang[b] = uBuf_2[b];vBuf_huanhang[b] = vBuf_2[b];}fwrite(yBuf_huanhang, 1, width * height, yuvFile);fwrite(uBuf_huanhang, 1, (width * height) / 4, yuvFile);fwrite(vBuf_huanhang, 1, (width * height) / 4, yuvFile);}2、效果演示
這里因為CSDN只支持上傳GIF圖片所以效果不是很好,且以淡入與垂直滑向兩者結合做演示
六、整體代碼演示
這里將三個轉場功能做了注釋,使用時可以根據要使用的轉場進行取消注釋
#include <stdio.h> #include <stdlib.h> #include <malloc.h> #include "rgb2yuv.h" #include <windows.h>#define u_int8_t unsigned __int8 #define u_int unsigned __int32 #define u_int32_t unsigned __int32 #define FALSE false #define TRUE true/** rgb2yuv* required arg1 should be the input RAW RGB24 file* required arg2 should be the output RAW YUV12 file*/ int main(int argc, char** argv) {BITMAPFILEHEADER File_header;BITMAPINFOHEADER Info_header;BITMAPFILEHEADER File_header_2;BITMAPINFOHEADER Info_header_2;bool flip = FALSE;char* yuvFileName = NULL;FILE* yuvFile = NULL;yuvFileName = argv[5];//argv6是yuv的名字yuvFile = fopen(yuvFileName, "wb");for(int z = 1; z < 4; z++){u_int32_t videoFramesWritten = 0;int i;u_int8_t* yBuf = NULL;u_int8_t* uBuf = NULL;u_int8_t* vBuf = NULL;u_int8_t* rgbBuf = NULL;u_int8_t* rgbBuf_2 = NULL;u_int8_t* yBuf_2 = NULL;u_int8_t* uBuf_2 = NULL;u_int8_t* vBuf_2 = NULL;u_int8_t* yBuf_low = NULL;u_int8_t* yBuf_high = NULL;u_int8_t* yBuf_huanhang = NULL;u_int8_t* uBuf_huanhang = NULL;u_int8_t* vBuf_huanhang = NULL;u_int8_t* yBuf_huanhang_2 = NULL;u_int8_t* uBuf_huanhang_2 = NULL;u_int8_t* vBuf_huanhang_2 = NULL;int width,height;//圖像寬高信息int width_2,height_2;char* bmpFileName = NULL;char* bmpFileName_2 = NULL;FILE* bmp;FILE* bmp_2;bmpFileName = argv[z];bmpFileName_2 = argv[z+1];bmp = fopen(bmpFileName, "rb");bmp_2 = fopen(bmpFileName_2, "rb");if (bmp == NULL){printf("cannot find bmp file\n");exit(1);}else{printf("The output bmp file is %s\n", bmpFileName_2);}if (bmp_2 == NULL){printf("cannot find bmp file\n");exit(1);}else{printf("The output bmp file is %s\n", bmpFileName_2);}if(fread(&File_header,sizeof(BITMAPFILEHEADER),1,bmp)!=1){printf("reader file header error!");exit(0);}fread(&Info_header,sizeof(BITMAPINFOHEADER),1,bmp);width = (int)Info_header.biWidth;//獲取寬度信息height = (int)Info_header.biHeight;//獲取高度信息if(fread(&File_header_2,sizeof(BITMAPFILEHEADER),1,bmp_2)!=1){printf("reader file header error!");exit(0);}fread(&Info_header_2,sizeof(BITMAPINFOHEADER),1,bmp_2);width_2 = (int)Info_header_2.biWidth;height_2 = (int)Info_header_2.biHeight;//開辟空間yBuf = (u_int8_t*)malloc(width * height);uBuf = (u_int8_t*)malloc((width * height) / 4);vBuf = (u_int8_t*)malloc((width * height) / 4);yBuf_2 = (u_int8_t*)malloc(width * height);uBuf_2 = (u_int8_t*)malloc((width * height) / 4);vBuf_2 = (u_int8_t*)malloc((width * height) / 4);uBuf_huanhang = (u_int8_t*)malloc((width * height) / 4);vBuf_huanhang = (u_int8_t*)malloc((width * height) / 4);uBuf_huanhang_2 = (u_int8_t*)malloc((width * height) / 4);vBuf_huanhang_2 = (u_int8_t*)malloc((width * height) / 4);yBuf_low = (u_int8_t*)malloc(width * height);yBuf_high = (u_int8_t*)malloc(width * height);yBuf_huanhang = (u_int8_t*)malloc(width * height);yBuf_huanhang_2 = (u_int8_t*)malloc(width * height);rgbBuf = (u_int8_t*)malloc(width * height * 3);rgbBuf_2 = (u_int8_t*)malloc(width * height * 3);fread(rgbBuf, 1, width * height * 3, bmp);fread(rgbBuf_2, 1, width * height * 3, bmp_2);if(RGB2YUV(width, height, rgbBuf, yBuf, uBuf, vBuf, flip)){printf("error");return 0;}if(RGB2YUV(width_2, height_2, rgbBuf_2, yBuf_2, uBuf_2, vBuf_2, flip)){printf("error");return 0;}for (i = 0; i < width*height; i++){if (yBuf[i] < 16) yBuf[i] = 16;if (yBuf[i] > 235) yBuf[i] = 235;if (yBuf_2[i] < 16) yBuf_2[i] = 16;if (yBuf_2[i] > 235) yBuf_2[i] = 235;}for (i = 0; i < width*height/4; i++){if (uBuf[i] < 16) uBuf[i] = 16;if (uBuf[i] > 240) uBuf[i] = 240;if (vBuf[i] < 16) vBuf[i] = 16;if (vBuf[i] > 240) vBuf[i] = 240;if (uBuf_2[i] < 16) uBuf_2[i] = 16;if (uBuf_2[i] > 240) uBuf_2[i] = 240;if (vBuf_2[i] < 16) vBuf_2[i] = 16;if (vBuf_2[i] > 240) vBuf_2[i] = 240;}變亮//for (i = 0; i < width*height; i++)//變亮//{// yBuf_high[i] = yBuf[i]/2.16;//}//for(int x=0;x<10;x++)//{// for (int j = 0; j < width*height; j++)//變亮// {// yBuf_high[j] = yBuf_high[j]*1.08;// }// for (int m = 0; m < width*height; m++)// {// if (yBuf_high[m] < 16) yBuf_high[m] = 16;// if (yBuf_high[m] > 235) yBuf_high[m] = 235;// }//// fwrite(yBuf_high, 1, width * height, yuvFile);// fwrite(uBuf, 1, (width * height) / 4, yuvFile);// fwrite(vBuf, 1, (width * height) / 4, yuvFile);//}//不變for(i=0;i<30;i++){fwrite(yBuf, 1, width * height, yuvFile);fwrite(uBuf, 1, (width * height) / 4, yuvFile);fwrite(vBuf, 1, (width * height) / 4, yuvFile);}// //換行//for(int b=0;b<width*height;b++)//將第一張圖片y存入緩存// {// yBuf_huanhang[b] = yBuf[b];// // }// for(int b=0;b<width*height/4;b++)//將第一張圖片u、v存入緩存//{// uBuf_huanhang[b] = uBuf[b];// vBuf_huanhang[b] = vBuf[b];// // }// for(int a=30;a>0;a--)//依次將后一張圖片y、u、v存入緩存中// {// for(int b=0;b<width*height/a;b++)// {// yBuf_huanhang[b] = yBuf_2[b];// //// }// for(int b=0;b<width*height/a/4;b++)// {// uBuf_huanhang[b] = uBuf_2[b];// vBuf_huanhang[b] = vBuf_2[b];// }// fwrite(yBuf_huanhang, 1, width * height, yuvFile);// fwrite(uBuf_huanhang, 1, (width * height) / 4, yuvFile);// fwrite(vBuf_huanhang, 1, (width * height) / 4, yuvFile);//}變暗// for (i = 0; i < width*height; i++)// //變暗//{// yBuf_low[i] = yBuf[i];//}//for(int x=0;x<10;x++)//{// for (int j = 0; j < width*height; j++)// //變暗//{// yBuf_low[j] = yBuf_low[j]/1.08;//}// for (int m = 0; m < width*height; m++)//{// if (yBuf_low[m] < 16) yBuf_low[m] = 16;// if (yBuf_low[m] > 235) yBuf_low[m] = 235;//}//fwrite(yBuf_low, 1, width * height, yuvFile);//fwrite(uBuf, 1, (width * height) / 4, yuvFile);//fwrite(vBuf, 1, (width * height) / 4, yuvFile);//}}return(0); }七、歸納總結
通過此次實驗熟悉了兩種圖像格式之間的轉換,以及對轉場的實現,并且熟悉鞏固了C++語言的使用,為后續進一步學習數據壓縮打下了基礎。
總結
以上是生活随笔為你收集整理的使用C++实现多张BMP图片转换为YUV动画----附加淡入淡出转场(逐渐变明变暗),及垂直滑像转场(逐行渐变)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用C++实现YUV格式图像与RGB格式
- 下一篇: u盘可以保存什么格式文件-(u盘可以保存