JPEG压缩的TIFF比原始JPEG大得多 [英] TIFF with JPEG-compression much larger than original JPEG

查看:135
本文介绍了JPEG压缩的TIFF比原始JPEG大得多的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 FreeImage.Net 和C#将具有JPEG压缩功能的 JPEG转换为TIFF .这很好用,但是,对于低质量的JPGES,TIFF文件比原始文件大很多.我认为TIFF的大小不取决于原始的JPEG质量,因为输出图像的大小总是相同的.

I am trying to convert JPEGs to TIFFs with JPEG-compression using FreeImage.Net and C#. This works fine, however, for low-quality JPGES the TIFF-files are a lot larger than the original. I assume the TIFF-size does not depend on the original JPEG-Quality, because the output-images were always about the same size.

例如(转换屏幕截图):

For example (converting a screenshot):

2065kb JPEG (quality: 100%) --> 1282kb TIFF
 379kb JPEG (quality:  50%) --> 1200kb TIFF

这种增加的尺寸对于我们公司是不可接受的,因为我们和我们的客户都在处理大量文档.

This increase in size is not acceptable for our company, because our clients and we are dealing with quite a lot of documents.

有趣的是,当我使用GIMP转换图像时,我得到的结果几乎相同.现在我想知道:这是按照TIFF标准还是FreeImage/GIMP特有的?(我认为两者都使用 libtiff.dll ).

Interestingly enough, I get about the same results when I convert the images with GIMP. Now I am wondering: is this according to the TIFF-standard or special to FreeImage/GIMP? (I think both use libtiff.dll).

我想还有另一种方法,因为我们公司有一个扫描仪,可以将图像生成为JPEG压缩的TIFF,而尺寸要小得多. 是否有人知道另一个库(无论是否免费),它们可以更有效地处理此转换,或在FreeImage中实现此转换?

I suppose there is another way, because we have a scanner in our company that produces images as JPEG-compressed TIFFs with a much smaller size. Does anyone know about another library (either free or not) that can handle this conversion more effectively, or achieve this in FreeImage?

更新:

UPDATE:

我查看了 TIFF 6.0规范,对扫描仪生成的文件进行了分析,并能够编写将JPEG包装到非常简单的TIFF容器中的功能(还可以将多个JPEG合并到多页TIFF中使用).

I took a look at the TIFF 6.0 specification, analyzed the files our scanner produced and was able to write a function that wraps a JPEG into a very simple TIFF container (works also with multiple JPEGs which are merged to a multi-page TIFF).

对于那些对TIFF有一点了解的人:我制作了一个新的TIFF文件(根据图像/页面的数量,包含一个或多个IFD),并将现有JPEG图像的原始数据写入单个条带(每个IFD),请使用以下字段/条目:

For those who know a little about TIFF: I produced a new TIFF file (with one or more IFDs, according to the number of images/pages) and wrote the raw data of an existing JPEG image into a single strip (per IFD), using the following fields/entries:

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)

(要获取原始图像的信息,我使用了FreeImage,但其他任何图像库也应适用.)

(To obtain the information of the original image, I used FreeImage, but any other image library should work as well.)

我知道可能会有一些我不知道的陷阱.它可能不适用于任何JPEG文件.另外,我不确定为什么必须使用PhotometricInterpretation = 6PlanarConfiguration = 1或其他某些字段的值.但是,它有效.

I know there could be some pitfalls I don't know about yet. It might not work with any JPEG-file. Also, I'm not sure why I had to use PhotometricInterpretation = 6 and PlanarConfiguration = 1 or some of the other fields' values. However, it works.

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

I suppose my problem with the other libraries was, that they produced a completely new JPEG with always the same quality (since you can only set TIFF-compression to JPEG but not specify any further options) and wrapped that into a TIFF container.

我现在也知道,TIFF中的JPEG压缩并不是完全最佳的选择(它有损,不常见且很少受支持,除了JPEG压缩的TIFF并没有普通的JPEG文件更好).但是,我们的客户要求.让我们看看以上是否将是合适的解决方案,或者我是否还能找到其他解决方案.

I know too now, JPEG compression in a TIFF is not exactly the best choice (it's lossy, uncommon and rarely supported, besides a JPEG-compressed TIFF not any better then a regular JPEG file). However, our clients demand that. Let's see if the above is going to be a suitable solution, or if I manage to find sth else.

推荐答案

JPEG压缩不是很受支持.如果库中确实包含这种压缩类型,则设置(如JPEG质量)通常是固定的,并且原始图像很有可能会被重新压缩.但是,我找到了一种方法,可以将原始JPEG包装在一个简单的TIFF容器中(->在我的原始问题中查看更新内容).

JPEG-compression is not supported very well. If a library does include this compression type, the settings (like JPEG-quality) are often fixed and the original image is most likely re-compressed. I found a way, however, to wrap the original JPEG in a simple TIFF container (--> see the update in my original question).

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

Remember: This might not work with any JPEG file! For instance, FreeImage was not able to read a wrapped progressive JPEG.

这是我使用的C#代码:

This is the C#-Code I used:

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();
        }
    }
}

这篇关于JPEG压缩的TIFF比原始JPEG大得多的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆