CSU多媒体技术及应用(实验)

前言

这学期学完多媒体技术及应用课程后,想把做实验时遇到的坑与技巧和大家分享(学弟学妹)。毕竟,可能 过几天我就全忘了,做的实验代码也可能会删了。

 

目录

  • 实验1《数字音频处理程序设计》
  • 实验2《数字图像处理程序设计》
  • 实验3《数字视频处理程序设计》

 

正文

实验一 音频处理

实验内容:

1.打开两个音频文件(限Wav文件,并具有相同的采样频率和量化深度),然后用第 二个音频文件左声道代替第一个音频文件左声道,但保留第一个音频文件的右声道,最 后播放第一个音频文件,并观察结果。

2.设计实现静音效果。

关键步骤

1. 将文件放入与主cpp文件同目录下:

CSU多媒体技术及应用(实验)_第1张图片

添加代码:

// 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. 水波纹:

效果展示:

CSU多媒体技术及应用(实验)_第2张图片

CSU多媒体技术及应用(实验)_第3张图片

CSU多媒体技术及应用(实验)_第4张图片

CSU多媒体技术及应用(实验)_第5张图片

CSU多媒体技术及应用(实验)_第6张图片

实验三 视频处理

实验要求:

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. 动态字幕:

使用的图片为: 与视频大小相同

CSU多媒体技术及应用(实验)_第7张图片

代码:

//直接显示
/*
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;

实验效果:

CSU多媒体技术及应用(实验)_第8张图片

CSU多媒体技术及应用(实验)_第9张图片

CSU多媒体技术及应用(实验)_第10张图片

CSU多媒体技术及应用(实验)_第11张图片

结语

文章很多细节没讲到的是老师已经给出的代码部分,如果实在遇到了问题的,别问我,我早就忘了。但是你可以来找我,我可以把整个项目文件发给你~qq975344363

你可能感兴趣的:(实验报告)