Масштабировать многостраничное изображение TIFF в java

Я хочу изменить высоту многостраничного изображения TIFF, поэтому я использую приведенный ниже фрагмент кода для его масштабирования. Но он возвращает только первую страницу из файла tiff, я думаю, он преобразует его в изображение JPEG. Как сохранить все страницы файла??

public static byte[] scale(byte[] fileData, int width, int height) {
  System.out.println("width:::"+width+"::::height:::"+height);
  ByteArrayInputStream in = new ByteArrayInputStream(fileData);
  ByteArrayOutputStream buffer=null;
  BufferedImage img=null;
  BufferedImage imageBuff=null;
  try {
    ImageInputStream imageStream = ImageIO.createImageInputStream(new ByteArrayInputStream(fileData));

    java.util.Iterator<ImageReader> readers = ImageIO.getImageReaders(imageStream); 
    while(readers.hasNext()) {  
      ImageReader nextImageReader = readers.next();  
      nextImageReader.reset();  
    } 

    img = ImageIO.read(in);
    if(height == 0) {
      height = (width * img.getHeight())/ img.getWidth(); 
    }
    if(width == 0) {
      width = (height * img.getWidth())/ img.getHeight();
    }
    Image scaledImage = img.getScaledInstance(width, height, Image.SCALE_SMOOTH);
    imageBuff = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    imageBuff.getGraphics().drawImage(scaledImage, 0, 0, new Color(0,0,0), null);

    buffer = new ByteArrayOutputStream();

    ImageIO.write(imageBuff, "TIF", buffer);


  } catch (IOException e) {
    e.printStackTrace();
  } finally{
    if(img!=null){
      img.flush();
      img=null;
    }
    if(imageBuff!=null){
      imageBuff.flush();
      imageBuff=null;
    }

    if(buffer!=null){
      try {
        buffer.close();
      } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }

    if(in!=null){
      try {
        in.close();
      } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
  }
  return buffer.toByteArray();
}

ОБНОВЛЕННЫЙ КОД:

public static byte[] resize(byte[] img,int height,int width) throws IOException {
        byte[] outimage = null;

        ImageReader reader=null;
        ImageWriter writer=null;
        ByteArrayOutputStream baos=null;
        ImageOutputStream ios=null;
        ImageInputStream imageStream=null;
        try {
            baos = new ByteArrayOutputStream(30000);
            ios = ImageIO.createImageOutputStream(baos);

            reader = getTiffImageReader();
            imageStream= ImageIO.createImageInputStream(new ByteArrayInputStream(img));
            reader.setInput(imageStream);

            int pages = reader.getNumImages(true);

            Iterator<ImageWriter> imageWriters = ImageIO.getImageWritersByFormatName("TIFF");
            writer = imageWriters.next();

            writer.setOutput(ios);
            ImageWriteParam writeParam = writer.getDefaultWriteParam();
            writeParam.setTilingMode(ImageWriteParam.MODE_DEFAULT);

            writer.prepareWriteSequence(reader.getStreamMetadata()); 

            for (int i = 0; i < pages; i++) {
                //IIOImage iioImage = reader.readAll(i, null);
                BufferedImage bufimage=null;
                BufferedImage imageBuff=null;
                bufimage=reader.read(i);
                imageBuff=Thumbnails.of(bufimage).size(1200, 1200).asBufferedImage();
                IIOImage scalediioImage = new IIOImage(imageBuff, null, null);
                writer.writeToSequence(scalediioImage, writeParam);
                bufimage.flush();
                imageBuff.flush();
            }

            writer.endWriteSequence();
            outimage = baos.toByteArray();            


        } catch (Exception e) {
            e.printStackTrace();
        }
        finally{
                if(imageStream!=null){
                    imageStream.close();
                    }
                if(ios!=null){
                    ios.flush();
                    ios.close();
                    }

                if(baos!=null){
                    baos.close();
                }
                if(reader!=null){
                     reader.dispose();
                }
                if(writer!=null){
                    writer.dispose();
                }
            }
        return outimage;
    }

person happy    schedule 04.04.2014    source источник
comment
По крайней мере, для этого вам понадобится java.net/projects/jai-imageio. Похоже, вы запрашиваете многостраничные файлы TIFF, но я не уверен, что ImageIO или даже JAI вообще могут их обрабатывать...   -  person Marco13    schedule 04.04.2014
comment
Я думаю, что это странно, что вы не получаете ни вывода, ни исключений в своем обновленном коде, но это может быть writer.setOutput(ios) внутри цикла, который каким-то образом сбрасывает процесс записи. Установите вывод только один раз и сделайте это до writer.prepareWriteSequence().   -  person Harald K    schedule 10.04.2014
comment
Боковое примечание: BufferedImage.flush() здесь избыточен, если только вы на самом деле не рисовали изображения на экране (т.е. если только очищаете собственную видеопамять). Изображение по-прежнему будет храниться в памяти кучи Java.   -  person Harald K    schedule 10.04.2014


Ответы (1)


ImageIO.write(...) будет записывать только отдельные отдельные изображения. Запись нескольких изображений в один и тот же выходной поток не устраняет эту проблему. Однако пакет ImageIO поставляется с полной поддержкой того, что вы хотите, просто для этого требуется больше кода.

Получите правильный ImageWriter для формата TIFF, используя:

ImageWriter writer =  ImageIO.getImageWritersByFormatName("TIFF").next();  // Assumes TIFF plugin installled

См. документацию по API для ImageIO.getImageWritersByFormatName(String) для получения дополнительной информации.

Затем используйте writer.canWriteSequence(). чтобы узнать, поддерживает ли ваш экземпляр writer последовательности записи. Он должен вернуть trueдля TIFF. Если нет, вам нужно найти другой плагин.

Затем используйте writer.prepareWriteSequence(...) для подготовки последовательности изображений.

Для каждого изображения (страницы), которое вы хотите добавить, используйте writer.writeToSequence(new IIOImage(..., bufferedImage, null), ...) в цикле for.

Затем, наконец, вне цикла for используйте writer.endWriteSequence(), чтобы завершить последовательность изображений.

Надеюсь, что эти указатели помогут вам двигаться в правильном направлении.

Обновление: вот очищенная и измененная версия вашего кода, которая, думаю, должна работать (у меня нет и я не могу установить JAI на свой рабочий ноутбук).

Важными изменениями являются удаление writer.setOutput(ios) из цикла (вы должны установить его только один раз) и перемещение writer.endWriteSequence() за пределы цикла. Я также удалил массив BufferedImage, чтобы не хранить все изображения в памяти, вы можете оставить его для удобства.

Дайте мне знать, если у вас все еще есть проблемы, и я посмотрю, что я могу сделать.

public static byte[] resize(byte[] img) throws IOException {
    byte[] outimage = null;

    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(30000);
        ImageOutputStream ios = ImageIO.createImageOutputStream(baos);

        ImageReader reader = getTiffImageReader();
        ImageInputStream imageStream = ImageIO.createImageInputStream(new ByteArrayInputStream(img));
        reader.setInput(imageStream);

        int pages = reader.getNumImages(true);

        Iterator<ImageWriter> imageWriters = ImageIO.getImageWritersByFormatName("TIFF");
        ImageWriter writer = imageWriters.next();

        writer.setOutput(ios);
        ImageWriteParam writeParam = writer.getDefaultWriteParam();
        writeParam.setTilingMode(ImageWriteParam.MODE_DEFAULT);

        writer.prepareWriteSequence(reader.getStreamMetadata()); // You want the stream metadata here

        for (int i = 0; i < pages; i++) {
            IIOImage iioImage = reader.readAll(i, null); // Save some lines by using readAll

            BufferedImage image = (BufferedImage) iioImage.getRenderedImage();

            // Modify image here...                

            iioImage.setRenderedImage(image);

            writer.writeToSequence(iioImage, writeParam);
        }

        writer.endWriteSequence(); // Crucial, must be done outside loop

        ios.flush();
        ios.close();

        outimage = baos.toByteArray();            
        baos.close();

        writer.dispose();
        reader.dispose();

    } catch (Exception e) {
        e.printStackTrace();
    }

    return outimage;
}
person Harald K    schedule 08.04.2014
comment
Спасибо. Я пытался использовать ImageWriter, но не получаю никакого изображения. Я разместил обновленный код в разделе вопросов. - person happy; 10.04.2014
comment
@happy Я обновил ответ с измененным кодом из вашего вопроса. - person Harald K; 10.04.2014
comment
ваш код работает правильно, но когда я снова добавил код масштабирования, я ничего не получил. Пожалуйста, проверьте обновленный код в разделе вопросов. - person happy; 10.04.2014
comment
Когда вы говорите, что он работает правильно, вы имеете в виду, что у вас есть полностью рабочий многостраничный TIFF? Если это так, у меня есть несколько идей, как это исправить. Попробуйте передать метаданные из iioImage в конструктор new IIOImage(...). - person Harald K; 10.04.2014
comment
да, я получил все страницы многостраничного изображения. Но когда я пытаюсь изменить высоту всех изображений в TIFF-файле, я получаю пустой апплет (который считывает массив байтов, возвращенный из вышеуказанного метода). - person happy; 10.04.2014
comment
Большое спасибо HaraldK. - person happy; 10.04.2014