8

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

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

//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。(该图像代表我要存储的图像。)

在确实有效的算法中(有些算法因各种错误而失败),最小的压缩文件是使用 创建的Compression.Deflate,但它只压缩到 5MB,我希望它更小(小于 1MB)。

必须有一些更高压缩率的算法;包含此图像的 PDF 文件大约为 500Kb。

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

如何通过无损压缩生成更小的文件?这甚至可能与这些类型的图像?

更新

PDF 文件
TIFF 文件

4

3 回答 3

3

测试图像的简单评估

只是在示例图像上给出一些数字(tiff 之一)。所有压缩都是无损的,并且可以重新创建任何其他无损格式,例如 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 将是一件容易的事;压缩每个;以某种方式存储连接
    • 这在数据库设置中也很自然
    • 如果这些多 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%

在此处输入图像描述

于 2016-10-09T11:04:51.747 回答
1

答案分两部分:

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

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

于 2016-10-03T02:14:48.267 回答
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);
    }
}
于 2016-10-06T14:03:10.563 回答