需要帮助开始通过 Java 将原始 G3 传真文件转换为 TIFF 格式
Need help getting started converting raw G3 FAX files to TIFF format via Java
我有一个原始传真文件(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 decoder = ImageCodec.createImageDecoder("tiff", stream, decodeParams);
应该替换为内部使用 FaxDecoder 解码 g3 原始传真文件的一些 ImageDecoder 实现。 jai 包中有一个受保护的 class TIFFFaxDecoder,是否可能以及如何将其用于我的目的?任何的想法?
谢谢
我认为 JAI 不支持直接读取 G3/T.4 原始传真数据。但是,您可以根据自己的需要修改和扩展这里的示例代码,实现评论中概述的想法(最初作为 Gist 发布)。
它不会以任何方式解码 G3/T.4 数据,它只是将原始传真数据包装在一个最小的 TIFF 容器中。这允许稍后将数据作为普通 TIFF 读取。它使用(我自己的)TwelveMonkeys ImageIO 库来做到这一点。
如果您不知道传真文件的 width/height,您可以通过使用 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];
}
}
}
图片高度不用猜,行数可以自己猜
如果你知道图像的样子,你可以按位读取编码图像数据(注意位序)并计算 'EOL flags'。有两个不同的标志,取决于以白色像素或黑色像素开头的行。完整描述位于 Tiff Format Specification 部分下:改进的霍夫曼压缩。
我有一个原始传真文件(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 decoder = ImageCodec.createImageDecoder("tiff", stream, decodeParams); 应该替换为内部使用 FaxDecoder 解码 g3 原始传真文件的一些 ImageDecoder 实现。 jai 包中有一个受保护的 class TIFFFaxDecoder,是否可能以及如何将其用于我的目的?任何的想法? 谢谢
我认为 JAI 不支持直接读取 G3/T.4 原始传真数据。但是,您可以根据自己的需要修改和扩展这里的示例代码,实现评论中概述的想法(最初作为 Gist 发布)。
它不会以任何方式解码 G3/T.4 数据,它只是将原始传真数据包装在一个最小的 TIFF 容器中。这允许稍后将数据作为普通 TIFF 读取。它使用(我自己的)TwelveMonkeys ImageIO 库来做到这一点。
如果您不知道传真文件的 width/height,您可以通过使用 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];
}
}
}
图片高度不用猜,行数可以自己猜
如果你知道图像的样子,你可以按位读取编码图像数据(注意位序)并计算 'EOL flags'。有两个不同的标志,取决于以白色像素或黑色像素开头的行。完整描述位于 Tiff Format Specification 部分下:改进的霍夫曼压缩。