【问题标题】:Jpeg from Tiff (jpeg-compressed)来自 Tiff 的 Jpeg(JPEG 压缩)
【发布时间】:2011-08-09 16:25:30
【问题描述】:

如何从 JPEG 压缩的 TIFF 文件中提取图像?

我已根据 StripOffests 和 StripBytesCount 字段读取字节,但无法从中加载图像。

【问题讨论】:

  • 您是在寻找压缩后的图像数据,还是在寻找实际图像?
  • 我在找实物图

标签: jpeg tiff


【解决方案1】:

如果您尝试从 TIFF、JPEG 或其他文件中提取实际图像,您最好使用 libtiff 之类的库来执行此操作。 TIFF 是一个非常复杂的规范,虽然您可以自己执行此操作并获得一两类图像,但您可能无法处理经常出现的其他情况,尤其是“旧式”JPEG是强加在 TIFF 上的子格式,不能很好地融入整体。

我的公司Atalasoft 制造了一个.NET 产品,其中包含一个非常好的TIFF 编解码器。如果您只需要担心单页图片,我们的免费产品将非常适合您。

在 .NET 领域,您还可以查看 Bit Miracle 的托管版本的 libtiff。这是图书馆的一个相当不错的端口。

【讨论】:

  • 感谢您的回答。我不能使用任何库。我对只解析新型 JPEG 压缩 TIFF 很感兴趣。您能否告诉我,根据 StripOffests 和 StripBytesCount 字段,我从 TIFF 文件中检索到的 JPEG 压缩数据有什么问题?
  • 我看到它包含 jpeg 的开始标记:X0FFD8,但不知何故无法打开。我必须对这些数据进行哪些操作才能获得图像?
【解决方案2】:

旧式 TIFF-JPEG(压缩类型 6)基本上将普通 JFIF 文件填充到 TIFF 包装器中。较新风格的 TIFF-JPEG(压缩类型 7)允许将 JPEG 表数据(Huffman,量化)存储在单独的标签(0x015B JPEGTables)中。这允许您在文件中放置带有 SOI/EOI 标记的 JPEG 数据条,而无需重复 Huffman 和量化表。这可能就是您在文件中看到的内容。各个片段以序列 FFD8 开头,但缺少霍夫曼和量化表。这是 Photoshop 产品通常写入文件的方式。

【讨论】:

  • 我尝试在图像数据的开头添加量化表,但没有帮助。我必须在哪里插入该表?
  • 霍夫曼表怎么样?图像是否分成多个条带。您想要每个条带 1 个 JPEG 文件吗?添加更多详细信息。
  • 有 1 个 JPEG 被分成多个条带。
  • 你没有回答我关于霍夫曼表的问题。为了解码 JPEG 压缩数据,解压缩器需要量化表和霍夫曼表。查看一个有效的 jpeg,您将在元数据 (FFEx) 之后看到一系列表格。
  • 如果您的 TIFF 文件包含 7 类 JPEG 数据并且压缩数据条缺少霍夫曼和量化表,那么它必须存储在 JPEGTables 标记中。您应该能够将 JPEGTables 标记中的信息插入到图像数据中(考虑 SOI/EOI),它应该可以工作。
【解决方案3】:

使用 JAI:

int TAG_COMPRESSION             = 259;
int TAG_JPEG_INTERCHANGE_FORMAT = 513;

int COMP_JPEG_OLD  = 6;
int COMP_JPEG_TTN2 = 7;

SeekableStream stream = new ByteArraySeekableStream(imageData);
TIFFDirectory    tdir = new TIFFDirectory(stream, 0);
int       compression = tdir.getField(TAG_COMPRESSION).getAsInt(0);

// Decoder name
String decoder2use = "tiff";
if (compression == COMP_JPEG_OLD) { 
    // Special handling for old/unsupported JPEG-in-TIFF format:
    // {@link: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4929147 }
    stream.seek(tdir.getField(TAG_JPEG_INTERCHANGE_FORMAT).getAsLong(0));
    decoder2use = "jpeg";
}

// Decode image
ImageDecoder  dec = ImageCodec.createImageDecoder(decoder2use, stream, null);
RenderedImage img = dec.decodeAsRenderedImage();

很好的解决方案,对我帮助很大。 补充一点,如果您在 TIFF 中有多个页面,则必须重复读取流,并在 TIFFDirectory 对象中定义不同的目录号并重复以上所有操作。

TIFFDirectory tdir = new TIFFDirectory(stream, 1);

【讨论】:

  • 非凡。谢谢你的救命稻草。
【解决方案4】:

提到的库 libtiff 的问题在于它确实提取了图像,然后将其重新压缩保存,这意味着在 jpg 的情况下另一个质量损失。也就是说,我可以通过调用 NET Framework 的 GDI+ 方法来完成同样的任务,甚至无需使用 3rd 方库。

这个帖子的最初作者试图获取 jpeg 二进制文件而不必重新压缩它,而这正是我想要做的。

如果您可以忍受质量损失并且不想使用除 .NET 库类之外的任何东西,这是一个可能的解决方案:

    public static int SplitMultiPage(string sourceFileName, string targetPath)
    {
        using (Image multipageTIFF = Image.FromFile(sourceFileName))
        { 
            int pageCount = multipageTIFF.GetFrameCount(FrameDimension.Page);

            if (pageCount > 1)
            {
                string sFileName = Path.GetFileNameWithoutExtension (sourceFileName);
                for (int i = 0; i < pageCount; i++)
                {                        
                    multipageTIFF.SelectActiveFrame(FrameDimension.Page, i);

                    // ein einzelner Frame könnte auch ein anderes Format haben, z.B. JPG, PNG, BMP, etc.
                    // Damit die Datei die korrekte Endung bekommt, holen wir uns eine Endung aus der Beschreibung des Codecs
                    // Interessanterweise liefert uns das RawFormat im Fall TIFF (der einzige Multiframefall) immer den Codec für TIFF, 
                    // statt den des Frames
                    ImageCodecInfo codec = Helpers.GetEncoder(multipageTIFF.RawFormat);
                    string sExtension = codec.FilenameExtension.Split(new char[] { ';' })[0];
                    sExtension = sExtension.Substring(sExtension.IndexOf('.') + 1);
                    string newFileName = Path.Combine(targetPath, string.Format("{0}_{1}.{2}", sFileName, i + 1, sExtension));

                    EncoderParameters encoderParams = new EncoderParameters(2);
                    encoderParams.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag, (long)EncoderValue.LastFrame);

                    // für TIF 1 Bit machen wir CompressionCCITT4 Kompression, da das die besten Ergebnisse liefert
                    switch (GetCompressionType(multipageTIFF))
                    {

                        case 1: // No compression -> BMP?
                            encoderParams.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.Compression, (long)EncoderValue.CompressionNone);
                            break;
                        case 2: // CCITT modified Huffman RLE 32773 = PackBits compression, aka Macintosh RLE
                            encoderParams.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.Compression, (long)EncoderValue.CompressionRle);
                            break;
                        case 3: // CCITT Group 3 fax encoding
                            encoderParams.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.Compression, (long)EncoderValue.CompressionCCITT3);
                            break;
                        case 4: // CCITT Group 4 fax encoding
                            encoderParams.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.Compression, (long)EncoderValue.CompressionCCITT4);
                            break;
                        case 5: // LZW
                            encoderParams.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.Compression, (long)EncoderValue.CompressionLZW);
                            break;
                        case 6: //JPEG ('old-style' JPEG, later overriden in Technote2)
                        case 7: // Technote2 overrides old-style JPEG compression, and defines 7 = JPEG ('new-style' JPEG)
                            {
                                codec = Helpers.GetEncoder(ImageFormat.Jpeg);
                                encoderParams.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 90);
                            }
                            break;
                    }

                    multipageTIFF.Save(newFileName, codec, encoderParams);
                }
            }

            return pageCount;
        }
    }

使用的辅助方法:

    public static ImageCodecInfo GetEncoder(ImageFormat format)
    {

        ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();

        foreach (ImageCodecInfo codec in codecs)
        {
            if (codec.FormatID == format.Guid)
            {
                return codec;
            }
        }
        return null;
    }

读取压缩标志:

    public static int GetCompressionType(Image image)
    {
        /*  TIFF Tag Compression
            IFD     Image
            Code        259 (hex 0x0103)
            Name        Compression
            LibTiff name        TIFFTAG_COMPRESSION
            Type        SHORT
            Count       1
            Default     1 (No compression)
            Description

            Compression scheme used on the image data.

            The specification defines these values to be baseline:

            1 = No compression
            2 = CCITT modified Huffman RLE
            32773 = PackBits compression, aka Macintosh RLE

            Additionally, the specification defines these values as part of the TIFF extensions:

            3 = CCITT Group 3 fax encoding
            4 = CCITT Group 4 fax encoding
            5 = LZW
            6 = JPEG ('old-style' JPEG, later overriden in Technote2)

            Technote2 overrides old-style JPEG compression, and defines:

            7 = JPEG ('new-style' JPEG)

            Adobe later added the deflate compression scheme:

            8 = Deflate ('Adobe-style')

            The TIFF-F specification (RFC 2301) defines:

            9 = Defined by TIFF-F and TIFF-FX standard (RFC 2301) as ITU-T Rec. T.82 coding, using ITU-T Rec. T.85 (which boils down to JBIG on black and white).
            10 = Defined by TIFF-F and TIFF-FX standard (RFC 2301) as ITU-T Rec. T.82 coding, using ITU-T Rec. T.43 (which boils down to JBIG on color). 
        */
        int compressionTagIndex = Array.IndexOf(image.PropertyIdList, 0x103);

        PropertyItem compressionTag = image.PropertyItems[compressionTagIndex];

        return BitConverter.ToInt16(compressionTag.Value, 0);
    }

【讨论】:

  • 这个方法:public static int GetCompressionType(Image image)应该以“return BitConverter.ToUInt16(compressionTag.Value, 0);”结尾由于返回的 16 位数组,您不想要有符号整数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-26
  • 2010-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-03
  • 1970-01-01
相关资源
最近更新 更多