CSU多媒體技術及應用(實驗)

前言

這學期學完多媒體技術及應用課程後,想把做實驗時遇到的坑與技巧和大家分享(學弟學妹)。畢竟,可能 過幾天我就全忘了,做的實驗代碼也可能會刪了。

 

目錄

  • 實驗1《數字音頻處理程序設計》
  • 實驗2《數字圖像處理程序設計》
  • 實驗3《數字視頻處理程序設計》

 

正文

實驗一 音頻處理

實驗內容:

1.打開兩個音頻文件(限Wav文件,並具有相同的採樣頻率和量化深度),然後用第 二個音頻文件左聲道代替第一個音頻文件左聲道,但保留第一個音頻文件的右聲道,最 後播放第一個音頻文件,並觀察結果。

2.設計實現靜音效果。

關鍵步驟

1. 將文件放入與主cpp文件同目錄下:

添加代碼:

// Global Variables:
HINSTANCE hInst;								// current instance
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// the main window class name

WCHAR *wszSourceFile = NULL;

const WCHAR *wszTargetFile = L"out1.wav";
const WCHAR *wszTargetFile2 = L"out2.wav";

2. 實現靜音與聲道替換的關鍵代碼: 

HRESULT WriteWaveData(
	HANDLE hFile,               // Output file1.
	HANDLE hFile2,               // Output file2.
    IMFSourceReader *pReader,   // Source reader.
	IMFSourceReader *pReaderAnother,   // Another Source reader.
    DWORD cbMaxAudioData,       // Maximum amount of audio data (bytes).
	DWORD cbMaxAudioDataAnother,       // Maximum amount of audio data (bytes).
    DWORD *pcbDataWritten,       // Receives the amount of data written.
	DWORD *pcbDataWrittenAnother       // Receives the amount of data written.
    )
{
    //......此處省略默認生成的代碼
    //Get a pointer to the audio data in the sample.
    hr = pSample->ConvertToContiguousBuffer(&pBuffer);
	if (FAILED(hr)) { break; }
	hr2 = pSampleAnother->ConvertToContiguousBuffer(&pBufferAnother);
	if (FAILED(hr2)) { break; }
	//鎖定內存,得到緩衝地址指針
	hr = pBuffer->Lock(&pAudioData, NULL, &cbBuffer);
	if (FAILED(hr)) { break; }
	hr2 = pBufferAnother->Lock(&pAudioDataAnother, NULL, &cbBufferAnother);
	if (FAILED(hr2)) { break; }
		
    // Make sure not to exceed the specified maximum size.
	if (cbMaxAudioData - cbAudioData < cbBuffer)
    {
        cbBuffer = cbMaxAudioData - cbAudioData;
    }
    if (cbMaxAudioDataAnother - cbAudioDataAnother < cbBufferAnother)
    {
         cbBufferAnother = cbMaxAudioDataAnother - cbAudioDataAnother;
    }
	////////////////////////////////////////////////////////////////////////////////
	//音頻數據處理模塊
	for (int i = 0; i < cbBuffer; i++)
	{
        //靜音
        *(pAudioData + i) = 100;
        //用第二支曲子代替左聲道,右聲道保持原曲不變
        if((i-2)%4 == 0 )     *(pAudioData + i) = (*(pAudioDataAnother + i)); //屏蔽左聲道的第三個字節
        if((i-3)%4 == 0 )     *(pAudioData + i) = (*(pAudioDataAnother + i)); //屏蔽左聲道的第四個字節
	  }
	////////////////////////////////////////////////////////////////////////////////
    // Write data1 and data2 to the output file1 and output file2.
	hr  = WriteToFile(hFile, pAudioData, cbBuffer);
	hr2 = WriteToFile(hFile2, pAudioDataAnother, cbBufferAnother);
	if (FAILED(hr)) { break; }
	if (FAILED(hr2)) { break; }

//......此處省略默認生成的代碼

}

3. 運行程序,生成結果音頻:

 

如果是靜音的話,out1.wav是靜音文件,out2.wav是導入的 第二個文件。

如果是聲道替換的話,out1.wav文件的左聲道爲第二個文件的左 聲道,右聲道不變。Out2.wav則是導入第二個文件本身。

 

實驗二 圖像處理

實驗內容:

1.向內存加載兩個或多個 BMP位圖文件

2.利用像素操作實現單色(R、G、B)、灰度圖像的顯示

3.通過操作像素實現圖像的倒立和正立顯示

4.實現兩個圖像的疊加(一張風景照 一張人物照)

5.改變教材給出的波紋模擬程序中石頭大小(stonesize)、石頭重量(stoneweight)和 顯示幀頻率等參數,觀察模擬效果,並分析所看到現象的原因。

關鍵步驟:

1. 顯示圖片

代碼中的dwBmpSize變量未定義,修改成hDib = GlobalAlloc(GHND, bi.biSizeImage);然後將圖片放入源代碼文件同一文件夾下,並修改代碼中的圖片名稱:

// TODO: 在此處放置代碼。
    if (LoadImage(hInstance, L"image_1.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE) == NULL)
    {
        MessageBox(NULL, L"加載圖像錯誤", L"message", NULL);
    }
    else
    {
        hbmp = (HBITMAP)LoadImage(hInstance, L"image_1.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    }
 GetObject(hbmp, sizeof(BITMAP), &bmp);
    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = bmp.bmWidth;
    bi.biHeight = bmp.bmHeight;
    bi.biPlanes = bmp.bmPlanes;
    bi.biBitCount = bmp.bmBitsPixel;
    bi.biCompression = bmp.bmType;
    bi.biSizeImage = bmp.bmWidth * bmp.bmHeight * bmp.bmBitsPixel / 8;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrImportant = 0;
    hDib = GlobalAlloc(GHND, bi.biSizeImage);
    lpbitmap = (BYTE*)GlobalLock(hDib);

2. 讀取多張圖片:

全局變量定義:

// 全局變量:
HINSTANCE hInst;                                // 當前實例
WCHAR szTitle[MAX_LOADSTRING];                  // 標題欄文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口類名
BITMAPINFOHEADER  bi, bi2;
HBITMAP             hbmp,hbmp2;
BITMAP               bmp,bmp2;
HANDLE              hDib,hDib2;
BYTE* lpbitmap = NULL;
BYTE* lpbitmap2 = NULL;

第二張圖片展示:

  //第二張圖片
    if (LoadImage(hInstance, L"image_2.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE) == NULL)
    {
        MessageBox(NULL, L"加載圖像錯誤", L"message", NULL);
    }
    else
    {
        hbmp2 = (HBITMAP)LoadImage(hInstance, L"image_2.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    }
    GetObject(hbmp2, sizeof(BITMAP), &bmp2);
    bi2.biSize = sizeof(BITMAPINFOHEADER);
    bi2.biWidth = bmp2.bmWidth;
    bi2.biHeight = bmp2.bmHeight;
    bi2.biPlanes = bmp2.bmPlanes;
    bi2.biBitCount = bmp2.bmBitsPixel;
    bi2.biCompression = bmp2.bmType;
    bi2.biSizeImage = bmp2.bmWidth * bmp2.bmHeight * bmp2.bmBitsPixel / 8;
    bi2.biXPelsPerMeter = 0;
    bi2.biYPelsPerMeter = 0;
    bi2.biClrImportant = 0;
    hDib2 = GlobalAlloc(GHND, bi2.biSizeImage);
    lpbitmap2 = (BYTE*)GlobalLock(hDib2);

避免兩張圖片重疊,需要對顯示位置進行處理:

case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此處添加使用 hdc 的任何繪圖代碼...
            GetDIBits(hdc,
                hbmp,
                0,
                (UINT)bmp.bmHeight,
                lpbitmap,
                (BITMAPINFO*)&bi,
                DIB_RGB_COLORS);

            GetDIBits(hdc,
                hbmp2,
                0,
                (UINT)bmp2.bmHeight,
                lpbitmap2,
                (BITMAPINFO*)&bi2,
                DIB_RGB_COLORS);

            SetDIBitsToDevice(hdc,
                20,
                20,
                bi.biWidth,
                bi.biHeight,
                0,
                0,
                0,
                bi.biHeight,
                lpbitmap,
                (BITMAPINFO*)&bi,
                DIB_RGB_COLORS);

            SetDIBitsToDevice(hdc,
                300,
                20,
                bi2.biWidth,
                bi2.biHeight,
                0,
                0,
                0,
                bi2.biHeight,
                lpbitmap2,
                (BITMAPINFO*)&bi2,
                DIB_RGB_COLORS);

3. 單色(R、G、B)、灰度圖像、倒立正立的顯示:

在GetDIBits後面添加對應的代碼:

// RGB單色顯示
for (int i = 0; i < bi.biHeight; i++)
    for (int j = 0; j < bi.biWidth; j++) {
        BYTE r = *(lpbitmap + 2 + j * 4 + (bi.biHeight - i - 1) * bi.biWidth * 4);
        BYTE g = *(lpbitmap + 1 + j * 4 + (bi.biHeight - i - 1) * bi.biWidth * 4);
        BYTE b = *(lpbitmap + 0 + j * 4 + (bi.biHeight - i - 1) * bi.biWidth * 4);
        SetPixel(hdc,j + 100, i + 50, RGB(r, 0, 0));
        SetPixel(hdc,j + 100 + bi.biWidth, i + 50, RGB(0,g,0));
        SetPixel(hdc,j + 100 + bi.biWidth * 2, i + 50, RGB(0, 0, b));
    }

//灰度顯示
for (int i = 0; i < bi.biHeight; i++)
    for (int j = 0; j < bi.biWidth; j++) {
        BYTE r = *(lpbitmap + 2 + j * 4 + (bi.biHeight - i - 1) * bi.biWidth * 4);
        BYTE g = *(lpbitmap + 1 + j * 4 + (bi.biHeight - i - 1) * bi.biWidth * 4);
        BYTE b = *(lpbitmap + 0 + j * 4 + (bi.biHeight - i - 1) * bi.biWidth * 4);
        BYTE average = (r + g + b) / 3;
        BYTE y = r * 0.299 + g * 0.58 + b * 0.114;
        SetPixel(hdc, j + 100, i + 50, RGB(g,g,g ));
        SetPixel(hdc, j + 100 + bi.biWidth + 2, i + 50, RGB(average, average, average));
        SetPixel(hdc, j + 100 + bi.biWidth * 2 + 4, i + 50, RGB(y, y, y));
    }

//倒立
for (int i = 0; i < bi.biHeight; i++)
    for (int j = 0; j < bi.biWidth; j++) {
        BYTE r = *(lpbitmap + 2 + j * 4 + i * bi.biWidth * 4);
        BYTE g = *(lpbitmap + 1 + j * 4 + i * bi.biWidth * 4);
        BYTE b = *(lpbitmap + 0 + j * 4 + i * bi.biWidth * 4);
        SetPixel(hdc, j + 100, i + 50, RGB(r, g, b));
    }

//正立
for (int i = 0; i < bi.biHeight; i++)
    for (int j = 0; j < bi.biWidth; j++) {
        BYTE r = *(lpbitmap + 2 + j * 4 + (bi.biHeight - i - 1) * bi.biWidth * 4);
        BYTE g = *(lpbitmap + 1 + j * 4 + (bi.biHeight - i - 1) * bi.biWidth * 4);
        BYTE b = *(lpbitmap + 0 + j * 4 + (bi.biHeight - i - 1) * bi.biWidth * 4);
        SetPixel(hdc, j + 100, i + 50, RGB(r, g, b));
    }

4. 圖像疊加:

for (int i = 0; i < bi.biHeight; i++)
    for (int j = 0; j < bi.biWidth; j++) {
        BYTE r1 = *(lpbitmap + 2 + j * 4 + (bi.biHeight - i - 1) * bi.biWidth * 4);
        BYTE g1 = *(lpbitmap + 1 + j * 4 + (bi.biHeight - i - 1) * bi.biWidth * 4);
        BYTE b1 = *(lpbitmap + 0 + j * 4 + (bi.biHeight - i - 1) * bi.biWidth * 4);
        //讀取第二幅圖像的RGB分量
        BYTE r2 = *(lpbitmap2 + 2 + j * 4 + (bi2.biHeight - i - 1) * bi2.biWidth * 4);
        BYTE g2 = *(lpbitmap2 + 1 + j * 4 + (bi2.biHeight - i - 1) * bi2.biWidth * 4);
        BYTE b2 = *(lpbitmap2 + 0 + j * 4 + (bi2.biHeight - i - 1) * bi2.biWidth * 4);
        //兩幅圖像的對應分量按比例疊加,a=0.5
        BYTE r = r1 / 2 + r2 / 2;
        BYTE g = g1 / 2 + g2 / 2;
        BYTE b = b1 / 2 + b2 / 2;
        //顯示合成圖像
        SetPixel(hdc, j + 180 + bi.biWidth * 2, i + 20, RGB(r,g,b));
    }

 5. 水波紋:

效果展示:

實驗三 視頻處理

實驗要求:

1. 將RGB視頻圖像轉換成YUV顏色模型的圖像序列,並顯示YUV視頻圖像。

2. YUV視頻文件顯示程序爲基礎,結合圖像融合原理與方法,設計並實現一個給YUV視頻添加動態字幕的程序(類似卡拉OK動態字幕)。

3. 設計實現一個視頻特效程序,能夠將兩個視頻實現淡入淡出。

4. 設計實現一個視頻特效程序,能夠將兩個(或多個)視頻拼接成一個寬幅視頻。

關鍵步驟:

1. 顯示YUV存儲的視頻:

//read a new frame of the yuv file
for (int i = 0; i < 144; i++)
    for (int j = 0; j < 176; j++)
    {
        u[i][j] = *(pBitu + j + 176 * (i));
        v[i][j] = *(pBitv + j + 176 * (i));
    }
//read y,and translate yuv int rgb and display the pixel
for (int i = 0; i < 288; i++)
    for (int j = 0; j < 352; j++)
    {
        //read y
        y[i][j] = *(pBity + j + (i) * 352);
        //translate
        int r = (298 * (y[i][j] - 16) + 409 * (v[i / 2][j / 2] - 128) + 128) >> 8;
        if (r < 0) r = 0;
        if (r > 255) r = 255;
        int g = (298 * (y[i][j] - 16) - 100 * (u[i / 2][j / 2] - 128) - 208 * (v[i / 2][j / 2] - 128) + 128) >> 8;
        if (g < 0) g = 0;
        if (g > 255) g = 255;
        int b = (298 * (y[i][j] - 16) + 516 * (u[i / 2][j / 2] - 128) + 128) >> 8;
        if (b < 0) b = 0;
        if (b > 255) b = 255;

        //直接顯示
        det_image[288 - i - 1][j].r = r;
        det_image[288 - i - 1][j].g = g;
        det_image[288 - i - 1][j].b = b;

 

2. 動態字幕:

使用的圖片爲: 與視頻大小相同

代碼:

//直接顯示
/*
det_image[288 - i - 1][j].r = r;
det_image[288 - i - 1][j].g = g;
det_image[288 - i - 1][j].b = b;*/

//取字幕圖標圖像的像素值
int rback = *(pbits + 2 + j * 3 + (cyDib - i - 1) * cxDib * 3);
int gback = *(pbits + 1 + j * 3 + (cyDib - i - 1) * cxDib * 3);
int bback = *(pbits + 0 + j * 3 + (cyDib - i - 1) * cxDib * 3);
// 如果當前字幕圖標圖像像素值是黑色,就傳送視頻像素值到目標圖像
if (rback == 0 && gback == 0 && bback == 0)
{
det_image[288 - i - 1][j].r = r;
det_image[288 - i - 1][j].g = g;
det_image[288 - i - 1][j].b = b;
}
// 上半部分的字幕處理 
else if (j <= changePosition1 && j > changePosition2 && i < 230) {
det_image[288 - i - 1][j].r = rback;
det_image[288 - i - 1][j].g = gback;
det_image[288 - i - 1][j].b = bback;
}
// 下半部分的字幕顏色改變部分
else if (i >= 230 && j < changeColor) {
det_image[288 - i - 1][j].r = rback;
det_image[288 - i - 1][j].g = gback + 100;
det_image[288 - i - 1][j].b = bback;
}
// 下半部分顏色不改變
else if (i >= 230 ) {
det_image[288 - i - 1][j].r = rback;
det_image[288 - i - 1][j].g = gback;
det_image[288 - i - 1][j].b = bback;
}
else//否則,就用原視頻
{
det_image[288 - i - 1][j].r = r;
det_image[288 - i - 1][j].g = g;
det_image[288 - i - 1][j].b = b;
}

 3. 兩個視頻實現淡入淡出:

//兩幀圖像的當前像素的融合,結果放入目標圖像矩陣中
det_image[288 - i - 1][j].r = r * (1 - para) + r2 * para;
det_image[288 - i - 1][j].g = g * (1 - para) + g2 * para;
det_image[288 - i - 1][j].b = b * (1 - para) + b2 * para;

4. 寬幅視頻:

//寬幅視頻
det_image[288 - i - 1][j].r = r;
det_image[288 - i - 1][j].g = g;
det_image[288 - i - 1][j].b = b;
det_image2[288 - i - 1 ][j ].r = r2;
det_image2[288 - i - 1 ][j ].g = g2;
det_image2[288 - i - 1 ][j ].b = b2;

實驗效果:

結語

文章很多細節沒講到的是老師已經給出的代碼部分,如果實在遇到了問題的,別問我,我早就忘了。但是你可以來找我,我可以把整個項目文件發給你~qq975344363

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