引言

随着多媒体技术及通信技术的快速发展,在嵌入式平台上实现连续图像压缩的需求已变得日益广泛。常用的系统结构是独立处理器配和专用图像压缩芯片或者是只用一个高主频的数字信号处理器完成主要功能。

但随着大规模集成电路技术的发展及市场对产品低成本的要求不断提高,一种新的在嵌入式平台上实现连续图像压缩的系统结构正逐步成为上述两种系统结构的替代者。这种新的结构就是Altera公司提出的基于Avalon总线的SOPC结构。SOPC结构可以把处理器,图像压缩IP核,通讯单元及控制单元集成到一块FPGA芯片上。较之以往的结构,不但极大地节约了系统资源和成本,更是减少了系统的复杂度。尤其是可以用硬件电路实现软件处理极为耗时的运算功能,使得在低主频,低功耗的嵌入式平台上实现连续图像实时压缩变为可能。连续图像压缩IP核也成为整个SOPC系统的一个关键部件。现有的MPEG系列,H26X系列IP核对FPGA的芯片资源及性能要求极高,难以满足系统对低成本,低功耗的要求。所以设计一个适合低成本,低功耗的FPGA芯片的视频压缩IP核变得极为重要。这即要求压缩算法的复杂度不能太高,又要求有适当的压缩效果,Motion JPEG算法恰好满足上述要求。

1.Motion JPEG压缩算法简介

Motion JPEG是一种基于静态图像JPEG压缩标准的动态图像压缩标准,压缩时将连续图像的每一个帧视为一幅静止图像进行压缩,从而可以生成序列化运动图像。压缩时不对帧间的时间冗余进行压缩,虽然降低了压缩比,但也同时降低了复杂度,易于硬件电路实现。Motion JPEG标准所根据的算法是基于离散余弦变换和熵编码,关键技术有二维离散余弦变换、量化、差分编码、霍夫曼编码和游程编码等。单帧的处理过程如下图所示。

2.IP核的结构设计

2.1二维离散余弦变换模块

二维离散余弦变换是由一维离散余弦变换衍生而来的,所以可以用两个级联的一维离散余弦变换实现。实现时要注意第一级一维离散余弦变换模块产生的结果不能直接作为第二级一维离散余弦变换模块的输入量,而是等到第一级一维离散余弦变换模块产生的结果形成一个8×8的矩阵后,对这个8×8的矩阵做转置处理,再把转置后得到的矩阵按行扫描的顺序输出,这时输出的数据才能作为第二级一维离散余弦变换模块的输入量。

二维离散余弦变换模块处理的对象是8×8的像素矩阵,来自其前一级模块预处理模块。预处理模块一个时钟周期只能输出一个数据,为了满足二维离散余弦变换模块中第一级一维离散余弦变换模块一次运算需要8个输入数据的要求,利用一个串行转并行模块,把每个时钟周期内预处理模块输出的一个数据缓存起来,当数据凑满8个后再一次传给二维离散余弦变换模块。

二维离散余弦变换模块输出的数据是8×8的二维离散余弦系数矩阵,传递给其后一级模块量化模块。量化模块一个时钟周期只能接收一个数据,为了满足二维离散余弦变换模块中第二级一维离散余弦变换模块一次运算产生8个输出数据的条件,使用一个并行转串行模块,把每隔8个时钟周期二维离散余弦变换模块输出一次的8个数据缓存起来,在等待下一次二维离散余弦变换模块输出数据的8个时钟周期的等待隔内,把数据串行传给量化模块。

2.1.1第一级一维离散余弦变换模块

根据一维离散余弦变换的定义和cos函数的互补对称性,参考Weiping Li提出的Skew Circular Convolution的概念,能够使一维离散余弦变换达到非常精简的硬件架构。但是考虑到处理的对像是连续帧这个条件,速度就成为主要因素,因此没有完全依照其提出的方法,而是适当的增加电路面积来够造新的11阶全流水线结构,从而获得最大的运算速度。处理过程可划分为4个阶段。

阶段1:消耗3个时钟周期,完成8位有符号数的加减运算,结果为9位有符号数。

阶段2:消耗2个时钟周期,完成9位有符号数的固定系数乘法,因为乘的系数是小数,所以要把小数转换为二进制表示。

阶段2中的乘法操作是用EP2C35芯片中的28个嵌入式乘法单元实现的。EP2C35芯片中共有70个嵌入式乘法单元,一个嵌入式乘法单元可以实现两个9位数的乘法,两个嵌入式乘法单元并联可以实现两个大于9位小于18位数的乘法。根据这一特性,假如在阶段1中实现乘法会因输入量是8位而浪费了一个嵌入式乘法单元的全部能力,假如在阶段3或阶段4中实现乘法又会因输入量或系数中每个元素的长度大于9位而耗用两个嵌入式乘法单元去完成一个乘法操作,又很不经济。所以在阶段2中实现乘法操作是最为合理的,阶段2的输入量为9位,恰好耗用一个个嵌入式乘法单元。这也同时要求乘法中的固定系数的位数为9位,位数的过多或过少都不能充分利用芯片上的硬件资源。

阶段3:消耗3个时钟周期完成9位有符号数的加减运算,结果为9位有符号数。一般的9位有符号数的加减的结果应该用10位有符号数来表示,因为进位操作或借位操作导致结果的位数从9位增长到10位。但是阶段3的输入序列是由9位有符号数乘以固定系数得到的,且固定系数的值都小于0.5,所以序列之间的加减操作不会引起进位操作或借位操作,9位有符号数足够表示加减操作的结果序列。

阶段4:消耗3个时钟周期完成9位有符号数的加减运算,结果为10位有符号数,也是第一级一维离散余弦变换的最终结果。

2.1.2并行矩阵转置模块

常用的8×8矩阵转置的方法是,先把64个矩阵元素按行扫描的顺序一个个串行输入到一个RAM(64个存储空间)中,然后按转置后的矩阵的行扫描顺序把RAM中64个矩阵元素逐次输出。这种方法最少需要128时钟周期才能完成一个8×8矩阵的转置。可称其为串行矩阵转置。串行矩阵转置一次接收一个输入数据与一维离散余弦变换一次产生8个输入数据在传输速率上不匹配,第一级一维离散余弦变换模块需要在串行矩阵转置工作期间等待,当串行矩阵转置完成后再产生新的一维离散余弦变换系数并传递给串行矩阵转置模块。这也是二维离散余弦变换难以设计为全并行的原因。

本文提出了一种新的矩阵转置方法,称为其并行矩阵转置。用8个RAM(每个RAM有8个存储空间)代替一个RAM(有64个存储空间),再增加两个8通道的旋转多路器,同时配合对8个RAM的变址读操作。一次输入原始矩阵的一列,20个周期后完成一个8×8矩阵的转置。比串行矩阵转置节省至少100个时钟周期。

并行矩阵转置模块的写操作:每个时钟周期变换一次RAM_BANK的接入顺序,每个RAM_BANK每次写入地址随时钟周期而顺序递增从,0增加到7,每次增加1。

并行矩阵转置模块的读操作:每个时钟周期变换一次RAM_BANK的输出顺序,每个RAM_BANK每次读出地址随时钟周期而变化,且同一时钟周期内不同的RAM_BANK有不同的读出地址。

在实际设计时,通过接入旋转多路器实现每个时钟周期改变第一级一维离散余弦变换输出端与RAM_BANK的接入顺序,通过输出旋转多路器实现每个时钟周期改变一次RAM_BANK与第二级一维离散余弦变换输入端的接入顺序。这两个旋转多路器,都是以8个时钟周期为1个旋转周期,在1个旋转周期内实现8个通道的旋转接通。每个RAM_BANK的读写地址都依靠同一个地址产生器产生,在一个时钟周期内所有RAM_BANK的写地址相同,读地址不同。

在设计具体的硬件电路时,一个RAM_BANK就是一个双端口RAM,为提高工作效率,每个RAM_BANK实际含有32个存储单元而不是理论上的8个存储单元。这样一个RAM_BANK就可以分为4个区块,每个区块8个存储单元,在写入一个区块的同时可以读出前一个已经写入数据的区块,形成一种乒乓缓冲的结构,用电路面积换取运行效率。整个矩阵转置模块是一个13阶的流水线结构,每阶流水耗用一个时钟周期。

图5是并行矩阵转置模块在ModelSim仿真软件中的时序仿真结果,仿真的时钟频率设定为100Mhz。从输入数据到开始得到结果,中间间隔了13个时钟周期。
2.1.3第二级一维离散余弦变换模块

第二级一维离散余弦变换模块在计算结构上与第一级一维离散余弦变换模块一样也分为4个阶段,完成一次运算耗用13个时钟周期,每个时钟周期完成一个流水线操作,实际的电路结构是13阶的流水线结构。

阶段1:消耗3个时钟周期,完成10位有符号数的加减运算,结果为11位有符号数。

阶段2:消耗4个时钟周期,完成11位有符号数的固定系数乘法。

这个阶段与第一级一维离散余弦变换模块的有所不同,没有使用嵌入式乘法单元,因为完成一个11位有符号数的固定系数乘法需要2个嵌入式乘法单元并联,完成整个阶段28个乘法操作需要56个嵌入式乘法单元,这占整个芯片嵌入式乘法单元的80%。一个模块过多的耗用嵌入式乘法单元会造成Cyclone II芯片在布局布线时跨越区域过大,从而导致布线延时过大。

第二级一维离散余弦变换模块在这个阶段中采用Altera的Mega function中的PARALLEL_ADD模块实现固定系数乘法操作。PARALLEL_ADD模块可以在4个时钟周期内完成8个16位有符号数的加法操作。11位有符号数的乘法转化为8个以内的11位有符号数的加法。固定系数都小于0.5,所以PARALLEL_ADD模块的结果可取11位。上述方法可理解为用并行加法来实现了一个简单的阵列乘法器。

这种用并行加法来实现了一个简单的阵列乘法,在不使用嵌入式乘法单元的条件下,是一种实现固定系数乘法的合理选择。在后来的验证中也证明,除了在计算过程比嵌入式乘法单元多两个时钟周期,电路面积有所增加以外,其计算精度和运行速度都基本与嵌入式乘法单元相同。运算过程中比使用嵌入式乘法单元多出的多两个时钟周期也会因为整个二维离散余弦变换是全流水结构而只增加了流水线的铺满时间,流水线一旦铺满后,这个两个时钟周期的延迟对运算延迟的影响将不再存在。

阶段3:消耗3个时钟周期,完成11位有符号数的加减法操作,结果仍为11位有符号数。结果仍为11位有符号数而不是12位有符号数的原因在第一级一维离散余弦变换模块的阶段3中已说明。

阶段4:消耗3个时钟周期完成11位有符号数的加减运算,结果为12位有符号数,也是二维离散余弦变换的最终结果。


2.1.4并行全流水结构的二维离散余弦变换模块

在不考虑二维离散余弦变换中输入数据的串行转为并行,输出数据的并行转为串行的条件下。即假设二维离散余弦变换模块的前一级模块可以在每一个时钟周期内为其提供64位数据,二维离散余弦变换模块的后一级模块可以在每一个时钟周期内接受其产生的96位数据。这时本文提出的并行全流水线结构的二维离散余弦变换模块只需要43个时钟周期就可以完成一个8×8矩阵的二维离散余弦变换。

图6及图7是该模块在ModelSim仿真软件中的时序仿真结果,仿真的时钟频率设定为100Mhz,43个时钟周期完成一次8×8二维离散余弦变换。


 

相同数据在Matlab中二维离散余弦变换结果见图8。



二者差值的分析:本文提出的二维离散余弦变换模块在对小数乘法结果的处理上采取了截尾方式,没有保留小数参加后续的运算。这与Matlab中64位的浮点运算的运算结果相比在变换系数中的直流系数上和第1个交流系数上相差的数值为8,其它系数可以认为是近似相同的。

对小数乘法结果的处理采取截尾方式的原因有两点:

1:保留小数位数过多会导致在后续的计算中输入数据位数过长,特别是对于全8路并行结构更易产生数据偏移或时钟偏移,同时也会降低模块的运行频率。

2:考虑到后续的量化模块,采用标准的量化表时直流系数的量化步长为16是差值8的2倍,而且量化过程中有对量化结果四舍五入的处理。所以差值8会在量化过程中基本被消除。这一点在后面的实际图像测试中得到了映证,压缩后的图像在视觉上与原始图像一致。

2.2量化模块

量化过程就是每个离散余弦变换系数除以各自的量化步长并对结果取整的过程。取整采用的是四舍五入的方式。
为了方便在FPGA中实现,离散余弦变换系数除以各自的量化步长改为乘以量化步长的倒数,量化步长的倒数使用二进制小数表示,选取长度为12位。乘法器用EP2C35中两个嵌入式乘法单元并联实现。因为离散余弦变换系数的长度为12位有符号的数,所以乘操作后得到的24位结果,保留高12位,低12位舍去。具体电路在算法结构上分为3个阶段,由5阶的流水线组成,完成整个量化操作耗时5个时钟周期。

2.3 ZigZag扫描模块

经过量化处理后的离散余弦变换系数的高频分量大部分为零。经过ZigZag扫描后,一个二维的8×8离散余弦变换系数矩阵变为一个一维含64个元素的序列,频率分量按从低到高排列,一维序列的高频部分会出现大量的连续零元素。产生这种一维序列的目的是为了使用熵编码中游程编码,进一步提高压缩效率。

从电路功能的角度上看,ZigZag扫描模块的作用就是把按列顺序输入的量化后的离散余弦变换系数以ZigZag扫描的顺序输出。处理时以一个8×8的离散余弦变换系数矩阵为一个处理单元,在接收一个以列顺序输入的8×8的离散余弦变换系数矩阵的同时,把前一个已经接收完毕的8×8的离散余弦变换系数矩阵以ZigZag扫描的顺序输出。一个拥有128个存储单元,每个存储单元长度为9位的双口RAM可以完成上述功能。RAM的128个存储单元在使用上分为两个操作区,每个操作区64个存储单元,一个操作区用于接收以列顺序输入的8×8的离散余弦变换系数矩阵,同时另一个操作区以ZigZag扫描的顺序输出前一个已经接收完毕的8×8的离散余弦变换系数矩阵。两个操作区轮换交替,形成一种乒乓操作模式。在这里使用乒乓操作是为了保持整个IP核的整体全流水线设计。

乒乓操作是一种常用于数据流控制的处理技巧。其特点是相互配合切换多个存储单元并完成数据的处理,把处理后的数据没有停顿的运送到下一个处理模块。把一个乒乓操作模块当作一个整体,从其两端看数据,输入数据和输出数据都是连续不断的,没有任何停顿,因此非常适合流水线结构。图11是ZigZag扫描模块在ModelSim仿真软件中的时序仿真结果,仿真的时钟频率设定为100Mhz。输入数据是一个8×8的二维矩阵。按列扫描顺序输入的如下矩阵:

14 

15 

27 

28 

13 

16 

26 

29 

42 

12 

17 

25 

30 

41 

43 

11 

18 

24 

31 

40 

44 

53 

10 

19 

23 

32 

39 

45 

52 

54 

20 

22 

33 

38 

46 

51 

55 

60 

21 

34 

37 

47 

50 

56 

59 

61 

35 

36 

48 

49 

57 

58 

62 

63 



2.4熵编码模块

熵编码与前面介绍的量化都是数据压缩的手段,但二者有所不同,熵编码利用信号的统计特征来降低位率,理论上不会丢失信息,量化会丢失信息。实现熵编码有多种方式,本文根据ISO/IEC10918协议,使用了游程编码和霍夫曼编码两种方式。游程编码的原理是把沿一定方向排列的等大小量化值的离散余弦变换系数作为连续的整体,用特定码字替代这种连续的整体就会达到数据量减少的效果。霍夫曼编码是一种变长编码,将多次出现的代码用较短的码字代表,很少出现的代码用较长的码字代表。产生哈夫曼编码要求扫描两遍原始数据,第一遍扫描是为了在原始数据中精确地统计每个值出现的频率,第二遍利用构造的哈夫曼树得到编码,两次扫描耗时巨大,因此数据压缩难以满足实时性要求。ISO/IEC10918协议中在对大量8位精度图像的平均统计基础上,给出了4个合适大多数应用的个哈夫曼码表。在实现硬件电路时把哈夫曼码表存储在片上ROM中,使用时直接查找。因为离散余弦变换中直流系数和交流系数分别使用不同的码表,且直流系数不需要进行游程编码,所以直流系数和交流系数使用不同的模块来处理。

2.4.1直流系数处理模块

直流系数是8×8矩阵内64个像素均值的度量,是包含了整个图像能量的重要部分。利用相邻的8×8矩阵的直流系数具有很强的相关性,对直流系数使用差分压缩编码。前一个8×8矩阵的直流系数作为当前矩阵的直流系数的预测值,求出现实值和预测值之间的差值后,再对差值做霍夫曼编码。在硬件实现时,直流系数只有一个值,所以不需要做游程编码,对其处理的第一步就是求得其与预测值之间的差值。根据差值的大小和正负查找存储在片上ROM的标准霍夫曼码表,得出前缀代码和前缀代码长度,同时根据ISO/IEC10918协议中提出的尾码产生方式得到对应的尾码代码及尾码代码长度。上述处理完成之后再经过合并前缀代码与尾码代码为霍夫曼代码,则直流系数的熵编码完成,之后等待被变长编码模块封装。具体的实现电路由4阶流水线组成,直流系数的熵编码处理过程耗时4个时钟周期。

2.4.2交流系数处理模块

交流系数首先要现经过游程编码处理。在游程编码中非零交流系数前的零交流系数的个数是游程长度,前缀代码及尾码代码的含义与直流系数中的一致,只不过在交流系数处理中游程长度和前缀代码重新组合为一个新的查找索引来查找新的前缀代码。ISO/IEC10918协议中给出的交流系数标准霍夫曼码表里有两个特殊的代码。

一个特殊代码ZRL,ZRL代表游程编码中游程长度大于16,如果游程长度大于32,48,56,分别用1个ZRL,2个ZRL,3个ZRL表示,盈余的游程长度加入下一个游程长度计算中。ZRL代码只有前缀代码,没有尾码代码。为了方便实现,直接把ZRL的尾码代码长度设为零,起到屏蔽尾码的作用。另一个特殊代码EOB,EOB代表最后一个零行程中只有零元素直接代表当前矩阵的游程编码已经扫描结束,若当前矩阵的最后一个交流系数是非零数的则以正常结束一个游程长度的计数作为当前矩阵游程编码的结束。同ZRL类似,EOB也没有尾码代码,所以使用同样的处理手段。具体的实现电路由4阶流水线组成,交流系数的熵编码处理耗时4个时钟周期。

2.4.3交织模块和冗余ZRL消除模块

交织模块的作用是把前缀代码和尾码代码合并为一个霍夫曼代码,合并后的代码易于进行下一阶段的变长编码操作,变长编码操作过程需要的移位位数由前缀代码长度加上尾码代码长度做和得到,这个求和过程也在交织模块中实现。该模块具体实现电路由2阶流水线组成。

冗余ZRL消除模块不是ISO/IEC10918协议的一部分,但它源于标准协议里对EOB的定义。从EOB之前直到最近一个的非零交流系数出现,中间产生的ZRL都是可以消除的。基于全流水线结构的电路设计一般很难满足这一要求,原因是全流水线结构的电路每级产生的结果都会直接传递给下一级,不对结果作保留。唯一的方法就是构造一个同步FIFO来缓存前几个时钟周期内产生的结果,根据之前输入的数据量和当前输入的数据量是否满足ZRL的冗余条件而选择性的对FIFO输出的结果做屏蔽。屏蔽的手段就是把FIFO输出数据的对应的霍夫曼代码长度清零。这样在下一步的变长编码中会因ZRL的霍夫曼代码长度为零而消除冗余的ZRL。

2.4.4变长编码模块

变长编码的作用是把交织编码输出的含有不等长有效位的霍夫曼码字,提取其中的有效位并将其组合为一个连续的32位码流。

编码原理是把交织编码输出的含有不等长有效位的霍夫曼码字向右位,移动的位数是前一个霍夫曼码字的代码长度。移位完成后的当前霍夫曼码字与提供移动的位数的前一个霍夫曼码字做或运算,同时累加两个霍夫曼码字的代码长度。累加和大于24时表明变长编码的第一步完成。第二步是检查前一步产生的24位封装结果中是否有FF字节,若有则直接在FF字节后面添加00字节。并不是所有的24位封装结果都需要在FF字节后面添加00字节,所以在第二步处理中还有移位处理。移位处理采用的方法与第一步移位处理的方法相同的,把24位封装结果和添加00字节后的32位封装结果,统一封装为32位结果输出。这个32位数据也是整个IP核输出的最终压缩结果。

3.基于SOPC结构的实际验证系统

经验证,Motion JPEG IP核可以实时处理由NTSC制式摄像头采集经ADV7181处理后输出的CCIR656标准数据,完成对连续视频帧的实时压缩。

验证系统结构如图15所示,lineswitcher模块把CCIR656标准数据的亮度分量以跳址写入的方式通过Multi-Port SDRAM Controller模块写入到SDRAM中,亮度分量数据从隔行扫描变为逐行存贮。Multi-Port SDRAM Controller模块是一个工业级的SDRAM控制器,可以将一个SDRAM数据端口仿真成四个虚拟的数据端口(两个写端口+两个读端口)。SDRAM在存储空间使用上划分为4个区块,在lineswitcher模块写入一个区块的同时,Motion JPEG IP核读出前一个已写入亮度分量的区块。Motion JPEG IP核输出端是一个Avalon总线上的具有流控制属性从端口。DMA控制器与Motion JPEG IP核以流控制的方式进行数据传输,并把数据转移到SRAM中。整个过程无需NIOSII处理器干预,只须等DMA控制器写满SRAM后以中断的方式通知NIOSII处理器以使其挂起Motion JPEG IP核,防止存入SRAM中数据被覆盖。最后使用DE2_Control_Panel传输SRAM中压缩后的数据到PC中,便可看到采集后图像经压缩后的效果。NIOSII处理器的作用是初始化DMA控制器和通过I2C总线设置ADV7181。

考虑到芯片上的资源,验证时只对ADV7181输出的亮度分量进行压缩,舍弃了色差分量。虽然没有了色差分量,但是仍然可以得到清晰直观的验证效果。

4.结论

本设计主要有以下几个特点。

1:以全流水线结构来实现Motion JPEG视频压缩IP核。

虽然流水线技术已经是一种众所周知的技术,但是现有的Motion JPEG视频压缩IP核仍未实现全部流水线结构,一般多以状态控制模块为核心来协调各个子模块。这就导致系统中最慢的子模块在处理数据时,其它子模块只能等待,对数据块访问的效率低下。同时由于数据块的被多个子模块所使用而又需要复杂的仲裁机制。本文提出的全流水线结构把整个处理过程分解为198个小操作,每个时钟周期内由一阶流水线完成一个小操作。当整个流水线铺满之后,整个数据处理过程中没有等待延迟,没有仲裁协议,大幅提高了系统的运行效率并降低了系统的复杂度。

2:并行矩阵转置结构的提出及基于并行矩阵转置的并行二维离散余弦变换结构。

并行矩阵转置结构较之以往的串行矩阵转置在处理8×8矩阵上至少节省了100个时钟周期。二维离散余弦变换在采用了并行矩阵转置之后,也实现了全部并行处理,43个时钟周期完成了一次二维离散余弦变换,效率提高显著。

3:因采用全流水线结构而取得的较高的运行频率。

本文设计的Motion JPEG视频压缩IP核在Quartus II 6.0中进行静态时序分析,得到的最高运行频率是150Mhz。

现有IP核与本文设计的IP核的运行频率比较

实际验证时不仅对处理CCIR656标准数据的能力给予验证,同时也为100Mhz的运行频率进行了验证,结果证明IP核可以在100Mhz的运行频率下正常工作。验证方法是把7帧952×568的亮度分量文件存入SDRAM中作为原始数据,整个验证系统以100Mhz频率运行,经过0.05秒完成7帧的压缩,帧率可达147 frame/s。压缩后图像的大小为原来亮度图像的十分之一。

4:可以在低成本,低功耗,高密度的CycloneII系列FPGA芯片上运行,通过Avalon总线与NIOSII处理器构成SOPC系统,为将来实际产品的设计搭建了一个良好的平台。