当前位置: 首页 > 知识库问答 >
问题:

使用JPEG的TIFF-压缩比原始JPEG大得多

高弘光
2023-03-14
2065kb JPEG (quality: 100%) --> 1282kb TIFF
 379kb JPEG (quality:  50%) --> 1200kb TIFF

我想还有另一种方法,因为我们公司有一个扫描仪,可以产生尺寸小得多的JPEG压缩的TIFF图像。有没有人知道另一个库(免费或不免费)可以更有效地处理这种转换,或者在FreeImage中实现这一点?

更新:

我查看了TIFF6.0规范,分析了扫描器生成的文件,并能够编写一个函数,将JPEG包装到一个非常简单的TIFF容器中(也可以处理合并到多页TIFF中的多个JPEG)。

NewSubfileType = 0
ImageWidth = //(width of the original JPEG)
ImageLength = //(height of the original JPEG)
BitsPerSample = {8, 8, 8} //(count: 3)
Compression = 7 //(JPEG)
PhotometricInterpretation = 6 //(YCbCr)
StripOffsets = //(offset of raw JPEG data, count: 1)
SamplesPerPixel = 3
RowsPerStrip = //(height of the original JPEG)
StripByteCounts = //(length of raw JPEG data, count: 1)
XResolution = //(horizontal resolution of original JPEG data)
YResolution = //(vertical resolution of original JPEG data)
PlanarConfiguration = 1 (chunky)
ResolutionUnit = 2 //(Inch)

我知道可能有些陷阱我还不知道。它可能无法与任何JPEG文件一起工作。另外,我也不确定为什么我必须使用photometricinterpreference=6planarconfiguration=1或其他一些字段的值。然而,它起作用了。

我想其他库的问题是,它们生成了一个质量始终相同的全新JPEG(因为您只能将TIFF-compression设置为JPEG,但不能指定任何其他选项),并将其包装到TIFF容器中。

我现在也知道,在TIFF中压缩JPEG并不是最好的选择(它是有损的,不常见的,很少被支持,而且JPEG压缩的TIFF并不比普通的JPEG文件更好)。然而,我们的客户要求这样做。让我们看看上面的是不是一个合适的解决方案,或者我能找到其他的方法。

共有1个答案

曾新
2023-03-14

不太支持JPEG压缩。如果一个库包含这种压缩类型,设置(如JPEG-quality)通常是固定的,并且原始图像很可能被重新压缩。然而,我找到了一种方法,将原始JPEG包装在一个简单的TIFF容器中(-->参见我最初问题中的更新)。

请记住:这可能不适用任何JPEG文件!例如,FreeImage无法读取包装的渐进式JPEG。

这是我使用的C#代码

using System;
using System.Collections.Generic;
using System.IO;
using FreeImageAPI;

namespace Tiff
{
    static class TiffConverter
    {
        /// <summary>
        /// <para>Wraps a list of JPEG images into a simple multi-page TIFF container.</para>
        /// <para>(Might not work with all JPEG formats.)</para>
        /// </summary>
        /// <param name="jpegs">The JPEG-image to convert</param>
        /// <returns></returns>
        public static byte[] WrapJpegs(List<byte[]> jpegs)
        {
            if (jpegs == null || jpegs.Count == 0 || jpegs.FindIndex(b => b.Length == 0) > -1)
                throw new ArgumentNullException("Image data must not be null or empty");

            MemoryStream tiffData = new MemoryStream();
            BinaryWriter writer = new BinaryWriter(tiffData);
            uint offset = 8; // size of header, offset to IFD
            ushort entryCount = 14; // entries per IFD

            #region IFH - Image file header

            // magic number
            if (BitConverter.IsLittleEndian)
                writer.Write(0x002A4949);
            else
                writer.Write(0x4D4D002A);

            // offset to (first) IFD
            writer.Write(offset);

            #endregion IFH

            #region IFD Image file directory

            // write image file directories for each jpeg
            for (int i = 0; offset > 0; i++)
            {
                // get data from jpeg with FreeImage
                FreeImageBitmap jpegImage;
                try
                {
                    jpegImage = new FreeImageBitmap(new MemoryStream(jpegs[i]));
                }
                catch (Exception ex)
                {
                    throw new Exception("Could not load image data at index " + i, ex);
                }
                if (jpegImage.ImageFormat != FREE_IMAGE_FORMAT.FIF_JPEG)
                    throw new ArgumentException("Image data at index " + i + " is not in JPEG format");

                // dta to write in tags
                uint width = (uint)jpegImage.Width;
                uint length = (uint)jpegImage.Height;
                uint xres = (uint)jpegImage.HorizontalResolution;
                uint yres = (uint)jpegImage.VerticalResolution;

                // count of entries:
                writer.Write(entryCount);

                offset += 6 + 12 * (uint)entryCount; // add lengths of entries, entry-count and next-ifd-offset

                // TIFF-fields / IFD-entrys:
                // {TAG, TYPE (3 = short, 4 = long, 5 = rational), COUNT, VALUE/OFFSET}
                uint[,] fields = new uint[,] { 
                    {254, 4, 1, 0}, // NewSubfileType
                    {256, 4, 1, width}, // ImageWidth
                    {257, 4, 1, length}, // ImageLength
                    {258, 3, 3, offset}, // BitsPerSample
                    {259, 3, 1, 7}, // Compression (new JPEG)
                    {262, 3, 1, 6}, //PhotometricInterpretation (YCbCr)
                    {273, 4, 1, offset + 22}, // StripOffsets (offset IFH + entries + values of BitsPerSample & YResolution & XResolution)
                    {277, 3, 1, 3}, // SamplesPerPixel
                    {278, 4, 1, length}, // RowsPerStrip
                    {279, 4, 1, (uint)jpegs[i].LongLength}, // StripByteCounts
                    {282, 5, 1, offset + 6}, // XResolution (offset IFH + entries + values of BitsPerSample)
                    {283, 5, 1, offset + 14}, // YResolution (offset IFH + entries + values of BitsPerSample & YResolution)
                    {284, 3, 1, 1}, // PlanarConfiguration (chunky)
                    {296, 3, 1, 2} // ResolutionUnit
                };

                // write fields
                for (int f = 0; f < fields.GetLength(0); f++)
                {
                    writer.Write((ushort)fields[f, 0]);
                    writer.Write((ushort)fields[f, 1]);
                    writer.Write(fields[f, 2]);
                    writer.Write(fields[f, 3]);
                }

                // offset of next IFD
                if (i == jpegs.Count - 1)
                    offset = 0;
                else
                    offset += 22 + (uint)jpegs[i].LongLength; // add values (of fields) length and jpeg length
                writer.Write(offset);

                #region values of fields

                // BitsPerSample
                writer.Write((ushort)8);
                writer.Write((ushort)8);
                writer.Write((ushort)8);

                // XResolution
                writer.Write(xres);
                writer.Write(1);

                // YResolution
                writer.Write(yres);
                writer.Write(1);

                #endregion values of fields

                // actual image data
                writer.Write(jpegs[i]);
            }
            #endregion IFD

            writer.Close();
            return tiffData.ToArray();
        }
    }
}
 类似资料:
  • 我需要压缩一个有几个灰色16bit图像(多页)的tif文件。我已经尝试使用ImageIO如下所示:使用Java ImageIO进行Tiff压缩最初,tif文件中的每个图像都来自另一个Tiff文件。当我想使用压缩机时,我有以下选项: null

  • ImageMagick,GraphicsMagick,OpenJPEG都显示了相同的结果(我假设是因为使用了Jasper对JPEG-2000进行编码),并且都缺少编码选项。使用Kakadu或联机/基本转换器也没有帮助。在目前的现状下,像imagemin这样的工具加上插件,可以在输出上提供比JPEG-2000高得多的质量,当对Web进行最大程度的压缩时。所以JPEG-2000对于Safari来说是很

  • 对于不知道DICOM文件是什么的人来说,它是一个保存着关于病人的医学成像数据的文件。它保存着患者数据和一些像素数据。您只需要知道像素数据在同一文件中,但与患者的其余数据分离。 我做了一个程序,可以读取DICOM文件中的原始像素数据。然而,像素数据经常使用JPEG压缩进行压缩。下面是我用来了解像素压缩方法的字典: 如您所见,有26种不同类型的JPEG压缩方法需要解压(所有这些方法的键都是1.2.84

  • JPEG或PNG是否支持元数据/辅助数据的压缩? 到目前为止,我读到的所有内容都集中在图像压缩上,我找不到任何关于扩展数据压缩的内容。 大多数压缩软件似乎会移除元数据,但是否有可能将其压缩呢?

  • 我试图在Java程序中打开jpeg文件,并注意到ImageIO和Apache commons imaging library工具都无法打开图像。commons库向我显示了以下错误: “目前只支持顺序的基线JPEG” 提前致谢

  • 我希望加载一个dicom图像并将其保存到jpeg,如下面的fo-dicom文档代码所示: 但是,我正在从MemoryStream打开dicom文件,我想将jpeg保存为字节数组。这在FO-DICOM中是可能的吗?