首页 文章

如何从TIFF图像数据创建TIFF文件

提问于
浏览
0

我有几个文件包含一个特定的 Headers 后跟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);

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

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

编辑:感谢haraldK,我终于可以创建一个TIFF Headers . 但我无法打开图像,可能是因为压缩:“M2 =修改后的读码II(MRII),即传真组4” .

这是我创建的 Headers :

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

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

2 回答

  • 0

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

    首先,打开文件流,并跳过专有标头(您的代码假定您可以总是跳过您喜欢的字节数,但情况并非总是这样):

    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
    

    由于您的输入数据似乎是双层或黑/白传真格式(参考注释),请参阅规范中的第3节:双层图像(第17页)以获取必填字段和允许值 . 有关普通RGB图像,请参阅第6节:RGB全彩色图像(第24页) .

    请注意,规范中的SHORT在Java中是(unsigned) short ,但LONG是(无符号) int . 另请注意,必须按增加的标记顺序写入字段 . RATIONAL可以写成两个LONG(即两个未签名的 int s) . 对于 XResolutionYResolution ,只需写入72/1,因为这是默认的72 DPI . StripOffsets 将是在IFD之前写入的字节的IFD 8的长度 . 如果没有条带,则将 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(并且不需要用于ImageIO的JAI或其他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);
    }
    
  • 0

    您可以尝试使用 javax.imageio.ImageIO 读/写操作,如下所示:

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

    或者你可以使用Java Advanced Imaging API .

相关问题