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