精华内容
下载资源
问答
  • 基于MATLAB的图像压缩程序(包含各种压缩编码解码方法),算法包含详细代码,是图像编码技术的总结
  • 以下我们给出了相关的编码解码(加密解密)代码。 压缩编码(加密)代码: 复制代码 代码如下:<?php function encode_file_contents($filename) { $type=strtolower(substr(strrchr($filename,’.’),1)); if...
  • 图像压缩编码解码原理

    千次阅读 2017-02-09 09:48:07
    一、图像压缩的基本途径  图像的数据量极大,必须对其数据总量大大压缩,才能够存储在直径12cm的光盘上。在实用技术上,可通过以下途径来压缩图像数据的总量。  1、采用亮度(Y)、色度(C)取样方式  ...


      一、图像压缩的基本途径

      图像的数据量极大,必须对其数据总量大大压缩,才能够存储在直径12cm的光盘上。在实用技术上,可通过以下途径来压缩图像数据的总量。

      1、采用亮度(Y)、色度(C)取样方式

      实用彩色电视技术没有传输、处理红、蓝、绿三基色信号,而传输、处理亮度信号Y和色度信号C。这种处理方法有利于实现彩色电视和黑白电视的兼容,也利于限制彩色电视信号的频带宽度。在数字图像处理技术中,仍然采用传输、处理亮度信号Y和色度信号C的方法。由于人眼晴对亮度信息敏感,对彩色信息不够敏感,因而对Y信号以较高清晰度传送,对C信号以较低清晰度传送。实际作法是这样的:对每个亮度Y像素都进行传送;而将色度C分解为U、V两个色差信号(或写为Cb、Cr、B-Y、R-Y),分别进行传送;对亮度Y实行逐点取样,而对色度C则取样较少。即对应于4个亮度取样点,仅对色度信号取样1个点,即对U、V像素的取样较低,各取1个取样点,这种取样格式称为YUV411格式。

      采用YUV411取样格式后,它的数据总量将比三基色取样量格式时减少一半。若采用三种基色取样方式时,各基色应与亮度信号取样方式一样,即对每个红、绿、蓝色采取逐点取样的方法。采用Y、C传输方式时,取样次数减少一半,传输数码也减少一半。人眼睛对色度的敏感程度较低,利用人眼睛这一生理视觉特性,人们在主观感觉上并没有感到图像清晰度下降。显然,这是压缩图像数据码率的一个得力措施。

      2、将整幅图像分割为小区域进行分割处理

      对图像进行数据处理时,对每帧图像进行分割处理。首先图像横向切成若干条,每一条称为一片,将每一片再纵向切成若干块,称宏块,宏块是图像压缩的基本单位。每个宏块的彩色图像可用1个亮度信号Y和两个色差信号Cb、Cr(即U、V)来表示,或者说,每个宏块分为三层,一层亮度Y,两层色度(各为Cb、Cr),统称为一个宏块。

      由于人眼睛对亮度、色度的主观敏感程度不同,通常把亮度宏块再平均分成4块,每一小块称为像块或区块,详见示意图2.2.1。每个区块可以进一步分割,称为像素或像点,像素是构成图像的最小单位。对于数字图像来说,每一个像素作为一个取样点,有一个对应的取样数值。可以看出,图像分割越细,像素数越多,取样点越多,图像清晰度越高;反之,像素数越少,图像清晰度越低。实际上,对图像压缩处理,就是对图像区块的数据、像素的数据进行压缩处理。

      将整幅图像分割为小区域进行分割处理 http://www.elecfans.com

      彩电制式不同,分割图像的具体数据将有所变化。例如PAL制,大多数为625行扫描标准,那么每帧图像被切为18片,每片再切成22个宏块,即每帧图像分成396个宏块;而525行的NTSC制,每帧图像被切为15片,每片再切成22个宏块,即每帧图像分成330个宏块。对亮度信号来说,每个宏块又分为4个区块,每个区块含有8×8=64个像素,则每个宏块含有256个像素。但对两个色差信号来说,宏块像素数等于区块像素数,即像素数是8×8=64个,是亮度像素的1/4。尽管两色差信号的像素较少,清晰度低,但不影响人眼睛的主观感觉。在进行数字图像处理时,按照图中各个8×8方块( 共64块) 编成次序,再按照编号顺序依次处理。也就是说,以8×8像素的方块作基本操作单元,依次处理每个像素(即取样点)的取样数值。

      3、采用帧间和帧内数据压缩技术。

      实用电视每秒钟传送25-30帧画面,使画面变化具有连续感,电视活动图像是由各帧画面差别很小的一系列画面组成的。各帧画面的微小变化主要表现于画面主体部分,画面的背景差别很小。图像是由亮度、色度信息来描述的,在各相邻帧图像内,若分别比较同一相对位置的亮度、色度信号,通常其差别较小。经大量统计发现,在各个像素当中仅有10%以下的像素点的亮度差值变化超过去时2%,而色度差值变化在0.1%以下。在各帧图像中具有大量重复内容,这些重复内容的数据属于多余(冗余)信息,于是,可以通过减少时域冗余信息的方法,即运作帧间数据压缩技术,来减少图像传输的数码率。

      经分析发现,在同一帧画面内也存在相当多的冗余信息。对图像主体部分和眼睛最敏感的部分,应当准确、详细地处理,需要对每个像素点进行精细传输;但对于图像非主体部分和眼睛不敏感的部分,则可以进行粗略地处理,即进行信息数据的压缩处理。于是,可以根据一帧图像内容的具体分布情况,对不同位置可采用不同的数据量来传送,减少传送图像的数据量,使图像数据得到压缩。这种压缩数据的方法,是在同一帧图像的不同空间部位进行数据压缩,称为空间域冗余压缩。例如,有一幅人像画面,其面部和头部的线条清晰度可以不相同,尤其是眼睛、嘴唇部位表情丰富,线条比较精细复杂,是观众最注意的部位,应当用高清晰度传送;而头顶部位和面颊侧面,轮廓变化较少,灰度层次变化较小,观众不太注意这些部位。显然,图像的主要部位,灰度层次变化较大的部位,人眼睛敏感的部位,应当以较大数据量进行精细传送;而那些图像的次要部位,灰度层次变化较小的部位,人眼睛不注意的部位,则可用较少数据量进行粗略传送,甚至于仅仅传送它们的平均亮度信息。

      以下具体讨论数字图像的数据压缩原理。先讨论静止图像的数据压缩技术,即帧内数据压缩技术;然后讨论活动图像的数据压缩技术,即帧间数据压缩技术。

      二、帧内数据压缩技术

      首先对整幅图像进行分割处理,经分割取得最小操作单元。下面按8×8=64个像素组成的区块来计论。每一个像素值都可以按一定规律取样,例如可对亮度各个像素的亮度值取样,若每个像素按8bit量化,则每个区块的总数据量为8bit×64(像素点),即512bit。可见,对全画面各像素量化处理后数据量十分庞大,需要进行数据压缩。通常,经过离散余弦变换,Z字型扫描,可变长度编码等处理过程,可将数据总量进行大量压缩。

      1、离散余弦变换(DCT)编码

      (1) 功能简述

      离散余弦变换简称为DCT(是英Discrete Cosine Transform的缩写词),是一种数字处理方法,经常用于数据处理。DCT是多种数字变换方法的一种,它是把空间域图像变换到频率域进行分析的方法。由于DCT的变换核构成的基向量与图像内容无关,而且变换核是可以分离的,既二维DCT可以用两次一维DCT来完成,使得数学运算难度大大简化,再配以已经发现的其它快速算法,使得DCT编码得到了广泛的应用。将DCT应用于图像数据压缩,可以减少代表图像亮度(或色度)层次数码信息,达到数据压缩的目的。利用DCT不仅可将图像编码,还可以在编码变换过程发现图像细节的位置,以便删去或略去对视觉不敏感的部分,而更加突出视觉的敏感部分,通过选择主要数据来传输、重视图像。

      利用DCT压缩图像数据,主要是根据图像信号在频率域的统计特性。在空间域看来,图像内容千差万别;但在频率域上,经过对大量图像的统计分析发现,图像经过DCT变换后,其频率系数的主要成分集中于比较小的范围,且主要位于低频部分。利用DCT变换揭示出这种规律后,可以再采取一些措施把频谱中能量较小的部分舍弃,尽量保留传输频谱中主要的频率分量,就能够达到图像数据压缩目的。

      (2)规律和特点

      ①时间域信号的频谱

      对于一个随时间变化的波形来说,它是随时间变化的周期信号,它是以一定幅度值为波形的直流平均值,其波形可看成是基波与无数次谐波叠加而成。其基波振幅最大,然后各次谐波振幅逐渐减小。各次谐波叠加次数越高,则合成波形越接近于理想矩形波。此分析方法就是应用日益广泛的频谱分析方法。其中各次正弦波谐波的振幅值经常称为频谱系数,将频谱系数排列起来,可以组成一个系数列。上述事实说明,周期性矩形波可以由时间域 (反映幅度-时间关系)来描述,也可以由频率域(幅度-频率关系)来描述。两者有互相对应的关系。实际上,各种时间域信号都可以由频率域的规律来描述,两种描述方法存在内在的联系,可以互相转换。

      ②空间域信号的频谱系数

      对于各种空间域分布的信号,也可以进行类似的频率变换,即将空间域信号转变为频率域信号。DCT就是其中一种频率分析方法。可参阅图2.2.2来说明DCT变换过程。

      由图像内取出一个区块,分成8×8个像素的64格阵列,即由图(a)转变为图(b)。经过对逐个像素的亮度(或讨论色度)数值取样,并将像素的亮度数值列成矩阵形表格,见图(C)。然后利用离散余弦变换(DCT)可将各空间取样值转变

      图2.2.2 DCT变换过程 http://www.elecfans.com

      为频率域的数值,这里称为DCT系数。

      对于上述64点阵列来说,可得到64个DCT系数,转换为图(d)矩形阵列表格。它已经将64个点的图像采样值组成的阵列,变为一个直流平均值和63个不同频率余弦波幅值组成的64个点阵列,并称为DCT系数阵列。经过上述变换后,已将空间坐标的数据转换为频率坐标的数据,即DCT频率系数。原有8×8区块的各个像素的数值取样量化后,转变为频率域图像信号的频谱系数,即可用64个频率系数来表述,称它们为64个“正交基信号”,每个基信号对应于64个独立二维空间频率中的一个。这些空间频率是由输入信号的“频谱”组成。所得64个变换系数当中,第一项代表直流分量,即64个空间图像采样值的平均值,其余63个系数代表各基信号的幅度。

      观察图2.3.2(d)数据可发现规律,矩阵左上角的数值较大,而右下角的数值较小,且趋近于零值。于是,可以按照Z字形扫描顺序,将各基信号的DCT系数列成一个表格。Z字形扫描的具体轨迹,如图2.3.2(e)所示。按照此规律将DCT系数排列成数据系列,成为DCT系数编码顺序。经过上述处理后,已将二维数据量转换为一维数据量,该数列第一项是该区块的平均亮度值,后面各项系数的分布和大小可以反映亮度起伏变化的剧烈程度。若系数较大,说明亮度起伏较大,该区域图像轮廓较细致;若数值较小,则说明该区内亮度变化较平缓;若数值为零,表示数列中高频分量数值为零,亮度电平无变化。在实际数据处理过程中,排在后面的系数值基本上都有是零值,或者趋于零值。由63个系数集合及变化情况,可反映出该区块内图像细节情况,即图像清晰度状况。

      图(d)矩阵数值非常具有实用价值。左上角数值较大,它们代表了图像信息的直流成分和低频分量,它是图像信息的主体部分,也是区块内信息的主要部分;而右下角数值较小,它们代表了图像信息的高频分量,其幅值原本就比较小,它主要反映图像的细节部分。人眼睛对图像的亮度信息有较高的相对灵敏度,对图像的彩色信息不够敏感;还有,人眼睛对图像信息的低频分量具有较高的视觉灵敏度。经Z字形字扫描后所形成的数据系列,恰好与人眼睛对图像信息的敏感程度形成良好的对应关系。根据视觉生理的上述规律,可对图像数据进行压缩。

    2、DCT系数的再量化处理

      经过上述DCT处理的频率数据可以进行再处理,进一步压缩数据量。人眼睛对各种频率的敏感程度不同,并可取得统计性灵敏度数值。由此可对每种频率分量设定不同的折算值,将前述经转换得到的DCT系数再次进行折算,以便进一步突出视觉效果影响大的成分,而消弱或忽略视觉效果影响小的成分。这种处理方法称为量化处理,简称Q处理。对于64点阵列的64个系数来说,对应了64种不同频率,可使用64个不同的折算值。通常称这64个折算值为量化表,每个折算值称为量化步长,或称量化值。在64点阵列中,左上角的数据量化值较小,右下角的数据量化值较大。对DCT系数的再量化处理,可利用量化器电路来实现。该电路可将区块的64个系数分别除以量化表中对应位置量化步长,再进行四舍五入取整后,即可得到经过再量化处理的64个数据值。

      经过量化处理后,量化值大的系数值所得商值较小,也就是数据压缩比较大,原图像相应部分的忽略内容较多;量化值小的系数所得商数值较大,也就是数据压缩比较小,原图像相应部分不予忽略或极小忽略。于是,经过量化处理后的DCT系数矩阵,可出现许多零值。一般左上角位置的数据的商数是非0,在右下角位置的数据的商数很小,经四舍五入取整值后可简写为0。在系数矩阵上出现了许多0值,则大大减少了数据量。一方面保留了图像信息的主体部分,另一方面大大压缩了像数据。

      3.可变长度编码(VLC)

      经量化处理的系数矩阵出现了许多0值,若进行Z字形扫描时,后面的系数将也出现连续0的状况。此时,数据传输总量已经明显减少,但码位并未减少,仍为64个系数位。为了进一步压缩数据总量,可采用可变长度编码,并简称VLC(Variable Length Coding)。

      通常,采用两种方法进行可变长度编码。第一种,是根据数据出现的频率,分配以不同长度的码字来代替,对于频繁出现的数据,分配以较短的码字,那些不经常出现的数据,则赋予较长的码字,这样处理后可减少传输的总码率。第二种方法,虽然Z字形扫描使系数列尾部出现多个0个值,但不需要逐位地传输0值,仅需传送表0的“个数”码,待重放时再按规定恢复为0位,以便填满矩阵的64位。例如00000,则可表示为50,在解码时恢复为00000。

      总之,对于静止画面来说,采用离散余弦变换,Z字形扫描、量化处理和可变长度编码等方法,可使图像数据量大大压缩。在数据解码时,先经过可变长度解码,恢复为数据的固定长度;再对系数进行反量化,恢复为原来的DCT频率系数;再经过反向离散余弦变换,恢复为图像的空间坐标数值,即原来图像的数据。

      三、帧间数据压缩技术

      对于活动图像来说,相邻帧的图像具有强烈的相关性。在保存和记录动态图像时,不需要将每一帧图像的全部信息都记录和保存下来,可以将前面第一帧图像全部数据都记录下来,把它看成是静态图像,可用静态图像数据压缩方法来处理。而后面诸帧图像,可以仅记录与前面帧图像有差异的信息。于是,在重放时,利用前面帧图像的数据和后面帧的差异数据,即可恢复出后面帧的图像。这种处理方法省去许多数据。

      1、三种画面

      按照MPEG-1标准,传送的活动画面可分为3种类型。第1种,是场景更换后的第1帧画面,它是一种独立的画面,这种画面采用较高清晰度的逐点取样法进行传送,此画面称为I画面(内码帧,或称帧内编码帧)。该画面信息是由自身画面决定,不必参考其它画面。该画面的数据代表了活动图像的主体内容和背景内容,它是电视画面的基础。第2种,是与I画面相隔一定时间、活动图像主体位置在同一背景上已发生明显变化的画面,此画面称P画面(预测帧,或称前向预测编码帧)。该画面用前面的I画面作为参考画面,该画面不传送背景等重复性信息,仅传送主体变化的差值,这就省略了一部分细节信息,而在重放时依靠帧存储器将I画面的主要部分和P画面的差值进行运算,即可得出新画面的完整内容,它是既有背景又有现时运动主体状态的实际画面。第3种,其情况与P画面相似,用来传送在I、P画面之间的画面,称B画面(双向预测帧,或称双向预测内插编码帧)。该画面仅反映在I、P画面之间的运动主体变化情况,并用位移矢量(或称运动矢量等)表示画面主体移动情况。其信息量更小些。因为在重放它时,既可参考I画面内容,也要参考P画面内容,所以称为双向预测帧。

      将一串连续相关的画面分为I、P、B型后,传输信息量明显减少。在P、B画面当中,几乎不传送反映实物的象素,仅传送其主体移动的差值,其具体的处理方法是采用了区块对比的方法,在两个变化的画面当中,将区块或宏块作为处理单元,将一个画面的宏、区块与参与 画面中邻近范围内的宏、区块进行数值运算对比,寻找与该块最相近、误差最小的区块,找到近似的该区块后,记录该区块在两个画面中的位移值,即为位移矢量以及反映两画面的差值量。若位移矢量坐标变化为0,说明该块没有移动,例如相同的背景景物;若位移矢量值有变化,而区块差值为0,则说明景物有移动,而形状没有变化,例如飞行中的球类和奔驰的车辆等。可见,位移矢量和区块差值可在重放时依靠参考画面得出新画面的完整场景,而传送时却省略了背景和主体内容,只传送代表位移矢量和差值的少量数据,使图像得到大量压缩。

      2、三种画面的连接

      通常,更换场景后的第一帧就是I帧,I帧应当全帧传送。从压缩的程度来看,I画面的压缩量最少;P画面次之,它是以I画面为基础;B画面压缩最多。为了加大压缩比,通常在I帧后面相隔2帧(最多3帧)设置1个P帧,在I、P帧之间都是B帧,在两个P帧之间也是设置2~3帧B帧。B帧传送它与I帧或P帧之间的差值信息,或者P帧与后面P帧或I帧之间的差值信息,或者它与前后I、P帧或P、P帧平均值之间的差值信息。当主体内容变化愈大时,两个I画面之间的帧数值越小;当主体内容变化小时,I面画的间隔可以适当大一些。或者说,B帧、P帧所占比例越大,图像压缩比越高。一般两个I画面相隔13~15帧,相隔帧数不宜再多。

      下面以15帧为例,说明VCD图像帧的排列顺序。I、P、B三种画面的典型设置方式,对NTSC制共约需半秒时间。节目输入顺序是按实际出现顺序排列的,即I、B、B、P、B、B、P、B、B……I、B、B、P……;但为了解码时便于从I、P画面插补得到B画面,在编码录制节目时,将顺序改变了,即按照I、P、B、B……顺序,即改为按原来0、3、1、2、6、4、5、9、7、8…的画面顺序。解码时先解出0帧、3帧,再由其插补预测计算得出1帧、2帧等等。为此,须在解码器内设置动态存储器,将I、P帧先解码并存储,再计算出各个B帧。不过最后输出时,还是应当按照实际播放顺序重组读出,按正确顺序输出。

      VCD采用的帧间压缩技术标准,对图像编码顺序和各帧间隔是有具体规定的。采用帧压缩技术后,各帧之间的信息冗余量大大减少,图像码率进一步压缩,压缩比可达3-20余倍。

      四、图像压缩编码过程和解压缩过程

      1、编码过程

      这里谈谈VCD所采用MPEG-1标准的编码过程。因为相邻帧画面相同或基本相同,将这种画面群的第1幅画面作为I画面,将它送入编码器。编码器首先将它割裂为许多片、宏块、区块等,将各区块分割为8×8=64点阵列,再进行Z字形描述和DCT变换,将64个亮度(或色度)取样数值变换为64个DCT系数,再对64个系数值分别进行相应的量化,经量化处理后再进行VLC处理,即得到了代表一个区块数据的最短的数码,至此,完成了该画面群第1帧的第1列图像中第1宏块的编码。依次类推,可得到第1帧画面的全部压缩数据编码。原为二维空间的一帧图像信息已经转变为一维空间的串行数据,这些数据被全部存储起来,成为继续进行数据处理的基础。至此,I画面数据处理完毕。

      完成第1帧图像压缩编码后,接着输入第2帧图像。编码器按照相同的方法步骤对第2帧进行压缩编码,得到第2帧数据。此时,编码器不再将第2帧数据进行完整的存储和传送,而是将它与第1帧数据进行比较运算。若运算中发现,两帧间数据差别很小时,说明两帧图像差别不大,仅将其差值存入存储器,而舍掉其大部分重复数据。按照此方法再进行第3、第4帧编码,并进行比较运算,直到找到某一帧,差别较大且超过规定值时,再将此帧数据中与第1帧的差别(包括位移矢量和差值)部分存储起来,并将此帧数据排在第1帧(I帧)后面传送出去,该帧就是P画面。当传送I、P画面后,再传送3、4帧的差别数据,这些画面都是B画面。它们之间的差别不大,是处于I、P之间的画面。按照此程序和方法,可再选出许多组P和B画面。通常,每隔13~15帧后,再设置一个I画面,作为后续画面的参考基准。如遇到较新的场景,将出现一幅不相同的新画面,这幅新出现的画面也作为I画面。

      MPEG-1视频压缩编码方框图 http://www.elecfans.com

      图2.2.3是MPEG-1图像压缩编码器方框图。代表亮度Y和色度分量CB、CR的二进制数码

      化信号,首先进入帧改组器(或称帧重排电路),将画面分割为片、宏块、区块。区块经过比较

      运算电路再进入DCT电路、量化器、VLC电路,取得已压缩数据。再将数据送到多路混合器和传输缓冲器。传输缓冲器用于暂存压缩数据,并按照控制指令的先后按时序输出数据。该缓冲器通过调整器(又称为量化自适应器)与量化器相连接。调整器可用来检测缓冲器的缓冲区的数据暂存程度,并根据暂存数据量自动调整量化步长。在编码器内设置有反馈通路,它主要包括反量化器(Q-1)、离散余弦逆变换(IDCT)、相加器以及IPB画面帧存储器等。反馈回路用于预测图像产生,进行画面分类处理(计算、区分并处理IPB画面),主要用于帧间数据压缩编码处理。还有,运动预测和补偿电路可用于运动补偿。

      2、图像解压缩电路方框图

      图像解压缩电路简称为解压电路、解码电路。VCD视盘机内,经过数字信号解调电路(CD-DSP)处理后,输出压缩编码视频数据流,需要经过视频解压缩电路进行数据解压缩,恢复为未压缩的视频信号。解码过程是编码的逆过程,图2.2.4是MPEG-1视频解压缩电路方框图,其电路结构比编码器稍简单一些。

      图2.2.4 MPEG-1视频解压缩电路方框图

      图2.2.4 MPEG-1视频解压缩电路方框图

      来自CD-DSP电路的压缩编码信号送到输入缓冲器,然后进入去混合电路,将图像的编码模式标志,运动向量(位移矢量)和图像数据分离开,分别送往帧存储器和解压缩主通道电路。

      主通道要处理I、P、B帧数据,这些数据已经按照图像编码系列的规定,以数据封包头标指出,这些数据分别暂存在缓冲存储器的存储区内,根据数据量大小暂存在容量不同的存储器区中。在微处理器控制下,先将I画面数据按序取出,送到VLC(可变长度码解调器),按照ROM存放的可变长度码对照表,逐一将编码时压缩的码位恢复为压缩前的DCT量化值,再将各区块分为64个数据的量化值逐位乘以反量化参数,这些参数位于ROM中存放的64位视觉心理模式量化表的相对位置,重新恢复为DCT频率系数,完成反量化过程。

      经过反量化的数据,再送入IDCT(离散余弦逆变换)电路。这是另一次逆变换,也是通过查表法,将反量化值所代表的各频率余弦分量的幅值进行逆变换,重新恢复为DCT变换前的图像(Y、CB、CR)取样数据,从而取得代表图像压缩前的区块信息。4个区块的信息组成一个宏区块,若干个宏区块组成片,再由若干片组成完整画面的总数据,这就是I帧画面。这些繁重的相加工作都需要在加法器中进行。

      恢复出来的I帧画面数据存入帧存储器。I画面与后续输入的P画面数据相加,可恢复出P画面,P画面也存入帧存储器。然后根据运动矢量和运动后图像差值(即B画面数据),与I、P画面存储数据在加法器中相加,并受编码模式信号的控制,以便决定I、P图像的成分多少,从而恢复出不同前后的B帧画面。经以上处理所得I、P、B各种画面数据都需要存入缓冲存储器,还要根据编码模式的指示及输出制式的帧频要求,按照I、B、B、P、B、B、P、B、B…B、I、B、B、P、B…的正常顺序进行重新编排,按照一定的速度从帧重排电路输出。输出的解压缩数据送到D/A转换器,转变为R、G、B三基色模拟信号。

      通常,在解压缩电路还要辅设视频编码器和调制器。视频编码器可将三基色信号编码为NTSC/PAL制彩色电视信号,并加入同步、消隐、色同步和彩色副载波信号等,以视频模拟全电视信号形式输出。这种输出形式的信号需要输送到电视接收机的AV输入端口。但是,有些老式电视机没有设置AV输入端口,为了适应这种现象,输出的视频全电视信号需要再一次进行高频调制,利用调制器以某个特定频道的RF调幅形式输出电视信号。此时,VCD机需要设置RF输出端口,其输出信号可直接送到电视机的天线输入端口。


    展开全文
  • 实现bmp图像的压缩编码解压,包括哈弗曼编码解码 dct变换 量化
  • ADPCM编码解码原理

    2019-02-18 20:54:50
    本文的以IMA的ADPCM编码标准为例进行描述,IMA-ADPCM 是Intel公司首先开发的是一种主要针对16bit采样波形数据的有损压缩算法,压缩比为 4:1.它与通常的DVI-ADPCM是同一算法。 (对8bit数据压缩时是3.2:1,也有非标准的...
  • 内有exe可执行文件,可直接运行来观看效果,本人用VS2008编写
  • MATLAB实现jpeg编码解码,包括编码DCT,哈夫曼编码,熵编码
  • java端:返回类型非字符接口调用StringUtils#base64AndCompressJson进行编码压缩 ,返回类型为字符接口调用StringUtils#base64Andcompress js 端:引入压缩包中的js文件 ,调用deBase64AndUncompress进行解压解码
  • 图像压缩编码解码

    2013-12-04 22:19:51
    图像压缩,jpeg,jpeg2000,xr简介,压缩编码
  • HTML5HTML5 选图 压缩 base64编码 上传 解码
  • 霍夫曼·高朗 通过霍夫曼算法进行编码和解码 目标 通过霍夫曼贪婪算法(显然是使用BT)实现文本的编码和解码 向API包添加JSON支持 在Cobra lib上创建应用 ... 支持将应用程序启动为编码解码服务器
  • 用MATLAB做的基于霍夫曼编码的图像压缩,里面有个文件时专门的霍夫曼编码函数,自己写的。
  • NULL 博文链接:https://touch-2011.iteye.com/blog/1058800
  • 基于MATLAB实现的游程编码解码,数字图像处理及MATLAB实现——实验7-实验报告,二值化,游程编码,游程解码
  • 实现了增量编码解码技术。
  • 霍夫曼编码解码本软件是原创作品,包含霍夫曼编码解码的全部代码,可直接用于文件的压缩和解压
  • 压缩过程就是编码过程,解压缩过程就是解码过程。压缩技术分为无损压缩和有损压缩两大类,前者在解码时可以精确地恢复原图像,没有任何损失;后者在解码时只能近似原图像,不能无失真地恢复原图像。
  • jbig2二值图像压缩算法实现,C++实现,jbig2是二值图像压缩效果效率平衡最佳的国际标准
  • 本节介绍图像压缩编码的基本原理,图像数据压缩和解压缩电路的基本结构。它们是看影碟机电路图的基础知识。  一、图像压缩的基本途径  图像的数据量极大,必须对其数据总量大大压缩,才能够存储在直径12cm的光盘...

    转自:http://blog.csdn.net/wishfly/article/details/52066859

    本节介绍图像压缩编码的基本原理,图像数据压缩和解压缩电路的基本结构。它们是看影碟机电路图的基础知识。

      一、图像压缩的基本途径

      图像的数据量极大,必须对其数据总量大大压缩,才能够存储在直径12cm的光盘上。在实用技术上,可通过以下途径来压缩图像数据的总量。

      1、采用亮度(Y)、色度(C)取样方式

      实用彩色电视技术没有传输、处理红、蓝、绿三基色信号,而传输、处理亮度信号Y和色度信号C。这种处理方法有利于实现彩色电视和黑白电视的兼容,也利于限制彩色电视信号的频带宽度。在数字图像处理技术中,仍然采用传输、处理亮度信号Y和色度信号C的方法。由于人眼晴对亮度信息敏感,对彩色信息不够敏感,因而对Y信号以较高清晰度传送,对C信号以较低清晰度传送。实际作法是这样的:对每个亮度Y像素都进行传送;而将色度C分解为U、V两个色差信号(或写为Cb、Cr、B-Y、R-Y),分别进行传送;对亮度Y实行逐点取样,而对色度C则取样较少。即对应于4个亮度取样点,仅对色度信号取样1个点,即对U、V像素的取样较低,各取1个取样点,这种取样格式称为YUV411格式。

      采用YUV411取样格式后,它的数据总量将比三基色取样量格式时减少一半。若采用三种基色取样方式时,各基色应与亮度信号取样方式一样,即对每个红、绿、蓝色采取逐点取样的方法。采用Y、C传输方式时,取样次数减少一半,传输数码也减少一半。人眼睛对色度的敏感程度较低,利用人眼睛这一生理视觉特性,人们在主观感觉上并没有感到图像清晰度下降。显然,这是压缩图像数据码率的一个得力措施。

      2、将整幅图像分割为小区域进行分割处理

      对图像进行数据处理时,对每帧图像进行分割处理。首先图像横向切成若干条,每一条称为一片,将每一片再纵向切成若干块,称宏块,宏块是图像压缩的基本单位。每个宏块的彩色图像可用1个亮度信号Y和两个色差信号Cb、Cr(即U、V)来表示,或者说,每个宏块分为三层,一层亮度Y,两层色度(各为Cb、Cr),统称为一个宏块。

      由于人眼睛对亮度、色度的主观敏感程度不同,通常把亮度宏块再平均分成4块,每一小块称为像块或区块,详见示意图2.2.1。每个区块可以进一步分割,称为像素或像点,像素是构成图像的最小单位。对于数字图像来说,每一个像素作为一个取样点,有一个对应的取样数值。可以看出,图像分割越细,像素数越多,取样点越多,图像清晰度越高;反之,像素数越少,图像清晰度越低。实际上,对图像压缩处理,就是对图像区块的数据、像素的数据进行压缩处理。

      彩电制式不同,分割图像的具体数据将有所变化。例如PAL制,大多数为625行扫描标准,那么每帧图像被切为18片,每片再切成22个宏块,即每帧图像分成396个宏块;而525行的NTSC制,每帧图像被切为15片,每片再切成22个宏块,即每帧图像分成330个宏块。对亮度信号来说,每个宏块又分为4个区块,每个区块含有8×8=64个像素,则每个宏块含有256个像素。但对两个色差信号来说,宏块像素数等于区块像素数,即像素数是8×8=64个,是亮度像素的1/4。尽管两色差信号的像素较少,清晰度低,但不影响人眼睛的主观感觉。在进行数字图像处理时,按照图中各个8×8方块( 共64块) 编成次序,再按照编号顺序依次处理。也就是说,以8×8像素的方块作基本操作单元,依次处理每个像素(即取样点)的取样数值。

      3、采用帧间和帧内数据压缩技术。

      实用电视每秒钟传送25-30帧画面,使画面变化具有连续感,电视活动图像是由各帧画面差别很小的一系列画面组成的。各帧画面的微小变化主要表现于画面主体部分,画面的背景差别很小。图像是由亮度、色度信息来描述的,在各相邻帧图像内,若分别比较同一相对位置的亮度、色度信号,通常其差别较小。经大量统计发现,在各个像素当中仅有10%以下的像素点的亮度差值变化超过去时2%,而色度差值变化在0.1%以下。在各帧图像中具有大量重复内容,这些重复内容的数据属于多余(冗余)信息,于是,可以通过减少时域冗余信息的方法,即运作帧间数据压缩技术,来减少图像传输的数码率。

      经分析发现,在同一帧画面内也存在相当多的冗余信息。对图像主体部分和眼睛最敏感的部分,应当准确、详细地处理,需要对每个像素点进行精细传输;但对于图像非主体部分和眼睛不敏感的部分,则可以进行粗略地处理,即进行信息数据的压缩处理。于是,可以根据一帧图像内容的具体分布情况,对不同位置可采用不同的数据量来传送,减少传送图像的数据量,使图像数据得到压缩。这种压缩数据的方法,是在同一帧图像的不同空间部位进行数据压缩,称为空间域冗余压缩。例如,有一幅人像画面,其面部和头部的线条清晰度可以不相同,尤其是眼睛、嘴唇部位表情丰富,线条比较精细复杂,是观众最注意的部位,应当用高清晰度传送;而头顶部位和面颊侧面,轮廓变化较少,灰度层次变化较小,观众不太注意这些部位。显然,图像的主要部位,灰度层次变化较大的部位,人眼睛敏感的部位,应当以较大数据量进行精细传送;而那些图像的次要部位,灰度层次变化较小的部位,人眼睛不注意的部位,则可用较少数据量进行粗略传送,甚至于仅仅传送它们的平均亮度信息。

      以下具体讨论数字图像的数据压缩原理。先讨论静止图像的数据压缩技术,即帧内数据压缩技术;然后讨论活动图像的数据压缩技术,即帧间数据压缩技术。

      二、帧内数据压缩技术

      首先对整幅图像进行分割处理,经分割取得最小操作单元。下面按8×8=64个像素组成的区块来计论。每一个像素值都可以按一定规律取样,例如可对亮度各个像素的亮度值取样,若每个像素按8bit量化,则每个区块的总数据量为8bit×64(像素点),即512bit。可见,对全画面各像素量化处理后数据量十分庞大,需要进行数据压缩。通常,经过离散余弦变换,Z字型扫描,可变长度编码等处理过程,可将数据总量进行大量压缩。

      1、离散余弦变换(DCT)编码

      (1) 功能简述

      离散余弦变换简称为DCT(是英Discrete Cosine Transform的缩写词),是一种数字处理方法,经常用于数据处理。DCT是多种数字变换方法的一种,它是把空间域图像变换到频率域进行分析的方法。由于DCT的变换核构成的基向量与图像内容无关,而且变换核是可以分离的,既二维DCT可以用两次一维DCT来完成,使得数学运算难度大大简化,再配以已经发现的其它快速算法,使得DCT编码得到了广泛的应用。将DCT应用于图像数据压缩,可以减少代表图像亮度(或色度)层次数码信息,达到数据压缩的目的。利用DCT不仅可将图像编码,还可以在编码变换过程发现图像细节的位置,以便删去或略去对视觉不敏感的部分,而更加突出视觉的敏感部分,通过选择主要数据来传输、重视图像。

      利用DCT压缩图像数据,主要是根据图像信号在频率域的统计特性。在空间域看来,图像内容千差万别;但在频率域上,经过对大量图像的统计分析发现,图像经过DCT变换后,其频率系数的主要成分集中于比较小的范围,且主要位于低频部分。利用DCT变换揭示出这种规律后,可以再采取一些措施把频谱中能量较小的部分舍弃,尽量保留传输频谱中主要的频率分量,就能够达到图像数据压缩目的。

      (2)规律和特点

      ①时间域信号的频谱

      对于一个随时间变化的波形来说,它是随时间变化的周期信号,它是以一定幅度值为波形的直流平均值,其波形可看成是基波与无数次谐波叠加而成。其基波振幅最大,然后各次谐波振幅逐渐减小。各次谐波叠加次数越高,则合成波形越接近于理想矩形波。此分析方法就是应用日益广泛的频谱分析方法。其中各次正弦波谐波的振幅值经常称为频谱系数,将频谱系数排列起来,可以组成一个系数列。上述事实说明,周期性矩形波可以由时间域 (反映幅度-时间关系)来描述,也可以由频率域(幅度-频率关系)来描述。两者有互相对应的关系。实际上,各种时间域信号都可以由频率域的规律来描述,两种描述方法存在内在的联系,可以互相转换。

      ②空间域信号的频谱系数

      对于各种空间域分布的信号,也可以进行类似的频率变换,即将空间域信号转变为频率域信号。DCT就是其中一种频率分析方法。可参阅图2.2.2来说明DCT变换过程。

      由图像内取出一个区块,分成8×8个像素的64格阵列,即由图(a)转变为图(b)。经过对逐个像素的亮度(或讨论色度)数值取样,并将像素的亮度数值列成矩阵形表格,见图(C)。然后利用离散余弦变换(DCT)可将各空间取样值转变为频率域的数值,这里称为DCT系数。

      对于上述64点阵列来说,可得到64个DCT系数,转换为图(d)矩形阵列表格。它已经将64个点的图像采样值组成的阵列,变为一个直流平均值和63个不同频率余弦波幅值组成的64个点阵列,并称为DCT系数阵列。经过上述变换后,已将空间坐标的数据转换为频率坐标的数据,即DCT频率系数。原有8×8区块的各个像素的数值取样量化后,转变为频率域图像信号的频谱系数,即可用64个频率系数来表述,称它们为64个“正交基信号”,每个基信号对应于64个独立二维空间频率中的一个。这些空间频率是由输入信号的“频谱”组成。所得64个变换系数当中,第一项代表直流分量,即64个空间图像采样值的平均值,其余63个系数代表各基信号的幅度。

      观察图2.3.2(d)数据可发现规律,矩阵左上角的数值较大,而右下角的数值较小,且趋近于零值。于是,可以按照Z字形扫描顺序,将各基信号的DCT系数列成一个表格。Z字形扫描的具体轨迹,如图2.3.2(e)所示。按照此规律将DCT系数排列成数据系列,成为DCT系数编码顺序。经过上述处理后,已将二维数据量转换为一维数据量,该数列第一项是该区块的平均亮度值,后面各项系数的分布和大小可以反映亮度起伏变化的剧烈程度。若系数较大,说明亮度起伏较大,该区域图像轮廓较细致;若数值较小,则说明该区内亮度变化较平缓;若数值为零,表示数列中高频分量数值为零,亮度电平无变化。在实际数据处理过程中,排在后面的系数值基本上都有是零值,或者趋于零值。由63个系数集合及变化情况,可反映出该区块内图像细节情况,即图像清晰度状况。

      图(d)矩阵数值非常具有实用价值。左上角数值较大,它们代表了图像信息的直流成分和低频分量,它是图像信息的主体部分,也是区块内信息的主要部分;而右下角数值较小,它们代表了图像信息的高频分量,其幅值原本就比较小,它主要反映图像的细节部分。人眼睛对图像的亮度信息有较高的相对灵敏度,对图像的彩色信息不够敏感;还有,人眼睛对图像信息的低频分量具有较高的视觉灵敏度。经Z字形字扫描后所形成的数据系列,恰好与人眼睛对图像信息的敏感程度形成良好的对应关系。根据视觉生理的上述规律,可对图像数据进行压缩。

      2、DCT系数的再量化处理

      经过上述DCT处理的频率数据可以进行再处理,进一步压缩数据量。人眼睛对各种频率的敏感程度不同,并可取得统计性灵敏度数值。由此可对每种频率分量设定不同的折算值,将前述经转换得到的DCT系数再次进行折算,以便进一步突出视觉效果影响大的成分,而消弱或忽略视觉效果影响小的成分。这种处理方法称为量化处理,简称Q处理。对于64点阵列的64个系数来说,对应了64种不同频率,可使用64个不同的折算值。通常称这64个折算值为量化表,每个折算值称为量化步长,或称量化值。在64点阵列中,左上角的数据量化值较小,右下角的数据量化值较大。对DCT系数的再量化处理,可利用量化器电路来实现。该电路可将区块的64个系数分别除以量化表中对应位置量化步长,再进行四舍五入取整后,即可得到经过再量化处理的64个数据值。

      经过量化处理后,量化值大的系数值所得商值较小,也就是数据压缩比较大,原图像相应部分的忽略内容较多;量化值小的系数所得商数值较大,也就是数据压缩比较小,原图像相应部分不予忽略或极小忽略。于是,经过量化处理后的DCT系数矩阵,可出现许多零值。一般左上角位置的数据的商数是非0,在右下角位置的数据的商数很小,经四舍五入取整值后可简写为0。在系数矩阵上出现了许多0值,则大大减少了数据量。一方面保留了图像信息的主体部分,另一方面大大压缩了像数据。

      3.可变长度编码(VLC)

      经量化处理的系数矩阵出现了许多0值,若进行Z字形扫描时,后面的系数将也出现连续0的状况。此时,数据传输总量已经明显减少,但码位并未减少,仍为64个系数位。为了进一步压缩数据总量,可采用可变长度编码,并简称VLC(Variable Length Coding)。

      通常,采用两种方法进行可变长度编码。第一种,是根据数据出现的频率,分配以不同长度的码字来代替,对于频繁出现的数据,分配以较短的码字,那些不经常出现的数据,则赋予较长的码字,这样处理后可减少传输的总码率。第二种方法,虽然Z字形扫描使系数列尾部出现多个0个值,但不需要逐位地传输0值,仅需传送表0的“个数”码,待重放时再按规定恢复为0位,以便填满矩阵的64位。例如00000,则可表示为50,在解码时恢复为00000。

      总之,对于静止画面来说,采用离散余弦变换,Z字形扫描、量化处理和可变长度编码等方法,可使图像数据量大大压缩。在数据解码时,先经过可变长度解码,恢复为数据的固定长度;再对系数进行反量化,恢复为原来的DCT频率系数;再经过反向离散余弦变换,恢复为图像的空间坐标数值,即原来图像的数据。

      三、帧间数据压缩技术

      对于活动图像来说,相邻帧的图像具有强烈的相关性。在保存和记录动态图像时,不需要将每一帧图像的全部信息都记录和保存下来,可以将前面第一帧图像全部数据都记录下来,把它看成是静态图像,可用静态图像数据压缩方法来处理。而后面诸帧图像,可以仅记录与前面帧图像有差异的信息。于是,在重放时,利用前面帧图像的数据和后面帧的差异数据,即可恢复出后面帧的图像。这种处理方法省去许多数据。

      1、三种画面

      按照MPEG-1标准,传送的活动画面可分为3种类型。第1种,是场景更换后的第1帧画面,它是一种独立的画面,这种画面采用较高清晰度的逐点取样法进行传送,此画面称为I画面(内码帧,或称帧内编码帧)。该画面信息是由自身画面决定,不必参考其它画面。该画面的数据代表了活动图像的主体内容和背景内容,它是电视画面的基础。第2种,是与I画面相隔一定时间、活动图像主体位置在同一背景上已发生明显变化的画面,此画面称P画面(预测帧,或称前向预测编码帧)。该画面用前面的I画面作为参考画面,该画面不传送背景等重复性信息,仅传送主体变化的差值,这就省略了一部分细节信息,而在重放时依靠帧存储器将I画面的主要部分和P画面的差值进行运算,即可得出新画面的完整内容,它是既有背景又有现时运动主体状态的实际画面。第3种,其情况与P画面相似,用来传送在I、P画面之间的画面,称B画面(双向预测帧,或称双向预测内插编码帧)。该画面仅反映在I、P画面之间的运动主体变化情况,并用位移矢量(或称运动矢量等)表示画面主体移动情况。其信息量更小些。因为在重放它时,既可参考I画面内容,也要参考P画面内容,所以称为双向预测帧。

      将一串连续相关的画面分为I、P、B型后,传输信息量明显减少。在P、B画面当中,几乎不传送反映实物的象素,仅传送其主体移动的差值,其具体的处理方法是采用了区块对比的方法,在两个变化的画面当中,将区块或宏块作为处理单元,将一个画面的宏、区块与参与 画面中邻近范围内的宏、区块进行数值运算对比,寻找与该块最相近、误差最小的区块,找到近似的该区块后,记录该区块在两个画面中的位移值,即为位移矢量以及反映两画面的差值量。若位移矢量坐标变化为0,说明该块没有移动,例如相同的背景景物;若位移矢量值有变化,而区块差值为0,则说明景物有移动,而形状没有变化,例如飞行中的球类和奔驰的车辆等。可见,位移矢量和区块差值可在重放时依靠参考画面得出新画面的完整场景,而传送时却省略了背景和主体内容,只传送代表位移矢量和差值的少量数据,使图像得到大量压缩。

      2、三种画面的连接

      通常,更换场景后的第一帧就是I帧,I帧应当全帧传送。从压缩的程度来看,I画面的压缩量最少;P画面次之,它是以I画面为基础;B画面压缩最多。为了加大压缩比,通常在I帧后面相隔2帧(最多3帧)设置1个P帧,在I、P帧之间都是B帧,在两个P帧之间也是设置2~3帧B帧。B帧传送它与I帧或P帧之间的差值信息,或者P帧与后面P帧或I帧之间的差值信息,或者它与前后I、P帧或P、P帧平均值之间的差值信息。当主体内容变化愈大时,两个I画面之间的帧数值越小;当主体内容变化小时,I面画的间隔可以适当大一些。或者说,B帧、P帧所占比例越大,图像压缩比越高。一般两个I画面相隔13~15帧,相隔帧数不宜再多。

      下面以15帧为例,说明VCD图像帧的排列顺序。I、P、B三种画面的典型设置方式,对NTSC制共约需半秒时间。节目输入顺序是按实际出现顺序排列的,即I、B、B、P、B、B、P、B、B……I、B、B、P……;但为了解码时便于从I、P画面插补得到B画面,在编码录制节目时,将顺序改变了,即按照I、P、B、B……顺序,即改为按原来0、3、1、2、*、5、9、7、8…的画面顺序。解码时先解出0帧、3帧,再由其插补预测计算得出1帧、2帧等等。为此,须在解码器内设置动态存储器,将I、P帧先解码并存储,再计算出各个B帧。不过最后输出时,还是应当按照实际播放顺序重组读出,按正确顺序输出。

      VCD采用的帧间压缩技术标准,对图像编码顺序和各帧间隔是有具体规定的。采用帧压缩技术后,各帧之间的信息冗余量大大减少,图像码率进一步压缩,压缩比可达3-20余倍。

      四、图像压缩编码过程和解压缩过程

      1、编码过程

      这里谈谈VCD所采用MPEG-1标准的编码过程。因为相邻帧画面相同或基本相同,将这种画面群的第1幅画面作为I画面,将它送入编码器。编码器首先将它割裂为许多片、宏块、区块等,将各区块分割为8×8=64点阵列,再进行Z字形描述和DCT变换,将64个亮度(或色度)取样数值变换为64个DCT系数,再对64个系数值分别进行相应的量化,经量化处理后再进行VLC处理,即得到了代表一个区块数据的最短的数码,至此,完成了该画面群第1帧的第1列图像中第1宏块的编码。依次类推,可得到第1帧画面的全部压缩数据编码。原为二维空间的一帧图像信息已经转变为一维空间的串行数据,这些数据被全部存储起来,成为继续进行数据处理的基础。至此,I画面数据处理完毕。

      完成第1帧图像压缩编码后,接着输入第2帧图像。编码器按照相同的方法步骤对第2帧进行压缩编码,得到第2帧数据。此时,编码器不再将第2帧数据进行完整的存储和传送,而是将它与第1帧数据进行比较运算。若运算中发现,两帧间数据差别很小时,说明两帧图像差别不大,仅将其差值存入存储器,而舍掉其大部分重复数据。按照此方法再进行第3、第4帧编码,并进行比较运算,直到找到某一帧,差别较大且超过规定值时,再将此帧数据中与第1帧的差别(包括位移矢量和差值)部分存储起来,并将此帧数据排在第1帧(I帧)后面传送出去,该帧就是P画面。当传送I、P画面后,再传送3、4帧的差别数据,这些画面都是B画面。它们之间的差别不大,是处于I、P之间的画面。按照此程序和方法,可再选出许多组P和B画面。通常,每隔13~15帧后,再设置一个I画面,作为后续画面的参考基准。如遇到较新的场景,将出现一幅不相同的新画面,这幅新出现的画面也作为I画面。

      图2.2.3是MPEG-1图像压缩编码器方框图。代表亮度Y和色度分量CB、CR的二进制数码化信号,首先进入帧改组器(或称帧重排电路),将画面分割为片、宏块、区块。区块经过比较

      运算电路再进入DCT电路、量化器、VLC电路,取得已压缩数据。再将数据送到多路混合器和传输缓冲器。传输缓冲器用于暂存压缩数据,并按照控制指令的先后按时序输出数据。该缓冲器通过调整器(又称为量化自适应器)与量化器相连接。调整器可用来检测缓冲器的缓冲区的数据暂存程度,并根据暂存数据量自动调整量化步长。在编码器内设置有反馈通路,它主要包括反量化器(Q-1)、离散余弦逆变换(IDCT)、相加器以及IPB画面帧存储器等。反馈回路用于预测图像产生,进行画面分类处理(计算、区分并处理IPB画面),主要用于帧间数据压缩编码处理。还有,运动预测和补偿电路可用于运动补偿。

      2、图像解压缩电路方框图

      图像解压缩电路简称为解压电路、解码电路。VCD视盘机内,经过数字信号解调电路(CD-DSP)处理后,输出压缩编码视频数据流,需要经过视频解压缩电路进行数据解压缩,恢复为未压缩的视频信号。解码过程是编码的逆过程,图2.2.4是MPEG-1视频解压缩电路方框图,其电路结构比编码器稍简单一些。

    图2.2.4 MPEG-1视频解压缩电路方框图

      来自CD-DSP电路的压缩编码信号送到输入缓冲器,然后进入去混合电路,将图像的编码模式标志,运动向量(位移矢量)和图像数据分离开,分别送往帧存储器和解压缩主通道电路。

      主通道要处理I、P、B帧数据,这些数据已经按照图像编码系列的规定,以数据封包头标指出,这些数据分别暂存在缓冲存储器的存储区内,根据数据量大小暂存在容量不同的存储器区中。在微处理器控制下,先将I画面数据按序取出,送到VLC(可变长度码解调器),按照ROM存放的可变长度码对照表,逐一将编码时压缩的码位恢复为压缩前的DCT量化值,再将各区块分为64个数据的量化值逐位乘以反量化参数,这些参数位于ROM中存放的64位视觉心理模式量化表的相对位置,重新恢复为DCT频率系数,完成反量化过程。

      经过反量化的数据,再送入IDCT(离散余弦逆变换)电路。这是另一次逆变换,也是通过查表法,将反量化值所代表的各频率余弦分量的幅值进行逆变换,重新恢复为DCT变换前的图像(Y、CB、CR)取样数据,从而取得代表图像压缩前的区块信息。4个区块的信息组成一个宏区块,若干个宏区块组成片,再由若干片组成完整画面的总数据,这就是I帧画面。这些繁重的相加工作都需要在加法器中进行。

      恢复出来的I帧画面数据存入帧存储器。I画面与后续输入的P画面数据相加,可恢复出P画面,P画面也存入帧存储器。然后根据运动矢量和运动后图像差值(即B画面数据),与I、P画面存储数据在加法器中相加,并受编码模式信号的控制,以便决定I、P图像的成分多少,从而恢复出不同前后的B帧画面。经以上处理所得I、P、B各种画面数据都需要存入缓冲存储器,还要根据编码模式的指示及输出制式的帧频要求,按照I、B、B、P、B、B、P、B、B…B、I、B、B、P、B…的正常顺序进行重新编排,按照一定的速度从帧重排电路输出。输出的解压缩数据送到D/A转换器,转变为R、G、B三基色模拟信号。

      通常,在解压缩电路还要辅设视频编码器和调制器。视频编码器可将三基色信号编码为NTSC/PAL制彩色电视信号,并加入同步、消隐、色同步和彩色副载波信号等,以视频模拟全电视信号形式输出。这种输出形式的信号需要输送到电视接收机的AV输入端口。但是,有些老式电视机没有设置AV输入端口,为了适应这种现象,输出的视频全电视信号需要再一次进行高频调制,利用调制器以某个特定频道的RF调幅形式输出电视信号。此时,VCD机需要设置RF输出端口,其输出信号可直接送到电视机的天线输入端口。

    展开全文
  • 赫夫曼编码广泛地用于数据文件压缩。其压缩率通常在 20%~90%之间 赫夫曼码是可变字长编码(VLC)的一种。Huffman 于 1952 年提出一种编码方法,称之为最佳编码 11.3.2 原理刨析 通信领域中信息的处理方式-----赫夫曼...

    11.3 赫夫曼编码

    11.3.1 基本介绍

    1. 赫夫曼编码也翻译为 哈夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式, 属于一种程序算法。
    2. 赫夫曼编码是赫哈夫曼树在电讯通信中的经典的应用之一。
    3. 赫夫曼编码广泛地用于数据文件压缩。其压缩率通常在 20%~90%之间
    4. 赫夫曼码是可变字长编码(VLC)的一种。Huffman 于 1952 年提出一种编码方法,称之为最佳编码

    11.3.2 原理刨析

    通信领域中信息的处理方式-----赫夫曼编码
    步骤如下:
    传输的字符串 i like like like java do you like a java

    1. i like like like java do you like a java
    2. d:1 y:1 u:1 j:2 v:2 o:2 l:4 k:4 e:4 i:5 a:5 :9(空格:9) // 各个字符对应的个数
    3. 按照上面字符出现的次数构建一颗赫夫曼树, 次数作为权值
      步骤:

    构成赫夫曼树的步骤:

    1. 从小到大进行排序, 将每一个数据,每个数据都是一个节点 , 每个节点可以看成是一颗最简单的二叉树

    2. 取出根节点权值最小的两颗二叉树

    3. 组成一颗新的二叉树, 该新的二叉树的根节点的权值是前面两颗二叉树根节点权值的和

    4. 再将这颗新的二叉树,以根节点的权值大小 再次排序, 不断重复 1-2-3-4 的步骤,直到数列中,所有的数据都被处理,就得到一颗赫夫曼树。
      在这里插入图片描述4) 根据赫夫曼树,给各个字符,规定编码 (前缀编码), 向左的路径为 0 向右的路径为 1 , 编码
      如下:
      o: 1000 u: 10010 d: 100110 y: 100111 i: 101
      a : 110 k: 1110 e: 1111 j: 0000 v: 0001
      l: 001 : 01(空格:01)

    5. 按照上面的赫夫曼编码,我们的"i like like like java do you like a java" 字符串对应的编码为 (注
      意这里我们使用的无损压缩)
      1010100110111101111010011011110111101001101111011110100001100001110011001111000011001111000100100100110111101111011100100001100001110 通过赫夫曼编码处理 长度为 133

    6. 长度为 : 133
      说明:如果不压缩转换成二进制的长度是 359 , 压缩了 (359-133) / 359 = 62.9%

    此编码满足前缀编码, 即字符的编码都不能是其他字符编码的前缀。不会造成匹配的多义性赫夫曼编码是无损处理方案

    注意事项
    注意, 这个赫夫曼树根据排序方法不同,也可能不太一样,这样对应的赫夫曼编码也不完全一样,但是 wpl 是一样的,都是最小的, 最后生成的赫夫曼编码的长度是一样,比如: 如果我们让每次生成的新的二叉树总是排在权值相同的二叉树的最后一个,则生成的二叉树为:

    在这里插入图片描述

    11.3.3 最佳实践-数据压缩(创建赫夫曼树)

    将给出的一段文本,比如 “i like like like java do you like a java” , 根据前面的讲的赫夫曼编码原理,对其进行数据 压 缩 处 理 , 形 式 如:
    “1010100110111101111010011011110111101001101111011110100001100001110011001111000011001111000100100100110111101111011100100001100001110”

    步骤 1:根据赫夫曼编码压缩数据的原理,需要创建 “i like like like java do you like a java” 对应的赫夫曼树。
    思路:前面已经分析过了。
    代码:(为了防止混乱,赫夫曼编码代码和下面的解码代码放在一起)

    11.3.4 最佳实践-数据压缩(生成赫夫曼编码和赫夫曼编码后的数据)

    我们已经生成了 赫夫曼树, 下面我们继续完成任务

    1. 生成赫夫曼树对应的赫夫曼编码 , 如下表: =01 a=100 d=11000 u=11001 e=1110 v=11011 i=101 y=11010 j=0010 k=1111 l=000 o=0011
    2. 使用赫夫曼编码来生成赫夫曼编码数据 ,即按照上面的赫夫曼编码,将"i like like like java do you like a java" 字符串生成对应的编码数据, 形式如:下:1010100010111111110010001011111111001000101111111100100101001101110001110000011011101000111100101000101111111100110001001010011011100
    3. 思路:前面已经分析过了。

    赫夫曼编码和解码代码实现

    赫夫曼编码实现文件的压缩和解压代码也在里面

    import java.io.*;
    import java.util.*;
    
    /**
     * @author zk
     * @version 1.0.0
     * @ClassName HuffmanCode.java
     * @Description TODO huffman编码
     * @createTime 2021年09月27日 12:52:00
     */
    public class HuffmanCode {
        public static void main(String[] args) {
            /*String str = "i like like like java do you like a java";
            byte[] bytes = str.getBytes();
            byte[] huffmanBytes = huffmanZip(bytes);
            System.out.println("huffman编码后:"+Arrays.toString(huffmanBytes));
            byte[] decode = decode(huffmanCode, huffmanBytes);
            System.out.println("huffman解码后:"+new String(decode));*/
    
            /*List<Node> nodes = huffmanTreeList(bytes);
            Node huffmanTreeRoot = createHuffmanTree(nodes);
            preOrder(huffmanTreeRoot);
            Map<Byte, String> huffmanCode = getHuffmanCode(huffmanTreeRoot);
            huffmanCode.forEach((k, v)->{
                System.out.print(k+"--"+v+"\t");
            });
            System.out.println();
            byte[] zip = zip(bytes, huffmanCode);
            System.out.println(Arrays.toString(zip));*/
    
            // 测试Huffman文件压缩
    //        String srcFile = "E:\\11.jpg";
    //        String dstFile = "E:\\11.zip";
    //        zipFile(srcFile,dstFile);
            // 测试Huffman文件解压
            String srcFile = "E:\\11.zip";
            String dstFile = "E:\\22.jpg";
            unZip(srcFile,dstFile);
        }
    
    
    
        // TODO ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓中间代码为huffman文件压缩和解码的相关方法↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
        /**
         *
         * @param srcFile 你传入的希望压缩的文件的全路径
         * @param dstFile 我们压缩后将压缩文件放到哪个目录
         */
        public static void zipFile(String srcFile, String dstFile){
            InputStream is = null;
            OutputStream os = null;
            ObjectOutputStream oos = null;
            try {
                is = new FileInputStream(srcFile);
                byte[] b = new byte[is.available()];
                is.read(b);
                os = new FileOutputStream(dstFile);
                oos = new ObjectOutputStream(os);
                // huffman压缩
                //把 赫夫曼编码后的字节数组写入压缩文件
                byte[] huffmanBytes = huffmanZip(b);
                oos.writeObject(huffmanBytes);
                //这里我们以对象流的方式写入 赫夫曼编码,是为了以后我们恢复源文件时使用
                //注意一定要把赫夫曼编码 写入压缩文
                oos.writeObject(huffmanCode);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (is != null){
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (oos!=null){
                    try {
                        oos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        /**
         * @param zipFile 准备解压的文件
         * @param dstFile 将文件解压到哪个路径
         **/
        public static void unZip(String zipFile,String dstFile){
            InputStream is = null;
            ObjectInputStream ois = null;
            OutputStream os = null;
            try {
                is = new FileInputStream(zipFile);
                ois = new ObjectInputStream(is);
                byte[] huffmanBytes = (byte[]) ois.readObject();
                Map<Byte,String> huffmanCode = (Map<Byte, String>) ois.readObject();
                byte[] bytes = decode(huffmanCode, huffmanBytes);
                os = new FileOutputStream(dstFile);
                os.write(bytes);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }finally {
                if (ois!=null){
                    try {
                        ois.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (os!=null){
                    try {
                        ois.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
        }
    
    
    
    
    
    
    
    
    
    
        // TODO ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑中间代码为huffman文件压缩和解码的相关方法↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
    
    
    
    
    
        // TODO ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓中间代码为huffman解码的相关方法↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
        //编写一个方法,完成对压缩数据的解码
        /**
         *
         * @param huffmanCodes 赫夫曼编码表 map
         * @param huffmanBytes 赫夫曼编码得到的字节数组
         * @return 就是原来的字符串对应的数组
         */
        private static byte[] decode(Map<Byte,String> huffmanCodes, byte[] huffmanBytes){
            // 获得huffmanBytes 数组对应的二进制字符串
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < huffmanBytes.length; i++) {
                byte b = huffmanBytes[i];
                boolean flag = (i==huffmanBytes.length-1);
                String string = byteToBitString(b, !flag);
                stringBuilder.append(string);
            }
    
            // 把huffmanCodes 反过来  接下来进行解码
            HashMap<String, Byte> map = new HashMap<>();
            for (Map.Entry<Byte, String> byteStringEntry : huffmanCodes.entrySet()) {
                map.put(byteStringEntry.getValue(),byteStringEntry.getKey());
            }
    
            // 把解码的数据先存放到list
            ArrayList<Byte> list = new ArrayList<>();
            for (int i = 0; i < stringBuilder.length();) {
                boolean flag = true;
                int count = 1;
                Byte b = null;
                while (flag){
                    // 截取字符串范围 start(包括)  end(不包括)
                    String str = stringBuilder.substring(i, i+count);
                    b = map.get(str);
                    if (b==null){
                        count++;
                    }else {
                        list.add(b);
                        flag = false;
                    }
                }
                i += count;
            }
    
            // 把list集合的数据放到 byte数组
            byte[] bytes = new byte[list.size()];
            for (int i = 0; i < list.size(); i++) {
                bytes[i] = list.get(i);
            }
            return bytes;
        }
    
        /**
         * 将一个 byte 转成一个二进制的字符串
         * @param b 传入的 byte
         * @param flag 标志是否需要补高位如果是 true ,表示需要补高位,如果是 false 表示不补, 如果是最后一个
        字节,无需补高位
         * @return 是该 b 对应的二进制的字符串,(注意是按补码返回)
         */
        public static String byteToBitString(byte b,boolean flag){
            int temp = b;
            if (flag){
                temp |= 256;  //按位与 256 1 0000 0000 | 0000 0001 => 1 0000 000
            }
            String str = Integer.toBinaryString(temp);
            if (flag){
                return str.substring(str.length()-8);
            }else {
                return str;
            }
        }
    
    
        // TODO↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑中间代码为huffman解码的相关方法↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
    
    
        /**
         * @Author zk
         * @Description //TODO
         * @Date 2021/9/27 15:52
         * @param bytes 待编码的数组
         * @return byte[] 使用huffman编码后的数组
         **/
    
        public static byte[] huffmanZip(byte[] bytes){
            List<Node> nodes = huffmanTreeList(bytes);
            Node huffmanTreeRoot = createHuffmanTree(nodes);
            Map<Byte, String> huffmanCode = getHuffmanCode(huffmanTreeRoot);
            return zip(bytes, huffmanCode);
        }
        /**
         * @Author zk
         * @Description //TODO
         * @Date 2021/9/27 15:31
         * @param bytes 待压缩的数组
         * @param huffmanCode 哈夫曼编码
         * @return void
         **/
    
        public static byte[] zip(byte[] bytes,Map<Byte, String> huffmanCode){
            // 1.利用huffmanCode 将bytes 转换成huffmanCode对应的字符串
            StringBuilder stringBuilder = new StringBuilder();
            for (byte b : bytes) {
                stringBuilder.append(huffmanCode.get(b));
            }
    
            //统计返回byte[] huffmanCodeBytes 长度
            int len = 0;
            if (stringBuilder.length() % 8==0){
                len = stringBuilder.length() / 8;
            }else {
                len = stringBuilder.length() / 8 + 1;
            }
            // 创建存储压缩后的bytes[]
            byte[] huffmanCodeBytes = new byte[len];
            int index = 0;
            for (int i = 0; i < stringBuilder.length(); i+=8) {
                String substring;
                if (i+8>stringBuilder.length()){
                    substring = stringBuilder.substring(i);
                }else {
                    substring = stringBuilder.substring(i,i+8);
                }
    
                huffmanCodeBytes[index] = (byte) Integer.parseInt(substring,2);
                index++;
            }
            return huffmanCodeBytes;
        }
    
        // 为了方便调用  重载getHuffmanCode()方法
        public static Map<Byte,String> getHuffmanCode(Node root){
            if (root !=null){
                getHuffmanCode(root,"",stringBuilder);
            }else {
                System.out.println("root node is null");
            }
            return huffmanCode;
        }
    
        /**
         * 功能:将传入的 node 结点的所有叶子结点的赫夫曼编码得到,并放入到 huffmanCodes 集合
         *
         * @param node          传入结点
         * @param code          路径: 左子结点是 0, 右子结点 1
         * @param stringBuilder 用于拼接路径
         */
        // 储存生成的哈夫曼编码
        static Map<Byte,String> huffmanCode = new HashMap<>();
        // 生成哈夫曼编码过程中拼接字符串
        static StringBuilder stringBuilder = new StringBuilder();
    
        public static void getHuffmanCode(Node node, String code, StringBuilder stringBuilder) {
            StringBuilder stringBuilder1 = new StringBuilder(stringBuilder);
            stringBuilder1.append(code);
            if (node != null) {
                if (node.data == null) {  //非叶子节点
                    //递归处理
                    //向左递归
                    if (node.left!=null){
                        getHuffmanCode(node.left,"0",stringBuilder1);
                    }
                    //向右递归
                    if (node.right!=null){
                        getHuffmanCode(node.right,"1",stringBuilder1);
                    }
    
                } else { // 叶子节点
                    huffmanCode.put(node.data,stringBuilder1.toString());
                }
            }
        }
    
        // 前序遍历huffmanTree
        public static void preOrder(Node root) {
            if (root != null) {
                root.preOrder();
            } else {
                System.out.println("root is null");
            }
        }
    
        public static Node createHuffmanTree(List<Node> nodeList) {
    
            while (nodeList.size() > 1) {
                Collections.sort(nodeList);
                Node leftNode = nodeList.get(0);
                Node rightNode = nodeList.get(1);
                Node parent = new Node(null, leftNode.weight + rightNode.weight);
                parent.left = leftNode;
                parent.right = rightNode;
                nodeList.add(parent);
                nodeList.remove(leftNode);
                nodeList.remove(rightNode);
            }
            return nodeList.get(0);
        }
    
        public static List<Node> huffmanTreeList(byte[] bytes) {
            List<Node> nodes = new ArrayList<>();
            Map<Byte, Integer> map = new HashMap<>();
            for (byte b : bytes) {
                Integer count = map.get(b);
                if (count == null) {
                    map.put(b, 1);
                } else {
                    map.put(b, count + 1);
                }
            }
    
            Set<Map.Entry<Byte, Integer>> entries = map.entrySet();
            for (Map.Entry<Byte, Integer> entry : entries) {
                nodes.add(new Node(entry.getKey(), entry.getValue()));
            }
            return nodes;
        }
    }
    
    
    class Node implements Comparable<Node> {
        Byte data;
        int weight;
        Node left;
        Node right;
    
        public Node(Byte data, int weight) {
            this.data = data;
            this.weight = weight;
        }
    
        @Override
        public int compareTo(Node o) {
            return this.weight - o.weight;
        }
    
        public void preOrder() {
            System.out.println(this);
            if (this.left != null) {
                this.left.preOrder();
            }
            if (this.right != null) {
                this.right.preOrder();
            }
        }
    
        @Override
        public String toString() {
            return "Node{" +
                    "data=" + data +
                    ", weight=" + weight +
                    '}';
        }
    
    
    }
    
    

    11.3.9 赫夫曼编码压缩文件注意事项

    1. 如果文件本身就是经过压缩处理的,那么使用赫夫曼编码再压缩效率不会有明显变化, 比如视频,ppt 等等文件【举例:压缩一个ppt】
    2. 赫夫曼编码是按字节来处理的,因此可以处理所有的文件(二进制文件、文本文件) [举例压一个.xml 文件]
    3. 如果一个文件中的内容,重复的数据不多,压缩效果也不会很明显
    展开全文
  • 基于matlab的jpeg图像编码解码,每部分都有注释,可以直接运行,很好的资源

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 83,242
精华内容 33,296
关键字:

压缩是编码还是解码