今天有朋友发了个tiff文件,说是损坏了,之前能打开,现在不知道为啥打不开了。问能不能帮忙修复。那得看看啊,图形图像相关的问题我都有兴趣看看。
先在网上找了几个工具,尝试了下万兴易修,没修复成功。
在网上找存在类似问题的文章看了下,提到有一种情况是磁盘坏道导致图像丢失部分数据,如果丢失的数据在头信息里面会导致整个图像读取不出来,如果丢失的数据是图像数据会导致图像缺行。
这个tiff文件能在文件信息里面能展示图像分辨率(1360x1024),但是图像打开是一片黑色。有分辨率信息说明部分头信息是正常的,图像全黑,说明部分关键头信息应该丢失了,图像数据大概率还是正常的。
那没办法了,只能对照tiff文件格式,逐个字节看文件的头信息了。大概看了眼TIFF的头信息文档,整体结构感觉比较简单,感觉可以分析一把。
文件用sublime打开,对照TIFF文档逐个查数据,遇到的第一个问题是低字节序和高字节序。之前当然知道这个概念,人工解码的时候发现还是容易混淆。
一图胜千言,下面两张图来自blog,简直救了命了:blog.gtwang.org/programming…
这个文件字节顺序是II(4949),低字节顺序,可读性比高字节顺序MM要差,需要人脑换位。
重点看IFD(图像文件目录),IFD里面每个12字节分四段记录一个图像目录项(DE,其实就是一个图像属性数据):tag、type、count、value/offset。
这个文件只有一个IFD,18个DE。挑几个代表性的属性记录:
| tag | type | count | value/offset | |
|---|---|---|---|---|
| DE1 | 0001 | 0400 | 0100 0000 | 5005 0000 |
| imageWidth | LONG | 1 | 1360 | |
| DE2 | 0101 | 0400 | 0100 0000 | 0004 0000 |
| imageLength | LONG | 1 | 1024 | |
| DE3 | 0201 | 0300 | 0300 0000 | 1a02 0000 |
| BitsPerSample | SHORT | 3 | 538 | |
| DE10 | 1701 | 0400 | 4000 0000 | e600 0000 |
| stripByteCounts | LONG | 64 | 230 |
一开始没太理解value/offset的意思,“如果占用的字节数少于等于4, 则数据直接存于此。如果超过4个,则这里存放的是指向实际数据的指针”。
我们以DE1和DE3为例子:
DE1的type是long,count是1,占用字节数是4*1(一个long四个字节,乘以count),而value/offset段刚好是4字节,存得下所以DE1的value/offset段直接存储数据
DE3的type是short,count是3,占用字节数是2*3(一个short两个字节,乘以count),因此value/offset段4字节存不下,因此存的是offset。从文件头开始数538个字节,然后读取3个short就是DE3的值。
最后汇总DE里面的文件信息
imageWidth=1360 图像宽度是1360
imageLength=1024 图像高度是1024
Compression=uncompressed 图像数据未压缩
photometricInterpretation=RGB 图像数据RGB格式
SamplesPerPixel=3 每个像素点占用3个字节
RowsPerStrip=16 每个条带内有16行
PlanarConfiguration=1 how the components of each pixel are stored 1:having all samples for a pixel next to each other within a single strip/tile 这个等于1说明每个像素的RGB是存储一起,不是像YUV那样每个sample一个条带。
查看了下文件大小,4178722字节,图像无压缩存储,136010243=4177920, 4178722-4177920=802,因此这个文件大概有802字节的头信息,剩下的刚好全部是图像数据。因此这个文件基本的文件头信息都是正确的,大概率图像数据都在,基本确定是能修复的。
这里有个条带的概念,其实就是图像被分成小块存储,这个文件就是每十六行一个小块,所以1024行应该有64个小块,这个值也叫StripsPerImage,后面偏移量信息里面需要用到这个数值。
看偏移量信息:
BitsPerSample=538 每个分量的Bit数,偏移538读取3个short:0800 0800 0800,说明图像每个分量用8bit。
StripOffsets=546 每个条带数据在文件中的偏移量,count=64=StripsPerImage,存储的是64个条带的实际图像数据在文件中的偏移量:
偏移量1:2203 0000 =31616+216+2=802=5016+2
偏移量2:2202 0100 =116^4+216^2+216+2=66082 和偏移量1的值相隔65280=161360*3
...
偏移量64:22c4 3e00 = 316^5+1416^4+1216^3+416^2+2*16+2=4113442
这个地方对偏移量数据简单做了验证:
首先偏移量1=802,还记得上面我们算过图像文件大小和整个图像数据大小的差别吗?刚好是802,因此第一个偏移量大概率是正确的。
偏移量2和偏移量1相差65280=1613603,刚好是一个strip的存储大小,而最后一个偏移量64=4113442,文件总大小4178722-4113442=65280。
因此文件从802开始全是条带图像数据。偏移量数据大概率没问题
stripByteCounts=230 每个条带内的字节数,count=64。这个其实根据上面的计算,不需要配置了(就是65280嘛),但是看了眼文件里面的数据,全是0,因此这块数据要么是走的老规范,要么就是被损坏了,因此这64个long数值,全替换成65280(00ff 0000)尝试下。
修改了stripByteCounts的64个long数值,文件就展示正常了。
参考资料: