有没有办法使用 .NET 使用 JPEG 压缩创建多页 TIFF?我可以使用 LZW 压缩创建 TIFF,但文件非常大。看起来EncoderValue
枚举(我用来设置压缩)甚至没有合适的成员。
问问题
6998 次
1 回答
6
您可以查看>>这篇文章,我在其中解释了如何将现有的 JPEG 包装在一个简单的多页 TIFF 容器中。
但是还有很多其他的可能性。FreeImage.Net (freeimage.sourceforge.net)是一个非常强大的库。您可以像这样创建一个简单的 JPEG 压缩 TIFF:
using FreeImageAPI;
// [...]
FIBITMAP image = FreeImage.Load(FREE_IMAGE_FORMAT.FIF_UNKNOWN, "filename", FREE_IMAGE_LOAD_FLAGS.DEFAULT);
FreeImage.Save(FREE_IMAGE_FORMAT.FIF_TIFF, image, "filename", FREE_IMAGE_SAVE_FLAGS.TIFF_JPEG)
创建多页 TIFF 有点棘手。据我所知,多页编辑只能在硬盘上进行。(我听说在较新的版本中有另一种方法,所以也许可以尝试一下。)但是,你可以这样使用:
using System.IO;
using FreeImageAPI;
// [...]
public byte[] MergeTiffs(List<byte[]> tiffs)
{
byte[] multiPageTiff = null;
string tmpfile = "... chose any file name ...";
MemoryStream singleStream = null;
FIBITMAP fib = FIBITMAP.Zero;
FIMULTIBITMAP fmb = FIMULTIBITMAP.Zero;
using (singleStream = new MemoryStream(tiffs[0])
{
fib = FreeImage.LoadFromStream(singleStream);
FreeImage.SaveEx(fib, dateiname, FREE_IMAGE_FORMAT.FIF_TIFF, FREE_IMAGE_SAVE_FLAGS.TIFF_JPEG);
FreeImage.UnloadEx(ref fib);
}
fmb = FreeImage.OpenMultiBitmap(
FREE_IMAGE_FORMAT.FIF_TIFF,
tmpfile,
false,
false,
false,
FREE_IMAGE_LOAD_FLAGS.TIFF_JPEG);
for (int i = 1; i < tiffs.Count; i++)
{
using (singleStream = new MemoryStream(tiffs[i])
{
fib = FreeImage.LoadFromStream(singleStream);
FreeImage.AppendPage(fmb, fib);
}
}
FreeImage.CloseMultiBitmapEx(ref fmb);
multiPageTiff = File.ReadAllBytes(tmpfile);
File.Delete(tmpfile);
return file;
}
除此之外,我最近发现了一种将现有 TIFF 与LibTiff.Net (bitmiracle.com) 合并的简单方法,无需更改其原始数据(因此您既不会降低质量也不会增加它们的大小)。可能有一种更简单的方法(也许你会找到一个),但我使用了以下代码(基于此示例):
using System.IO
using BitMiracle.LibTiff.Classic;
// [...]
/// <summary>
/// Merges multiple TIFFs into one multi-page TIFF
/// </summary>
/// <param name="tiffs">The TIFFs' raw data (can also be multi-page)</param>
/// <returns></returns>
public static byte[] MergeTiffs(List<byte[]> tiffs)
{
// the byteStream will contain the merged tiff's raw data
MemoryStream byteStream = new MemoryStream();
// create the output-TIFF (empty stream)
using (Tiff output = Tiff.ClientOpen("InMemory", "w", byteStream, new TiffStream()))
{
for (short i = 0; i < tiffs.Count; i++)
{
// provide input-TIFF as custom TiffStream, with byteStream (output-TIFF) as output
TiffStreamForBytes tiffStream = new TiffStreamForBytes(tiffs[i]);
using (Tiff input = Tiff.ClientOpen("bytes", "r", null, tiffStream))
{
// *** now copy all the TIFF-data: ***
// copy all directories (= all pages)
int numberOfDirectories = input.NumberOfDirectories();
for (short d = 0; d < numberOfDirectories; ++d)
{
// set this as the current directory (to work in)
input.SetDirectory(d);
// copy all tags
for (ushort t = ushort.MinValue; t < ushort.MaxValue; ++t)
{
TiffTag tag = (TiffTag)t;
FieldValue[] tagValue = input.GetField(tag);
if (tagValue != null)
output.GetTagMethods().SetField(output, tag, tagValue);
}
// copy all strips
int numberOfStrips = input.NumberOfStrips();
int stripSize = input.StripSize();
for (int s = 0; s < numberOfStrips; ++s)
{
// buffer for the current strip
byte[] stripData = new byte[stripSize];
// read strip from input image (not decompressed)
int length = input.ReadRawStrip(s, stripData, 0, stripData.Length);
// write strip to output image (uncompressed)
output.WriteRawStrip(s, stripData, 0, length);
}
// add the new directory to output image
output.WriteDirectory();
}
}
}
}
// return the new TIFF as byte array
return byteStream.ToArray();
}
/// <summary>
/// Custom read-only stream for byte buffer that can be used
/// with Tiff.ClientOpen method.
/// </summary>
private class TiffStreamForBytes : TiffStream
{
private byte[] m_bytes;
private int m_position;
public TiffStreamForBytes(byte[] bytes)
{
m_bytes = bytes;
m_position = 0;
}
public override int Read(object clientData, byte[] buffer, int offset, int count)
{
if ((m_position + count) > m_bytes.Length)
return -1;
Buffer.BlockCopy(m_bytes, m_position, buffer, offset, count);
m_position += count;
return count;
}
public override void Write(object clientData, byte[] buffer, int offset, int count)
{
throw new InvalidOperationException("This stream is read-only");
}
public override long Seek(object clientData, long offset, SeekOrigin origin)
{
switch (origin)
{
case SeekOrigin.Begin:
if (offset > m_bytes.Length)
return -1;
m_position = (int)offset;
return m_position;
case SeekOrigin.Current:
if ((offset + m_position) > m_bytes.Length)
return -1;
m_position += (int)offset;
return m_position;
case SeekOrigin.End:
if ((m_bytes.Length - offset) < 0)
return -1;
m_position = (int)(m_bytes.Length - offset);
return m_position;
}
return -1;
}
public override void Close(object clientData)
{
// nothing to do
}
public override long Size(object clientData)
{
return m_bytes.Length;
}
}
于 2013-02-15T13:45:57.973 回答