2009年8月23日星期日

全球船运业危机

2009年8月23日 南方都市报
全球经济危机正在重创船运业:需求锐减,价格下降,船坞停满了空仓的货船。危机引发残酷竞争,部分公司必然成为牺牲品。单是德国的赫伯罗特船务公司就需要17.5亿欧元才能避免倒闭。
庞大的安德罗墨达号停靠在汉堡布尔夏德码头,无数的补给船和集装箱堆垛车像蜜蜂一样在它周围忙碌。安德罗墨达号是世界上最大的集装箱船之一,号称拥有10万马力的引擎,能运载11400个集装箱,长363米,造价高达1.11亿欧元,几个月前才在韩国完工交货。
安德罗墨达号是为经济繁荣期建造的。那时候这个世界似乎对集装箱、庞大货船和船坞有着无尽的需求。
升降吊桥忙着把装卸堤上的集装箱吊到甲板上。安德罗墨达号上有着广阔空间,能容纳18×86×19个集装箱。最后,只有2/3的空间被用到。"这还不算糟糕,"船长伊凡·波扎尼可说,"至少在目前的形势下。"
第二天,安德罗墨达号启程,开往中国。那里也是它68天往返旅程的起始点。安德罗墨达号专门跑东亚和北欧航线,这条线堪称全球贸易高速公路。它定期把中国生产的电视机、移动电话、T恤等货物运往西方,再把工件、机器和空货柜运回东方。
不久之前,船运业还是经济全球化的动脉和最大的受益者,使得各种商品以越来越快的速度在全球流通。自从中国成为世界工厂之后,船运业年复一年持续增长。2008年,共有大约5亿个标准集装箱飘洋过海,比2000年翻了一番。
每一年都有更新更庞大的货船诞生,港口被扩建,航班增加。世界集装箱舰队的总载货量从2000年代的400万个增加到今天的1250万个。
在经济膨胀的年代,许多人发家致富。这其中包括货船老板、银行家和投资人。汉堡港受益颇大。在过去10年,这个德国北部港口城市成为世界金融和船运中心。德国人拥有全球35%的集装箱船,近60%的船运银行的总部位于汉堡。汉堡的赫伯罗特(H
apag-Lloyd)成为世界最大的船运公司之一。
可现在,全球经济危机扼杀船运业的繁荣,变故似乎发生在一夜之间。船运业史无前例地停止增长,甚至开始萎缩。今年上半年,集装箱货运量减少16%.
新的超级轮船对于需要运输的货物而言太大,常常无法装满。花费几十亿欧元扩建的码头现在完全用不上。主要航线运营商、船运银行处于破产边缘。经济全球化曾经的最大受益者现在即将成为它的最大牺牲品。
"还从未出现过这样的危机,"世界最大海上货运商德讯公司(Kühne+N
agel)的首席执行官莱茵哈特·兰格说。2009年,全球船运公司亏损总计可能达到200亿美元。英国海事报《劳埃德报》说,"集装箱运输正陷于全面惊慌中。"
德鲁里船运咨询公司警告说:"船运业正站在深渊边缘。"这种惊慌感在汉堡尤其明显。
危机象征
100多年来,赫伯罗特总部大楼在阿尔斯特湖边巍然耸立。威廉二世时期建筑风格的大门上铭刻着:"世界是我的战场。"这座大楼是汉堡作为世界重要港口城市的象征,也显示了这家有着160年历史的公司在世界船运业的地位。
但现在,它变成了危机的象征。具有讽刺意味的是,在船运界以高效率著称的赫伯罗特现在竟然要为公司的生死存亡而斗争。它的案例引起了轮船主、银行和世界船务执行官的关注。如果连赫伯罗特这样的公司也不能幸存,谁将是下一个牺牲品?
赫伯罗特在今年第一季度的亏损就达到2.22亿欧元,未来18个月,它需要近17.5亿欧元才能持续营业。"以目前的价格,我们的所有线路都是亏损,"董事会成员乌尔里希·柯兰尼希指出了他们所面临的困难。现在,将一个集装箱从亚洲运往欧洲只能得到500美元———比成本还低300美元。1年前,每个集装箱的运费还高达1500美元。
货物消失
虽然船运业的盈利存在周期性波动,但船运公司认为当前的困境绝非周期性低潮,情况已经急剧恶化。"在过去,货物总是源源不断,"柯兰尼希说。现在已经不比从前。由于西方消费锐减,东方生产跟着紧缩,全球集装箱舰队庞大的空间再也无法装满。运费骤然减少意味着所有船运公司都面临严重亏损。
船运公司尝试提高价格,但收效甚微。最近,船运公司将亚洲至欧洲的运费提高了500美元。
然而,高运费对消费者和生产商几乎没有什么影响,因为海运成本几乎不进入流通。一台电视从亚洲运往欧洲的成本为10美元,一台吸尘器只要1美元,一瓶啤酒只要1美分。
集装箱的发明使这样的低廉运费成为可能。可以说集装箱的普及是全球化前进的最大推动力。如果没有这些钢铁盒子,将无法想象中国崛起成为世界主要经济实体。运费越低廉,西方公司越有理由将生产转包到遥远的、劳动力廉价的地方。"中国工厂大多没有仓储能力,"德讯公司执行官兰格说,"它们可以说是直接在集装箱中生产。"
事实上,运费如此的低廉,即使把西班牙的西红柿运往中国,加工成番茄酱再运回欧洲也是划算的。荷兰郁金香花球被运往新西兰栽培,成熟之后再运回荷兰准备采摘上市。
残酷竞争
在目前的经济危机形势下,为了争夺市场份额,那些财力雄厚的船运公司开始大肆杀价。一位船主说"正在上演一场殊死搏斗。"
世界船运业老大马士基航公司的首席执行官埃文德·科尔丁曾公开表示,如果世界上仅仅剩下那么几家船运公司,海上运输业的发展将更加健康。许多人将目前的灾难局面部分归咎于马士基。
虽然今年第一季度马士基同样亏损(3.73亿美元),但这家丹麦船运公司隶属于一家石油燃气公司,财大气粗,可以轻易把船运部门的损失分摊出去。
目前,世界主要船运公司无一盈利。新加坡海皇轮船公司(N O
L)亏损2.45亿美元,韩国韩进海运株式会社亏损1.1亿美元。
和马士基一样,这些公司有着强大的经济后盾。海皇船运的老板是新加坡政府投资基金淡马锡(T
em asek );中国的中远集团(C osco)和中海集团(C
hinaShip-ping)有政府资助。日本邮船(N Y K )同样有政府后盾。
赫伯罗特似乎在最糟糕的时候,被严重的危机所累。去年公司的大部分盈利被用于填补老旧母公司T
U I的亏空,导致这家汉堡船运公司几乎没有留下资金储备。去年年末,TU
I把赫伯罗特卖给了汉堡城市财团,与此同时还让它背上了13亿欧元的债务,对已经开始亏损的企业而言无异于雪上加霜。一位公司执行官说,"没有外界帮助,我们无法支撑下去。"
为了让这家历史悠久的德国船运公司度过危机,政府正考虑提供最多10亿欧元的贷款。原则上说,集装箱生意依然前景看好。全球贸易已有反弹趋势,运费有望跟着提高。"来自亚洲的货物数量已经增加,"赫伯罗特执行官柯兰尼希说。
管理咨询公司罗兰贝格的一份报告对德国最大的几家船运企业的未来生存前景进行了简评。结论认为赫伯罗特的前景相当乐观。两周前送交给银行的评估报告说,"赫伯罗特具备了船务运营商的所有成功因素。"
这份报告将被交给赫伯罗特的监事会。如果获得批准,将于本周向柏林政府提交贷款担保申请。此前,所有公司股东必须统一增资7.5亿欧元,此前董事会一直犹豫不决。
无论如何,裁员和减薪是不可避免的。此外,赫伯罗特还得摆脱租借到的船只。公司经营的128艘集装箱船中有一半是租来的。已经有30艘船被还给原来主人。其他船运公司也在采取同样策略。内幕人士说,瑞士M
SC已决定不再更新80艘货船的包租合同。法国C M
A公司据说有170艘船租赁合同即将到期。
对那些船租给其它公司的船运公司而言,这当然不是什么好消息。
被重创的汉堡
在全世界4619艘集装箱船中,1644艘为德国人所有。在刚结束的经济繁荣时期,约12家汉堡船运公司和当地银行及投资基金一起成为投资建造新集装箱船的主要力量。很多时候,公司并不自己经营这些船只,而是把它们租出去,通常连同船员一起。
过去几年的繁荣在一定程度上也得益于低廉的利率和充足的现金。在节税政策和高回报的刺激下,投资者和银行将数千亿美元投入船运业。
在汉堡,凭借和金融市场、船运公司的传统良好关系,许多人知道如何利用天时地利。一群低调的德国船主逐渐取代了希腊船运王朝在集装箱船运领域的主宰地位。这群德国船主中有一位65岁的克劳斯-彼得·奥芬。他在33岁时从一家破产企业购买了第一艘货船,现在名下拥有128艘船。最多的时候他订购的新船达到20艘,包括世界上最大的集装箱货船,负载量14000个标准箱。另一位船主厄克·里克迈尔出生于船运世家,短短10年内将旗下舰队船只数量从46艘增加到118艘。还有一位叫彼得·杜勒的船主经营着200艘船。
虽然为人低调,汉堡船主们在最近几年大赚特赚。一位内幕人士说,"连公司的清洁工也开保时捷。"专营船运金融的银行数量也迅速增加,20年前还屈指可数,今天已接近60家。
考虑到全球化不可阻挡的势头,对集装箱船队需求仿佛是个无底洞。商品和货物以史无前例的速度在全球流通。一些设备老旧的船运公司装船的速度几乎赶不上来货的速度。
多米诺效应
"租赁货船将大量返回,"一位船务经理警告说,"对许多汉堡人而言,日子将变得难过起来。"另一位业内专家说:"大型租船公司将能度过这场危机,小企业之间必将有一场生死攸关的恶斗。"缺少了充足的租船收入,船运公司将无法偿还他们的银行贷款。
在过去发生危机的时候,船主和银行通常会允许租赁者延期付款最多达24个月。但这场全球金融危机让借款人比从前更加谨慎。"现在银行的决策者不是银行家而是风险经理,"一位沮丧的船主说。
致命的多米诺骨牌效应正在袭击全球船运业。航线运营商已经无法支付轮船租金,与此同时,船主和船运基金无法向银行偿还债务。进而导致一些银行陷入麻烦。
世界最大船运贷款银行汉堡北方银行发放的船舶建造贷款高达330亿欧元。银行已经采取措施减轻损失。"我们已经把申请贷款船运公司的必须具备的产权比率提高。"北方银行船运贷款部负责人哈拉尔德·库兹尼克说。银行还将风险准备金提高了20倍,从贷款额的0.05%增加到1%.
兼并压力
即便是银行业都不敢宣称汉堡船运业已经度过危机。库兹尼克说,"目前的市场低迷肯定会造成并兼并压力。那些过度贷款的公司将遇到麻烦。强大的对手将很快利用这个机会。"
航线运营商保持还贷能力当然是有利于整个行业的。如果运营商倒闭,银行和独立船主也将遭受巨大损失。正是由于这个原因,两个月前,汉堡银行和船主们才决定携手拯救陷入困境的智利船运公司CSA
V.通过换股方式,CSA V的债务减少了1/3.
赫伯罗特在去年6月写给其所租货船船主的一封信中指控他们"扭曲竞争",要求重新商谈"延租合同的租金问题"。这一努力显然取得成效,据赫伯罗特说商谈后的船舶租金已经减少了多达30%.
新船过剩
但是德国船运公司依然面临最严峻的问题。2008年年中前订购的1550艘新船将在未来几年内陆续交付使用。承建一方的亚洲船厂拒绝取消合同。
由于缺乏后续资金,一些投资者不得不放弃已经支付给船厂的预付金,有时预付相当于轮船总造价的40%.多数公司试图和船厂重新协商,至少争取延缓交货日期,期望在未来几年内形势会有所好转。
目前,在船厂的账上订购集装箱船的总运载量达到530万个标准箱,大约相当于目前全球集装箱船队总运载量的50%.即使,明年全球贸易彻底恢复,多余的新船仍将导致运费下降。
为减少运载量,已有大量货船提前退休,静静躺在船坞里生锈。具有讽刺意味的是,这些闲置的货船大多停泊在世界贸易主动脉———新加坡港的附近。
要拜访全球化剩余产品的坟墓需要从新加坡港出发,坐船45分钟。大批的退役轮船就停靠于此,其中包括各个级别的油轮、货轮,放眼望去是一片独特壮观的景致。
每年大约有13万艘船进入新加坡港,通常它们只在那里停留几天时间。一年前,船运业在新加坡还相当的红火,装得满满的集装箱船从这里进入马六甲海峡,前往欧洲,或者走相反方向到中国、日本和韩国。2005年,新加坡取代香港,成为世界第一大集装箱港口,年吞吐能力达到2900万标准箱。
当然,今天,依然有很多船停在新加坡港口内,但它们所装载的集装箱数量只有去年的25%.
庞大的货船也失去了往日的生气,船运公司将大多数船员遣散回家,这其中有很多是菲律宾人。剩下的少数必需船员整日待在驾驶舱内,或者忙着船只维护。
10%货船闲置
路西安·王是新加坡海事和港务局局长。他说,到今年4月,全球有约450艘集装箱船闲置———相当于全球集装箱船总数的10%.
在新加坡以北几千公里外的上海,危机的后果同样明显。上海船运交易所变得出奇的安静,大厅中一块显示屏上的数据无不在昭告集装箱运输量的锐减。一张表示中国集装箱运价指数的曲线图从去年起就一直呈下滑态势。
今年6月,中国出口量比去年同期减少21.4%.现在,信使送到交易所柜台需要盖章的表格只有薄薄的几张。一年前,他们手中通常有厚厚一叠文件。上海船运交易所行政主管王建民的办公室就位于显示屏西方。王主管说,早在2008年6月,华尔街爆发金融危机之前,他已经注意到上海港的集装箱处理量开始减少。北京希望在2020年前,把上海变成全球船运主要中心。
上海的目标
王主管认为,目前的船运危机为上海创造了赶上外国竞争者的机会。尽管危机当前,上海市政府依然在大力扩建港口和船厂。
位于上海入海口附近的洋山深水港表明了上海不惜代价发展船运业的决心。由于城市附近的水太浅,无法停泊大型油轮和集装箱船,一座位于深水区的海岛被铲除,变成了一个巨大的码头。根据目前计划,洋山将修建30个码头,2020年具备年1500万标准箱的处理量。
中国还制定了野心勃勃的造船业发展计划,力图取代韩国,成为世界第一造船大国。再往北,在长江入海口,上海正将长兴岛建设成一个庞大的综合造船基地。
长兴从前是一个以渔业和柑橘种植为主的小岛,现在变成了一个庞大建筑工地。多家国有造船厂已经在此开工。在炎炎夏日之下,成群的造船工人在挥汗如雨地焊接轮船部件。而旁边的工地上,建筑工人正在修建船厂工人宿舍。官方报纸报道,明年长兴岛居民数量将翻一番,达到20万人。
码头上,一块巨大的广告牌上说,把长兴建成造船业中心是中国国策。全球经济危机爆发后,北京已暂缓未来3年的新船厂建设计划,但已经开始的计划并未停工。
在政府支持下,中国北方港口天津已经成立了一个造船投资基金。目标是收购老旧船厂的股份,支持兼并收购订单被取消的船厂。这也是帮助造船厂度过危机的策略之一。
这一计划能否成功很大程度上取决于世界,尤其是美国经济的发展。德讯公司执行官兰格说"只有在美国经济复苏后,全球贸易才能重新启动。"
前景灰暗
在美国南卡罗来纳州的查尔斯顿看不到丝毫复苏的迹象。在美国屈指可数的大港口,竟然只看到一艘孤零零的船,M
SC布拉格号。去年,查尔斯顿港口才新装了多座巨大的起重机。现在,它们都闲着,部分原因是德国机械制造公司的订单锐减。
美国进口的德国产品中有45%经过查尔斯顿港。赫伯罗特等德国船运公司的集装箱高高地堆积在码头上。刚下组装线的宝马汽车在码头上排队等候。其中包括附近工厂生产的准备运往欧洲的SU
V和为美国市场生产的轿车。
港口发言人拜伦·米勒的衣领缝着一面小小的德国国旗,脸上挂着微笑。可当被问到当前形势时,他立刻摇头说,"不好,不好。"2008年7月至2009年5月,北欧地区运往查尔斯顿的集装箱数量下降了10万个。港口总吞吐量减少近18%.
根据去年批准的港口扩建计划,一个新码头将在2014年完工,使港口处理能力提高50%.
不仅在查尔斯顿,为了满足越建越大的集装箱船的需要,为了提高吞吐量,世界各地的港口都在疯狂扩建。看来,没有国家愿意因为港口缺陷而堵塞全球化高速公路。
可是,现在,交通堵塞早已成为历史。德鲁里咨询公司的专家估计,要到2010年,集装箱运输量才能恢复到2008年的水平。
虽然如此,查尔斯顿的港口扩建依然在继续。米勒说"也许现在世界上没有一个港口不曾考虑过延迟工期。"比如在迪拜,港口扩建计划已经无限期推迟。
汉堡也有一个7.5亿欧元的港口扩建计划。过去多年里,汉堡港一直是欧洲发展最迅速的港口,吞吐量在10年利翻了3倍,达到近1000万标准箱。港口官员预测,2015年还将达到2000万箱。但现在,再也没有人相信这些预言。事实上,2009年第一季度,汉堡港的吞吐量减少了25%,原来的扩建计划也被推迟。"毕竟,"一位船运经理说,"为什么要为也许永远不会到来的船只花费上亿欧元呢?"
文:A.Jung,T.Schulz&W.Wagner

BootLoader和内核image的关系

昨晚听舍友说起编译linux内核这个事情,作为计算机专业毕业的人,对这个东西完全不了解,太郁闷了。经过多方了解,总算是有了一个比较模糊的概念。在linux中内核image一般放在boot/grub或者boot/lilo中,经过舍友的讲解,大致了解了这个image的左右,但是谁来读这个grub.conf呢?舍友也不晓得。躺在床上想来想去,BootLoader!这个东西一定是比image加载更早的!联系工作中的一些开发流程,更加确定了这个东西,兴奋。。今天上午过来就查了一下BootLoader,下面是找到的一个比较入门的解说,大概跟我想的差不多。继续学习,交换机作为嵌入式系统的一种,一样是需要BootLoader来加载image运行的。
BootLoader是系统加电后运行的第一段代码。一般它只在系统启动时非常短的时间内运行。对于由DaVinci构成的嵌入式系统来说,这是至关重要的一步。
在PC中,整个BootLoader由BIOS(主板上固化的一段程序)、位于硬盘MBR区的OS
Loader一起组成。BIOS完成第一级引导加载工作,OS
Loader完成第二级引导加载工作(可能有些系统不只两级加载)。上电后,系统开始执行BIOS中的代码,这段代码负责进行硬件检测和资源分配,完成这步工作后,将按照CMOS中设定的顺序检索硬盘。BIOS将第一个检索到的硬盘上MBR中的内容读到系统RAM中,然后将系统控制权交给相应的OS
Loader。最后由OS
Loader负责将所要引导的操作系统的内核映象从硬盘上读到系统RAM中,然后跳转到内核的入口点上。
在由DaVinci平台构成的嵌入式系统中,通常不存在BIOS那样的一段固定内容的固化的程序。原因是PC平台尽管品牌等有差异,但通常都有相近甚至是相同的体系结构,遵循一个共同的工业标准,因而可以使用同一个BIOS代码来引导。而通常对嵌入式系统来说,即使是使用相同的架构,甚至是同一个CPU如都是DM6446来构建,但因为并不能遵的一个共同的工业标准。因而基于DaVinci平台构建的嵌入式系统上除非两者的各方面与引导过程相关设计完成一致,否则不能使用同一个BootLoader。
可以说BootLoader是一个由DaVinci平台构成的嵌入式系统的钥匙。没有这把钥匙就无法进入系统的大门。也就是说在完成自己的硬件研发后,首要的工作就是BootLoader的移植。
BootLoader的概念
BootLoader是系统加电启运行的第一段软件代码.回忆一下PC的体系结构我们可以知道,PC机中的引导加载程序由BIOS(其本质就是一段固件程序)和位于硬盘MBR中的引导程序一起组成。BIOS在完成硬件检测和资源分配后,将硬盘MBR中的引导程序读到系统的RAM中,然后将控制权交给引导程序。引导程序的主要运行任务就是将内核映象从硬盘上读到RAM中
然后跳转到内核的入口点去运行,也即开始启动操作系统。
而在嵌入式系统中,通常并没有像BIOS那样的固件程序(有的嵌入式系统也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由BootLoader来完成.比如在一个基于
ARM7TDMI core的嵌入式系统中,系统在上电或复位时都从地址
0x00000000开始执行.而在这个地址处安排的通常就是系统的BootLoader程序。
简单地说BootLoader就是在操作系统内核或用户应用程序运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图(有的CPU没有内存映射功能如
S3C44B0),从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核或用户应用程序准备好正确的环境。对于一个嵌入式系统来说,可能有的包括操作系统,有的小型系统也可以只包括应用程序,但是在这之前都需要BootLoader为它准备一个正确的环境。通常,BootLoader是依赖于硬件而实现的,特别是在嵌入式领域,为嵌入式系统建立一个通用的BootLoader是很困难的。当然,我们可以归纳出一些通用的概念来,以便我们了解特定BootLoader的设计与实现。
u BootLoader的移植和修改
每种不同的CPU体系结构都有不同的BootLoader。除了依赖于CPU的体系结构外,BootLoader实际上也依赖于具体的嵌入式板级设备的配置,比如板卡的硬件地址分配,RAM芯片的类型,其他外设的类型等。这也就是说,对于两块不同的嵌入式板而言,即使它们是基于同一种CPU而构建的,如果他们的硬件资源和配置不一致的话,要想让运行在一块板子上的BootLoader程序也能运行在另一块板子上,也还是需要作一些必要的修改。
u BootLoader的安装
系统加电或复位后,所有的CPU通常都从CPU制造商预先安排的地址上取指令。比如,S3C44B0在复位的都从地址0x00000000取它的第一条指令。而嵌入式系统通常都有某种类型的固态存储设备(比如:ROM、EEPROM或FLASH等)被安排这个起始地址上,因此在系统加电后,CPU将首先执行BootLoader程序。也就是说对于基于S3C44B0的这套系统,我们的BootLoader是从0地址开始存放的,而这块起始地址需要采用可引导的固态存储设备如FLASH。
u 用来控制BootLoader的设备或机制
串口通讯是最简单也是最廉价的一种双机通讯设备,所以往往在BootLoader中主机和目标机之间都通过串口建立连接,BootLoader程序在执行时通常会通过串口来进行
I/O,比如:输出打印信息到串口,从串口读取用户控制字符等。当然如果认为出口通讯速度不够,也可以采用网络或者USB通讯,那么相应的在BootLoader中就需要编写各自的驱动
u BootLoader的启动过程
多阶段的BootLoader能提供更为复杂的功能,以及更好的可移植性。从固态存储设备上启动的
BootLoader大多都是2阶段的启动过程,也即启动过程可以分为 stase1和stase
2两部分,具体功能将在下一节介绍。
u Boot Loader的操作模式
大多数BootLoader都包含两种不同的操作模式。"启动加载"模式和"下载"模式,这种区别仅对于开发人员才有意义。但从最终用户的角度看,BootLoader的作用就是用来加载操作系统,而并不存在所谓的启动加载模式与下载工作模式的区别。
启动加载(Boot
loading)模式:这种模式也称为"自主"(Autonomous)模式,也即BootLoader从目标机上的某个固态存储设备上将操作系统加载到RAM中运行,整个过程并没有用户的介入。这种模式是BootLoader的正常工作模式。因此在嵌入式产品发布的时候,BootLoader显然必须工作在这种模式下.
下载(Down loading)模式:在这种模式下
目标机上的BootLoader将通过串口连接或网络连接等通信手段从主机下载文件,比如:下载应用程序、数据文件、内核映像等.从主机下载的文件通常首先被BootLoader保存到目标机的RAM中然后再被BootLoader写到目标机上的固态存储设备中。BootLoader的这种模式通常在系统更新时使用。工作于这种模式下的BootLoader通常都会向它的终端用户提供一个简单的命令行接口。
在教学系统中提供的BootLoader中没有实现自主模式,可以通过修改代码来实现该功能。
u BootLoader与主机之间进行文件传输所用的通信设备及协议
最常见的情况就是,目标机上的BootLoader通过串口与主机之间进行文件传输,传输可以简单的采用直接数据收发,当然在串口上也可以采用xmode/ymode/zmode协议以及在以太网上采用TPTP协议。
此外,在论及这个话题时,主机方所用的软件也要考虑。比如,在通过以太网连接和TFTP协议来下载文件时,主机方必须有一个软件用来提供TFTP服务。

JPEG 原理详细实例分析及其在嵌入式 Linux 中的应用

JPEG 原理详细实例分析及其在嵌入式 Linux 中的应用 - Video - Embeded Linux
   


JPEG 原理详细实例分析及其在嵌入式 Linux 中的应用
作为一个基本的图像压缩方式,JPEG 已经得到了广泛的运用,但 JPEG 相关的基本原理,却经常被忽视,或解释得很不确切。 这里我们详细讨论一下 JPEG 的编码原理,并结合实例来给出一个更加感性的认识。JPEG 编码的详细过程有着诸多的信息可以给我们巨大的启发,我们在这里讨论的就是要对这些信息做一个具体细致的分析,通过我们的讨论,大家会对 JPEG 编码过程中出现的内容有一个确切的了解,并且能了解到这些内容的来龙去脉。

一、系统架构

本文以一个实际的产品为例,来说明 JPEG 在其中的应用。


 

本系统为一个嵌入式 Linux 网络播放器,主要的功能为播放家庭网络中的多媒体文件,在家庭客厅等环境中有着大量的应用,它可以给用户提供更方便快捷的媒体文件的播放方式,并能充分利用家庭音响系统的巨大功能,而非 PC 环境下有限的外部设备,大大改善了媒体文件的播放体验。

系统主要的功能包括:


 

本系统架构如下图:


 

本系统是基于嵌入式 Linux 的一个应用,使用的是 ucLinux 2.4.22,并使用了 microwindows 作为 GUI 界面,底层使用了 Linux kernel 的 FrameBuffer 作为显示输出。

此系统在两个方面使用到了 JPEG 库:

1、 UI 的显示,即各种人机交互界面,考虑到用户体验,所以大量使用了贴图来美化 UI

2、 JPEG 图片文件的全屏播放,包括用户手中的各种照片等

二、JPEG 概述

JPEG 是 Joint Photographic Experts Group 的缩写,即 ISO 和 IEC 联合图像专家组,负责静态图像压缩标准的制定,这个专家组开发的算法就被称为 JPEG 算法,并且已经成为了大家通用的标准,即 JPEG 标准。 JPEG 压缩是有损压缩,但这个损失的部分是人的视觉不容易察觉到的部分,它充分利用了人眼对计算机色彩中的高频信息部分不敏感的特点,来大大节省了需要处理的数据信息。

人眼对构成图像的不同频率成分具有不同的敏感度,这个是由人眼的视觉生理特性所决定的。如人的眼睛含有对亮度敏感的柱状细胞1.8亿个,含有对色彩敏感的椎状细胞0.08亿个,由于柱状细胞的数量远大于椎状细胞,所以眼睛对亮度的敏感程度要大于对色彩的敏感程度。

总体来说,一个原始图像信息,要对其进行 JPEG 编码,过程分两大步:

1、 去除视觉上的多余信息,即空间冗余度

2、 去除数据本身的多余信息,即结构(静态)冗余度

1、去除视觉上的多余信息

当你拿到一个原始未经处理的图像,是由各种色彩组成的,即在一个平面上,有各种色彩,而这个平面是由水平和垂直方向上的很多点组成的。实际上,每个点的色彩,也即计算机能表示的每个像素点的色彩,能分解成红、绿、蓝,即 RGB 三元色来表示,即这三种颜色的一定比例的混合就能得到一个实际的色彩值。


 

所以,实际上,这个平面的图像,可以理解为除了水平 X 和垂直 Y 以外,还有一个色彩值的 Z 的三维的系统。Z 代表了三元色中各个分支 R/G/B 的混合时所占的具体数值大小,每个像素的 RGB 的混合值可能都有所不同,各个值有大有小,但临近的两个点的 R/G/B 三个值会比较接近。


 

由于这个原始图像是由很多个独立的像素点组成的,也就是说它们都是分散的,离散的。比如有些图像的尺寸为640X480,就表示水平有640个像素点,垂直有480个像素点。

从上面的内容,我们可以知道两个相邻的点,会有很多的色彩是很接近的,那么如何能在最后得到的图片中,尽量少得记录这些不需要的数据,也即达到了压缩的效果。

这个就要涉及到图像信号的频谱特性了。

图像信号的频谱线一般在0-6MHz范围内,而且一幅图像内,包含了各种频率的分量。但包含的大多数为低频频谱线,只在占图像区域比例很低的图像边缘的信号中才含有高频的谱线。这个是对 JPEG 图像压缩的理论依据。

因此具体的方法就是,在对图像做数字处理时,可根据频谱因素分配比特数:对包含信息量大的低频谱区域分配较多的比特数,对包含信息量低的高频谱区域分配较少的比特数,而图像质量并没有可察觉的损伤,达到数据压缩的目的。

将原始图像这个色彩空间域,转换为频谱域,怎么转呢,这个就用到了数学上的离散余弦变换,即 DCT(Discrete Cosine Transform) 变换。

DCT 是可逆的、离散的正交变换。变换过程本身虽然并不产生压缩作用,但是变换后的频率系数却非常有利于码率压缩。即这个变换过程得到一个 DCT 变换系数,而对这个系数可以再进行更进一步的处理,即所谓的量化。经过量化,就能达到数据压缩的作用了。

总体说来,这第一步,对图像进行编码,去除多余的信息,要用到 DCT 变换中的正向 DCT(FDCT),然后再对变换的系数做量化(Quantization),这个过程就是依据的经验值,来处理人眼视觉系统所不敏感的高频数据,从而极大地减少了需要处理的数据量,这个是结合数学方法与经验值而做的处理。

2、去除数据本身的多余信息

利用 Huffman 编码,来将最后的数据用无损的方式做压缩,这个是纯数学上的处理方式。

总体来说,上面的两步即:

如果处理的是彩色图像,JPEG 算法首先将 RGB 分量转化成亮度分量和色差分量,同时丢失一半的色彩信息(空间分辨率减半)。然后,用 DCT 来进行块变换编码,舍弃高频的系数,并对余下的系数进行量化以进一步减小数据量。最后,使用 RLE 行程编码和 Huffman 编码来完成压缩任务。

三、JPEG 原理详细分析

下面将更加详细地介绍这两步中的各个细节。

JPEG 编码中主要涉及到的内容主要包括:

1. Color Model Conversion (色彩模型)

2. DCT (Discrete Cosine Transform 离散余弦变换)

3. 重排列 DCT 结果

4. 量化

5. RLE 编码

6. 范式 Huffman 编码

7. DC 的编码


 

1、色彩空间 color space

在图像处理中,为了利用人的视角特性,从而降低数据量,通常把 RGB 空间表示的彩色图像变换到其他色彩空间。

现在采用的色彩空间变换有三种:YIQ,YUV 和 YCrCb。

每一种色彩空间都产生一种亮度分量信号和两种色度分量信号,而每一种变换使用的参数都是为了适应某种类型的显示设备。


 

YUV 不是哪个英文单词的缩写,而只是符号,Y 表示亮度,UV 用来表示色差,U、V 是构成彩色的两个分量;

YUV 表示法的重要性是它的亮度信号(Y)和色度信号(U、V)是相互独立的,也就是 Y 信号分量构成的黑白灰度图与用 U、V 信号构成的另外两幅单色图是相互独立的。由于 Y、U、V 是独立的,所以可以对这些单色图分别进行编码。此外,黑白电视能接收彩色电视信号也就是利用了 YUV 分量之间的独立性。

举例来说明一下:

要存储 RGB 8∶8∶8的彩色图像,即 R、G 和 B 分量都用8位二进制数(1个字节)表示,图像的大小为640×480像素,那么所需要的存储容量为640×480×(1+1+1)=921 600字节,即900KB,其中(1+1+1)表示 RGB 各占一个字节。


 

如果用 YUV 来表示同一幅彩色图像,Y 分量仍然为640×480,并且 Y 分量仍然用8位表示,而对每四个相邻像素(2×2)的 U、V 值分别用相同的一个值表示,那么存储同样的一幅图像所需的存储空间就减少到640×480×(1+1/(2*2)+1/(2*2))=460 800字节,即450KB。也就是把数据压缩了一半。


 

无论是用 YIQ、YUV 和 YCrCb 还是其他模型来表示的彩色图像,由于现在所有的显示器都采用 RGB 值来驱动,这就要求在显示每个像素之前,须要把彩色分量值转换成 RGB 值。

对电视机,在考虑人的视觉系统和电视阴极射线管(CRT)的非线性特性之后,RGB 和 YUV 的对应关系可以近似地用下面的方程式表示:


 

即:

Y=0.3R+0.59G+0.11B

U=B-Y

V=R-Y

对计算机而言,计算机用的数字域的色彩空间变换与电视模拟域的色彩空间变换不同,它们的分量使用 Y、Cr 和 Cb 来表示,与 RGB 空间的转换关系如下:


 

从这里,就可以看出,计算出来的 Y、Cr 和 Cb 分量,会出现大量的小数,即浮点数,从而导致了在 JPEG 编码过程中会出现大量的浮点数的运算,当然经过一定的优化,这些浮点数运算可以用移位与加法这些计算机能更快速处理的方式来对其进行编码。

RGB 与 YCrCb 之间的逆变换关系可写成如下的形式:


 

总体来说,上面讲的这些内容,主要就是对原始图片,可以先进行色彩空间的处理,使采集到的图像数据有所减少。

请注意,实际上,JPEG 算法与色彩空间无关,色彩空间是涉及到图像采样的问题,它和数据的压缩并没有直接的关系。

因此"RGB 到 YUV 变换"和"YUV 到 RGB 变换"不包含在 JPEG 算法中。JPEG 算法处理的彩色图像是单独的彩色分量图像,因此它可以压缩来自不同色彩空间的数据,如 RGB,YcbCr 和 CMYK。

2、色彩深度 color depth

在图像中,它是由很多个点来组成的,那么存储每个像素点所用的位数就叫做像素深度。对一个图片,这个值是可以有所不同的,从而会使得图片的数据有多和少的区别。

一幅彩色图像的每个像素用 R,G,B 三个分量表示,若每个分量用8位,那么一个像素共用3X8=24位表示,就说像素的深度为24 bit,每个像素可以是2的24次方=16 777 216种颜色中的一种。表示一个像素的位数越多,它能表达的颜色数目就越多。

在用二进制数表示彩色图像的像素时,除 R,G,B 分量用固定位数表示外,往往还增加1位或几位作为属性(Attribute)位。例如,RGB 5∶5∶5表示一个像素时,用2个字节共16位表示,其中 R,G,B 各占5位,剩下一位作为属性位。在这种情况下,像素深度为16位,而图像深度为15 位。

在用32位表示一个像素时,若 R,G,B 分别用8位表示,剩下的8位常称为 alpha 通道(alpha channel)位,或称为覆盖(overlay)位、中断位、属性位。它的用法可用一个预乘 α 通道(premultiplied alpha)的例子说明。假如一个像素(A,R,G,B)的四个分量都用归一化的数值表示,(A,R,G,B)为(1,1,0,0)时显示红色。当像素为 (0.5,1,0,0)时,预乘的结果就变成(0.5,0.5,0,0),这表示原来该像素显示的红色的强度为1,而现在显示的红色的强度降了一半。

这个 alpha 值,在这里就用来表示该像素如何产生特技效果。

总体来说,图像的宽高、分辨率越高,就是组成一幅图的像素越多,则图像文件越大;像素深度越深,就是表达单个像素的颜色和亮度的位数越多,图像文件就越大。

只有黑白两种颜色的图像称为单色图像(monochrome),每个像素的像素值用1位存储,它的值只有"0"或者"1",一幅640×480的单色图像需要占据37.5 KB的存储空间。

而灰度图像,即有色深的黑白图像,如果每个像素的像素值用一个字节表示,而不是仅仅只有一位,那么灰度值级数就等于256级,每个像素可以是0~255之间的任何一个值,一幅640×480的灰度图像就需要占用300 KB的存储空间,类似上面说到过的 Y 分量。

3、离散余弦变换 DCT

将图像从色彩域转换到频率域,常用的变换方法有:


 

DCT变换的公式为:


 

f(i,j) 经 DCT 变换之后,F(0,0) 是直流系数,其他为交流系数。

还是举例来说明一下。

8x8的原始图像:


 

推移128后,使其范围变为 -128~127:


 

使用离散余弦变换,并四舍五入取最接近的整数:


 

上图就是将取样块由时间域转换为频率域的 DCT 系数块。

DCT 将原始图像信息块转换成代表不同频率分量的系数集,这有两个优点:其一,信号常将其能量的大部分集中于频率域的一个小范围内,这样一来,描述不重要的分量只需要很少的比特数;其二,频率域分解映射了人类视觉系统的处理过程,并允许后继的量化过程满足其灵敏度的要求。

当u,v = 0 时,离散余弦正变换(DCT)后的系数若为F(0,0)=1,则离散余弦反变换(IDCT)后的重现函数 f(x,y)=1/8,是个常 数值,所以将 F(0,0) 称为直流(DC)系数;当 u,v≠0 时,正变换后的系数为 F(u,v)=0,则反变换后的重现函数 f(x,y) 不是常数,此时 正变换后的系数 F(u,v) 为交流(AC)系数。

DCT 后的64个 DCT 频率系数与 DCT 前的64个像素块相对应,DCT 过程的前后都是64个点,说明这个过程只是一个没有压缩作用的无损变换过程。

单独一个图像的全部 DCT 系数块的频谱几乎都集中在最左上角的系数块中。

DCT 输出的频率系数矩阵最左上角的直流 (DC)系数幅度最大,图中为-415;以 DC 系数为出发点向下、向右的其它 DCT 系数,离 DC 分量越远,频率越高,幅度值越小,图中最右下角为2,即图像信息的大部分集中于直流系数及其附近的低频频谱上,离 DC 系数越来越远的高频频谱几乎不含图像信息,甚至于只含杂波。

DCT 本身虽然没有压缩作用,却为以后压缩时的"取"、"舍" 奠定了必不可少的基础。

4、量化

量化过程实际上就是对 DCT 系数的一个优化过程。它是利用了人眼对高频部分不敏感的特性来实现数据的大幅简化。

量化过程实际上是简单地把频率领域上每个成份,除以一个对于该成份的常数,且接着四舍五入取最接近的整数。

这是整个过程中的主要有损运算。

以这个结果来说,经常会把很多高频率的成份四舍五入而接近0,且剩下很多会变成小的正或负数。

整个量化的目的是减小非"0"系数的幅度以及增加"0"值系数的数目。

量化是图像质量下降的最主要原因。

因为人眼对亮度信号比对色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值。


 

使用这个量化矩阵与前面所得到的 DCT 系数矩阵:


 

如,使用−415(DC系数)且四舍五入得到最接近的整数


 

总体上来说,DCT 变换实际是空间域的低通滤波器。对 Y 分量采用细量化,对 UV 采用粗量化。

量化表是控制 JPEG 压缩比的关键,这个步骤除掉了一些高频量;另一个重要原因是所有图片的点与点之间会有一个色彩过渡的过程,大量的图像信息被包含在低频率中,经过量化处理后,在高频率段,将出现大量连续的零。

5、"Z"字形编排

量化后的数据,有一个很大的特点,就是直流分量相对于交流分量来说要大,而且交流分量中含有大量的0。这样,对这个量化后的数据如何来进行简化,从而再更大程度地进行压缩呢。

这就出现了"Z"字形编排,如图:


 

对于前面量化的系数所作的 "Z"字形编排结果就是:

底部 −26,−3,0,−3,−3,−6,2,−4,1 −4,1,1,5,1,2,−1,1,−1,2,0,0,0,0,0,−1,−1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 顶部

这样做的特点就是会连续出现多个0,这样很有利于使用简单而直观的行程编码(RLE:Run Length Coding)对它们进行编码。

8×8图像块经过 DCT 变换之后得到的 DC 直流系数有两个特点,一是系数的数值比较大,二是相邻8×8图像块的 DC 系数值变化不大。根据这个特点,JPEG 算法使用了差分脉冲调制编码(DPCM)技术,对相邻图像块之间量化 DC 系数的差值(Delta)进行编码。即充分利用相邻两图像块的特性,来再次简化数据。

即上面的 DC 分量-26,需要单独处理。

而对于其他63个元素采用zig-zag("Z"字形)行程编码,以增加行程中连续0的个数。

6、行程编码

Run Length Coding,行程编码又称"运行长度编码"或"游程编码",它是一种无损压缩编码。

例如:5555557777733322221111111

这个数据的一个特点是相同的内容会重复出现很多次,那么就可以用一种简化的方法来记录这一串数字,如

(5,6)(7,5)(3,3)(2,4)(l,7)

即为它的行程编码。

行程编码的位数会远远少于原始字符串的位数。

对经过"Z"字形编排过的数据,即可以用行程编码来对其进行大幅度的数据压缩。

我们来用一个简单的例子来详细说明一下:

57,45,0,0,0,0,23,0,-30,-16,0,0,1,0,0,0,0 ,0 ,0 ,0,..,0

可以表示为

(0,57) ; (0,45) ; (4,23) ; (1,-30) ; (0,-16) ; (2,1) ; EOB

即每组数字的头一个表示0的个数,而且为了能更有利于后续的处理,必须是 4 bit,就是说,只能是 0~15,这是的这个行程编码的一个特点。

7、范式 Huffman 编码

在直流 DC 系数经过上面的 DPCM 编码,交流 AC 系数经过 RLE 编码后,得到的数据,还可以再进一补压缩,即使用 Huffman 编码来处理。

范式 Huffman 编码即 Canonical Huffman Code,现在流行的很多压缩方法都使用了范式哈夫曼编码技术,如 GZIB、ZLIB、PNG、JPEG、MPEG 等。

对上面的例子中 RLC 后的结果,对它的存储,JPEG 里并不直接保存这个数值,这样主要是为了提高效率。


 

对上面的例子内容,就可以得到:

57 为第 6 组的,实际保存值为 111001,编码为 (6,111001)

45编码为 (6,101101)

23为(5,10111)

-30为(5,00001)

这个时候前面的例子就变为:

(0,6),111001 ; (0,6),101101 ; (4,5),10111; (1,5),00001; (0,4) ,0111 ; (2,1),1 ; (0,0)

这样,括号里的数值正好再合成一个字节,高4位是前面0的个数,低4位描述了后面数字的位数;后面被编码的数字表示范围是 -32767..32767。

使用上面这个表简化后的内容,再到 Huffman 编码表里去查询,从而得到最后的编码。

如06对应 Huffman 表的111000,那么

69 = (4,5) --- 1111111110011001 (69=0x45=4*16+5 )

21 = (1,5) --- 11111110110

从而得到最后的结果:

111000 111001 ; 111000 101101 ; 1111111110011001 10111 ; 11111110110 00001…

使用范式 Huffman 编码表的好处就是使得出现频率高的数字小于8位,而出现频率低的数字大于8位,这样对整体而言,就会极大地减少数据量。

需要注意的是,在 JPG 文件中,一般有两个 Huffman 表,一个是 DC 用,一个是 AC 用,它们是类似的。

对 DC 编码的部分是单独来处理的,并且是放在上面这个串的最前面。

总体来说,到目前为止,我们就得到了最后需要真正存储用的简化后,也即压缩后的数据了。

四、JPEG文件存储格式

介绍了 JPEG 的原理,我们再来结合一个具体的实例来详细讨论上面所涉及到的细节。

我们先来制作一个简单的8X8大小的像素图,然后把它存成JPEG格式。

方法是用 windows 的画图工具,定义一个8X8大小的图,用一些色块填充进去,然后另存为 JPEG 格式,如 test8x8.jpg。如下图所示:


 

保存成的文件后缀为 jpg,但按标准来说,它是一种 JFIF 格式标准的文件,里面的图像的压缩方式是 JPEG。

JFIF 是一个文件格式标准,JPEG 是一个压缩标准,总体来说它们不是一个概念。

JFIF 是 JPEG File Interchange Format 的缩写,也即 JPEG 文件交换格式。JFIF 是一个图片文件格式标准,它是一种使用 JPEG 图像压缩技术存储摄影图像的方法。JFIF 代表了一种"通用语言"文件格式,它是专门为方便用户在不同的计算机和应用程序间传输 JPEG 图像而设计的语言。

JFIF 文件格式定义了一些内容是 JPEG 压缩标准未定义的,如 resolution/aspect ratio,color space 等。


 

我们可以打开 JPEG 文件查看里面的内容,即可看到上面的各个标记段:


 

从图上可以看出:

在头部有 FFD8 ,表示图像的开始;结束部分有 FFD9 ,表示图像的结束。

在中间有两个量化表 DQT 对应的标记 FFDB ;

还有图像大小信息对应的 FFC0

再后面有四个 Haffman 表对应的 FFC4 ;

一般一个 JPG 文件里会有 2 类 Haffman 表:一个用于 DC 一个用于 AC ,也即实际有 4个表,亮度的 DC,AC 两个,色度的 DC,AC 两个。

然后是图像数据段标记 FFDA;

我们再来看看各个标记的细部,具体分析一下各个部分的含义。

1、图片的识别信息


 

上面的内容,在标记 FFE0 后,即为长度16。然后是5字节的 JFIF 标识符号,说明这是一个 JPEG 压缩的文件。然后是主/次版本号码。下一个为 XY 像素的单位,这里为1,表示单位为点数/英寸。然后是 XY 方向的像素密度,这里是 96DPI,最后是缩略图有关信息,这里为0。

2、量化表的实例


 

上面这个内容,FFDB 标记后的长度值为67,接下来的是 QT 信息,占一个字节;这里是0,表示这个 QT 表编号为0,并且精度是8bit。然后后面就是64个8x8的 QT 表的各个 item 了。

也即第一个 DQT 量化表的内容表示为十进制是:


 

这个表即为 JPEG 亮度量化表。

第二个量化表的内容为:


 

这个表的内容即为 JPEG 色度量化表。

当你打开不同的 JPEG 文件,你会看到这两个表可能也是会有区别的。这个主要是使用了不同的量化方式的结果。

3、图像信息段


 

上面这个内容,FFC0 标记后即是长度,为17,然后是一个字节的数据精度,通常是为8,代表样本位数。接下来是图片的高度,占两字节,这里即为8,然后是图片的宽度,也为8,这也就是我们定义的8x8的内容。然后是 component 的个数,这里是3,表示 YUV。接下来是三组数据,每组数据里,第一个是 component ID,第二个是采样系数,这里 Y 的采样系数为22,说明垂直是2,水平是2。再后面就是量化表的编号了。

4、Haffman 表的实例


 

上面这个内容,FFC4 标记后的内容为数据长度,再接着的1字节为 Huffman Table 的信息,低4位是 HT ID 号,第5位是 HT 表类型标记,再高三位是为0。

第一个 DHT 表,00,类型为 DC table,HT ID 号为 0;

第二个 DHT 表,10,类型为 AC table,HT ID 号也为 0;

第三个 DHT 表,01,类型为 DC table,HT ID 号为 1;

第四个 DHT 表,11,类型为 AC table,HT ID 号为 1;

即前两个表为Y亮度分量的 DC/AC 表,后两个为 UV 色度分量的 DC/AC 表。

以第一个表为例,因为长度只有 31,那么 00 后面的 16 字节,即绿色部分:


 

组号为 1 的组中,代码有 0 个;

组号为 2 的,代码有 1 个;

组号为 3 的代码有 5 个;

组号为 4/5/6/7/8/9 的代码各 1 个。

总共 12 个。

再看后续的数据:

00 01 02 03 04 05 06 07 08 09 0A 0B

即对应:


 

其他未出现的组号,对应的数据未使用到。也就是说前面提到过的范式 Huffman 编码里,目前只使用部分数据即可,原因是这个 8x8 的图像数据很小。

第二个 DHT 表就更复杂些了,长度有 181。

5、图像数据段


 

这里 SOS 段,长度为 12,后面所含有的 component 数量为 3 个,也即 Y UV。然后后面是各 component 的编号,及对应所使用的 Huffman 表的 ID 是多少。

在这个段的后面就是所有压缩后的数据。直到结束的问题,即 FFD9,EOI(End Of Image)。

五、JPEG 压缩过程的优化

JPEG 在目前的应用范围是非常广泛的,各种嵌入式系统中也大量地使用了 JPEG 压缩,如 IPCAM 摄像头、数字相机、移动存贮等。在这些领域由于传输数据的带宽限制或者是存贮数据的容量的限制,常常需要使用图像压缩技术来将原始大量的图像数据压缩后在进行传输或存贮,以充分利用带宽与存贮空间,达到更好的利用效率。这样,在嵌入系统中,就会使用到 JPEG 压缩。而且由于嵌入系统的资源有限的特点,在很多情况下,很需要再对 JPEG 编码压缩的过程做更进一步的优化,我们这里详细讨论一下如何实现这些优化。

浮点运算的优化

我们回头查看一下 JPEG 压缩中的 DCT 变换过程,公式:


 

由于公式中有两个 i/j=0~7 的部分,这样要获得一个 DCT 系数,需要做 8 x 8=64 次乘法和 8 x 8=64 次加法, 而完成整个 8 x 8 像素的 DCT 需要 8 x 8 x 8 x 8=4096 次乘法和 8 x 8 x 8 x 8=4096 次加法. 计算量是相当的大。

对于有些无浮点运算的嵌入式系统或无专门的数学运算协处理器的系统,会造成大量的运算,极大地占用CPU的资源。

上面的公式属于 DCT 的二维计算方式,经过简化,可以将其简化为两个一维的公式:


 

这样,上面的过程就可以简化为分别计算行和列的 DCT 变换。

对于一行来说需要计算的是 (8 x 8) 次乘法和 (8 x 8) 次加法,8 行就是 8 x (8 x 8) 次乘法和 8 x (8 x 8) 次加法,然后列也是相同,那么总数就为 2 x (8 x (8 x 8))=1024 次乘法和 2 x (8 x (8 x 8))=1024 次加法, 运算量变为二维计算的1/4。

但是这样的运算数量还是太大,还需要进一步优化。

在很多嵌入系统中,很多情况下需要不使用浮点运算,这样就需要再找出一维 DCT 的一些规律,然后对其进行进一步的优化。

在对一维 DCT 的运算中,还可以分为奇数列/行和偶数列/行


 

对上面的处理,就又出现了多种优化:ChenDCT,LeeDCT,AAN 算法和 LLM 算法。

其中 AAN 算法只需要 29 次加法和 5 次乘法。(注意,它是指每次一维运算要 29 次加法和 5 次乘法,一共是需要 29*8*2 次加法和 5*8*2 次乘法的)。


 

其中 Y[0]-Y[7] 都是 1*8 的矩阵,X[1]-X[7] 也都是 1*8 的矩阵。

{a, b, c, d, e, f, g} = 1/2 { cos(pi/4), cos(pi/16), cos(pi/8), cos(3pi/16), cos(5pi/16), cos(3pi/8), cos(7pi/16) }

再对上面的含有 pi 的系数进行整数优化,从而避免浮点运算,就会得到:


 

其中:

3/8=1/4+1/8

5/8=1/2+1/8

7/8=1-1/8

上面的除以 2,除以 8,都可以通过移位来实现,即右移一位和右移三位。即总数为 30 次加法,12 次移位即可。

这样就在很大程度上将原本需要使用乘法,浮点运算的过程全部转换成了简单的加法和移位处理了,这样使用数学的方法,用近似的值来完成整个转换过程,会有很好的性能和处理效果。

在处理上面的数据中,可以使用一些中间变量来记录中间结果,这样就可以减少反复计算中间值,而直接使用已经计算得到了的中间值。

tmp0 = x[0] + x[7];

tmp7 = x[0] - x[7];

tmp1 = x[1] + x[6];

tmp6 = x[1] - x[6];

tmp2 = x[2] + x[5];

tmp5 = x[2] - x[5];

tmp3 = x[3] + x[4];

tmp4 = x[3] - x[4];

tmp10 = tmp0 + tmp3;

tmp13 = tmp0 - tmp3;

tmp11 = tmp1 + tmp2;

tmp12 = tmp1 - tmp2;

/* 对偶数项进行运算 X 0,4,6,2 */

X[0]=tmp1+tmp11;

X[4]=tmp10 /2 - tmp11

X[6]=tmp12-(tmp13/4+ tmp13/8);

X[2]=tmp12/4+tmp12/8-tmp13;

其他的各个值也是类似处理的。

六、JPEG 在本嵌入式 Linux 应用中遇到的问题

在本系统中,提供给用户一些播放图片和预览图片的功能,在这个过程中就需要使用到对 JPEG 的处理。

1、JPEG 出错的处理

在对图片做预览处理的时候,有些图片原始尺寸很大,那么就需要将其转换成较小的缩略图,在转换为缩略图进行显示时,遇到了一个问题,即有时需要显示的图片,会导致系统无响应。

后来查找原因,定位到 JPEG 文件的数据不完整,才导致 jpeg decoder 出现无响应。

在前面的部分,有说到 JPEG 文件的格式中,JPEG 结束的标记 EOI (End Of Image) 为"FFD9"。

如果需要显示的图片,在传输过程中,或转换过程中,出现了没有 EOI 数据,那么应该在程序中,将其废弃,避免出现系统无响应。

2、JPEG 解码的效率优化

在解码 JPEG 时,可以使用 software decode 或 hardware decode 来处理。Hardware decode 的优点是充分利用 DSP 所提供的硬件解码功能,其解码速度会较 software decode 有数量级的提高。但有时使用 hardware decode 有一些限制,如各种 DSP 提供的 SDK 会是直接访问硬件,将 jpeg 直接输出到显示设备,从而会导致 hardware decode 与应用系统集成的麻烦。

而使用 software decode,就能在应用层完全掌握 jpeg decode 的数据缓冲结果,并可做一些图片的叠加效果或对其进行半透明混合处理,从而会有较高的灵活性。并且使用 decode buffer cache,来将已经解码的数据进行保存,在 UI 后续的使用中,就可不必反复去解码 JPEG 图片,从而也能有效提高绘图效率。





回页首


七、总结

上面的内容是本人对 JPEG 原理做的一个详细的实例分析,还介绍了 JPEG 编码过程中对浮点运算的优化处理,它特别适用于在资源有限的嵌入系统中避免大量的浮点运算。

在对 JPEG 原理做了一个详细的分析后,大家会对 JPEG 涉及到的各个细节有了一个更加明确的认识。当你自己动手结合 JPEG 编码过程来分析时,将会有一个清楚的全局观。

本文结合应用实例,对在嵌入式 Linux 应用中遇到的 JPEG 有关的问题,做了一个说明,大家在自己的设计过程中也可以作为参考。

现在,你如果再回头去看 JPEG 的原理,你现在应该能看懂它整个过程的来龙去脉了。


JPEG 原理详细实例分析及其在嵌入式 Linux 中的应用




















































作为一个基本的图像压缩方式,JPEG 已经得到了广泛的运用,但 JPEG 相关的基本原理,却经常被忽视,或解释得很不确切。 这里我们详细讨论一下 JPEG 的编码原理,并结合实例来给出一个更加感性的认识。JPEG 编码的详细过程有着诸多的信息可以给我们巨大的启发,我们在这里讨论的就是要对这些信息做一个具体细致的分析,通过我们的讨论,大家会对 JPEG 编码过程中出现的内容有一个确切的了解,并且能了解到这些内容的来龙去脉。

一、系统架构

本文以一个实际的产品为例,来说明 JPEG 在其中的应用。



本系统为一个嵌入式 Linux 网络播放器,主要的功能为播放家庭网络中的多媒体文件,在家庭客厅等环境中有着大量的应用,它可以给用户提供更方便快捷的媒体文件的播放方式,并能充分利用家庭音响系统的巨大功能,而非 PC 环境下有限的外部设备,大大改善了媒体文件的播放体验。

系统主要的功能包括:



本系统架构如下图:



本系统是基于嵌入式 Linux 的一个应用,使用的是 ucLinux 2.4.22,并使用了 microwindows 作为 GUI 界面,底层使用了 Linux kernel 的 FrameBuffer 作为显示输出。

此系统在两个方面使用到了 JPEG 库:

1、 UI 的显示,即各种人机交互界面,考虑到用户体验,所以大量使用了贴图来美化 UI

2、 JPEG 图片文件的全屏播放,包括用户手中的各种照片等

二、JPEG 概述

JPEG 是 Joint Photographic Experts Group 的缩写,即 ISO 和 IEC 联合图像专家组,负责静态图像压缩标准的制定,这个专家组开发的算法就被称为 JPEG 算法,并且已经成为了大家通用的标准,即 JPEG 标准。 JPEG 压缩是有损压缩,但这个损失的部分是人的视觉不容易察觉到的部分,它充分利用了人眼对计算机色彩中的高频信息部分不敏感的特点,来大大节省了需要处理的数据信息。

人眼对构成图像的不同频率成分具有不同的敏感度,这个是由人眼的视觉生理特性所决定的。如人的眼睛含有对亮度敏感的柱状细胞1.8亿个,含有对色彩敏感的椎状细胞0.08亿个,由于柱状细胞的数量远大于椎状细胞,所以眼睛对亮度的敏感程度要大于对色彩的敏感程度。

总体来说,一个原始图像信息,要对其进行 JPEG 编码,过程分两大步:

1、 去除视觉上的多余信息,即空间冗余度

2、 去除数据本身的多余信息,即结构(静态)冗余度

1、去除视觉上的多余信息

当你拿到一个原始未经处理的图像,是由各种色彩组成的,即在一个平面上,有各种色彩,而这个平面是由水平和垂直方向上的很多点组成的。实际上,每个点的色彩,也即计算机能表示的每个像素点的色彩,能分解成红、绿、蓝,即 RGB 三元色来表示,即这三种颜色的一定比例的混合就能得到一个实际的色彩值。



所以,实际上,这个平面的图像,可以理解为除了水平 X 和垂直 Y 以外,还有一个色彩值的 Z 的三维的系统。Z 代表了三元色中各个分支 R/G/B 的混合时所占的具体数值大小,每个像素的 RGB 的混合值可能都有所不同,各个值有大有小,但临近的两个点的 R/G/B 三个值会比较接近。



由于这个原始图像是由很多个独立的像素点组成的,也就是说它们都是分散的,离散的。比如有些图像的尺寸为640X480,就表示水平有640个像素点,垂直有480个像素点。

从上面的内容,我们可以知道两个相邻的点,会有很多的色彩是很接近的,那么如何能在最后得到的图片中,尽量少得记录这些不需要的数据,也即达到了压缩的效果。

这个就要涉及到图像信号的频谱特性了。

图像信号的频谱线一般在0-6MHz范围内,而且一幅图像内,包含了各种频率的分量。但包含的大多数为低频频谱线,只在占图像区域比例很低的图像边缘的信号中才含有高频的谱线。这个是对 JPEG 图像压缩的理论依据。

因此具体的方法就是,在对图像做数字处理时,可根据频谱因素分配比特数:对包含信息量大的低频谱区域分配较多的比特数,对包含信息量低的高频谱区域分配较少的比特数,而图像质量并没有可察觉的损伤,达到数据压缩的目的。

将原始图像这个色彩空间域,转换为频谱域,怎么转呢,这个就用到了数学上的离散余弦变换,即 DCT(Discrete Cosine Transform) 变换。

DCT 是可逆的、离散的正交变换。变换过程本身虽然并不产生压缩作用,但是变换后的频率系数却非常有利于码率压缩。即这个变换过程得到一个 DCT 变换系数,而对这个系数可以再进行更进一步的处理,即所谓的量化。经过量化,就能达到数据压缩的作用了。

总体说来,这第一步,对图像进行编码,去除多余的信息,要用到 DCT 变换中的正向 DCT(FDCT),然后再对变换的系数做量化(Quantization),这个过程就是依据的经验值,来处理人眼视觉系统所不敏感的高频数据,从而极大地减少了需要处理的数据量,这个是结合数学方法与经验值而做的处理。

2、去除数据本身的多余信息

利用 Huffman 编码,来将最后的数据用无损的方式做压缩,这个是纯数学上的处理方式。

总体来说,上面的两步即:

如果处理的是彩色图像,JPEG 算法首先将 RGB 分量转化成亮度分量和色差分量,同时丢失一半的色彩信息(空间分辨率减半)。然后,用 DCT 来进行块变换编码,舍弃高频的系数,并对余下的系数进行量化以进一步减小数据量。最后,使用 RLE 行程编码和 Huffman 编码来完成压缩任务。

三、JPEG 原理详细分析

下面将更加详细地介绍这两步中的各个细节。

JPEG 编码中主要涉及到的内容主要包括:

1. Color Model Conversion (色彩模型)

2. DCT (Discrete Cosine Transform 离散余弦变换)

3. 重排列 DCT 结果

4. 量化

5. RLE 编码

6. 范式 Huffman 编码

7. DC 的编码



1、色彩空间 color space

在图像处理中,为了利用人的视角特性,从而降低数据量,通常把 RGB 空间表示的彩色图像变换到其他色彩空间。

现在采用的色彩空间变换有三种:YIQ,YUV 和 YCrCb。

每一种色彩空间都产生一种亮度分量信号和两种色度分量信号,而每一种变换使用的参数都是为了适应某种类型的显示设备。



YUV 不是哪个英文单词的缩写,而只是符号,Y 表示亮度,UV 用来表示色差,U、V 是构成彩色的两个分量;

YUV 表示法的重要性是它的亮度信号(Y)和色度信号(U、V)是相互独立的,也就是 Y 信号分量构成的黑白灰度图与用 U、V 信号构成的另外两幅单色图是相互独立的。由于 Y、U、V 是独立的,所以可以对这些单色图分别进行编码。此外,黑白电视能接收彩色电视信号也就是利用了 YUV 分量之间的独立性。

举例来说明一下:

要存储 RGB 8∶8∶8的彩色图像,即 R、G 和 B 分量都用8位二进制数(1个字节)表示,图像的大小为640×480像素,那么所需要的存储容量为640×480×(1+1+1)=921 600字节,即900KB,其中(1+1+1)表示 RGB 各占一个字节。



如果用 YUV 来表示同一幅彩色图像,Y 分量仍然为640×480,并且 Y 分量仍然用8位表示,而对每四个相邻像素(2×2)的 U、V 值分别用相同的一个值表示,那么存储同样的一幅图像所需的存储空间就减少到640×480×(1+1/(2*2)+1/(2*2))=460 800字节,即450KB。也就是把数据压缩了一半。



无论是用 YIQ、YUV 和 YCrCb 还是其他模型来表示的彩色图像,由于现在所有的显示器都采用 RGB 值来驱动,这就要求在显示每个像素之前,须要把彩色分量值转换成 RGB 值。

对电视机,在考虑人的视觉系统和电视阴极射线管(CRT)的非线性特性之后,RGB 和 YUV 的对应关系可以近似地用下面的方程式表示:



即:

Y=0.3R+0.59G+0.11B

U=B-Y

V=R-Y

对计算机而言,计算机用的数字域的色彩空间变换与电视模拟域的色彩空间变换不同,它们的分量使用 Y、Cr 和 Cb 来表示,与 RGB 空间的转换关系如下:



从这里,就可以看出,计算出来的 Y、Cr 和 Cb 分量,会出现大量的小数,即浮点数,从而导致了在 JPEG 编码过程中会出现大量的浮点数的运算,当然经过一定的优化,这些浮点数运算可以用移位与加法这些计算机能更快速处理的方式来对其进行编码。

RGB 与 YCrCb 之间的逆变换关系可写成如下的形式:



总体来说,上面讲的这些内容,主要就是对原始图片,可以先进行色彩空间的处理,使采集到的图像数据有所减少。

请注意,实际上,JPEG 算法与色彩空间无关,色彩空间是涉及到图像采样的问题,它和数据的压缩并没有直接的关系。

因此“RGB 到 YUV 变换”和“YUV 到 RGB 变换”不包含在 JPEG 算法中。JPEG 算法处理的彩色图像是单独的彩色分量图像,因此它可以压缩来自不同色彩空间的数据,如 RGB,YcbCr 和 CMYK。

2、色彩深度 color depth

在图像中,它是由很多个点来组成的,那么存储每个像素点所用的位数就叫做像素深度。对一个图片,这个值是可以有所不同的,从而会使得图片的数据有多和少的区别。

一幅彩色图像的每个像素用 R,G,B 三个分量表示,若每个分量用8位,那么一个像素共用3X8=24位表示,就说像素的深度为24 bit,每个像素可以是2的24次方=16 777 216种颜色中的一种。表示一个像素的位数越多,它能表达的颜色数目就越多。

在用二进制数表示彩色图像的像素时,除 R,G,B 分量用固定位数表示外,往往还增加1位或几位作为属性(Attribute)位。例如,RGB 5∶5∶5表示一个像素时,用2个字节共16位表示,其中 R,G,B 各占5位,剩下一位作为属性位。在这种情况下,像素深度为16位,而图像深度为15 位。

在用32位表示一个像素时,若 R,G,B 分别用8位表示,剩下的8位常称为 alpha 通道(alpha channel)位,或称为覆盖(overlay)位、中断位、属性位。它的用法可用一个预乘 α 通道(premultiplied alpha)的例子说明。假如一个像素(A,R,G,B)的四个分量都用归一化的数值表示,(A,R,G,B)为(1,1,0,0)时显示红色。当像素为 (0.5,1,0,0)时,预乘的结果就变成(0.5,0.5,0,0),这表示原来该像素显示的红色的强度为1,而现在显示的红色的强度降了一半。

这个 alpha 值,在这里就用来表示该像素如何产生特技效果。

总体来说,图像的宽高、分辨率越高,就是组成一幅图的像素越多,则图像文件越大;像素深度越深,就是表达单个像素的颜色和亮度的位数越多,图像文件就越大。

只有黑白两种颜色的图像称为单色图像(monochrome),每个像素的像素值用1位存储,它的值只有“0”或者“1”,一幅640×480的单色图像需要占据37.5 KB的存储空间。

而灰度图像,即有色深的黑白图像,如果每个像素的像素值用一个字节表示,而不是仅仅只有一位,那么灰度值级数就等于256级,每个像素可以是0~255之间的任何一个值,一幅640×480的灰度图像就需要占用300 KB的存储空间,类似上面说到过的 Y 分量。

3、离散余弦变换 DCT

将图像从色彩域转换到频率域,常用的变换方法有:



DCT变换的公式为:



f(i,j) 经 DCT 变换之后,F(0,0) 是直流系数,其他为交流系数。

还是举例来说明一下。

8x8的原始图像:



推移128后,使其范围变为 -128~127:



使用离散余弦变换,并四舍五入取最接近的整数:



上图就是将取样块由时间域转换为频率域的 DCT 系数块。

DCT 将原始图像信息块转换成代表不同频率分量的系数集,这有两个优点:其一,信号常将其能量的大部分集中于频率域的一个小范围内,这样一来,描述不重要的分量只需要很少的比特数;其二,频率域分解映射了人类视觉系统的处理过程,并允许后继的量化过程满足其灵敏度的要求。

当u,v = 0 时,离散余弦正变换(DCT)后的系数若为F(0,0)=1,则离散余弦反变换(IDCT)后的重现函数 f(x,y)=1/8,是个常 数值,所以将 F(0,0) 称为直流(DC)系数;当 u,v≠0 时,正变换后的系数为 F(u,v)=0,则反变换后的重现函数 f(x,y) 不是常数,此时 正变换后的系数 F(u,v) 为交流(AC)系数。

DCT 后的64个 DCT 频率系数与 DCT 前的64个像素块相对应,DCT 过程的前后都是64个点,说明这个过程只是一个没有压缩作用的无损变换过程。

单独一个图像的全部 DCT 系数块的频谱几乎都集中在最左上角的系数块中。

DCT 输出的频率系数矩阵最左上角的直流 (DC)系数幅度最大,图中为-415;以 DC 系数为出发点向下、向右的其它 DCT 系数,离 DC 分量越远,频率越高,幅度值越小,图中最右下角为2,即图像信息的大部分集中于直流系数及其附近的低频频谱上,离 DC 系数越来越远的高频频谱几乎不含图像信息,甚至于只含杂波。

DCT 本身虽然没有压缩作用,却为以后压缩时的"取"、"舍" 奠定了必不可少的基础。

4、量化

量化过程实际上就是对 DCT 系数的一个优化过程。它是利用了人眼对高频部分不敏感的特性来实现数据的大幅简化。

量化过程实际上是简单地把频率领域上每个成份,除以一个对于该成份的常数,且接着四舍五入取最接近的整数。

这是整个过程中的主要有损运算。

以这个结果来说,经常会把很多高频率的成份四舍五入而接近0,且剩下很多会变成小的正或负数。

整个量化的目的是减小非“0”系数的幅度以及增加“0”值系数的数目。

量化是图像质量下降的最主要原因。

因为人眼对亮度信号比对色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值。



使用这个量化矩阵与前面所得到的 DCT 系数矩阵:



如,使用−415(DC系数)且四舍五入得到最接近的整数



总体上来说,DCT 变换实际是空间域的低通滤波器。对 Y 分量采用细量化,对 UV 采用粗量化。

量化表是控制 JPEG 压缩比的关键,这个步骤除掉了一些高频量;另一个重要原因是所有图片的点与点之间会有一个色彩过渡的过程,大量的图像信息被包含在低频率中,经过量化处理后,在高频率段,将出现大量连续的零。

5、“Z”字形编排

量化后的数据,有一个很大的特点,就是直流分量相对于交流分量来说要大,而且交流分量中含有大量的0。这样,对这个量化后的数据如何来进行简化,从而再更大程度地进行压缩呢。

这就出现了“Z”字形编排,如图:



对于前面量化的系数所作的 “Z”字形编排结果就是:

底部 −26,−3,0,−3,−3,−6,2,−4,1 −4,1,1,5,1,2,−1,1,−1,2,0,0,0,0,0,−1,−1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 顶部

这样做的特点就是会连续出现多个0,这样很有利于使用简单而直观的行程编码(RLE:Run Length Coding)对它们进行编码。

8×8图像块经过 DCT 变换之后得到的 DC 直流系数有两个特点,一是系数的数值比较大,二是相邻8×8图像块的 DC 系数值变化不大。根据这个特点,JPEG 算法使用了差分脉冲调制编码(DPCM)技术,对相邻图像块之间量化 DC 系数的差值(Delta)进行编码。即充分利用相邻两图像块的特性,来再次简化数据。

即上面的 DC 分量-26,需要单独处理。

而对于其他63个元素采用zig-zag(“Z”字形)行程编码,以增加行程中连续0的个数。

6、行程编码

Run Length Coding,行程编码又称“运行长度编码”或“游程编码”,它是一种无损压缩编码。

例如:5555557777733322221111111

这个数据的一个特点是相同的内容会重复出现很多次,那么就可以用一种简化的方法来记录这一串数字,如

(5,6)(7,5)(3,3)(2,4)(l,7)

即为它的行程编码。

行程编码的位数会远远少于原始字符串的位数。

对经过“Z”字形编排过的数据,即可以用行程编码来对其进行大幅度的数据压缩。

我们来用一个简单的例子来详细说明一下:

57,45,0,0,0,0,23,0,-30,-16,0,0,1,0,0,0,0 ,0 ,0 ,0,..,0

可以表示为

(0,57) ; (0,45) ; (4,23) ; (1,-30) ; (0,-16) ; (2,1) ; EOB

即每组数字的头一个表示0的个数,而且为了能更有利于后续的处理,必须是 4 bit,就是说,只能是 0~15,这是的这个行程编码的一个特点。

7、范式 Huffman 编码

在直流 DC 系数经过上面的 DPCM 编码,交流 AC 系数经过 RLE 编码后,得到的数据,还可以再进一补压缩,即使用 Huffman 编码来处理。

范式 Huffman 编码即 Canonical Huffman Code,现在流行的很多压缩方法都使用了范式哈夫曼编码技术,如 GZIB、ZLIB、PNG、JPEG、MPEG 等。

对上面的例子中 RLC 后的结果,对它的存储,JPEG 里并不直接保存这个数值,这样主要是为了提高效率。



对上面的例子内容,就可以得到:

57 为第 6 组的,实际保存值为 111001,编码为 (6,111001)

45编码为 (6,101101)

23为(5,10111)

-30为(5,00001)

这个时候前面的例子就变为:

(0,6),111001 ; (0,6),101101 ; (4,5),10111; (1,5),00001; (0,4) ,0111 ; (2,1),1 ; (0,0)

这样,括号里的数值正好再合成一个字节,高4位是前面0的个数,低4位描述了后面数字的位数;后面被编码的数字表示范围是 -32767..32767。

使用上面这个表简化后的内容,再到 Huffman 编码表里去查询,从而得到最后的编码。

如06对应 Huffman 表的111000,那么

69 = (4,5) --- 1111111110011001 (69=0x45=4*16+5 )

21 = (1,5) --- 11111110110

从而得到最后的结果:

111000 111001 ; 111000 101101 ; 1111111110011001 10111 ; 11111110110 00001…

使用范式 Huffman 编码表的好处就是使得出现频率高的数字小于8位,而出现频率低的数字大于8位,这样对整体而言,就会极大地减少数据量。

需要注意的是,在 JPG 文件中,一般有两个 Huffman 表,一个是 DC 用,一个是 AC 用,它们是类似的。

对 DC 编码的部分是单独来处理的,并且是放在上面这个串的最前面。

总体来说,到目前为止,我们就得到了最后需要真正存储用的简化后,也即压缩后的数据了。

四、JPEG文件存储格式

介绍了 JPEG 的原理,我们再来结合一个具体的实例来详细讨论上面所涉及到的细节。

我们先来制作一个简单的8X8大小的像素图,然后把它存成JPEG格式。

方法是用 windows 的画图工具,定义一个8X8大小的图,用一些色块填充进去,然后另存为 JPEG 格式,如 test8x8.jpg。如下图所示:



保存成的文件后缀为 jpg,但按标准来说,它是一种 JFIF 格式标准的文件,里面的图像的压缩方式是 JPEG。

JFIF 是一个文件格式标准,JPEG 是一个压缩标准,总体来说它们不是一个概念。

JFIF 是 JPEG File Interchange Format 的缩写,也即 JPEG 文件交换格式。JFIF 是一个图片文件格式标准,它是一种使用 JPEG 图像压缩技术存储摄影图像的方法。JFIF 代表了一种"通用语言"文件格式,它是专门为方便用户在不同的计算机和应用程序间传输 JPEG 图像而设计的语言。

JFIF 文件格式定义了一些内容是 JPEG 压缩标准未定义的,如 resolution/aspect ratio,color space 等。



我们可以打开 JPEG 文件查看里面的内容,即可看到上面的各个标记段:



从图上可以看出:

在头部有 FFD8 ,表示图像的开始;结束部分有 FFD9 ,表示图像的结束。

在中间有两个量化表 DQT 对应的标记 FFDB ;

还有图像大小信息对应的 FFC0

再后面有四个 Haffman 表对应的 FFC4 ;

一般一个 JPG 文件里会有 2 类 Haffman 表:一个用于 DC 一个用于 AC ,也即实际有 4个表,亮度的 DC,AC 两个,色度的 DC,AC 两个。

然后是图像数据段标记 FFDA;

我们再来看看各个标记的细部,具体分析一下各个部分的含义。

1、图片的识别信息



上面的内容,在标记 FFE0 后,即为长度16。然后是5字节的 JFIF 标识符号,说明这是一个 JPEG 压缩的文件。然后是主/次版本号码。下一个为 XY 像素的单位,这里为1,表示单位为点数/英寸。然后是 XY 方向的像素密度,这里是 96DPI,最后是缩略图有关信息,这里为0。

2、量化表的实例



上面这个内容,FFDB 标记后的长度值为67,接下来的是 QT 信息,占一个字节;这里是0,表示这个 QT 表编号为0,并且精度是8bit。然后后面就是64个8x8的 QT 表的各个 item 了。

也即第一个 DQT 量化表的内容表示为十进制是:



这个表即为 JPEG 亮度量化表。

第二个量化表的内容为:



这个表的内容即为 JPEG 色度量化表。

当你打开不同的 JPEG 文件,你会看到这两个表可能也是会有区别的。这个主要是使用了不同的量化方式的结果。

3、图像信息段



上面这个内容,FFC0 标记后即是长度,为17,然后是一个字节的数据精度,通常是为8,代表样本位数。接下来是图片的高度,占两字节,这里即为8,然后是图片的宽度,也为8,这也就是我们定义的8x8的内容。然后是 component 的个数,这里是3,表示 YUV。接下来是三组数据,每组数据里,第一个是 component ID,第二个是采样系数,这里 Y 的采样系数为22,说明垂直是2,水平是2。再后面就是量化表的编号了。

4、Haffman 表的实例



上面这个内容,FFC4 标记后的内容为数据长度,再接着的1字节为 Huffman Table 的信息,低4位是 HT ID 号,第5位是 HT 表类型标记,再高三位是为0。

第一个 DHT 表,00,类型为 DC table,HT ID 号为 0;

第二个 DHT 表,10,类型为 AC table,HT ID 号也为 0;

第三个 DHT 表,01,类型为 DC table,HT ID 号为 1;

第四个 DHT 表,11,类型为 AC table,HT ID 号为 1;

即前两个表为Y亮度分量的 DC/AC 表,后两个为 UV 色度分量的 DC/AC 表。

以第一个表为例,因为长度只有 31,那么 00 后面的 16 字节,即绿色部分:



组号为 1 的组中,代码有 0 个;

组号为 2 的,代码有 1 个;

组号为 3 的代码有 5 个;

组号为 4/5/6/7/8/9 的代码各 1 个。

总共 12 个。

再看后续的数据:

00 01 02 03 04 05 06 07 08 09 0A 0B

即对应:



其他未出现的组号,对应的数据未使用到。也就是说前面提到过的范式 Huffman 编码里,目前只使用部分数据即可,原因是这个 8x8 的图像数据很小。

第二个 DHT 表就更复杂些了,长度有 181。

5、图像数据段



这里 SOS 段,长度为 12,后面所含有的 component 数量为 3 个,也即 Y UV。然后后面是各 component 的编号,及对应所使用的 Huffman 表的 ID 是多少。

在这个段的后面就是所有压缩后的数据。直到结束的问题,即 FFD9,EOI(End Of Image)。

五、JPEG 压缩过程的优化

JPEG 在目前的应用范围是非常广泛的,各种嵌入式系统中也大量地使用了 JPEG 压缩,如 IPCAM 摄像头、数字相机、移动存贮等。在这些领域由于传输数据的带宽限制或者是存贮数据的容量的限制,常常需要使用图像压缩技术来将原始大量的图像数据压缩后在进行传输或存贮,以充分利用带宽与存贮空间,达到更好的利用效率。这样,在嵌入系统中,就会使用到 JPEG 压缩。而且由于嵌入系统的资源有限的特点,在很多情况下,很需要再对 JPEG 编码压缩的过程做更进一步的优化,我们这里详细讨论一下如何实现这些优化。

浮点运算的优化

我们回头查看一下 JPEG 压缩中的 DCT 变换过程,公式:



由于公式中有两个 i/j=0~7 的部分,这样要获得一个 DCT 系数,需要做 8 x 8=64 次乘法和 8 x 8=64 次加法, 而完成整个 8 x 8 像素的 DCT 需要 8 x 8 x 8 x 8=4096 次乘法和 8 x 8 x 8 x 8=4096 次加法. 计算量是相当的大。

对于有些无浮点运算的嵌入式系统或无专门的数学运算协处理器的系统,会造成大量的运算,极大地占用CPU的资源。

上面的公式属于 DCT 的二维计算方式,经过简化,可以将其简化为两个一维的公式:



这样,上面的过程就可以简化为分别计算行和列的 DCT 变换。

对于一行来说需要计算的是 (8 x 8) 次乘法和 (8 x 8) 次加法,8 行就是 8 x (8 x 8) 次乘法和 8 x (8 x 8) 次加法,然后列也是相同,那么总数就为 2 x (8 x (8 x 8))=1024 次乘法和 2 x (8 x (8 x 8))=1024 次加法, 运算量变为二维计算的1/4。

但是这样的运算数量还是太大,还需要进一步优化。

在很多嵌入系统中,很多情况下需要不使用浮点运算,这样就需要再找出一维 DCT 的一些规律,然后对其进行进一步的优化。

在对一维 DCT 的运算中,还可以分为奇数列/行和偶数列/行



对上面的处理,就又出现了多种优化:ChenDCT,LeeDCT,AAN 算法和 LLM 算法。

其中 AAN 算法只需要 29 次加法和 5 次乘法。(注意,它是指每次一维运算要 29 次加法和 5 次乘法,一共是需要 29*8*2 次加法和 5*8*2 次乘法的)。



其中 Y[0]-Y[7] 都是 1*8 的矩阵,X[1]-X[7] 也都是 1*8 的矩阵。

{a, b, c, d, e, f, g} = 1/2 { cos(pi/4), cos(pi/16), cos(pi/8), cos(3pi/16), cos(5pi/16), cos(3pi/8), cos(7pi/16) }

再对上面的含有 pi 的系数进行整数优化,从而避免浮点运算,就会得到:



其中:

3/8=1/4+1/8

5/8=1/2+1/8

7/8=1-1/8

上面的除以 2,除以 8,都可以通过移位来实现,即右移一位和右移三位。即总数为 30 次加法,12 次移位即可。

这样就在很大程度上将原本需要使用乘法,浮点运算的过程全部转换成了简单的加法和移位处理了,这样使用数学的方法,用近似的值来完成整个转换过程,会有很好的性能和处理效果。

在处理上面的数据中,可以使用一些中间变量来记录中间结果,这样就可以减少反复计算中间值,而直接使用已经计算得到了的中间值。

tmp0 = x[0] + x[7];

tmp7 = x[0] - x[7];

tmp1 = x[1] + x[6];

tmp6 = x[1] - x[6];

tmp2 = x[2] + x[5];

tmp5 = x[2] - x[5];

tmp3 = x[3] + x[4];

tmp4 = x[3] - x[4];

tmp10 = tmp0 + tmp3;

tmp13 = tmp0 - tmp3;

tmp11 = tmp1 + tmp2;

tmp12 = tmp1 - tmp2;

/* 对偶数项进行运算 X 0,4,6,2 */

X[0]=tmp1+tmp11;

X[4]=tmp10 /2 - tmp11

X[6]=tmp12-(tmp13/4+ tmp13/8);

X[2]=tmp12/4+tmp12/8-tmp13;

其他的各个值也是类似处理的。

六、JPEG 在本嵌入式 Linux 应用中遇到的问题

在本系统中,提供给用户一些播放图片和预览图片的功能,在这个过程中就需要使用到对 JPEG 的处理。

1、JPEG 出错的处理

在对图片做预览处理的时候,有些图片原始尺寸很大,那么就需要将其转换成较小的缩略图,在转换为缩略图进行显示时,遇到了一个问题,即有时需要显示的图片,会导致系统无响应。

后来查找原因,定位到 JPEG 文件的数据不完整,才导致 jpeg decoder 出现无响应。

在前面的部分,有说到 JPEG 文件的格式中,JPEG 结束的标记 EOI (End Of Image) 为“FFD9”。

如果需要显示的图片,在传输过程中,或转换过程中,出现了没有 EOI 数据,那么应该在程序中,将其废弃,避免出现系统无响应。

2、JPEG 解码的效率优化

在解码 JPEG 时,可以使用 software decode 或 hardware decode 来处理。Hardware decode 的优点是充分利用 DSP 所提供的硬件解码功能,其解码速度会较 software decode 有数量级的提高。但有时使用 hardware decode 有一些限制,如各种 DSP 提供的 SDK 会是直接访问硬件,将 jpeg 直接输出到显示设备,从而会导致 hardware decode 与应用系统集成的麻烦。

而使用 software decode,就能在应用层完全掌握 jpeg decode 的数据缓冲结果,并可做一些图片的叠加效果或对其进行半透明混合处理,从而会有较高的灵活性。并且使用 decode buffer cache,来将已经解码的数据进行保存,在 UI 后续的使用中,就可不必反复去解码 JPEG 图片,从而也能有效提高绘图效率。





回页首


七、总结

上面的内容是本人对 JPEG 原理做的一个详细的实例分析,还介绍了 JPEG 编码过程中对浮点运算的优化处理,它特别适用于在资源有限的嵌入系统中避免大量的浮点运算。

在对 JPEG 原理做了一个详细的分析后,大家会对 JPEG 涉及到的各个细节有了一个更加明确的认识。当你自己动手结合 JPEG 编码过程来分析时,将会有一个清楚的全局观。

本文结合应用实例,对在嵌入式 Linux 应用中遇到的 JPEG 有关的问题,做了一个说明,大家在自己的设计过程中也可以作为参考。

现在,你如果再回头去看 JPEG 的原理,你现在应该能看懂它整个过程的来龙去脉了.