【问题标题】:How to create a TIFF file from TIFF image data如何从 TIFF 图像数据创建 TIFF 文件
【发布时间】:2014-08-13 20:02:08
【问题描述】:

我有几个包含特定标题的文件,后面是 TIFF 图像数据。 如何将这些 TIFF 图像数据写入 TIFF 文件? 感谢您的帮助。

编辑:这是我测试的:

InputStream is = new FileInputStream(filePath);
is.skip(252);
BufferedImage bufferedImage = ImageIO.read(is);
File fileOut = new File(fileOutPath);
ImageIO.write(bufferedImage,"TIFF", fileOut);

我跳过文件的特定标头 (252Bytes) 以获取 Tiff 图像数据字节。 但是 bufferedImage 是空的,所以我得到一个 java.lang.IllegalArgumentException: im == null!例外。

在简历中,我有一个没有 TIFF 标头的 TIFF 文件。 TIFF 标头已被特定的标头替换,但图像字节与 TIFF 文件中的完全相同。

编辑: 感谢 haraldK,我终于可以创建一个 TIFF 标头了。但我无法打开图像,可能是因为压缩:“M2 = Modified Read Code II (MRII),即传真组 4”。

这是我创建的标题:

SubFileType (1 Long): Zero
ImageWidth (1 Long): 210
ImageLength (1 Long): 297
BitsPerSample (3 Short): 8, 8, 8
Compression (1 Short): Group 4 Fax (aka CCITT FAX4)
Photometric (1 Short): RGB
StripOffsets (1 Long): 306
SamplesPerPixel (1 Short): 3
RowsPerStrip (1 Short): 297
StripByteCounts (1 Long): 187110
ResolutionUnit (1 Short): None
XResolution (72 Rational): 
YResolution (1 Rational): Unexpected numeric
DateTime (20 ASCII): 2014:07:12 10:51:51 
Software (28 ASCII): Put your software name here 
ImageDescription (30 ASCII): Put an image description here 

在合并标题和图像数据之前我应该​​解压缩图像数据吗?

【问题讨论】:

  • 提供示例 TIFF 数据。你是如何获得 TIFF 图像数据的。还提供您尝试过的内容。
  • JAI.
  • TIFF 标头已被另一个特定标头替换,但图像数据字节相同。所以我跳过了标题,我得到了图像数据字节。但我不知道如何重建 TIFF 标头。
  • 不,是完全相同的问题。
  • No is 不为空,我可以正常阅读并获得正确的长度。关于 ImageIO.read(Inputstream) 的文档说:如果没有注册的 ImageReader 声称能够读取结果流,则返回 null

标签: java image tiff


【解决方案1】:

免责声明:这不是一个完整的示例(在这种情况下我需要一些示例文件来验证),而是概述了这个想法。

首先,打开文件的流,并跳过专有标头(您的代码假定您始终可以跳过任意数量的字节,但情况并非总是如此):

InputStream is = new FileInputStream(filePath);

int toSkip = 252;
int skipped = 0;

while (toSkip > 0 && (skipped = is.skip(toSkip)) >= 0) {
    toSkip -= skipped;
}

然后,根据TIFF specification,重新创建一个有效的最小 TIFF 标头。这其实并不难:

ByteArrayOutputStream bytes = new ByteArrayOutputStream();
DataOutputStream dataOut = new DataOutputStream(bytes);

dataOut.write('M');
dataOut.write('M');     // "Motorola" (network) byte order
dataOut.writeShort(42); // TIFF magic identifier (42)

dataOut.writeUnsignedInt(8); // Offset to 1st IFD

// ... write IFD, containing minimal info as per the spec

由于您的输入数据似乎是双层或黑白传真格式(参考 cmets),请参阅规范中的第 3 节:双层图像(第 17 页)以了解必填字段和允许的值。对于普通 RGB 图像,请参阅第 6 节:RGB 全彩图像(第 24 页)。

请注意,规范中的 SHORT 在 Java 中是(无符号)short,但 LONG 是(无符号)int。另请注意,字段必须按标签递增顺序编写。 RATIONAL 可以写成两个 LONG(即两个无符号 ints)。对于XResolutionYResolution,只需写入 72/1,因为这是默认的 72 DPI。 StripOffsets 将是 IFD 的长度 + 8 用于在 IFD 之前写入的字节。如果您没有条带,请将RowsPerStrip 设置为等于ImageLengthStripByteCounts 等于整个(压缩)图像数据的长度。

完成对标头的编码后,将标头和图像数据合并,并读取:

ByteArrayInputStream header = new ByteArrayInputStream(bytes.toByteArray());
InputStream stream = new SequenceInputStream(header, is); // Merge header and image data

现在,您可以阅读图片了:

BufferedImage image = ImageIO.read(stream); // Read image

// TODO: Test that image is non-null before attempting to write

但是,如果您只是将 TIFF 写回文件,您可以将数据从 stream 复制到新文件,然后尝试在外部工具中打开它。与在 Java 中解码图像然后将其编码回 TIFF(并且不需要 JAI 或 ImageIO 的其他 TIFF 插件)相比,这种方法会更快并且需要更少的内存:

OutputStream os = new FileOutputStream(new File(...));

byte[] buffer = new byte[1024];
int read;

while ((read = stream.read(buffer) >= 0) {
   os.write(0, read, buffer);
}

【讨论】:

  • 非常感谢,我终于可以创建 TIFF 标头了,但我看不到图像。你能看看我的编辑吗?
  • @loulou8284 YResolution 值似乎是假的(缺少分母部分?)。想一想,Compression“CCITT FAX type 4”仅支持双级(黑白)数据(请参阅规范中的 TIFF B 类)。因此CompressionBitsPerSamplePhotometric 必须相应更改。
【解决方案2】:

您可以尝试像这样使用javax.imageio.ImageIO 读/写操作:

ImageIO.write(img, "TIFF", new File('fileName'));

或者您可以使用Java Advanced Imaging API

【讨论】:

  • 有一个project 添加了 TIFF 读/写操作。到 ImageIO。
  • 我的问题是获取“img”。我已经更好地解释了我的编辑中的情况
  • @EJP ImageIO 内置了一些格式,但理论上它可以支持任何格式,通过plugins
  • @harafk 我知道这一点,我也知道我引用的页面说的是什么,这是 ImageIO 在所有标准实现中支持的实际格式的列表,并且确实不包括 TIFF。这就是我引用它的原因。我想知道你是否在评论之前阅读了它。正如我在另一条评论中所说,他需要的是 JAI,它确实支持 TIFF。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-05
相关资源
最近更新 更多