TIFF

时间:2016-09-26 20:52:47

标签: libtiff.net

我正在尝试将TIFF图像存档在数据库中,我想尽可能地压缩图像,即使以更高的CPU使用率和高内存为代价。

为了测试LibTiff.NET中可用的压缩,我使用了以下代码(从this sample修改):

//getImageRasterBytes and convertSamples are defined in the sample
void Main() {
    foreach (Compression cmp in Enum.GetValues(typeof(Compression))) {
        try {
            using (Bitmap bmp = new Bitmap(@"D:\tifftest\200 COLOR.tif")) {
                using (Tiff tif = Tiff.Open($@"D:\tifftest\output_{cmp}.tif", "w")) {
                    byte[] raster = utils.getImageRasterBytes(bmp, PixelFormat.Format24bppRgb);
                    tif.SetField(TiffTag.IMAGEWIDTH, bmp.Width);
                    tif.SetField(TiffTag.IMAGELENGTH, bmp.Height);
                    tif.SetField(TiffTag.COMPRESSION, cmp);
                    tif.SetField(TiffTag.PHOTOMETRIC, Photometric.RGB);

                    tif.SetField(TiffTag.ROWSPERSTRIP, bmp.Height);

                    tif.SetField(TiffTag.XRESOLUTION, bmp.HorizontalResolution);
                    tif.SetField(TiffTag.YRESOLUTION, bmp.VerticalResolution);

                    tif.SetField(TiffTag.BITSPERSAMPLE, 8);
                    tif.SetField(TiffTag.SAMPLESPERPIXEL, 3);

                    tif.SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG);

                    int stride = raster.Length / bmp.Height;
                    utils.convertSamples(raster, bmp.Width, bmp.Height);

                    for (int i = 0, offset = 0; i < bmp.Height; i++) {
                        tif.WriteScanline(raster, offset, i, 0);
                        offset += stride;
                    }
                }
            }
        } catch (Exception ex) {
            //code was run in LINQPad
            ex.Dump(cmp.ToString());
        }
    }
}

测试图像为200dpi 24bpp,宽度为1700,高度为2200,并使用LZW压缩;文件大小接近7 MB。 (图像代表我想要存储的图像。)

在有效的算法(some failed with various errors)中,最小的压缩文件是使用Compression.Deflate创建的,但只压缩到5MB,我希望它明显更小(小于1 MB)。

必须有一些更高压缩的算法;包含此图像的PDF文件类似于500Kb。

如果特定算法与其他TIFF查看器/库不兼容,这不是问题,只要我们可以从数据库中提取压缩的TIFF并使用LibTiff.Net将其转换为System.Drawing.Bitmap或某些其他图书馆。

如何使用无损压缩生成更小的文件?这些图像甚至可以实现吗?

更新

PDF file
TIFF file

3 个答案:

答案 0 :(得分:3)

简单评估测试图像

只是在示例图像(tiff one)上给出一些数字。所有压缩都是无损的,可以重新创建任何其他无损格式,如bmp / png(已经过检查)。

tiff-orig         5.779.814  
png (unoptimized) 3.084.641  53.37%
png (optimized)   2.795.230  48.36%  
png (zopfli)      2.791.680  48.30%
jpeg2000          2.230.967  38.60%
webp              2.021.710  34.98%  BSD
gralic            1.795.457  31.06%  
flif              1.778.976  30.78%  LGPL3

说明

  • 这些只是一张图片的结果
    • 其中大部分仍有潜在收益,但压缩需要大量时间
    • 虽然一般观察(关于这些压缩机的压缩效率的排序)应该成立,但是对于更大的测试集,值会发生变化
  • 创建大多数压缩器仅用于处理单张图像
    • 将多tiff拆分为单个tiff将是一件容易的事;压缩每个;以某种方式存储连接
    • 这在DB-setup
    • 中也很自然
    • 如果这些多tiff图像密切相关,则可以使用它(例如通用压缩器;或定制方法)
  • 正如我在评论中指出的那样,对于大多数类型的图像(例如照片或扫描;坚持无损压缩),您无法实现所需的缩减
    • 有很多要说的,但最重要的方面是:它们含有大量噪音,噪音无法压缩

为了好玩:去噪+无损压缩

由于噪声是杀死无损压缩电位的最重要因素,让我们删除一些。我们正在使用这个基于python的代码执行此操作,但还有更多可能的方法。以下代码使用非线性滤波器,它试图在保持重要边缘的同时消除噪声。

当然信息在这里丢失了,但我实际上更喜欢去噪图像,因为它更好看(在我看来)。

去噪代码

from skimage.io import imread, imsave
from skimage.restoration import denoise_bilateral

img = imread("200 DPI.tif")
img_denoised = denoise_bilateral(img, multichannel=True, sigma_range=0.05, sigma_spatial=15)
imsave("200 DPI_denoised.png", img_denoised)

评价

flif (denoised) 1.140.497  19.73%

enter image description here

答案 1 :(得分:1)

答案的两个部分:

  • 以您选择的方式使其有损,而不是有损编解码器的方式。例如,如果您正在处理扫描的文本图像,请执行亮度/对比度标准化(可能是局部标准化),以使页面背景为纯白色。这将大大提高可压缩性;它可以制作一个10MB灰度文本页面,几乎但不完全是白色背景,分为200kB页面,纯白色背景和灰度文本(使用LZW)

  • 使用JPEG2000。如果你想要最好的无损压缩,那么带有无损设置的JPEG2000可能会击败任何其他算法,例如PNG,特别是对于像照片这样的内容,也适用于扫描页面。将JPEG2000存储在TIFF容器中也应该是可能的,但它不是TIFF库的常见功能。你可能想要也可能不想那样做。我认为JPEG2000在一个文件中也有多个图像的功能。

答案 2 :(得分:0)

阅读G4压缩方法: https://en.wikipedia.org/wiki/Group_4_compression

平均来说,该方法可以提供20:1的压缩率。

这是C#示例(信用到:https://www.experts-exchange.com/viewCodeSnippet.jsp?codeSnippetId=20-41218205-1):

byte[] imgBits = File.ReadAllBytes(@"multipage_tif.tif");
using (MemoryStream ms = new MemoryStream(imgBits)) {
    using (Image i = Image.FromStream(ms)) {
        EncoderParameters parms = new EncoderParameters(1);
        ImageCodecInfo codec = ImageCodecInfo.GetImageDecoders().FirstOrDefault(decoder => decoder.FormatID == ImageFormat.Tiff.Guid);    
        parms.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionCCITT4);

        i.Save("out.tif", codec, parms);
    }
}