【问题标题】:Need help getting started converting raw G3 FAX files to TIFF format via Java需要帮助开始通过 Java 将原始 G3 FAX 文件转换为 TIFF 格式
【发布时间】:2015-11-10 10:02:16
【问题描述】:

我有一个原始传真文件(G3/T.4 格式),需要通过 Java 以编程方式将其转换为多页 TIFF。到目前为止,JAI 对我来说还没有成功,即使我认为它应该有效。 sFaxTools 的工具已成功将我的原始传真文件转换为 TIFF(Batch Fax2Tif 或 Faxsee),但我需要通过 Java 以编程方式执行此操作。我认为应该有可能使用 java 高级成像,请查看下面的代码片段:

 private void writeTiff(byte[] buffer, OutputStream outStream) {
    try {
         //reading image from given buffer
         RenderedImage rendImage = null;
         TIFFDecodeParam decodeParams = new TIFFDecodeParam();
         ByteArrayInputStream stream = new ByteArrayInputStream(buffer);
         ImageDecoder decoder = ImageCodec.createImageDecoder("tiff", stream, decodeParams);
         TIFFEncodeParam encodeParams = new TIFFEncodeParam();
         int numPages = decoder.getNumPages();
         for (int i = 0; i < numPages; i++) {
            rendImage = decoder.decodeAsRenderedImage(i);
            ImageEncoder encoder = ImageCodec.createImageEncoder("TIFF", outStream, encodeParams);
            encoder.encode(rendImage);
         }
    } catch (Exception e) {
        e.printStackTrace();
    } catch (Error err) {
        err.printStackTrace();
    }
}

问题是,阅读部分尤其 ImageDecoder 解码器 = ImageCodec.createImageDecoder("tiff", stream, decodeParams); 应该由一些 ImageDecoder 实现替换,该实现在内部使用 FaxDecoder 来解码 g3 原始传真文件。 jai 包中有一个受保护的类 TIFFFaxDecoder,是否有可能以及如何将其用于我的目的?任何的想法? 谢谢

【问题讨论】:

  • 如果您在 Java 之外找到了适合您的工具,您可以使用 Runtime.getRuntime().exec(command) 通过 Java 执行该工具。或者,Java 在 JAI 图像 I/O 工具中支持 TIFF,这与 JAI 如此不同以至于您可能没有找到它:stackoverflow.com/questions/7502181/…
  • 或者,查看有关 TIFF 和 JAI 入门的 SO 问题:stackoverflow.com/questions/30320434/write-a-tiff-with-jai
  • 是否有关于“原始 G3 传真文件”在某处的规范?如果它与 G3 (T.4) 压缩 TIFF 的图像数据部分相同,则应该可以(并且非常容易)将数据包装在 TIFF 容器中。
  • 只是为了证明这一点,我下载了我能找到的唯一sample file,编写了一个 TIFF 容器(使用 TwelveMonkeys ImageIO),其中包含来自TIFF Class F spec 的必填字段,然后附加了原始 G3 数据.该文件现在打开并显示为有效的 TIFF。见the following Gist。我不保证它适用于除该示例文件之外的任何东西。 ;-)
  • 对不起,但我要求基于已使用框架的解决方案,我再次修改了我的问题。我不明白,如果你仍然抱怨它。如今的软件开发总是意味着尝试使用现有的(开源)框架,如果我不允许在我的帖子中提及它们或询问它是否支持我的要求,那么我认为这个论坛已经过时了。问候

标签: java tiff jai fax


【解决方案1】:

我认为 JAI 不支持直接读取 G3/T.4 原始传真数据。但是,这里的示例代码您可以修改和扩展以满足您的需要,实现 cmets 中概述的想法(最初作为 Gist 发布)。

它不会以任何方式解码 G3/T.4 数据,它只是将原始传真数据包装在一个最小的 TIFF 容器中。这允许稍后将数据作为普通 TIFF 读取。它使用(我自己的)TwelveMonkeys ImageIO 库来执行此操作。

如果您不知道传真文件的宽度/高度,您可以通过使用CCITTFaxDecoderStream 尝试标准中定义的不同宽度(列)来实现一种算法来查找它们,并且看看你能读多少整行。如果你的数字正确,你应该完全消耗流。

import com.twelvemonkeys.imageio.metadata.AbstractEntry;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.exif.EXIFWriter;
import com.twelvemonkeys.imageio.metadata.exif.Rational;
import com.twelvemonkeys.imageio.metadata.exif.TIFF;

import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import java.io.*;
import java.util.ArrayList;

public class G3Test {
    public static void main(String[] args) throws IOException {
        File input = new File(args[0]);
        File output = new File(args.length > 1 ? args[1] : input.getName().replace(".g3", ".tif"));

        // ImageWidth = 1728, 2048, 2482. SHORT or LONG. These are the fixed page widths in pixels defined in CCITT Group 3.
        int columns = 1728; // The default
        int rows = 100;     // Trial and error for sample file found at http://www.filesuffix.com/en/extension/fax

        ArrayList<Entry> entries = new ArrayList<>();

        // http://cool.conservation-us.org/bytopic/imaging/std/tiff-f.html
        // Required Class F tags
        entries.add(new TIFFEntry(TIFF.TAG_COMPRESSION, TIFF.TYPE_SHORT, 3)); // CCITT T.4
        entries.add(new TIFFEntry(TIFF.TAG_FILL_ORDER, TIFF.TYPE_SHORT, 1));  // Left to right
        entries.add(new TIFFEntry(TIFF.TAG_GROUP3OPTIONS, TIFF.TYPE_LONG, 0)); // No options set
        entries.add(new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, TIFF.TYPE_LONG, columns));
        entries.add(new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, TIFF.TYPE_LONG, rows));
        entries.add(new TIFFEntry(TIFF.TAG_SUBFILE_TYPE, TIFF.TYPE_LONG, 2)); // Page
        entries.add(new TIFFEntry(TIFF.TAG_RESOLUTION_UNIT, TIFF.TYPE_SHORT, 2)); // Inches
        entries.add(new TIFFEntry(TIFF.TAG_X_RESOLUTION, TIFF.TYPE_RATIONAL, new Rational(204))); // 204
        entries.add(new TIFFEntry(TIFF.TAG_Y_RESOLUTION, TIFF.TYPE_RATIONAL, new Rational(98))); // 98, 196
        // Required Bilevel (Class B) tags
        entries.add(new TIFFEntry(TIFF.TAG_BITS_PER_SAMPLE, TIFF.TYPE_SHORT, 1)); // 1 bit/sample
        entries.add(new TIFFEntry(TIFF.TAG_PHOTOMETRIC_INTERPRETATION, TIFF.TYPE_SHORT, 0)); // White is zero
        entries.add(new TIFFEntry(TIFF.TAG_SOFTWARE, TIFF.TYPE_ASCII, "TwelveMonkeys FAX2TIFF 0.1 BETA ;-)"));
        entries.add(new TIFFEntry(TIFF.TAG_ROWS_PER_STRIP, TIFF.TYPE_LONG, rows));
        entries.add(new TIFFEntry(TIFF.TAG_SAMPLES_PER_PIXEL, TIFF.TYPE_SHORT, 1)); // 1 sample/pixel
        entries.add(new TIFFEntry(TIFF.TAG_STRIP_BYTE_COUNTS, TIFF.TYPE_LONG, input.length()));
        entries.add(new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, TIFF.TYPE_LONG, -1)); // placeholder for now

        // We now have all our entries, compute size of the entries, and make that the offset (we'll write the data right after).
        EXIFWriter writer = new EXIFWriter();
        long offset = 12 + writer.computeIFDSize(entries); // + 12 for TIFF magic (4), IFD0 pointer (4) and EOF (4)
        entries.remove(entries.size() - 1);
        entries.add(new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, TIFF.TYPE_LONG, offset));


        try (InputStream in = new FileInputStream(input)) {
            try (ImageOutputStream out = ImageIO.createImageOutputStream(output)) {
                // Write the TIFF IFD for the image data
                writer.write(entries, out);

                // Copy the already G3 compressed bytes verbatim to the output
                byte[] buffer = new byte[1024];
                int read;
                while ((read = in.read(buffer)) >= 0) {
                    out.write(buffer, 0, read);
                }
            }
        }
    }

    // API stupidity, should be fixed in later verisons (ie. contain a predefined TIFFEntry class)
    static final class TIFFEntry extends AbstractEntry {
        private final short type;

        TIFFEntry(int identifier, short type, Object value) {
            super(identifier, value);
            this.type = type;
        }

        @Override
        public String getTypeName() {
            return TIFF.TYPE_NAMES[type];
        }
    }
}

【讨论】:

    【解决方案2】:

    为了不猜测图像高度,可以找出行数。

    如果您知道图像的外观,您可以按位读取编码的图像数据(注意位顺序)并计算“EOL 标志”。有两种不同的标志,取决于行以白色像素或黑色开头。完整的描述在这里Tiff Format Specification 部分:修改后的霍夫曼压缩。

    【讨论】:

      猜你喜欢
      • 2017-06-15
      • 2019-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-02
      • 2014-08-27
      • 1970-01-01
      相关资源
      最近更新 更多