【问题标题】:Scale multi page TIFF Image in java在java中缩放多页TIFF图像
【发布时间】:2014-04-04 07:36:40
【问题描述】:

我想改变多页 TIFF 图像的高度,所以我使用下面的代码 sn-p 来缩放它。但它只返回 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;
    }

【问题讨论】:

  • 您至少需要java.net/projects/jai-imageio。您似乎在要求 multi-page TIFF 文件,但我不确定 ImageIO 甚至 JAI 是否可以处理它们...
  • 我认为在更新的代码中没有输出也没有异常,这很奇怪,但它可能是循环内的 writer.setOutput(ios) 以某种方式重置了编写过程。只设置一次输出,在writer.prepareWriteSequence()之前做。
  • 旁注:BufferedImage.flush() 在这里是多余的,除非您实际将图像绘制到屏幕上(即,如果仅刷新本机视频 RAM)。图像仍将保存在 Java 堆内存中。

标签: java image scale tiff jai


【解决方案1】:

ImageIO.write(...) 只会编写单个独立图像。将多个图像写入同一输出流并不能解决此问题。然而 ImageIO 包完全支持你想要的,它只需要更多的代码。

为 TIFF 格式获取正确的ImageWriter,使用:

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

有关更多信息,请参阅ImageIO.getImageWritersByFormatName(String) 的 API 文档。

然后使用writer.canWriteSequence() 来判断您的writer 实例是否支持写入序列。它应该返回truefor TIFF。如果没有,你需要找到一个不同的插件。

接下来,使用writer.prepareWriteSequence(...) 准备您的图像序列。

对于您要附加的每个图像(页面),在for 循环中使用writer.writeToSequence(new IIOImage(..., bufferedImage, null), ...)

最后,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;
}

【讨论】:

  • 谢谢。我尝试使用 ImageWriter,但没有返回任何图像。我已在问题部分发布了更新的代码。
  • @happy 我用您问题中的修改代码更新了答案。
  • 您的代码工作正常,但是当我再次添加缩放代码时,我什么也没得到。请检查问题部分中的更新代码。
  • 当你说它工作正常时,你的意思是你有一个完全工作的多页 TIFF?如果是这样,我对如何解决这个问题有一些想法。尝试将元数据从 iioImage 传递给 new IIOImage(...) 构造函数。
  • 是的,我得到了多页图像的所有页面。但是当我尝试更改 tiff 文件中所有图像的高度时,我得到一个空白小程序(它读取从上述方法返回的字节数组)。
猜你喜欢
  • 1970-01-01
  • 2012-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-20
  • 1970-01-01
  • 2010-10-17
  • 1970-01-01
相关资源
最近更新 更多