【转】相当精辟的PDF图像介绍

J2EE 专栏收录该内容
51 篇文章 0 订阅

http://www.comicer.com/stronghorse/water/software/pic2pdf.htm 向这位细心细致的大虾致敬

图像转PDF的问题、方法及题外话

作者:马健
邮箱:stronghorse@tom.com
主页:http://stronghorse.yeah.net
发布:2006.04.05
更新:2006.11.21
测试用例:用例1(一组各种格式的图片),用例2libtiff 3.7.1所带测试图片)

友情提示:虽然我已经尽量简化,但是这篇文章感觉还是有点绕,我自己都没有自信一遍就能看懂。如果您的时间很宝贵, 建议不要随便浪费,还是去做该做的事吧,这里讨论的不过是些鸡毛蒜皮的小问题。

目录
一、需要解决的问题
    1、图像数据流重新压缩造成的问题
    2、阅读的顺畅性问题
    3、对特殊图像格式的支持问题
二、预备知识
    1、PDF支持的图像格式
    2、图像在PDF中的物理表示
    3、图像在PDF中的逻辑表示
三、问题的解决办法
四、小结
五、题外话一:PDF转图像
六、题外话二:除了PDF,还有什么?
    1、多页TIFF
    2、JBIG2
    3、DjVu
    4、双层PDF

一、需要解决的问题

图像转PDF似乎正在成为一个热门话题:对企业或组织来说,随着信息化的深入,需要将大量纸质档案电子化,实现在线查询、共享;对个人来说,随着家用数码相机等的普及,越来越多的人希望将电子图像信息转换成方便浏览、共享的格式。由于PDF文件本身的标准化、方便性,目前在企业和家庭应用越来越多,由此也带动了诸多图像转PDF软件的诞生。 当然“树林子大了,什么样的鸟都有”,至于鸟叫得怎么样,只能实际比较一下再说。

正好由于工作需要,我近期参与了某金融企业的无纸化办公平台建设项目,其中一个重要内容就是将该企业遍布全国的三十余个分支机构积存的巨额(最先开始扫描的一个分支机构就有近三百万页)纸质档案扫描成电子文档。作为项目负责人,我代表客户与国内数家扫描外包公司进行了接触、考察,并对能够搜集到的图像转PDF工具进行了测试、比较,在这个过程中我发现目前图像转PDF软件大致可以分为两类:

  • 基于虚拟打印原理。最著名的大概要算Adobe Acrobat ProfessionalPDF Factory。 基于虚拟打印原理的软件开发门槛稍高一些(需要提供打印驱动程序),所以多为收费软件,通用性较好,除图像文件外还能将Word等所有可打印格式转换成PDF。
  • 直接将图像嵌入PDF文件。如ACD Systems出品的ACDSEE中的Create PDF Wizzard、verypdf出品的Image2Pdf等。直接将图像嵌入PDF文件的软件实现相对简单,所以收费、免费的都有。

从测试的结果看,我认为这两类工具普遍存在一些共性问题,包括图像数据流重新压缩造成的问题生成的PDF文件的阅读顺畅性问题对特殊图像格式的支持问题等。本文将对这些问题的成因、解决方法加以探讨。

1、图像数据流重新压缩造成的问题

对基于虚拟打印原理实现的转换软件来说,其工作过程为:

  • 转换工具提供一个虚拟打印机。如Acrobat提供的打印机名为Adobe PDF。
  • 图像浏览软件打开图像文件,在收到打印命令后,象在真实打印机上打印一样,将图像逐象素描绘到虚拟“纸”上,形成发送给虚拟打印机的数据流。
  • 虚拟打印机收到数据流后,根据图像的色彩空间等信息,选择“合适”的压缩算法,对数据流再次进行压缩以减小文件长度,然后将压缩后的数据流存入PDF。

为了测试虚拟打印机对图像的处理,我选择了一组图像(用例1),在ACDSEE 8.0 Build 39中打开,选中所有图像,然后选择“File->Print”,打印到Acrobat 7.07提供的PDF虚拟打印机(ACDSEE和PDF打印机的所有参数均为默认值),然后比较原始图像数据和PDF中的图像数据,结果见表1.1表1.1中各种“解码器”的解释见本文后续的“PDF支持的图像格式”部分,“PDF中的图像数据”各栏中的数据来自开源的PdfView。如果您有兴趣查看PDF文件内部细节,建议用UltraEdit-32,仅看PDF文件结构 用PdfView足矣。

表1.1 从ACDSEE打印图像到Acrobat PDF虚拟打印的结果
原始图像PDF中的图像数据
序号说明宽×长
(象素)
图像解码器文件长度
(字节)
PDF解码器BitsPerComponent
/ColorSpace
数据流长度
(字节)
01黑白TIFF1728×1103CCITT G350,401CCITT G41/ICCBased54,329
02黑白TIFF3315×2334CCITT G435,518CCITT G41/ICCBased36,110
03彩色JPEG格式TIFF512×384DCTDecode24,428DCTDecode8/ICCBased21,753
04灰度JPG445×600DCTDecode34,167FlateDecode8/Indexed200,404
05彩色JPG1024×768DCTDecode102,776DCTDecode8/ICCBased71,540
0616级灰度GIF800×1199LZWDecode124,738FlateDecode4/Indexed128,925
07256色GIF130×129LZWDecode8,408FlateDecode8/Indexed6,990
08黑白PNG32×32FlateDecode164CCITT G41/ICCBased41
092色彩色PNG32×32FlateDecode112FlateDecode1/ICCBased21
10256级灰度PNG600×905FlateDecode289,059FlateDecode8/Indexed286,947
1116级灰度PNG720×1053FlateDecode74,322FlateDecode4/Indexed74,943
1224位色PNG350×560FlateDecode72,107FlateDecode8/ICCBased79,351
1315位色BMP260×235未压缩122,266DCTDecode8/ICCBased5,783
1416色BMP940×20RLE 8,134FlateDecode4/Indexed2,868
图像文件总长度(字节)

946,600

PDF文件总长度(字节)989,431
注1. 图像01.tif经ACDSEE转成PDF后,图像 物理表示的高度变成2206,原因 后面会加以解释
注2. 对于索引色(Indexed)图像,“数据流长度”仅包含图像数据流长度,不包含索引(调色板)数据流长度。严格说来这会造成一些误差,但影响不大。以下各表与此相同,不再特殊说明。

表1.1可以看出,对于ACDSEE发送过来的数据流,Acrobat PDF虚拟打印机进行如下处理:

  • 黑白图像重新压缩为CCITT G4数据流。
  • 灰度、索引色(调色板)图像压缩为Flate(ZIP)数据流,色深(BitsPerComponent)不变。
  • 非索引色(如15位色、24位色)图像压缩为DCT(JPG)或Flate数据流。似乎Acrobat PDF虚拟打印机能自动识别压缩为哪种数据流更有利,但压缩成JPG数据流时似乎质量系数很低:文件更小,质量更差。
  • 考虑跨平台特性,所有色彩均表示为ICCBased,并给出对照表。

这种处理带来的问题是:

  • 对于有损压缩的灰度JPG,转换后就成了无损压缩的数据流,必然导致文件长度的膨胀。
  • 对于原来无损压缩的彩色图像(PNG、BMP),转换后可能成为有损压缩的JPG数据流,造成图像质量下降(从转换后的数据流长度看,重新压缩的JPG质量系数不会太高)。对于原来就是有损压缩的彩色JPG图像来说,由于是解码后再压缩,图像质量将会逐次衰减。

为了确认是否所有软件在使用虚拟打印机转换PDF时,均会对JPG图像进行再压缩,我进行了第二个试验:在Word 2003中插入用例1中的13张图像(Word 2003不支持03.tif),每张图像前面再插入一点文字(图像编号),然后打印到Acrobat PDF虚拟打印。限于篇幅,这里不列举具体结果(用例1是公开的,Word 2003、Acrobat也不难找,想要较真的人可以自己动手试一下),仅说明结果:

  • 对01、02两个TIFF文件,Word转换成CCITT G4压缩,而且01.tif物理表示的高度未变,这 比ACDSEE强。但是这两个图像均被分解成了多个对象(01.tif分成2个,02.tif分成8个),相当于将原始图像切割成多个水平横条,每个对象代表一条。
  • 对04、05两个JPG图像,Word将原始文件完整地嵌入了PDF文件,但在结尾添加了一个\r(0AH)字符。显然,Word并没有对原始JPG文件进行解码、再压缩。
  • 对06、07两个GIF文件,Word打印后均成为ZIP数据流,与ACDSEE相似。
  • 对08、09两个PNG文件,Word打印后成了4位索引色图像(BitsPerComponent=4,Indexed),压缩算法仍然为ZIP。10~12三个PNG文件转换结果与ACDSEE相似。
  • 13、14两个BMP文件转换结果与ACDSEE相似。

从转换结果的对比来看,Word和ACDSEE的打印结果在TIFF、JPG、PNG方面存在较大差距:

  • 对于TIFF图像,Word对图像进行了切割,这个估计与Word对图像的显示方式有关;另外Word没有改变图像的物理表示, 这与下面要谈的“直接将图像嵌入PDF文件”的转换软件类似。
  • 对于JPG图像,Word没有再压缩的过程,因此不会造成图像质量的衰减。
  • 对于PNG图像,Word似乎有自己的表示方法,存在解码、重新压缩的过程,并且将BitsPerComponent小于4的PNG转换成BitsPerComponent等于4的图像,这点与CxImage很象。

为了搞清为什么Word不需要对JPG图像进行解压即可直接打印到虚拟打印机,我查了一下微软的MSDN,其中对StretchDIBits函数的解释引起了我的注意:

Windows 98/Windows 2000: This function allows a JPEG or PNG image to be passed as the source image. How each parameter is used remains the same, except :

  • If the biCompression member of BITMAPINFOHEADER is BI_JPEG or BI_PNG, lpBits points to a buffer containing a JPEG or PNG image, respectively. The BITMAPINFOHEADER's biSizeImage member specifies the size of the buffer. The iUsage parameter must be set to DIB_RGB_COLORS. The dwRop parameter must be set to SRCCOPY.
  • To ensure proper metafile spooling while printing, applications must call the CHECKJPEGFORMAT or CHECKPNGFORMAT escape to verify that the printer recognizes the JPEG or PNG image, respectively, before calling StretchDIBits.
StretchDIBits是在绘制、打印图像时的常用函数,虽然我不可能看到Word的源代码,但我相信Word直接或间接使用了这个函数。为了证实Acrobat虚拟打印机对JPG数据流的支持,我用VC++写了一个小程序,核心打印代码来自MSDN文章“Testing a Printer for JPEG or PNG Support”,最终证实了我的猜测:Acrobat虚拟打印机允许直接将JPG数据流发送给它,但是不支持PNG数据流。

综上所述,对于基于虚拟打印原理实现的图像转PDF工具,可能会存在如下问题:

  • 对于有损压缩的JPG文件,转换成PDF后的质量与发出打印命令的软件密切相关。如象ACDSEE这样先解码再打印,必然会因为图像的再压缩而造成质量衰减或文件膨胀。而象Word这样直接将JPG数据流发送到虚拟打印机,则与软件内部的打印设置有关,设置好了可以直接将数据流完整嵌入PDF而不造成损失或膨胀,设置不好则同样可能造成损失。另外打印机对JPG数据流的支持受平台限制,我猜这就是为什么包括ACDSEE在内的大多数软件宁愿先解码后打印的原因:解码成bitmap再打印可以不受平台限制。
  • 对于无损压缩的图像文件,如GIF、PNG、BMP等,真彩图像往往会被转换成有损压缩的JPG数据流,造成图像质量损失;灰度、索引色图像往往会被解码后再压缩成某种无损压缩数据流,如果虚拟打印机所选压缩算法的压缩效低于原图像压缩算法,则可能造成PDF文件的膨胀。

直接将图像嵌入PDF的转换软件工作原理与基于虚拟打印机的转换软件不同,其工作过程为:

  • 用户在转换软件中选择需要转换的图像文件。
  • 转换工具按照PDF文件规范创建PDF文件,写入文件头信息。
  • 转换工具逐一从图像文件中抽取图像数据,视需要对数据进行转换,然后将数据打包成PDF对象,写入PDF文件。
  • 转换工具写入PDF文件尾,打包结束。

为了测试图像嵌入式转换工具的效果,我将与表1.1相同的图像文件(用例1), 用ACDSEE 8.0 (Build 39)的Create PDF Wizzard转换成PDF,结果见表1.2

表1.2 ACDSEE 8 PDF创建插件转换结果
原始图像PDF中的图像数据
序号说明宽×长
(象素)
解码器文件长度
(字节)
解码器BitsPerComponent
/ColorSpace
数据流长度
(字节)
01黑白TIFF1728×1103CCITT G350,401DCTDecode8/DeviceGray905,917
02黑白TIFF3315×2334CCITT G435,518DCTDecode8/DeviceGray693,129
03彩色JPEG格式TIFF512×384DCTDecode24,428DCTDecode8/DeviceRGB34,496
04灰度JPG445×600DCTDecode34,167DCTDecode8/DeviceGray59,338
05彩色JPG1024×768DCTDecode102,776DCTDecode8/DeviceRGB129,655
0616级灰度GIF800×1199LZWDecode124,738DCTDecode8/DeviceRGB319,743
07256色GIF130×129LZWDecode8,408DCTDecode8/DeviceRGB6,706
08黑白PNG32×32FlateDecode164DCTDecode8/DeviceGray936
092色彩色PNG32×32FlateDecode112DCTDecode8/DeviceRGB896
10256级灰度PNG600×905FlateDecode289,059DCTDecode8/DeviceGray185,845
1116级灰度PNG720×1053FlateDecode74,322DCTDecode8/DeviceGray206,121
1224位色PNG350×560FlateDecode72,107DCTDecode8/DeviceRGB38,468
1315位色BMP260×235未压缩122,266DCTDecode8/DeviceRGB8,209
1416色BMP940×20RLE 8,134DCTDecode8/DeviceRGB22,018
图像文件总长度(字节)946,600PDF文件总长度(字节)2,619,592

表1.2中可以看出,ACDSEE 8.0 (Build 39)的Create PDF Wizzard对图像的转换原则是:

  • 灰度图像一律转换成灰度JPG数据流。
  • 彩色图像一律转换成彩色JPG数据流。

看来ACSEE对它的JPG转换引擎还真不是一般地有信心!但从表1.2所列数据看,这种转换也不是没有问题:

  • 对于黑白图像,CCITT G4的压缩比通常比灰度JPG高许多,毕竟它是专为压缩黑白图像而研发的压缩算法。因此将CCITT G3/G4转换成灰度JPG无疑将会造成文件膨胀,而且膨胀得很明显。这点对电子文档来说比较重要:大多数白纸黑字的纸质文档扫描后都是黑白图像。
  • 对于低色深(如16级灰度)图像来说,转换成灰度JPG(256级灰度)同样可能造成文件膨胀。
  • 对于本来就是JPG压缩格式的图像,用ACSEE转换后也会出现文件膨胀的问题,莫非是ACSEE转换插件用的JPG质量系数比较高?
  • 不论原来的图像格式是什么,经过ACSEE的转换插件转换后全部解码再重新压缩成有损的JPG数据流,无疑会对图像质量造成损伤。

ACSEE的转换插件效果很令我失望,为了比较其它嵌入式工具的转换效果,我又用用例1测试了verypdf的Image2Pdf v1.7,转换结果见表1.3

表1.3 Image2Pdf转换结果
原始图像PDF中的图像数据
序号说明宽×长
(象素)
解码器文件长度
(字节)
解码器BitsPerComponent
/ColorSpace
数据流长度
(字节)
01黑白TIFF1728×1103CCITT G350,401CCITT G41/DeviceGray41,638
02黑白TIFF3315×2334CCITT G435,518CCITT G41/DeviceGray34,981
03彩色JPEG格式TIFF512×384DCTDecode24,428DCTDecode8/DeviceRGB32,815
04灰度JPG445×600DCTDecode34,167DCTDecode8/DeviceGray34,167
05彩色JPG1024×768DCTDecode102,776DCTDecode8/DeviceRGB102,776
0616级灰度GIF800×1199LZWDecode124,738RunLengthDecode4/Indexed/DeviceRGB206,880
07256色GIF130×129LZWDecode8,408RunLengthDecode8/Indexed/DeviceRGB13,380
08黑白PNG32×32FlateDecode164FlateDecode/PNG1/DeviceGray91
092色彩色PNG32×32FlateDecode112FlateDecode/PNG1/Indexed/DeviceRGB21
10256级灰度PNG600×905FlateDecode289,059FlateDecode/PNG8/DeviceGray288,582
1116级灰度PNG720×1053FlateDecode74,322FlateDecode/PNG4/Indexed/DeviceRGB74,063
1224位色PNG350×560FlateDecode72,107FlateDecode/PNG8/DeviceRGB71,954
1315位色BMP260×235未压缩122,266DCTDecode8/DeviceRGB8,707
1416色BMP940×20RLE 8,134DCTDecode8/DeviceRGB20,890
图像文件总长度(字节)946,600PDF文件总长度(字节)942,458

表1.3看,Image2Pdf对图像数据的处理要比ACDSEE的PDF创建插件智能得多:

  • 对于黑白TIFF文件,能够自动压缩成CCITT G4;彩色TIFF解码后压缩成JPG。
  • 对于JPG文件,根本就没有解压、再压缩的过程,直接将原始JPG文件一个字节不改就嵌入PDF文件,从而避免因为再次压缩而造成质量衰减,而且解压、再压缩的时间也省了。
  • 对于GIF文件,解压后压缩为RLE(行程编码)。由于RLE的压缩率远不如GIF本身的LZW算法,因此这种再压缩会造成文件膨胀。估计这种吃力不讨好的做法与传说中LZW压缩算法的版权有关。
  • 对于PNG文件,数据流压缩算法不变(PNG压缩算法在PDF中对应/DecodeParms[<</Predictor 15>>]),但数据流长度会稍小一些,估计是去掉了PNG文件中的无关信息。
  • 对于彩色BMP文件,全部重新压缩成JPG数据流,在色彩数较多、色调过渡自然时能够减小文件长度,否则会增加文件长度。当然不论哪种情况画面质量都会损失。

其它图像转PDF的软件我还试过一些,不过基本与以上几种工具类似,都可能因为对图像数据流重新压缩而产生一些问题,差别只在问题的多与少、严重与不严重:

  • 将无损压缩转换成有损压缩,或对有损压缩解码后再次有损压缩,必然造成图像质量下降。
  • 改变文件数据流的压缩方法,在某些情况下可以减小文件长度,在某些情况下则会造成文件长度膨胀。关键是看数据与压缩方法的搭配是否合适。
  • 对于直接读取图像数据的转换工具,由于可以从原始图像文件中获取丰富的图像信息,包括原始数据压缩算法等,因此可以针对不同的文件格式或不同的图像情况做出选择;基于虚拟打印原理实现的转换工具,如果打印机只能得到解码后的数据流,选择的余地就会小一些:只能从bitmap数据流中获取色深等信息,然后自行选择算法重新压缩数据。

2、阅读的顺畅性问题

这里说的阅读顺畅性问题,是指:

  • 如果PDF纸张选择A4、B5等标准纸张,而原始图像的长宽比例与所选纸张的长宽比例不一致,必然会在上下或左右出现较多空白,影响阅读。
  • 如果PDF纸张随图像大小而变化,则转换出来的页面可能大小不一,在阅读时感觉页面跳来跳去,很是不爽。

对于第一个问题,目前没有什么好的解决方案。对于第二个问题,可能的解决方案包括:

  • 建议用户在阅读时将PDF Reader设置为单页、适合宽度,这样每一页都会自动缩放到Reader的窗口宽度。如果嫌麻烦,也可以在生成PDF时就指定“初始视图”中的“页面布局”为“单页”,“放大率”为“适合宽度”。这种方法的缺点是前后翻页时不如“连续”模式顺畅。
  • 在生成PDF文件时为页面指定一个固定宽度,页面的长度按照原始图像的长宽比自动伸缩。这种方法能保证在以“连续”模式阅读时页面不会跳来跳去,当然打印出来还是会在纸张的上下或左右产生空白。

3、对特殊图像格式的支持问题

这里说的“特殊图像格式”,其实主要就是TIFF格式。在常见的图像格式中,JPG、GIF、PNG、BMP等都有严格的格式规定,可以发挥的余地不多。但是对于TIFF来说,由于标准本身希望能够包容尽可能多的东西,但是对实现细节没有给出具体的规定,所以各家软件生成的TIFF文件五花八门,令人头疼。

以我提供的测试用例2为例,这个其实是支持TIFF文件最权威的开源项目libtiff 3.7.1版所带测试图片,不过去掉了一张caspian.tif(该图片共3通道,单通道采样位数高达64位浮点数,我的32位真彩显示器单通道采样位数只有8位整数,显示不了这么高级的图片)。但仅凭剩下的这些图片,已经可以难倒包括verypdf的Image2Pdf在内的一大批图像转PDF软件,就算是ACDSEE这样“专业”的图像浏览器,5.0.1版在看这些图像时也会出现比例失调(fax2d.tif、g3test.tif)、看不了(quad-tile.tif)、颜色失真(smallliz.tif、zackthecat.tif)等问题;8.0版虽然修正了上述问题,但又出现新的问题:看dscf0013.tif时颜色失真。

其实这些文件还算好,毕竟是libtiff组织提供的,至少它自己的源代码还能解出来。但在我接触到的国内专业扫描外包公司中,大多数公司提供的TIFF文件只要采用了有损压缩,多半就连libtiff也解不开,ACDSEE更是想都不用想。有些甚至连专门显示TIFF文件的Microsoft Office Document Imaging(微软Office 2003所带附件之一)都解不开。

偏偏由于工作需要,我必须和这些怪异TIFF文件打交道。我想到的出路包括:

  • 不要在TIFF文件中使用有损压缩,尤其不要用各品牌专业高速扫描仪所带扫描软件生成有损压缩TIFF文件。由于历史原因,这些软件遵循的多半是古老的TIFF标准,生成的文件大概只有它们自己的阅读软件能打开。如果有必要对图像进行有损压缩,直接存储为标准JPG格式即可,这个很难玩什么花样。
  • libtiff源代码为基础,针对这些图像不规范的地方,逐步修正libtiff代码。这就是为什么前段时间我自己写的ComicEnhancer Pro一直在升级的原因。我甚至怀疑,可能就是因为TIFF格式支持起来太麻烦,所以IE才不支持。

除TIFF外,PNG文件也是一种可能会造成潜在麻烦的格式。但是与TIFF不同,PNG的麻烦不在于文件格式本身或数据压缩算法,而在于它丰富的色彩表示:PNG支持灰度、索引、彩色、Alpha通道彩色图像,并且色深除低端的1、2、4、8位外,还支持16位色深。有兴趣又喜欢较真的人,可以到libpng下载一份libpng源代码,里面的contrib\pngsuite文件夹下就包含了一堆图片,专门用于测试软件对PNG色彩支持的能力。

从我测试的结果看,软件在处理PNG图像时可能出现的问题包括:

  • 将16位色深简化成8位色深。这个在通常24位/32位显示器上看不出问题,因为这些显示器最多只支持8位色深,但是将来高端显示成本降低后可能就会被人看出差异了。PDF文件也是从PDF 1.5版(Acrobat 6.0)开始才支持16位色深。
  • 对于低色深(BitsPerComponent < 4)的PNG图像,转换成BitsPerComponent=4的索引色图像,造成轻微的文件膨胀。

综上所述,目前图像转PDF工具普遍存在一些共性的问题,包括图像数据流重新压缩造成的问题生成的PDF文件的阅读顺畅性问题对特殊图像格式的支持问题等。为了更好地理解这些问题,并找到解决办法,下面先简单介绍一下PDF中与图像相关的基本概念。

二、预备知识

事先声明:本部分所有内容均来自Adobe公司发布的《PDF Reference 5th edition》, 说白了就是我看这份文档时做的笔记的一部分,所以看起来可能有点无头无尾,各位看得懂就看,看不懂就去看《PDF Reference 5th edition》原文吧。

1、PDF支持的图像格式

在PDF文件中,图像点阵信息以压缩数据流的形式存在,PDF通过过滤器(filter)对数据流解码。在《PDF Reference 5th edition》中,共介绍了十种过滤器,其中与图像相关的如表2.1所示。

表2.1 PDF文件支持的图像过滤器
过滤器名称对应压缩算法通称对应图像格式压缩类型说明
LZWDecodeLZWGIF、TIFF无损通常用于索引色(调色板)图像
FlateDecodeZIPPNG、TIFF无损除图像外,也用于文本压缩
RunLengthDecodeRLEBMP、TIFF无损通常用于单色图像
CCITTFaxDecodeG3/G4TIFF无损专为黑白图像研发的高效压缩算法
JBIG2DecodeJBIG2JBG无损专为黑白图像研发的高效压缩算法
DCTDecodeJPEGJPG、TIFF有损用于256级灰度、24位真彩自然图像
JPXDecodeJPEG2000J2K、JP2有损/无损JPEG的最新标准,压缩比与质量并重

表2.1看,其实对大多数常见图像格式,都可以将原数据流直接嵌入PDF文件,不需要再重新编码。当然某些数据,如JPG文件中的注释、PNG文件的文件头/文件尾,在PDF文件中没用,可以先剔除再将剩余部分嵌入PDF文件。而对于TIFF文件,需要针对具体压缩算法,将真正图像数据抽取出来再嵌入PDF文件。

直接使用原始数据流而不再重新编码,不仅能够节省图像转换成PDF的时间,而且对于有损压缩,可以避免因为反复压缩而造成图像质量的衰减。但是对于GIF格式的LZW压缩来说,情况有点复杂:由于Unisys公司声称对LZW算法拥有专利权,导致很多软件,包括大名鼎鼎的xpdf在内,放弃 对LZW的支持,改用开源的Flate压缩算法。Flate其实是Winzip等软件使用的ZIP压缩算法的另外一个名字。《PDF Reference 5th edition》中对这两种算法的描述如下(黑体效果是我自己加的):

Because of its cascaded adaptive Huffman coding, Flate-encoded output is usually much more compact than LZWencoded output for the same input. Flate and LZW decoding speeds are comparable, but Flate encoding is considerably slower than LZW encoding.

由于上文中黑体部分的描述,及诸多公司、组织卷入与Unisys的专利纠纷,因此在我见到的图像转PDF工具中,没有使用LZW压缩算法的,都宁愿将用LZW压缩的GIF图像解码后再压缩成Flate数据流。这种转换在多数情况下能获得更好的压缩比,但例外总是有的(所以上文用的词是usually,而不是always),如用例1中的06.gif,LZW就比ZIP有效得多,这样的图片我还有几张,不过限于篇幅和空间,就不节外生枝了。

对于LZW和Flate压缩,PDF还支持预报器(Predictor),预报器表示根据图像的某些特征,先对图像进行某些预处理后再对处理结果进行压缩,以获取更高的压缩比。在《PDF Reference 5th edition》中定义的预报器见表2.2。小于10的预报器称TIFF预报器,源自libtiff;10以上的称PNG预报器,源自libpng。因此如果PDF文件中定义图像的物理表示时使用了Predictor属性,多半可以猜到原始图像的格式。 在表3.1和表1.3中,为了更好地进行比较,采用PNG预报器的Flate解码器均标注为FlateDecode/PNG。

表2.2 预报器
预报器值含义
1No prediction (the default value)
2TIFF Predictor 2
10PNG prediction (on encoding, PNG None on all rows)
11PNG prediction (on encoding, PNG Sub on all rows)
12PNG prediction (on encoding, PNG Up on all rows)
13PNG prediction (on encoding, PNG Average on all rows)
14PNG prediction (on encoding, PNG Paeth on all rows)
15PNG prediction (on encoding, PNG optimum)

2、图像在PDF中的物理表示

一幅图像在PDF文件中通常用一个XObject对象表示(某些TIFF图像可能要用多个对象表示),这个对象描述图像的原始象素点阵信息,因为这些点阵信息由产生图像的设备本身的物理性质(如扫描仪的DPI、 数码相机的有效象素数等)决定,因此在这里称为图像的物理表示,在《PDF Reference 5th edition》中又称为采样表示(Sample Representation)。

要描述图像的物理表示,需要提供下列信息:

  • 图像的宽度(width),以象素为单位。
  • 图像的高度(height),以象素为单位。
  • 每象素的颜色通道数,或色彩空间(The number of color components per sample, ColorSpace)。
  • 每通道的采样位数(The number of bits per color component, BitsPerComponent)。
  • 图像象素点阵数据流(stream)。
  • 解码图像数据流所需的过滤器(Filter)名称,及过滤器采用的预报器(Predictor)。

用例1为例,在ACDSEE 8 PDF创建插件转换的PDF中,用如表2.3所示的XObject定义第二 幅图像(数据来自PdfView,右侧双斜杠后面是我自己加的注释):

表2.3 图像对象定义实例
9 0 obj
<<
/Type /XObject
/Subtype /Image
/Name /Image90
/Width 3315
/Height 2334
/BitsPerComponent 8
/ColorSpace /DeviceGray
/Filter [/DCTDecode]
/Length 693129
>>
stream
……
endstream
endobj
// 对象定义开始,对象ID为9
// 字典(dictionary)定义开始
// 对象类型为XObject
// 对象子类型为图像
// 对象名称Image90
// 图像宽度3315象素
// 图像高度2334象素
// 每通道采样位数为8
// 色彩空间为256级灰度
// 解码过滤器为JPG(参见表2.1
// 数据流长度693129字节
// 字典定义结束
// 数据流开始
// 数据流内容,一串16进制数,此处从略
// 数据流结束
// 对象定义结束

PDF中的每个对象均有一个ID(编号),通过对象ID,可以对对象本身进行引用。如一个LOGO图像可能作为背景出现在每一个页面上,在每一页中没有必要都包含这个LOGO图像的实际数据,只要引用这个LOGO图像的ID即可。这样无疑可以提高PDF文件的存储效率。上例中图像的对象ID就是9。

3、图像在PDF中的逻辑表示

前面说的图像的物理表示是用象素点来表示图像,但是如果直接按照象素点对图像进行显示、打印,可能会出现问题。以用例1中的第二 幅图像为例,象素点阵为3315×2334,如果在分辨率为96 DPI的显示器上显示,尺寸是34.5英寸×24.3英寸(1英寸=2.54厘米,实际英寸数=象素数÷DPI,如3315÷96=34.5英寸),而在分辨率为300 DPI的打印机上打印,打出来只有11.1英寸×7.8英寸,这显然与PDF要求的“在任何平台上均可获得相同的效果”不符。因此在用物理表示定义出图像的象素点阵后,在实际需要显示图像的地方,不仅要给出图像物理表示的对象ID,还需要给出图像的逻辑表示,包括:

  • 图像的逻辑尺寸。这个尺寸的单位是1/72英寸,因此是一个逻辑概念,即不论在什么样的设备上输出图像,图像的大小都是固定的英寸值,而不会随着输出设备的DPI值而变化。
  • 图像的偏移量,即图像左上角点距离页面左上角点的距离,这同样是一个与设备的物理分辨率无关的逻辑量,单位为1/72英寸。
  • 图像的旋转角度。

这种物理与逻辑表示的分离,可以带来一些好处:

  • 同一份物理数据,可以在不同的地方、用不同的大小、以不同的旋转角度进行显示。
  • 通过将物理表示映射成逻辑表示,可以脱离设备的物理性能限制,在不同的设备上获得相同的效果。

具体到PDF文件格式上,在一个页面上显示一幅图像,除了前面说过的图像的物理表示对象外,还需要定义页面(Page)对象,然后在Page对象中:

  • 用MediaBox属性定义页面的逻辑大小,单位为1/72英寸。
  • 用Resources属性定义页面中包含的资源,即前面说的图像物理表示的对象ID。
  • 用Contents属性定义资源对象(图像)的逻辑表示。

Contents属性通常定义一个六元组,表示为[a, b, c, d, e, f],则从图像物理坐标(x, y)映射为逻辑坐标(x', y')的映射关系可以表示为如下矩阵运算:

  ab0 
 [x' y' 1] = [x y 1] ×cd0      式 2.1
  ef0 

或表示为如下解析表达式:

 x' = ax + cy + e           式 2.2
 y' = bx + dy + f      

从《计算机图形学》知识可知,式2.1式2.2中参数a、d分别为x、y向比例系数,实现从物理尺寸到逻辑尺寸的映射;c、b为旋转系数,表示图像显示时的旋转角度;e、f为平移系数,表示图像到页面左上角的偏移量。

用例1为例,在ACDSEE 8 PDF创建插件转换的PDF中,用如表2.4所示的结构定义第二页。

表2.4 页面对象定义实例
8 0 obj
<<
/Type /Page
/Parent 3 0 R
/Contents 7 0 R
/MediaBox [0 0 3315 2334]
/Resources <</ProcSet [/PDF /ImageC] /XObject <</Image90 9 0 R>>>>
>>
endobj
// 对象定义开始
// 字典定义开始
// 对象类型
// 父对象
// 内容在对象7定义
// 页面大小
// 页面包含的图像
// 字典定义结束
// 对象定义结束

内容对象7中定义了图像对象的逻辑表示,如表2.5所示。

表2.5 图像对象的逻辑表示实例
7 0 obj
<<
/Length 38
>>
stream
q
3315 0 0 2334 0 0 cm
/Image90 Do
Q
endstream
endobj
// 对象定义开始
// 字典定义开始
// 数据流长度38字节
// 字典定义结束
// 数据流开始
// 保存图像状态(Save graphics state)
// 式2.1座标映射所需的六元组(Coordinate transformation Matrix)
// 绘制映射后的图像(Paint image)
// 恢复图像状态(Restore graphics state)
// 数据流结束
// 对象定义结束

表2.5的六元组参数看,ACDSEE 8 PDF创建插件用一种很偷懒的方法构造该六元组:直接用图像的物理象素尺寸作为逻辑尺寸。由于屏幕DPI通常为96 DPI,而PDF为72 DPI,这种偷懒造成的后果就是图像在PDF Reader中按照“实际大小”显示时,看起来会比在ACDSEE中按照“完整大小”显示更大一些。精确一点说,这张图片在PDF中的“实际大小”达到了46.04英寸×32.42英寸。其中46.04=3315÷72,32.42=2334÷72。

对于喜欢较真的人来说,ACDSEE的这种偷懒造成了更深层次的失真:用例1中的第二幅图像是一张扫描形成的TIFF图像,在TIFF文件结构中记载了扫描时扫描仪使用的DPI值——200 DPI。在ACDSEE 8中打开此图像文件,点击“文件->属性”菜单,在显示出来的文件属性中即可看到扫描DPI,及按照扫描DPI换算,这张图片对应原始纸质页面的大小——16.57英寸×11.67英寸。这个尺寸与46.04英寸×32.42英寸相比,实在是差得太远了一点。

而如果用verypdf的Image2Pdf v1.7对同一张图片进行转换,可以看出转换后的PDF页面大小为16.57英寸×11.67英寸,即在PDF Reader中选择按照“实际大小”进行显示,显示出来的图像大小,正好与原始纸质文件的大小一模一样。显然这样的结果更符合文档电子化的要求和习惯。

Image2Pdf的这种“保真”转换过程可以描述如下:

  • 如果图像文件中记录了扫描时扫描仪的DPI设置,则先按照“英寸数=象素数÷扫描DPI”,计算出图像的原始尺寸,以英寸为单位。
  • 按照“PDF尺寸=英寸数×72”,将以英寸为单位的图像原始尺寸转换成PDF中的逻辑尺寸,并据此填写六元组。

三、问题的解决办法

在了解了相关预备知识后,再回顾前面提到的图像转PDF需要面对的问题,其答案自然明了:

  • 图像数据流重新压缩造成的问题:对有损压缩图像数据,应尽量将原始数据流嵌入PDF文件,避免重新压缩造成图像质量衰减;对无损压缩图像数据,可以根据图像特征选择合适的无损压缩算法重新压缩图像数据,以节省存储空间,也可以直接将原始图像数据嵌入PDF,以节省重新压缩所需的时间。
  • 阅读的顺畅性问题:提供灵活多样的页面布局供用户按需选择,包括固定纸张大小、固定纸张宽度、按照图像大小定制页面等。页面大小的不同不应对原始图像数据流(图像的物理表示)造成影响,而是通过定义图像的逻辑表示,由PDF Reader本身来完成必要的图像缩放工作。
  • 特殊图像格式的支持问题:这个问题靠等待很难等到结果,最简单的办法就是自己去面对、解决。

总之,对于象我这样有特殊要求的人来说,在目前的情况下要想得到满意的结果,还是只能贯彻“人要靠自己”的原则。当然也没有必要重新发明轮子,在我看来最理想的情况就是能够在现有开源项目基础上,通过必要的修改和补充,就能达到我的要求。但是google的结果令我稍微有点惊讶:虽然目前最权威的图像codec开源项目都是基于C的,包括JPEG LIBlibpnglibtiff等,但是偏偏在PDF生成领域,JAVA似乎比C多,包括iText等一大批开源项目,而C只有PDFlibClibPDFPanda等有数的几个。

ClibPDF从参考手册上看,在绘图等功能方面很有特色,但是对于图像文件的支持较差,而且要付费,所以我粗看了一下,没有深究。

Panda的功能、接口都非常简洁明了,生成的PDF文件也没多少费话,所以我花时间详细试了一下。结果发现一个小小的小缺点:它的内存漏洞实在是太多 了点,补都补不过来,最后只好放弃。

相比之下,PDFlib堪称内容丰富、功能强大,某些高级功能(web优化、权限控制等)虽然要付费才能

  • 1
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

1.绪论 应用有两个主要方向:(1)改善图片便于人理解;(2)便于机器存储、传输和表示而进行图像处理。 1.1什么的数字图像处理 数字图像处理是指借助于数字计算机来处理数字图像。 1.2数字图像处理的起源 选取CT图像部分:20世纪70年代发明“计算机轴向断层术”简称计算机断层(CT),是数字图像处理在医学诊断领域最重要的应用之一。 2.数字图像基础 图像的函数表述——f(x,y) 灰度级256下,分辨率从2014*1024降到32*32 分辨率不变的情况下,灰度级分别为16、8、4和2 ROI图像选取 空间域图像处理——带通滤除周期噪声 标准差分别为14.3、31.6、49.2 3.灰度变换与空间滤波 主要在空间域,针对像素进行操作。灰度变换主要对单个像素操作,处理对比度和阈值;空间滤波主要针对像素的领域处理。 灰度变换:由r分布到s分布 空间滤波:相当于使用卷积核,可实现多种操作,如平滑、锐化等等 4.频率域滤波 空间域到频域 理想的低通滤波器 低通≈平滑去皱纹 高通≈提取边界信息,如指纹 选择性滤波≈去除周期性的噪声 5.图像复原与重建 图像增强是主观过程;图像复原是客观过程,拟建立退化模型恢复出原图像。 注:这一点在胃癌CT图像中可以建立退化函数。 噪声类型 在某些特定场景的噪声,确实会呈现一种确定分布,这就有了逼近估计的可能性,尤其是知道先验的情况下。 不同参数的湍流退化模型 6.彩色图像处理 分为全彩色处理和伪彩色处理:第一类由全彩色传感器获取;第二类对灰度赋予颜色。 CT均为灰度,在此略过 7.小波和多分辨率处理 小波比传统的傅里叶变换改进在于:除了提供频率还能提供该频率发生的时间点。 多分辨率处理:综合使用多种技术,如子带编码、正交镜像滤波、金字塔图像处理等。 图像金字塔是一种以多分辨率来解释图像的有效但概念简单的结构。应用于图像分割,机器视觉和图像压缩。 金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。 8.图像压缩 图像压缩是一种减少描绘一幅图像所需数据量的技术 9.形态学图像处理 将数学形态学作为工具,从图像中提取表达和描绘区域形状的有用图像分量,如边界、骨架和凸壳等。 腐蚀 膨胀 孔洞填充 10.图像分割 之前的处理,输入输出都是图像;从分割开始,有了另一个方向——输入图像输出是某些属性。 灰度图像的分割,分为两类:(1)根据灰度的不连续性,进行边缘分割;(2)根据灰度相似性,把图像分割为几个相似的区域。 1)不连续性—点、线、面,可以用差分以及导数来表示 2)分布特性—阈值处理(全局阈值、局部阈值) 3)基于区域的分割 区域生长、区域分裂与聚合 形态学分水岭 11.表示和描述 对分割后的区域进行表示和描述,也从两种角度:(1)外部特征(如边界);(2)内部特征(组成像素)。 表示:边界追踪、链码、最小周长多边形近似 边界描述:简单描绘子、形状数、傅里叶描绘子、统计矩 区域描绘子:简单描绘子、拓扑描绘子、纹理、不变矩 主分量描绘 关系描绘子 注:纹理分析是区域描绘中的一种方法。 12.目标识别 主要分为两大领域:决策理论方法(定量)和结构方法(定性)。 基于决策理论的识别: (1)最小距离分类器 (2)相关匹配 (3)统计分类器,如高斯分类器 (4)神经网络 总结: 这本书很经典,即使在深度学习火热的情况下,也没有逃离这经典的结构。依旧是完成了一种特征提取描述以及匹配分类的过程。
百度云盘分享 简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级、中级、高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情、执着,对IT的憧憬、向往!此时此景,笔者只专注Android、Iphone等移动平台开发,看着这些源码心中有万分感慨,写此文章纪念那时那景! Java 源码包 Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除,从账户中取出amt,如果amt>账户余额抛出异常,一个实体Bean可以表示不同的数据实例,我们应该通过主键来判断删除哪个数据实例…… ejbCreate函数用于初始化一个EJB实例 5个目标文件,演示Address EJB的实现,创建一个EJB测试客户端,得到名字上下文,查询jndi名,通过强制型得到Home接口,getInitialContext()函数返回一个经过初始化的上下文,用client的getHome()函数调用Home接口函数得到远程接口的引用,用远程接口的引用访问EJB。 EJB中JNDI的使用源码例子 1个目标文件,JNDI的使用例子,有源代码,可以下载参考,JNDI的使用,初始化Context,它是连接JNDI树的起始点,查找你要的对象,打印找到的对象,关闭Context…… ftp文件传输 2个目标文件,FTP的目标是:(1)提高文件的共享性(计算机程序和/或数据),(2)鼓励间接地(通过程序)使用远程计算机,(3)保护用户因主机之间的文件存储系统导致的变化,(4)为了可靠和高效地传输,虽然用户可以在终端上直接地使用它,但是它的主要作用是供程序使用的。本规范尝试满足大型主机、微型主机、个人工作站、和TACs 的不同需求。例如,容易实现协议的设计。 Java EJB中有、无状态SessionBean的两个例子 两个例子,无状态SessionBean可会话Bean必须实现SessionBean,获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,计算利息等;在有状态SessionBean中,用累加器,以对话状态存储起来,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除…… Java Socket 聊天通信演示代码 2个目标文件,一个服务器,一个客户端。 Java Telnet客户端实例源码 一个目标文件,演示Socket的使用。 Java 组播组中发送和接受数据实例 3个目标文件。 Java读写文本文件的示例代码 1个目标文件。 java俄罗斯方块 一个目标文件。 Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密   Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。   设定字符串为“张三,你好,我是李四”   产生张三的密钥对(keyPairZhang)   张三生成公钥(publicKeyZhang)并发送给李四,这里发送的是公钥的数组字节   通过网络或磁盘等方式,把公钥编码传送给李四,李四接收到张三编码后的公钥,将其解码,李四用张三的公钥加密信息,并发送给李四,张三用自己的私钥解密从李四处收到的信息…… Java利用DES私钥对称加密代码实例 同上 java聊天室 2个目标文件,简单。 java模拟掷骰子2个 1个目标文件,输出演示。 java凭图游戏 一个目标文件,简单。 java求一个整数的因子 如题。 Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,密钥   Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥,通常应对私钥加密后再保存、如何从文件中得到公钥编码的字节数组、如何从字节数组解码公钥。 Java数据压缩与传输实例 1个目标文件 摘要:Java源码,文件操作,数据压缩,文件传输   Jav
©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值