WedX - журнал о программировании и компьютерных науках

Нужна помощь, чтобы начать конвертировать необработанные файлы G3 FAX в формат TIFF через Java

У меня есть необработанный файл факса (формат G3/T.4), и мне нужно программно преобразовать его в многостраничный TIFF через Java. JAI пока не приносил мне успеха, даже если я думаю, что он должен работать. Инструменты sFaxTools успешно преобразовывают необработанные файлы факсов в формат TIFF (пакетный 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); следует заменить некоторой реализацией ImageDecoder, которая внутренне использует FaxDecoder для декодирования исходного факсимильного файла g3. В пакете jai есть защищенный класс TIFFFaxDecoder, возможно ли это и как использовать его для моей цели? Любая идея? Спасибо

10.11.2015

  • Если вы нашли инструмент вне Java, который вам подходит, вы можете запустить его через Java, используя Runtime.getRuntime().exec(command). С другой стороны, Java имеет поддержку TIFF в инструментах ввода-вывода изображений JAI, которая настолько отличается от JAI, что вы могли ее не найти: stackoverflow.com/questions/7502181/ 10.11.2015
  • В качестве альтернативы, проверьте этот вопрос SO о начале работы с TIFF и JAI: ">stackoverflow.com/questions/30320434/write-a-tiff-with-jai 10.11.2015
  • Есть ли спецификация для того, что где-то находится необработанный файл G3 FAX? Если это то же самое, что часть данных изображения сжатого TIFF G3 (T.4), должна быть возможность (и довольно легко) просто поместить данные в контейнер TIFF. 11.11.2015
  • Чтобы доказать это, я загрузил единственный пример файла, который смог найти, и написал TIFF контейнер (используя TwelveMonkeys ImageIO) с обязательными полями из спецификация TIFF Class F, а затем добавил необработанные данные G3. Теперь файл открывается и отображается как действительный TIFF. См. следующую суть. Я не даю никаких гарантий, что он будет работать для чего угодно, кроме этого файла примера. ;-) 11.11.2015
  • В отредактированном вопросе по-прежнему запрашивается рекомендация по программному обеспечению. 11.11.2015
  • извините, но я прошу решение на основе уже используемого фреймворка, я снова изменил свой вопрос. Я бы не понял, если бы вы еще жаловались на это. Разработка программного обеспечения в наши дни всегда означает попытку поиграть с существующими (с открытым исходным кодом) фреймворками, и если мне не разрешено упоминать их в своем посте или спрашивать, соответствует ли он моим требованиям, этот форум, на мой взгляд, устаревает. С уважением 11.11.2015
  • Спасибо AdrianHHH, я попробовал ваш код, и он, по крайней мере (частично), работает с моими образцами. Какая-то часть факса отсутствует, и я пока не знаю, как обрабатывать многостраничные TIFF, но это уже был действительно хороший намек. Я все еще рад другим вкладам ... 11.11.2015

Ответы:


1

Я не думаю, что JAI поддерживает чтение необработанных факсимильных данных G3/T.4 напрямую. Тем не менее, вот пример кода, который вы можете изменить и расширить в соответствии со своими потребностями, реализуя идею, изложенную в комментариях (первоначально опубликованных как 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 https://www.filesuffix.com/en/extension/fax

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

        // https://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];
        }
    }
}
19.11.2015

2

Чтобы не угадывать высоту изображения, можно узнать количество строк.

Если вы знаете, как выглядит изображение, вы можете прочитать закодированные данные изображения побитно (обратите внимание на порядок битов) и подсчитать «флаги EOL». Есть два разных флага, в зависимости от того, начинается строка с белого пикселя или с черного. Полное описание находится здесь Спецификация формата Tiff в разделе: Модифицированный Хаффман Сжатие.

27.11.2015
Новые материалы

Как создать диаграмму градиентной кисти с помощью D3.js
Резюме: Из этого туториала Вы узнаете, как добавить градиентную кисть к диаграмме с областями в D3.js. Мы добавим градиент к значениям SVG и применим градиент в качестве заливки к диаграмме с..

Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что это выглядит сложно…
Просто начните и учитесь самостоятельно Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что он кажется мне сложным, и я бросил его. Это в основном инструмент..

Лицензии с открытым исходным кодом: руководство для разработчиков и создателей
В динамичном мире разработки программного обеспечения открытый исходный код стал мощной парадигмой, способствующей сотрудничеству, инновациям и прогрессу, движимому сообществом. В основе..

Объяснение документов 02: BERT
BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

Как проанализировать работу вашего классификатора?
Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

Работа с цепями Маркова, часть 4 (Машинное обучение)
Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..


Для любых предложений по сайту: [email protected]