2

私はFreeImage.NetとC#を使用してJPEG圧縮でJPEGをTIFFに変換しようとしています。これは問題なく機能しますが、低品質のJPGESの場合、TIFFファイルは元のファイルよりもはるかに大きくなります。出力画像は常にほぼ同じサイズであったため、TIFFサイズは元のJPEG品質に依存しないと思います。

例(スクリーンショットの変換):

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

私たちのクライアントと私たちはかなり多くのドキュメントを扱っているので、このサイズの増加は私たちの会社には受け入れられません。

興味深いことに、GIMPで画像を変換してもほぼ同じ結果が得られます。今私は疑問に思っています:これはTIFF標準によるものですか、それともFreeImage / GIMPに特別なものですか?(どちらもlibtiff.dllを使用していると思います)。

別の方法があると思います。私たちの会社には、はるかに小さいサイズのJPEG圧縮TIFFとして画像を生成するスキャナーがあります。この変換をより効果的に処理できる、またはFreeImageでこれを実現できる別のライブラリ(無料かどうかに関係なく)について誰か知っていますか?

アップデート:

TIFF 6.0仕様を調べ、スキャナーが生成したファイルを分析し、JPEGを非常に単純なTIFFコンテナーにラップする関数を記述できました(複数ページのTIFFにマージされた複数のJPEGでも機能します)。

TIFFについて少し知っている人のために:私は新しいTIFFファイル(画像/ページの数に応じて1つ以上のIFDを含む)を作成し、既存のJPEG画像の生データを単一のストリップに書き込みました(IFDごと)。次のフィールド/エントリを使用します。

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を使用しましたが、他の画像ライブラリも同様に機能するはずです。)

まだ知らない落とし穴があるかもしれません。JPEGファイルでは動作しない可能性があります。また、なぜ他のフィールドの値を使用PhotometricInterpretation = 6しなければならなかったのかわかりません。PlanarConfiguration = 1ただし、動作します。

他のライブラリでの私の問題は、常に同じ品質の完全に新しいJPEGを生成し(TIFF圧縮をJPEGに設定することしかできず、それ以上のオプションを指定できないため)、それをTIFFコンテナにラップしたことだと思います。

私も今知っていますが、TIFFでのJPEG圧縮は、必ずしも最良の選択ではありません(JPEG圧縮されたTIFFは、通常のJPEGファイルよりも優れているだけでなく、損失が多く、一般的ではなく、ほとんどサポートされていません)。しかし、私たちのクライアントはそれを要求します。上記が適切な解決策になるかどうか、または私がなんとか他の解決策を見つけることができるかどうかを見てみましょう。

4

1 に答える 1

2

JPEG 圧縮はあまりサポートされていません。ライブラリにこの圧縮タイプが含まれている場合、設定 (JPEG 品質など) は多くの場合固定され、元の画像は再圧縮される可能性が高くなります。ただし、元の 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();
        }
    }
}
于 2013-02-14T14:51:26.280 に答える