又是早新闻。
美军昨天空袭了一个场伊拉克人的婚礼,造成四十余人死亡。一场喜庆的婚礼转眼变成悲哀的葬礼。
让我想起一部不久前的美国电影《杀死比尔》,也是一场婚礼,九具尸体,十条人命(后来好像变成八具尸体,八条人命了,续集我还没看)。死而复生的新娘的报复是何其执着和残忍。
如果这就是美国人所推祟的东西,他们就应该可以理解911。
死人都是无辜的,而死有余辜的人却总也死不了。
猛禽的归档8挂
又是早新闻。
美军昨天空袭了一个场伊拉克人的婚礼,造成四十余人死亡。一场喜庆的婚礼转眼变成悲哀的葬礼。
让我想起一部不久前的美国电影《杀死比尔》,也是一场婚礼,九具尸体,十条人命(后来好像变成八具尸体,八条人命了,续集我还没看)。死而复生的新娘的报复是何其执着和残忍。
如果这就是美国人所推祟的东西,他们就应该可以理解911。
死人都是无辜的,而死有余辜的人却总也死不了。
斯德哥尔摩是第二站。
路线是这样的:莫斯科(火车)—圣彼得堡(大巴)—赫尔辛基——土尔库(游轮)—斯德哥尔摩(大巴)—奥斯陆(游轮)——哥本哈根(大巴)——斯德哥尔摩(游轮)——土尔库(大巴)—赫尔辛基(大巴)——圣彼得堡(火车)—莫斯科。
在斯德哥尔摩因为相机没电了,所以景色照的很少。
城内最古老的水井
对视的大兵
教堂内部1
教堂内部吊灯
老城1
老城2
老城一瞥
老皇宫1
鸟瞰1
鸟瞰3
瑞典最大的哥特式教堂(Uppsala)
市政厅
隔了这么长时间,再重新捡起Alex的书来看,感觉有点脱节了:P
Alex说:各种模式是相互关联的,形成了模式语言。–这个好像前两篇都有提到:)
但是当使用了模式语言构造出建筑以后,就形成了一个各部分相互依存的“系统”。软件亦是如此。
正如前面所说,模式有好坏之分,如果这个系统中存在着坏的模式(我前面说过,模式的好坏是相对的,不是绝对的,一个好的模式,如果用的不是地方,同样会变成一个坏模式)。对于建筑来说,我们可以说,这个系统存在着应力(印象中这个术语好像是来自“结构力学”,用于说明材料内部用于平衡外力的内力)。对于软件来说,我们就会说:这个软件有Bad smell。
正如当材料中的应力超过一定程度后,会对材料造成破坏。软件中的Bad smell同样有这样的效果。如果说有什么不同的话,应力造成的破坏不一定都是坏的,它也可能使系统达到另外一个平衡,并最终消除应力。但软件中的Bad smell好像没有这样的效果。
这个时候,我们需要“重构”。–看来这是一段通往“软件的永恒之道”了^_^
然而要注意到:一个系统中的各个部分都是相互依赖而又相互影响的。在这样的系统中,存在着“牵一发而动全身”的情况。其表现可以用“蝴蝶效应”来形容。
1979年12月,洛伦兹在华盛顿的美国科学促进会的一次讲演中提出:一只蝴蝶在巴西扇动翅膀,有可能会在美国的德克萨斯引起一场龙卷风。他的演讲和结论给人们留下了极其深刻的印象。从此以后,所谓“蝴蝶效应”之说就不胫而走,名声远扬了。
以上就是关于“蝴蝶效应”的简单说明(注:其中的洛伦兹是混沌动力学家、气象学家L.A.洛伦兹,不是那个爱因斯坦时代的物理学家H.A.洛伦兹[1853-1928])。BTW:最近还有一部以此为题的电影,还不错,顺便推荐一下。^_^
为了防止“蝴蝶效应”的不良影响,所以我们还需要:“小步迭代”和“配置管理”。–这也可以说是通向“软件的永恒之道”的重要一段。
昨天看新闻联播,有一条很重要的新闻:
证监会批准深交所开设中小企业板,也就是传闻了至少两三年的所谓二板市场。
前两年炒得沸沸扬扬,仿佛过几天就能设立似的,结果一拖就是几年。这一两年已经没有人提这个话题了,却突然暴出这么个消息,真让人有点措手不及。
不过就是这么一条重要的好消息,居然在新闻联播里是作为简讯,一句话就带过了。以至于在看完天气预报后,我都忘记这条新闻的内容,只记得好像有一则重要消息,但就是想不起是什么来。
直至今天早上看早新闻才又看到它。
不得不说这个好消息来得太晚了,不过也许这未必不是好事。
就在现在TOP之流在主板骗钱的伪高科技企业的倒下后开辟这样一个二板市场,有助于投资者更冷静和理性地看待所谓的高科技企业,让资金能有更准确的流向,去支持那些真正有价值的高科技企业,有效地降低二板市场的投资风险。
设想一下,如果在两三年设立二板,像TOP这样的企业会有多少,现在损失又会有多大。
所以,在这一点上,不论对那些真正有前途的中小高科技企业来说,还是对投资者–特别是中小投资者、散户等–来说,都是有极大的好处。
波罗的海落日
赫尔辛基大教堂
赫尔辛基歌剧院
赫尔辛基教堂广场
赫尔辛基教堂广场纪念碑
火车站雕塑
进关难,出关更难
我们乘坐的游轮VIKING LINE
西贝柳斯纪念碑1
西贝柳斯纪念碑2
岩石教堂的管风琴
沿途风光
见我五月十日的BLOG《隆重推出偶MM在法国拍的PP》
她那次去的那个地方叫做“未来世界”,所以那些建筑都是很有未来气息的。
那些个球形建筑里面是一些展馆,比如其中一个里面是星空展示馆,在里面可以从360度角观看整个夜空(当然是模拟出来)。
还有其它一些展馆如三维演示馆,在其中可以看到非常逼真的三维视觉体验。比如看到一条金鱼从你身边游过,你甚至会有伸手去抓它的尾巴的想法,不过当然是只能抓到空气了:P
据偶MM说,她那天在那里玩了一天,一大早就去了,玩到晚上七八点还不想走,而且门票也不贵,只要二十欧元。
55555555555,可惜我去不了:(
BTW:最近国内在搞科技周活动,昨天在新闻上看到上海科技馆也有一些类似的节目(比如三维金鱼),有时间可以去看看。
刚才吃过午饭就看了一会电视,很久没有象样地看看电视了。
正好碰上方宏进主持的《中国经营者》,采访的对象是UT斯达康的吴鹰。
在谈到小灵通被认为是落后技术的问题时,吴鹰的回答我觉得很好:评价一个技术是先进还是落后,不应该看它是否前卫,因为历史上有太多的前卫技术最终归于消亡并且几乎没有留下什么痕迹,比如“铱星”;最关键的还是看它是不是降低了成本,创造了效益。
那些指责小灵通技术落后的人为什么不问问自己:为什么UT斯达康能凭着一项“落后”的PHS技术,取得了每年近60%的增长率?
归根结底还是取决于市场!
最后吴鹰说了:小灵通最终是一定会被淘汰的,但它具有的历史意义始终存在,那就是–它是中国电信史上第一项被市场选择的技术,而不是被政策所选择的。
软件业其实也一样,我们常常会被大公司层出不穷的新技术引得团团转,按孟岩兄的说法就是:被不断地反复蹂躏。
事实上很多人却忽视了最关键和重要的一点:我们的用户其实最关心的并不是你用的什么技术,而是你的软件是否能解决他们的问题!
只有被市场所选择的技术,才是好的技术!
那么市场是什么?
大公司不可能是我们的市场。如果我们跟着大公司走,正是中了他们的圈套,因为我们就是他们的市场。
要记住的是:我们的用户才是我们的市场!
如果不能意识到这一点,就只能跟在大公司的PG后面转,永远不会有大的成长。
传说中的“厦大”
南普陀
那天吃完活鱼在员当湖边拍的湖滨北路
中山公园
中山路(从文化宫方向)
大中路
从轮渡上看黄昏的鼓浪屿
厦鼓轮渡
鼓浪屿轮渡的海豚雕塑
鼓浪屿最高点:日光岩
菽庄花园外的海滩(人多垃圾就多啊,汗)
一轮满月照耀下的菽庄花园
夜之海
环岛路的灯火
厦门之夜
轮渡的标志性建筑
老字号黄则和花生汤
熙熙攘攘的定安夜市
EXIF格式分析及通过XML处理
随着数码相机的普及,EXIF已经被大多数图像处理软件所支持。虽然我做的是一个小玩意儿(见《人个信息助理之我的相册》)但毕竟也是用于图像处理的,虽然目前支持JPEG文件格式,但是还不支持EXIF。
那么,什么是EXIF呢?EXIF是Exchangeable image file format的缩写,即“可交换图像文件格式”,它是由日本电子与信息技术工业协会(JEITA)所制定的一项标准,用于实现在不同的软件或设备之间交流图像数据,典型的应用就是数码相机直接连接打印机打印照片。当然,EXIF中还包含了很丰富的信息,从中可以知道这个数码照片是用什么相机拍的,拍摄时用的光圈、速度、ISO等。而且最新版本的EXIF还支持音频格式文件。
关于EXIF的最权威文档资料当然是JEITA的标准规范[1],目前最新的版本是2.2。不过JEITA的网站上虽然提供了两个语言版本(日语和英语,并且JEITA声明以日文版为准)的规范文档,但是需要收费的。还好通过GOOGLE还是找到了一个英文版的。
EXIF只提供对两种图像文件格式的支持:TIFF[2]和JPEG[3,4]。其中对不压缩图像使用TIFF格式,对压缩图像使用JPEG格式。本文主要讨论JPEG格式。
我们知道JPEG文件格式是通过所谓的Marker Segments来记录图像的相关信息的,这种方式具有非常好的灵活性和可扩充性,较之早年的PCX,GIF,BMP等采用固定格式文件头记录的方式要好很多(PCX原先是为16色图像设计的,在256色图像出现后,就破坏了原先的格式定义,将调色板续在文件尾部;而GIF虽然内部也有分段机制,后来被扩充为实现动画功能,但仍然是采用固定格式的文件头记录基本信息),而EXIF就是利用了这一点。
JPEG文件中的每一个Marker Segments都是以一个WORD类型的数值开始(注意:这个数值记录在文件中时是高位字节在前,低位字节在后,将在后面介绍这个字节顺序的问题),这个数值即所谓的Marker,每个Marker代表着相应的Segment的意义,如果这个Segment有内容(即长度大于0,是否有内容视具体Marker而定),接下来的一个WORD类型的数值就是这个Segment的长度(这个数值的字节顺序与Marker相同),至于Segment的具体内容,则根据Marker的不同有不同的定义。如FFD8这个Marker叫做SOI,表示图像的开始,这个段是没有内容的;如FFE0则是APP0,即应用程序段0,属于可自定义的数据,它已经被用于JFIF[4],这个段则是有内容的,接下来的一个WORD就是段长度,段内容的定义是由JFIF规范所定义。
EXIF也是一种扩展定义,类似于JFIF,它使用了APP1和APP2这两个Marker Segments。之所以要用两个Marker是因为如前面所说,Segment的长度是用一个WORD来表示,即最大不超过64K。因为EXIF支持一种被称为Flashpix的无损图像格式,其数据很可能超过64K,所以用了APP2,其中APP2可以有多个,不过因为对Flashpix的支持属于EXIF的扩展功能(在规范文档的附录F中说明[1]),通常很少用到,本文不作讨论。
EXIF定义的APP1段是一个标准的JPEG Marker Segment,如表1所示。其中的APP1 Marker的值为FFE1,Length为这个段的长度,其值包括Length本身所占的两个字节,但不包括Marker所占的两个字节。段中剩下的部分便是EXIF数据。
EXIF数据的格式定义也很简单,如表2所示。它包括两个部分:EXIF< span>头和TIFF头。EXIF头由六个字节组成,其内容为一个长度为4的ASCIIZ(以NULL结尾的ASCII)字符串,加一个字节的0(用于使数据按WORD对齐),而这个ASCIIZ串内容就是“Exif”。而TIFF头则是采用了标准的TIFF文件格式的定义(TIFF同样是一种定义灵活的文件格式,在某种程度上说是太灵活了),这样可以让JPEG和TIFF两种格式中的EXIF信息可以以一致的方法进行处理。
起始 |
长度(Bytes) |
内容 |
0x00 |
2 |
APP1 Marker(0xFFE1) |
0x02 |
2 |
Length |
0x04 |
Length – 2 |
EXIF Data |
表1:APP1段格式定义
起始 |
长度(Bytes) |
内容 |
0x00 |
6 |
EXIF Header |
0x06 |
APP1 Length – 8 |
TIFF Header |
表2:EXIF格式定义
起始 |
长度(Bytes) |
内容 |
0x00 |
2 |
Byte order |
0x02 |
2 |
Flag(0x2A) |
0x04 |
4 |
The offset of the first IFD |
表3:TIFF Image File Header格式定义
TIFF Header[2]包括两个部分:Image File Header和IFD(Image File Directory)链表。其中Image File Header的定义如表3所示。其中Byte order用于说明此TIFF文件所采用的字节顺序,用两个字符表示,有两种选择,分别是:II和MM(这个MM跟美眉无关J),其中II是指采用Intel字节顺序,而MM是指采用Motolora字节顺序(见下面的说明)。Flag是TIFF文件格式的标志,总是为0x002A,即十进制数42。最后一个DWORD是指向第一个IFD的起始位置,其偏移量的计算起点是TIFF Header的起点,即如果第一个IFD是紧接着Image File Header的话,这一项的值就为8(Image File Header的大小)。
关于字节顺序的说明:
字节顺序是可交换文件格式中,特别需要注意的一个问题。所谓“可交换文件格式”就是说这种文件格式可以在各种不同的软硬件平台下被正确地解读。字节顺序问题的起因在于硬件上。
在CPU发展的早期(8位CPU的时代),由于指令集的丰富,许多8位CPU都可以处理16位数据,当然都是分两次进行的,这时就出现的字节顺序的问题:是先处理高位字节还是先处理低位字节?不同的CPU厂商采用不同的选择!以Intel, Zilog等公司为代表的CPU厂商是采用先低后高的方式,即低位地址保存低位字节的数据;
而以Motolora(它可不止是做手机,它曾经是世界上最大的电子产品制造商)则是采用先高后低的方式,与通常人的阅读顺序一致。对应的硬件就是采用Intel架构的IBM PC及其兼容机上运行的软件都是采用Intel顺序的,而采用由IBM,Motolora,Apple共同设计的Power PC芯片的Apple Mac则是采用Motolora顺序的。
现在,字节顺序问题不只出现在图像格式上,由于Unicode字符集(UCS)也是采用了16位(UCS-2)或32位(UCS-4)来表示一个字符,所以也面临着字节顺序的问题。
另外,按照各自字节顺序的特点,Intel的字节顺序也叫做little-endian,而Motolora的字节顺序就叫做big-endian。
图1:IFD链表结构
IFD是一个链表结构,如图1所示,在每个IFD的末尾包含一个指向下一个IFD的偏移量(同样是从TIFF Header算起),如果这个偏移量为0,则表示已经到了链表的末尾。EXIF只使用了两个TIFF IFD,分别被称作IFD0和IFD1,但定义了三个自己的IFD:EXIF IFD, GPS IFD, Interoperability IFD,它们的结构与标准TIFF IFD相同,但不是记录于TIFF的IFD链表中,而是作为IFD0的扩展记录的。
起始 |
长度(Bytes) |
内容 |
0x00 |
2 |
Number of Directory Entries(Count) |
0x02 |
12 * Count |
Directory Entries |
2 + 12 * Count |
4 |
Offset of next IFD |
表4:IFD格式定义
每个IFD由三个部分组成,如表4所示,包括:Number of Directory Entries,Directory Entries和Offset of next IFD。其中Number of Directory Entries指定在Directory Entries中包含多少个Entry。Directory Entries是一个数组,包含若干个Directory Entry。最后的Offset of next IFD即是下个IFD所在的位置,如果此项为0,则表示这是链表中的最后一个IFD。
起始 |
长度(Bytes) |
内容 |
0x00 |
2 |
Tag |
0x02 |
2 |
Type |
0x04 |
4 |
Size |
0x08 |
4 |
Value |
表5:IFD Entry格式定义
IFD Entry是一个12字节长的结构,如表5所示。正如TIFF的名称所说的那样:A tag-based file format for storing and interchanging raster images[2]。所有的IFD Entry都是通过Tag来标识的,每一个Tag都是一个WORD类型的数值,每个数值有其特定的含义。如0x0131这个Tag表示此Entry记录的是生成此TIFF文件的软件名等。具体每个Tag的含义可能查阅TIFF的规范文档[2]。EXIF只用到了其中部分Tag,另外还扩充了三个Tag用于链接EXIF的三个扩充IFD,这些在EXIF的规范文档中有说明[1]。
IFD Entry中的Type是指明此Entry中记录的数据类型,TIFF规范只定义了五种类型,EXIF增加了三种。各类型说明如表6所示:
Type |
类型 |
Size |
Value |
1 |
BYTE |
1 |
字节数据,Size一般为1 如果Size大于4,则Value为其位置 |
2 |
ASCII |
n |
一个ASCIIZ的字符串,Size为串长度,包括结尾的NULL字符 Size小于等于4则直接存放在Value中 Size大于4,则在Value中指定其位置 |
3 |
SHORT |
1 |
无符号短整数,Size一般也为1 如果Size大于2,则Value为其位置 |
4 |
LONG |
1 |
无符号长整数,Size一般也是1 |
5 |
RATIONAL |
1 |
有理数,TIFF是用分数的形式来表达,用了两个LONG类型的数据,前一个LONG为分子,后一个LONG为分母,Size一般也是1。 因为一个RATIONAL类型包含两个LONG,无法记录在Value中,所以Value中记录的是这个RATIONAL数所在的位置(从TIFF Header开始的偏移) |
以上为TIFF定义的类型,以下为EXIF扩展定义类型 |
|||
7 |
UNDEFINED |
n |
任意的字节数据,根据具体情况定义 Size小于等于4则直接存放在Value中 Size大于4,则在Value中指定其位置 |
9 |
SLONG |
1 |
有符号长整数,与LONG类似,以2的补码形式表示 |
10 |
SRATIONAL |
1 |
有符号有理数,与RATIONAL类似,不过是用两个SLONG来表示 |
表6:Type定义
关于Value的内容有一点要注意的是,它可能是数据本身,也可能是数据存放位置的偏移,这取决于Type和Size的大小。数据存放位置都是从TIFF Header开始计算的偏移量。
有一点要注意的是:EXIF的三个扩充IFD Tag也是LONG类型,它记录的是相应IFD的起始位置(从TIFF Header开始的偏移)。在扩充IFD中用到的Tag全部是EXIF重新定义的。
下面是一个典型的EXIF JPEG文件格式分析结果(源文件为一张用Nikon CoolPixel 775相机拍摄的照片,所用的EXIF版本是2.1,与2.2版差别不大):
JPEG SOI : FF D8 // 图片起始
JPEG APP1: FF E1
APP1 Size : 1C 45 // 注意:前面这三个WORD都是big endian的
EXIF Flag : ‘Exif’, 0, 0
TIFF Header:
Byte Order: ‘II’
Flag : 2A 00
IFD0 offset : 08 00 00 00
Entries Count : 0B 00 // 11
IFD Entry :
Tag : 0E 01 // Image Description 图像说明
Type : 02 00 // ASCII
Size : 0B 00 00 00
Value : 92 00 00 00 // from TIFF Header
IFD Entry:
Tag : 0F 01 // Make 制造
Type : 02 00
Size : 06 00 00 00
Value: B2 00 00 00
IFD Entry:
Tag : 10 01 // Model 型号
Type : 02 00
Size : 05 00 00 00
Value: CA 00 00 00
…
IFD Entry:
Tag : 69 87 // EXIF IFD
Type : 04 00 // LONG
Size : 01 00 00 00
Value: 1C 01 00 00 // Offset of EXIF IFD
END of IFD0
IFD1 Offset : 18 03 00 00
… // 存放IFD的Value数据
EXIF IFD :
Entries Count : 18 00
IFD Entry :
Tag : 9A 82 // Exposure time
…
END of EXIF IFD
Next IFD : 00 00 00 00 // 按标准IFD链表约定,表示没有后继IFD
… // 存放EXIF IFD的Value数据
IFD1 : // EXIF中用于存放缩略图
Entries Count : 06 00
IFD Entry :
Tag : 03 01
…
END of IFD1
Next IFD : 00 00 00 00 // EXIF只用到两个TIFF IFD
… // Thumbnail etc.
// end of TIFF header
// 其它JPEG Marker segments
JPEG EOI : FF D9 // 图片结束
对EXIF格式的分析,至此基本上告一个段落了。从分析结果上可以看出,EXIF是一种非常灵活的格式,具有非常好的可扩充性,要想较好地处理其中的相关数据也是比较麻烦的。
其困难主要在于几个方面:
1、对于每种不同的IFD Entry Type,需要用不同的方法获取数据,特别是对于数据长度不同时,可能采用不同的数据存储方式,而IFD Entry的数量又可能很多,每个Entry根据Tag不同又有不同的意义
2、EXIF IFD是作为TIFF IFD的子链表形式存在(因为EXIF的IFD里定义了不同于TIFF标准的Tag,要保持与标准的TIFF格式互用,必须这样做),使得原来的链表结构变成了树形结构
3、Tag的种类和数量非常之大,在EXIF规范里定义了各个Tag的支持级别(见[1]4.6.8),光是JPEG格式下必须支持的Tag就有十几个,TIFF格式更多,再加上可选支持的Tag,有几十上百个,并且还存在未来继续扩充的可能
4、对于可支持不同语言的软件来说,同一个Tag的意义要用每一种支持的语言表达一次,如果将这部分处理写入代码,对于增加新的语言支持会带来不必要的麻烦
为了解决这些困难,必须要找到一个同样是非常灵活的处理方法来处理EXIF数据。而XML正是这样一种方法。从前面的分析结果可以看出,EXIF的数据记录方式是层层嵌套的树形结构,是非常适合用XML的,因为XML也是这样的树形结构。
通过定义一套XML标签,然后将EXIF数据转换成XML文档,可以最大限度地保留EXIF数据的原始内容及结构。并且作为一种通用格式,XML可以很方便地进行再处理,比如:通过XSLT进行转换,使之成为HTML或其它便于显示的格式;或者将此XML传递给其它软件作进一步处理等。
对于前面说到的困难,XML都很好地解决:
1、不同类型的问题,通过转换为XML,将所有的Value都转换成字符串,便于统一处理
2、XML本来就是树形结构,可以在转换的时候方便地通过调整节点位置,使各IFD统一处理
3、可以将所有的Tag原样导出到XML中,在以后对XML的处理时再根据Tag进行处理,比如通过修改XSL文件实现对新增Tag的支持
4、同样是对XML处理时才需要面对具体的Tag,比如为不同的语言提供相应的XSL文件即可
下面的代码片断(Borland C++ Builder)实现了从EXIF数据到XML的转换:
//---------------------------------------------------------------------------#include typedef struct {WORD EntryTag;WORD EntryType;DWORD EntrySize;DWORD EntryValue;} TIFDEntry;#include //---------------------------------------------------------------------------BYTE * __fastcall TExifXML::GetIFD(_di_IXMLNode aNode, BYTE * aTIFFHeader, int aPosition, AnsiString aName){_di_IXMLNode pIFD = aNode->AddChild( "IFD" );if ( aName != "" )pIFD->Attributes["name"] = aName;BYTE * p = aTIFFHeader + aPosition;WORD nWord;memcpy( &nWord, p, sizeof ( nWord ) );p += sizeof ( nWord );_di_IXMLNode pChild = pIFD->AddChild( "Count" );pChild->Text = Format( "0x%X", ARRAYOFCONST( ( ( int )nWord ) ) );TIFDEntry ent;_di_IXMLNode pEntry;BYTE * pTemp;for ( int i = nWord; i > 0; --i ){memcpy( &ent, p, sizeof ( ent ) );p += sizeof ( ent );pEntry = pIFD->AddChild( "Entry" );pChild = pEntry->AddChild( "Tag" );pChild->Text = Format( "0x%X", ARRAYOFCONST( ( ( int )ent.EntryTag ) ) );pChild = pEntry->AddChild( "Type" );pChild->Text = IntToStr( ent.EntryType );pChild = pEntry->AddChild( "Size" );pChild->Text = Format( "0x%X", ARRAYOFCONST( ( ( int )ent.EntrySize ) ) );pChild = pEntry->AddChild( "Value" );switch ( ent.EntryType ) {case 1 : // BYTEif ( ent.EntrySize == 1 )pChild->Text = Format( "0x%.02X", ARRAYOFCONST( ( ( int )( BYTE )ent.EntryValue ) ) );elsethrow Exception( "Unsupported!" );break;case 2 : // ASCIIif ( ent.EntrySize <= 4 )pChild->Text = reinterpret_cast( &ent.EntryValue );elsepChild->Text = reinterpret_cast( aTIFFHeader + ent.EntryValue );break;case 3 : // SHORTif ( ent.EntrySize == 1 )pChild->Text = Format( "0x%.04X", ARRAYOFCONST( ( ( int )( WORD )ent.EntryValue ) ) );elsethrow Exception( "Unsupported!" );break;case 5 : // RATIONALpChild->Text = FloatToStr( *reinterpret_cast( aTIFFHeader + ent.EntryValue )/ ( double )( *reinterpret_cast( aTIFFHeader + ent.EntryValue + sizeof ( DWORD ) ) ) );break;case 7 : // UNDEFINEDif ( ent.EntrySize <= 4 )pTemp = reinterpret_cast( &ent.EntryValue );elsepTemp = aTIFFHeader + ent.EntryValue;pChild->Text = "";for ( int j = 0; j < ( int )ent.EntrySize; ++j ){pChild->Text = pChild->Text+ Format( " 0x%.02X", ARRAYOFCONST( ( ( int )( BYTE )( *pTemp ) ) ) );pTemp++;if ( j % 16 == 15 )pChild->Text = pChild->Text + "rn";}break;case 9 : // SLONGif ( ent.EntrySize == 1 )pChild->Text = IntToStr( ent.EntryValue );elsethrow Exception( "Unsupported!" );break;case 10: // SRATIONALpChild->Text = FloatToStr( *reinterpret_cast( aTIFFHeader + ent.EntryValue )/ ( double )( *reinterpret_cast( aTIFFHeader + ent.EntryValue + sizeof ( int ) ) ) );break;default: // LONG & other unknown typepChild->Text = Format( "0x%.08X", ARRAYOFCONST( ( ( int )ent.EntryValue ) ) );break;}switch ( ent.EntryTag ) {case 0x8769 : // Exif IFDGetIFD( aNode, aTIFFHeader, ent.EntryValue, "EXIF" );break;case 0x8805 : // GPS IFDGetIFD( aNode, aTIFFHeader, ent.EntryValue, "GPS" );break;case 0xA005 : // Interoperability IFDGetIFD( aNode, aTIFFHeader, ent.EntryValue, "InterOp" );break;}}return p;}//---------------------------------------------------------------------------void __fastcall TExifXML::GetTIFFHeader(_di_IXMLNode aNode, BYTE * aTIFFHeader){BYTE * p = aTIFFHeader;char sByteOrder[3];memcpy( sByteOrder, p, 2 );p += 2;sByteOrder[2] = 0;_di_IXMLNode pChild = aNode->AddChild( "ByteOrder" );pChild->Text = sByteOrder;WORD nFlag;memcpy( &nFlag, p, sizeof ( nFlag ) );p += sizeof ( nFlag );pChild = aNode->AddChild( "Flag" );pChild->Text = Format( "0x%.04X", ARRAYOFCONST( ( ( int )nFlag ) ) );DWORD nPointer;memcpy( &nPointer, p, sizeof ( nPointer ) );int i = 0;while ( nPointer > 0 ){p = GetIFD( aNode, aTIFFHeader, nPointer, AnsiString( "IFD" ) + IntToStr( i++ ) );if ( !p )break;memcpy( &nPointer, p, sizeof ( n Pointer ) );}}//---------------------------------------------------------------------------int __fastcall TExifXML::LoadFromStream(TStream * aStream){if ( !FXMLDoc )throw Exception( "XMLDoc property is null!" );TMauto_ptr ms( new TMemoryStream( ) );ms->CopyFrom( aStream, aStream->Size );ms->Seek( 0, soFromBeginning );FXMLDoc->FileName = "";FXMLDoc->Active = true;FXMLDoc->Version = "1.0";FXMLDoc->Encoding = "GB2312";_di_IXMLNode pNode = FXMLDoc->AddChild( "ExifAPP1" );_di_IXMLNode pChild = pNode->AddChild( "ExifID" );char sExifID[6];ms->Read( sExifID, 6 );pChild->Text = sExifID;pChild = pNode->AddChild( "TIFFHeader" );BYTE * pHeader = static_cast( ms->Memory ) + ( int )ms->Position;GetTIFFHeader( pChild, pHeader );return ms->Size;}
其中FXMLDoc是一个TXMLDocument控件,用于生成XML。LoadFromStream方法读入的内容为JPEG APP1这个Marker Segment的内容(注意,不是JPEG文件)。GetTIFFHeader方法用于读出TIFFHeader的内容,包括Image File Header和IFD链表。GetIFD则是用于解读IFD的具体内容,其中包括对EXIF的三个扩充IFD的递归解读,并且其中包含了将各种数据类型转换为字符串的部分,特别是对不定长的UNDEFINED类型的处理(其结果见下面转换后的XML)。
转换后的XML大致如下:
<?xml version="1.0" encoding="GB2312"?><ExifAPP1><ExifID>Exif</ExifID><TIFFHeader><ByteOrder>II</ByteOrder><Flag>0x002A</Flag><IFD name="IFD0"><Count>0xB</Count><Entry><Tag>0x10E</Tag><Type>2</Type><Size>0xB</Size><Value> </Value></Entry><Entry><Tag>0x10F</Tag><Type>2</Type><Size>0x6</Size><Value>NIKON</Value></Entry><Entry><Tag>0x110</Tag><Type>2</Type><Size>0x5</Size><Value>E775</Value></Entry>...<Entry><Tag>0x8769</Tag><Type>4</Type><Size>0x1</Size><Value>0x0000011C</Value></Entry></IFD><IFD name="EXIF"><Count>0x18</Count>...<Entry><Tag>0x9000</Tag><Type>7</Type><Size>0x4</Size><Value> 0x30 0x32 0x31 0x30</Value></Entry>...<Entry><Tag>0x9286</Tag><Type>7</Type><Size>0x7D</Size><Value> 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x200x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x200x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x200x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x200x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x200x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x200x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x200x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20</Value></Entry>...<Entry><Tag>0xA005</Tag><Type>4</Type><Size>0x1</Size><Value>0x00000376</Value></Entry>...</IFD><IFD name="InterOp"><Count>0x2</Count><Entry><Tag>0x1</Tag><Type>2</Type><Size>0x4</Size><Value>R98</Value></Entry><Entry><Tag>0x2</Tag><Type>7</Type><Size>0x4</Size><Value> 0x30 0x31 0x30 0x30</Value></Entry></IFD><IFD name="IFD1"><Count>0x6</Count><Entry><Tag>0x103</Tag><Type>3</Type><Size>0x1</Size><Value>0x0006</Value></Entry>...</IFD></TIFFHeader></ExifAPP1>
有了这个XML就可以很方便地进行下一步处理了,比如用下面这个XSL文件对上面这个XML进行转换:
<?xml version="1.0" encoding="GB2312" ?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/TR/WD-xsl"><xsl:template match="/"><xsl:for-each select="ExifAPP1/TIFFHeader/IFD/Entry"><xsl:choose><xsl:when test="Tag[.='0x10F']">制造商=<xsl:value-of select="Value" /></xsl:when><xsl:when test="Tag[.='0x110']">型号=<xsl:value-of select="Value" /></xsl:when></xsl:choose></xsl:for-each></xsl:template></xsl:stylesheet>
即可得出下面的结果:
制造商=NIKON
型号=E775
以后不论是增加Tag还是要改语言,只要修改这个XSL文件即可实现,完全不用修改EXIF处理部分的程序代码,非常的灵活方便。
2004-4-3
参考文献:
[1] JEITA CP-3451. Exchangeable image file format for digital still cameras:Exif Version 2.2. JEITA(Japan Electronics & Information Technolog Industries Association). April, 2002.
[2] Aldus Corporation. TIFF Revision 6.0 Final – June 3,1992.
[3] Gregory K.Wallace. The JPEG Still Picture Compression Standard.Communications of the ACM. April, 1991.
[4] Eric Hamilton. JPEG File Interchange Format Version 1.02. C-Cube Microsystem. September 1, 1992.
今天刚写了一篇跟物理有关的BLOG,就在CSDN上看到这个:《[教育]尴尬的国际地位-可怜的中国物理!》
可怜的又何止物理呢?软件业不是一样,推而广之,整个中国计算机业也是。归根到底是整个中国的学术界都有问题。那些所谓的砖家,鞋者们,很多其实在真正的大师面前,是连给人提鞋也不配的。这不是我要妄自菲薄,实在是因为它们“学术腐败”。当然这其中教育要承担很大的责任,所谓上梁不正下梁歪,老师教授都不是靠真正的学术成果得到升迁或好的待遇,那学生自然要靠抄袭才能完成毕业论文了(单MOP上就常见有人散尽MP求论文的,CSDN上也有)。
不过真要深究下去就没底了,因为整个社会的道德水准都在沦丧,这一点只要每个周日看看中央台的《每周质量报告》应该就会深有体会了。
还是回到软件业上。中国自从有了所谓的软件行业,就始终是笼罩在一种急功近利的浮躁氛围中。这其中IT媒体对所谓的科技明星的炒作有一份很大的功劳。于是乎,听说Bill.Gates靠软件发大财了,就一窝蜂地去做软件;听说.com流行了,就一窝蜂地去Internet上淘金;听说JAVA不错,就全开始J2EE/EJB起来;听说.net要后来居上,就全都开始啃C#;听说印度人做软件不错,就排着队上西天取经去了;听说CMM/ISO 9000好,就一个个去过认证,最可笑的是好像很少有过不了的;听说RUP好,就都开始ROSE上了;听说XP不错,就一个个Agile起来……转眼二十几年过去了,MS从一个几十个人的小公司成为世界第一大软件公司,我们呢?什么也没有……不,还有巨人、科利华、TOP之类,都是“大”“软件”公司啊……
我们不是革命者,没有能力去改变这个社会,但至少我们能做到:踏实做好自己的事。浮躁之风,能少一点是一点。