目錄
- 一 實驗目標
- 二 實驗原理
- 三 代碼實作
- 四 實驗結果
- 五 總結
一 實驗目標
-
在影像處理軟體中自行生成多個BMP檔案,至少含5個不同的場景畫面,要求帶含有班級、學號后四位和本人姓名(縮寫或昵稱均可)的logo,(基本要求為24bit的BMP,進階要求為支持小于24bit的BMP,)
-
撰寫將第一步所生成的多個BMP檔案轉化為YUV檔案,要求可在命令列中設定每個畫面出現的幀數,最后形成的YUV檔案應至少包含200幀,重點掌握函式定義、緩沖區分配、倒序讀寫、結構體的操作,
-
對整個程式進行除錯,并將生成的YUV檔案用播放軟體觀看,驗證是否正確,
二 實驗原理
- 檔案格式
BMP(全稱 Bitmap)是 Windows 作業系統中的標準影像檔案格式,可以分成兩類:設備相關位圖(DDB)和設備無關位圖(DIB),使用廣泛,它采用位映射存盤格式,除了影像深度可選以外,在絕大多數應用中不采用其他任何壓縮,因此,BMP 檔案所占用的空間很大,BMP 檔案的影像深度可選 lbit、4bit、8bit、16bit 及 24bit,BMP 檔案存盤資料時,影像的掃描方式是按從左到右、從下到上的順序,由于 BMP 檔案格式是 Windows 環境中交換與圖有關的資料的一種標準,因此在 Windows 環境中運行的圖形影像軟體都支持 BMP 影像格式,
- c語言代碼
- 位圖頭檔案資料結構
typedef struct tagBITMAPFILEHEADER
{
WORD bfType; /* 說明檔案的型別 */
DWORD bfSize; /* 說明檔案的大小,用位元組為單位 注意位元組序*/
WORD bfReserved1; /* 保留,設定為0 */
WORD bfReserved2; /* 保留,設定為0 */
DWORD bfOffBits; /* 說明從BITMAPFILEHEADER結構開始到實際的影像資料之間的位元組偏移量 */
} BITMAPFILEHEADER;
- 位圖資訊資料結構
typedef struct tagBITMAPINFOHEADER
{
DWORD biSize; /* 說明結構體所需位元組數 */
LONG biWidth; /* 以像素為單位說明影像的寬度 */
LONG biHeight; /* 以像素為單位說明影像的高度 */
WORD biPlanes; /* 說明位面數,必須為1 */
WORD biBitCount; /* 說明位數/像素,1、2、4、8、24 */
DWORD biCompression; /* 說明影像是否壓縮及壓縮型別BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS */
DWORD biSizeImage; /* 以位元組為單位說明影像大小,必須是4的整數倍*/
LONG biXPelsPerMeter; /*目標設備的水平解析度,像素/米 */
LONG biYPelsPerMeter; /*目標設備的垂直解析度,像素/米 */
DWORD biClrUsed; /* 說明影像實際用到的顏色數,如果為0,則顏色數為2的biBitCount次方 */
DWORD biClrImportant; /*說明對影像顯示有重要影響的顏色索引的數目,如果是0,表示都重要,*/
} BITMAPINFOHEADER;
- 調色盤
調色板實際上是一個陣列,它所包含的元素與位圖 所具有的顏色數相同,決定于biClrUsed和biBitCount欄位,陣列中每個元素的型別是一個RGBQUAD結構,真彩色無調色板部分,
typedef struct tagRGBQUAD
{
BYTE rgbBlue; /*指定藍色分量*/
BYTE rgbGreen; /*指定綠色分量*/
BYTE rgbRed; /*指定紅色分量*/
BYTE rgbReserved; /*保留,指定為0*/
} RGBQUAD;
- 影像資料位元組陣列
即位圖資料,緊跟在調色板之后的是影像資料位元組陣列,對于用到調色板的位圖,圖象資料就是該象素顏色在調色板中的索引值 (邏輯色),對于真彩色圖,圖象資料就是實際的R、G、B 值,影像的每一掃描行由表示影像像素的連續的位元組組成,每一行的位元組數取決于影像的顏色數目和用像素表示的影像寬度,規定每一掃描行的位元組數必須是 4 的整倍數,也就是DWORD 對齊的,掃描行是由底向上存盤的,這就是說,陣列中的第一個位元組表示位圖左下角的像素,而最后一個位元組表示位圖右上角的像素,
三 代碼實作
- 主函式
主函式實作以下程序:
- 讀引數中的bmp檔案,獲取寬高,色彩資訊
- rgb轉yuv
- 實驗要求最少200幀,即每張圖片(實驗共5張圖片)重復寫入40次
代碼如下:
#include <winGDI.h>
#include <stdio.h>
#include <iostream>
#include<windows.h>
BITMAPFILEHEADER FILE_header;
BITMAPFILEHEADER Info_header; // read file &info header
using namespace std;
static float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256];
static float RGBYUV01684[256], RGBYUV03316[256];
static float RGBYUV04187[256], RGBYUV00813[256];
int main(int argc, char* argv[])
{
BITMAPFILEHEADER FileHeader;
BITMAPINFOHEADER InfoHeader;
int width,height,picNum;
char* bmpFileName[5] = { 0 };
char* yuvFileName = NULL;
int videoFramesWritten = 0;
FILE* bmpFile = NULL, * yuvFile = NULL;
unsigned char* bmpBuf = NULL, * yBuf = NULL, * uBuf = NULL, * vBuf = NULL;
picNum = atoi(argv[1]);
//命令引數到變數
for (int t = 0; t < picNum; t++) {
bmpFileName[t] = argv[t + 2];
}
yuvFileName = argv[7];
//打開(新建)yuv檔案
errno_t err;
if ((err = fopen_s(&yuvFile, yuvFileName, "wb")) != 0) {
cout << "FAIL TO OPEN!";
exit(1);
}
//打開bmp檔案
for (int i = 0; i < 5; i++) {
if ((err = fopen_s(&bmpFile, bmpFileName[i], "rb")) != 0) {
cout << "FAIL TO OPEN BMP FILE!";
exit(1);
}
//讀取當前影像fileheader和infoheader
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
//file header
if (fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, bmpFile) != 1) {
cout << "read file header error!";
exit(0);
}
if (fileHeader.bfType != 0x4D42){
cout<<"Not bmp file!";
exit(0);
}
//info header
if (fread(&infoHeader, sizeof(BITMAPINFOHEADER), 1, bmpFile) != 1){
cout<<"read info header error!";
exit(0);
}
//處理寬高
//寬(按位元組)必須是4的的整數倍
if (infoHeader.biWidth * infoHeader.biBitCount % 32 == 0)
//infoHeader.biWidth 以像素為單位說明影像的寬度
//infoHeader.biBitCount 說明位數/像素,1、2、4、8、24
//一行總的資料量(按位來說),是寬度*位深,而規定則是每一掃描行的位元組數必須是 4 的整倍數(32位的整數倍)
width = infoHeader.biWidth;
else
width = (infoHeader.biWidth * infoHeader.biBitCount + 31) / 32 * (32 / infoHeader.biBitCount);
//(x/32)得整數,再加上32就是比x大的最近的32的倍數,除以8就是像素的位元組數
//高(按位元組)必須是2的整數倍
if (infoHeader.biHeight % 2 == 0)
height = infoHeader.biHeight;
else
height = infoHeader.biHeight + 1;
//開辟所需變數快取
bmpBuf = (unsigned char*)malloc(width * height * 3);//24 單位像素三位元組
yBuf = (unsigned char*)malloc(width * height);
uBuf = (unsigned char*)malloc((width * height) / 4);
vBuf = (unsigned char*)malloc((width * height) / 4);
if (bmpBuf == NULL || yBuf == NULL || uBuf == NULL || vBuf == NULL){
cout<<"no enought memory\n";
exit(1);
}
// fseek(bmpFile, fileHeader.bfOffBits, 0);
//讀取影像資料
fread(bmpBuf, sizeof(unsigned char), width * height * 3, bmpFile);
RGB2YUV(width, height, bmpBuf, yBuf, uBuf, vBuf, 0);
//防溢位及保護電平
for (int i = 0; i < width * height; i++) {
if (yBuf[i] < 16)yBuf[i] = 16;
if (yBuf[i] > 235)yBuf[i] = 235;
}
for (int 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;
}
//至少200幀,則5張圖片每張圖片重復存盤40次
for (int i = 0; i < 40; i++) {
fwrite(yBuf, 1, width * height, yuvFile);
fwrite(uBuf, 1, (width * height) / 4, yuvFile);
fwrite(vBuf, 1, (width * height) / 4, yuvFile);
++videoFramesWritten;
}
// printf("\r...%d", videoFramesWritten);
free(bmpBuf);
free(yBuf);
free(uBuf);
free(vBuf);
}
printf("\n%u %ux%u video frames written\n", videoFramesWritten, width, height);
/* cleanup */
fclose(bmpFile);
fclose(yuvFile);
return(0);
}
- 頭檔案
int RGB2YUV (int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip);
void InitLookupTable();
其中,rgb2yuv的代碼可見先前博客內容,
四 實驗結果
- 首先,五張圖片如下:

- 生成檔案為:

- 播放設定:

- 結果

五 總結
- 對于c語言掌握的還不是非常好,并沒有能力做出轉場特效,
- yuv視頻冗余非常大,占記憶體空間高,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/271907.html
標籤:其他
