多媒体技术及应用实验报告
班级:
姓名:学号:电信1301秦行U201313480
实验一:Huffman编码
一.实验内容
1、了解BMP图像的格式,实现BMP图片格式的数据域及文件头的分离
2、熟悉Huffman编码原理
3、用C语言使用Huffman编码算法对给定图像文件进行编解码
二.实验原理
(1)Huffman编码:
Huffman编码是一种基于图像统计特征的变长编码方法:概率小的符号用较长的码字表示,概率大的符号用较短的码字表示。
Huffman编码的步骤:
1.根据待编码的符号串,统计各个符号的概率;
2.根据符号的概率统计特征,构建霍夫曼编码表,即计算每个符号的编码结果;
3.
用得到的编码表对符号序列进行编码。
哈夫曼编码实现步骤
(2)位图BMP
文件格式
对应在代码中的实现:
文件头:
TypedefstructtagBITMAPFILEHEADER{WORDbfType;//必须是0x424D,”BM”DWORDbfSize;//文件大小,包括结构本身WORDbfReserved1;WORDbfReserved2;DWORDbfOffBits;//实际图像数据便宜量}BITMAPFILEHEADER;
信息头:
typedefstructtagBITMAPINFOHEADER{DWORDbiSize;//结构本身的大小-40LONGbiWidth;//图像宽度LONGbiHeight;//图像高度WORDbiPlanes;//显示设备的平面数目-1WORDbiBitCount//描述每个象素颜色需要的位数DWORDbiCompression;//是否压缩DWORDbiSizeImage;//实际的位图数据占用的字节数LONGbiXPelsPerMeter;//显示设备的分辨率LONGbiYPelsPerMeter;DWORDbiClrUsed;//指定本图象实际用到的颜色数,如果该值为零,则用到的颜色数为2的biBitCount次方。
DWORDbiClrImportant;//重要颜色数目,如果为零,则都是重要的。}BITMAPINFOHEADER;
调色板:
typedefstructtagRGBQUAD{BYTErgbBlue;//蓝色分量BYTErgbGreen;//绿色分量BYTErgbRed;//红色分量BYTErgbReserved;//保留值}RGBQUAD;
三.实验结果:
(1)如图为编码生成的哈夫曼文件“lena1_Huffman.bhd”,生成树文件“lena1_Huffman.bpt”,以及一个文本文件“lena1_Huffman.txt”里面有有每个灰度值及其对应频率值还有对应的哈夫曼码。
原图为lena1的BMP图像文件,而经过编码和还原后生成文件名为“lena1_Huffman_Decode”的BMP图像文件,由于哈夫曼编码是无损编码所以
两个文件除文件名外完全相同。
四.实现的部分代码
(1)huffman树数据结构
由于huffman树为二叉树,定义数据结构为:
structSNode{
longintfreq;//频率
intdepth;//深度,编码长度
chargray;
structSNode*pPar;//父结点
structSNode*pLeft;//左结点
structSNode*pRight;//右结点
char*code;//字节编码
};
(2)灰度概率统计与排序
读取文件流时,跳过文件头的54个字节,直接读取数据域,从头到尾遍历统计,将统计结果存入freq中,通过链表的插入排序算法形成有序链表:
voidaddtolist(structSNode*pNode){if(head==NULL)//队列为空,{head=pNode;//直接加入头部}else{if(pNode->freqfreq)//当新加入的结点的频数小于head结点的频数时,将新结点放到队首
{pNode->pPar=head;head=pNode;}else{structSNode*p=head;while((p->pPar!=0)&&(p->pPar->freqfreq)){p=p->pPar;}pNode->pPar=p->pPar;p->pPar=pNode;}}}
(3)Huffman树生成与编码
已知的有序单链表生成huffman树:
voidcreatecodetree(structSNode*ptree,intlen){intoffset,i=0;
chargray;
structSNode*pPar;//父结点
structSNode*pLeft;//左结点
structSNode*pRight;//右结点
char*code;//字节编码
};
(2)灰度概率统计与排序
读取文件流时,跳过文件头的54个字节,直接读取数据域,从头到尾遍历统计,将统计结果存入freq中,通过链表的插入排序算法形成有序链表:
voidaddtolist(structSNode*pNode){if(head==NULL)//队列为空,{head=pNode;//直接加入头部}else{if(pNode->freqfreq)//当新加入的结点的频数小于head结点的频数时,将新结点放到队首
{pNode->pPar=head;head=pNode;}else{structSNode*p=head;while((p->pPar!=0)&&(p->pPar->freqfreq)){p=p->pPar;}pNode->pPar=p->pPar;p->pPar=pNode;}}}
(3)Huffman树生成与编码
已知的有序单链表生成huffman树:
voidcreatecodetree(structSNode*ptree,intlen){intoffset,i=0;
while(head!=NULL&&head->pPar!=NULL){//取出前两个频数最小结点structSNode*p1=head;structSNode*p2=head->pPar;head=p2->pPar;
offset=len+i;ptree[offset].freq=p1->freq+p2->freq;//频率为两者之和ptree[offset].pLeft=p1;ptree[offset].pRight=p2;p1->pPar=&ptree[offset];p2->pPar=&ptree[offset];AddChildLen(&ptree[offset]);//调整树中每个结点的深度addtolist(&ptree[offset]);//将新产生的结点,放到临时队列中i++;}
}//构建树完成,生成编码for(i=0;i0){createcode(&nodes[i]);}}
(4)解码
解码过程为编码的逆过程。编码是从叶子节点自下而上编码(即先生成最末位向前编码),而解码是从根节点自上而下,直到节点为叶子节点截止:
voiddecode(){FILE*hufin,*hufout,*imgin;structSNode*p;inti=0,n=0,num=0;charb;chardebuf[507132];//507132是当前目录huffma.txt文件中所有字符的个数,也就是'0'和'1'的个数。
if((imgin=fopen("lena.bmp","rb"))==NULL){printf("fileopenerror\n");exit(1);}
if((hufout=fopen("decode.bmp","wb+"))==NULL){//打开存储解码结果的文件
printf("fileopenerror");exit(1);}
if((hufin=fopen("huffman.txt","rt+"))==NULL){//打开存储编码结果的文件
printf("huffman.txtopenerror\n");exit(1);}fread(debuf,sizeof(char),54,imgin);//读出原始图像的文件头,图像信息放到b中,因为文件头没有进行编码所以无需解码直接拷贝
fwrite(debuf,sizeof(char),54,hufout);//将文件头,图像信息放到解码后的文件中
//n=len+1;fgets(debuf,507132,hufin);//从文件中读出所有的编码序列,只读n-1个第,并自动使b[n]为‘\0’
p=head;//每次解码都要从树的根节点开始for(i=0;debuf[i]!='\0';i++){//开始解码switch(debuf[i]){case'0':{if(p->pLeft!=NULL){//如果编码为0并且左孩子不为空,那么p指向p的左孩子。
p=p->pLeft;break;}else{//如果左孩子为空,则此结点为叶子
结点,取出其灰度值作为解码结果
b=p->gray;fwrite(&b,sizeof(char),1,hufout);
p=head;i=i-1;//此时i指向下一个码元的开始位置,但是for
循环还有一个i++所以此时需要将i减一
break;}
}
if(p->pRight!=NULL){//如果编码为1并且右孩子孩子不
为空,那么p指向p的右孩子。
p=p->pRight;case'1':{
}break;
else{//如果右孩子孩子为空,则此结点为叶子结点,取出
其灰度值作为解码结果
b=p->gray;fwrite(&b,sizeof(char),1,hufout);p=head;i=i-1;break;}}default:break;}}b=p->gray;//上面的switch语句未能统计左后一个解码结果,所以在此统
计一下
fwrite(&b,sizeof(char),1,hufout);}
五.实验小结
本次试验对我来说难度比较大,因为我的编程能力比较弱。哈夫曼编码的理虽然理解了,但用代码来实现对我来说还是比较吃力。于是我询问了同学,他们帮我把工程建立起来,并且把程序的框架架构起来,然后我自己在他们的帮助下完善了整个实验的各个部分的代码,最后终于完成了工程,进行调试并成功编码并解码成功,完成验收,可以说是收获颇多。
基于深度的图像修
一.实验内容
1、单幅图像的修补
2、结合彩色图像和深度图像的图像修补
二.实验原理
图像修复技术:
应用图像修复技术可以保证图像信息的完整性,根据空白区域周围的信息完成对空白区域的填充,以实现图像的恢复。
图像修复技术的用途:
艺术品修复、文物保护、影视特技制作、多余目标物体剔除、图像缩放、图像的有损压缩、视频通信的错误隐匿。
常用的两种修复方式如下:
利用深度图的图像修补
1图像的前景与背景
实际场景中存在前景与背景的区别,前景会遮挡背景,而且前景与背景往往差距比较大。
2深度图
用于表示3D空间中的点与成像平面距离的灰度图。0~255表示,灰度值越大,表示场景距离成像平面越近,反之,灰度值越小,表示场景距离成像平面越远。
前景的灰度值大,背景的灰度值小。
如下左彩色图,右深度图
(彩色图)
3(深度图)普通的图像修补区分不了图像的前景和背景,简单的加权求和填补空白点的方法会导致前景和背景的混杂。引入深度图之后,可以利用深度图区分图像的前景和背景,在对背景进行修补的时候,可以利用深度图滤除那些前景参考点的影响,从而使背景的空白点
公式如下:
三.实现过程
基本思想:
1.读入一个像素点,判断其是否为空白点;
2.若不是空白点,则跳过该点,判断下一个点;
3.若该点是空白点,则选取以该店为中心的41*41大小的区域为参考窗口,由上述的基于深度图的修补公式进行修补;
4.重复上述步骤,对图像中的每一个点都进行如此处理,直至全图处理完毕,则图像修补完成;
具体实现代码(matlab):
clearallI=imread('color.bmp');B=imread('depth.bmp');B=rgb2gray(B);imshow(I),title('修复之前的图')[m,n,hh]=size(I);A=rgb2gray(I);%彩色图转换灰度图I1=I;[rowind,columnind]=find(A
pointnum=length(rowind);%受污染像素的个数
行
rowstart(rowstartm)=m;%起始行不能小于1,终止行不能大于m
columnstart=columnind-20;columnend=columnind+20;%统计模板的起,止列污染点)为中心41*41模板
fornum=1:pointnum%对每个受污染的像素进行修复x=rowind(num);y=columnind(num);basedepth=B(x,y);%深度图基准点灰度值xstart=rowstart(num);xend=rowend(num);x1=x-xstart+1;%得到基点的相对坐标ystart=columnstart(num);yend=columnend(num);y1=y-ystart+1;%得到基点的相对坐标depthpos=A(xstart:xend,ystart:yend)>3&abs(B(xstart:xend,ystart:yend)-basedepth)
TT=1./W;columnstart(columnstartn)=n;%以基准点(受rowstart=rowind-20;rowend=rowind+20;%统计每个受污染像素修复模板的起,止
Aloc=I(xstart:xend,ystart:yend,1);%对红色通道进行处理Aloc=double(Aloc(depthpos));R=Aloc.*TT;R=sum(R);I1(x,y,1)=R/T;T=sum(TT);
Aloc=I(xstart:xend,ystart:yend,2);%对绿色通道进行处理Aloc=double(Aloc(depthpos));R=Aloc.*TT;R=sum(R);I1(x,y,2)=R/T;
Aloc=I(xstart:xend,ystart:yend,3);%对蓝色通道进行处理Aloc=double(Aloc(depthpos));R=Aloc.*TT;R=sum(R);I1(x,y,3)=R/T;endfigure,imshow(I1),title('基于深度图的图像修复')
(单幅图像的修补类似,去除红色部分代码即可,即第23行的“&abs(B(xstart:xend,ystart:yend)-basedepth)
四.实验结果:
原有损图及两种方式修复的图像
有损图像
基于深度图的修复图像
.
单幅图像修复图像
可以看到原有损图像的黑色损伤部分均已被修复和还原,而且可以看到基于深度图的修补方法在图中人物的边缘处处理得更好,边缘处更加清晰,边界更加明显。
五.实验感受
这个实验相比哈夫曼编码实验对我来说难度小了许多,因为matlab有很多可以直接使用的函数,而且这次实验原理也不算太难,所以成功地完成了两种图像修复。让我学习到了很多,收获了很多。
多媒体技术及应用实验报告
班级:
姓名:学号:电信1301秦行U201313480
实验一:Huffman编码
一.实验内容
1、了解BMP图像的格式,实现BMP图片格式的数据域及文件头的分离
2、熟悉Huffman编码原理
3、用C语言使用Huffman编码算法对给定图像文件进行编解码
二.实验原理
(1)Huffman编码:
Huffman编码是一种基于图像统计特征的变长编码方法:概率小的符号用较长的码字表示,概率大的符号用较短的码字表示。
Huffman编码的步骤:
1.根据待编码的符号串,统计各个符号的概率;
2.根据符号的概率统计特征,构建霍夫曼编码表,即计算每个符号的编码结果;
3.
用得到的编码表对符号序列进行编码。
哈夫曼编码实现步骤
(2)位图BMP
文件格式
对应在代码中的实现:
文件头:
TypedefstructtagBITMAPFILEHEADER{WORDbfType;//必须是0x424D,”BM”DWORDbfSize;//文件大小,包括结构本身WORDbfReserved1;WORDbfReserved2;DWORDbfOffBits;//实际图像数据便宜量}BITMAPFILEHEADER;
信息头:
typedefstructtagBITMAPINFOHEADER{DWORDbiSize;//结构本身的大小-40LONGbiWidth;//图像宽度LONGbiHeight;//图像高度WORDbiPlanes;//显示设备的平面数目-1WORDbiBitCount//描述每个象素颜色需要的位数DWORDbiCompression;//是否压缩DWORDbiSizeImage;//实际的位图数据占用的字节数LONGbiXPelsPerMeter;//显示设备的分辨率LONGbiYPelsPerMeter;DWORDbiClrUsed;//指定本图象实际用到的颜色数,如果该值为零,则用到的颜色数为2的biBitCount次方。
DWORDbiClrImportant;//重要颜色数目,如果为零,则都是重要的。}BITMAPINFOHEADER;
调色板:
typedefstructtagRGBQUAD{BYTErgbBlue;//蓝色分量BYTErgbGreen;//绿色分量BYTErgbRed;//红色分量BYTErgbReserved;//保留值}RGBQUAD;
三.实验结果:
(1)如图为编码生成的哈夫曼文件“lena1_Huffman.bhd”,生成树文件“lena1_Huffman.bpt”,以及一个文本文件“lena1_Huffman.txt”里面有有每个灰度值及其对应频率值还有对应的哈夫曼码。
原图为lena1的BMP图像文件,而经过编码和还原后生成文件名为“lena1_Huffman_Decode”的BMP图像文件,由于哈夫曼编码是无损编码所以
两个文件除文件名外完全相同。
四.实现的部分代码
(1)huffman树数据结构
由于huffman树为二叉树,定义数据结构为:
structSNode{
longintfreq;//频率
intdepth;//深度,编码长度
chargray;
structSNode*pPar;//父结点
structSNode*pLeft;//左结点
structSNode*pRight;//右结点
char*code;//字节编码
};
(2)灰度概率统计与排序
读取文件流时,跳过文件头的54个字节,直接读取数据域,从头到尾遍历统计,将统计结果存入freq中,通过链表的插入排序算法形成有序链表:
voidaddtolist(structSNode*pNode){if(head==NULL)//队列为空,{head=pNode;//直接加入头部}else{if(pNode->freqfreq)//当新加入的结点的频数小于head结点的频数时,将新结点放到队首
{pNode->pPar=head;head=pNode;}else{structSNode*p=head;while((p->pPar!=0)&&(p->pPar->freqfreq)){p=p->pPar;}pNode->pPar=p->pPar;p->pPar=pNode;}}}
(3)Huffman树生成与编码
已知的有序单链表生成huffman树:
voidcreatecodetree(structSNode*ptree,intlen){intoffset,i=0;
chargray;
structSNode*pPar;//父结点
structSNode*pLeft;//左结点
structSNode*pRight;//右结点
char*code;//字节编码
};
(2)灰度概率统计与排序
读取文件流时,跳过文件头的54个字节,直接读取数据域,从头到尾遍历统计,将统计结果存入freq中,通过链表的插入排序算法形成有序链表:
voidaddtolist(structSNode*pNode){if(head==NULL)//队列为空,{head=pNode;//直接加入头部}else{if(pNode->freqfreq)//当新加入的结点的频数小于head结点的频数时,将新结点放到队首
{pNode->pPar=head;head=pNode;}else{structSNode*p=head;while((p->pPar!=0)&&(p->pPar->freqfreq)){p=p->pPar;}pNode->pPar=p->pPar;p->pPar=pNode;}}}
(3)Huffman树生成与编码
已知的有序单链表生成huffman树:
voidcreatecodetree(structSNode*ptree,intlen){intoffset,i=0;
while(head!=NULL&&head->pPar!=NULL){//取出前两个频数最小结点structSNode*p1=head;structSNode*p2=head->pPar;head=p2->pPar;
offset=len+i;ptree[offset].freq=p1->freq+p2->freq;//频率为两者之和ptree[offset].pLeft=p1;ptree[offset].pRight=p2;p1->pPar=&ptree[offset];p2->pPar=&ptree[offset];AddChildLen(&ptree[offset]);//调整树中每个结点的深度addtolist(&ptree[offset]);//将新产生的结点,放到临时队列中i++;}
}//构建树完成,生成编码for(i=0;i0){createcode(&nodes[i]);}}
(4)解码
解码过程为编码的逆过程。编码是从叶子节点自下而上编码(即先生成最末位向前编码),而解码是从根节点自上而下,直到节点为叶子节点截止:
voiddecode(){FILE*hufin,*hufout,*imgin;structSNode*p;inti=0,n=0,num=0;charb;chardebuf[507132];//507132是当前目录huffma.txt文件中所有字符的个数,也就是'0'和'1'的个数。
if((imgin=fopen("lena.bmp","rb"))==NULL){printf("fileopenerror\n");exit(1);}
if((hufout=fopen("decode.bmp","wb+"))==NULL){//打开存储解码结果的文件
printf("fileopenerror");exit(1);}
if((hufin=fopen("huffman.txt","rt+"))==NULL){//打开存储编码结果的文件
printf("huffman.txtopenerror\n");exit(1);}fread(debuf,sizeof(char),54,imgin);//读出原始图像的文件头,图像信息放到b中,因为文件头没有进行编码所以无需解码直接拷贝
fwrite(debuf,sizeof(char),54,hufout);//将文件头,图像信息放到解码后的文件中
//n=len+1;fgets(debuf,507132,hufin);//从文件中读出所有的编码序列,只读n-1个第,并自动使b[n]为‘\0’
p=head;//每次解码都要从树的根节点开始for(i=0;debuf[i]!='\0';i++){//开始解码switch(debuf[i]){case'0':{if(p->pLeft!=NULL){//如果编码为0并且左孩子不为空,那么p指向p的左孩子。
p=p->pLeft;break;}else{//如果左孩子为空,则此结点为叶子
结点,取出其灰度值作为解码结果
b=p->gray;fwrite(&b,sizeof(char),1,hufout);
p=head;i=i-1;//此时i指向下一个码元的开始位置,但是for
循环还有一个i++所以此时需要将i减一
break;}
}
if(p->pRight!=NULL){//如果编码为1并且右孩子孩子不
为空,那么p指向p的右孩子。
p=p->pRight;case'1':{
}break;
else{//如果右孩子孩子为空,则此结点为叶子结点,取出
其灰度值作为解码结果
b=p->gray;fwrite(&b,sizeof(char),1,hufout);p=head;i=i-1;break;}}default:break;}}b=p->gray;//上面的switch语句未能统计左后一个解码结果,所以在此统
计一下
fwrite(&b,sizeof(char),1,hufout);}
五.实验小结
本次试验对我来说难度比较大,因为我的编程能力比较弱。哈夫曼编码的理虽然理解了,但用代码来实现对我来说还是比较吃力。于是我询问了同学,他们帮我把工程建立起来,并且把程序的框架架构起来,然后我自己在他们的帮助下完善了整个实验的各个部分的代码,最后终于完成了工程,进行调试并成功编码并解码成功,完成验收,可以说是收获颇多。
基于深度的图像修
一.实验内容
1、单幅图像的修补
2、结合彩色图像和深度图像的图像修补
二.实验原理
图像修复技术:
应用图像修复技术可以保证图像信息的完整性,根据空白区域周围的信息完成对空白区域的填充,以实现图像的恢复。
图像修复技术的用途:
艺术品修复、文物保护、影视特技制作、多余目标物体剔除、图像缩放、图像的有损压缩、视频通信的错误隐匿。
常用的两种修复方式如下:
利用深度图的图像修补
1图像的前景与背景
实际场景中存在前景与背景的区别,前景会遮挡背景,而且前景与背景往往差距比较大。
2深度图
用于表示3D空间中的点与成像平面距离的灰度图。0~255表示,灰度值越大,表示场景距离成像平面越近,反之,灰度值越小,表示场景距离成像平面越远。
前景的灰度值大,背景的灰度值小。
如下左彩色图,右深度图
(彩色图)
3(深度图)普通的图像修补区分不了图像的前景和背景,简单的加权求和填补空白点的方法会导致前景和背景的混杂。引入深度图之后,可以利用深度图区分图像的前景和背景,在对背景进行修补的时候,可以利用深度图滤除那些前景参考点的影响,从而使背景的空白点
公式如下:
三.实现过程
基本思想:
1.读入一个像素点,判断其是否为空白点;
2.若不是空白点,则跳过该点,判断下一个点;
3.若该点是空白点,则选取以该店为中心的41*41大小的区域为参考窗口,由上述的基于深度图的修补公式进行修补;
4.重复上述步骤,对图像中的每一个点都进行如此处理,直至全图处理完毕,则图像修补完成;
具体实现代码(matlab):
clearallI=imread('color.bmp');B=imread('depth.bmp');B=rgb2gray(B);imshow(I),title('修复之前的图')[m,n,hh]=size(I);A=rgb2gray(I);%彩色图转换灰度图I1=I;[rowind,columnind]=find(A
pointnum=length(rowind);%受污染像素的个数
行
rowstart(rowstartm)=m;%起始行不能小于1,终止行不能大于m
columnstart=columnind-20;columnend=columnind+20;%统计模板的起,止列污染点)为中心41*41模板
fornum=1:pointnum%对每个受污染的像素进行修复x=rowind(num);y=columnind(num);basedepth=B(x,y);%深度图基准点灰度值xstart=rowstart(num);xend=rowend(num);x1=x-xstart+1;%得到基点的相对坐标ystart=columnstart(num);yend=columnend(num);y1=y-ystart+1;%得到基点的相对坐标depthpos=A(xstart:xend,ystart:yend)>3&abs(B(xstart:xend,ystart:yend)-basedepth)
TT=1./W;columnstart(columnstartn)=n;%以基准点(受rowstart=rowind-20;rowend=rowind+20;%统计每个受污染像素修复模板的起,止
Aloc=I(xstart:xend,ystart:yend,1);%对红色通道进行处理Aloc=double(Aloc(depthpos));R=Aloc.*TT;R=sum(R);I1(x,y,1)=R/T;T=sum(TT);
Aloc=I(xstart:xend,ystart:yend,2);%对绿色通道进行处理Aloc=double(Aloc(depthpos));R=Aloc.*TT;R=sum(R);I1(x,y,2)=R/T;
Aloc=I(xstart:xend,ystart:yend,3);%对蓝色通道进行处理Aloc=double(Aloc(depthpos));R=Aloc.*TT;R=sum(R);I1(x,y,3)=R/T;endfigure,imshow(I1),title('基于深度图的图像修复')
(单幅图像的修补类似,去除红色部分代码即可,即第23行的“&abs(B(xstart:xend,ystart:yend)-basedepth)
四.实验结果:
原有损图及两种方式修复的图像
有损图像
基于深度图的修复图像
.
单幅图像修复图像
可以看到原有损图像的黑色损伤部分均已被修复和还原,而且可以看到基于深度图的修补方法在图中人物的边缘处处理得更好,边缘处更加清晰,边界更加明显。
五.实验感受
这个实验相比哈夫曼编码实验对我来说难度小了许多,因为matlab有很多可以直接使用的函数,而且这次实验原理也不算太难,所以成功地完成了两种图像修复。让我学习到了很多,收获了很多。