如何将多个PNG合并为一个大PNG文件?
image
image-processing
java
5
0

我大约。 6000个PNG文件(256 * 256像素),并希望将它们组合成一个大PNG,以编程方式保存它们。

最好/最快的方法是什么?

(其目的是在纸上打印,因此,使用某些网络技术不是一种选择,只有一个图片文件将消除许多使用错误。)

我尝试了fahd的建议,但尝试创建宽24576像素,高15360像素的BufferedImage时遇到NullPointerException 。有任何想法吗?

参考资料:
Stack Overflow
收藏
评论
共 6 个回答
高赞 时间 活跃

我看不到“不进行处理和重新编码”怎么可能。如果您坚持使用Java,那么我建议您使用JAI此处为项目页面)。这样,您将创建一个较大的BufferedImage加载较小的图像并将它们绘制在较大的 BufferedImage

或仅使用ImageMagick montage

montage *.png output.png

有关montage更多信息,请参见用法

收藏
评论

梳理图像

private static void combineALLImages(String screenNames, int screens) throws IOException, InterruptedException {
    System.out.println("screenNames --> D:\\screenshots\\screen   screens --> 0,1,2 to 10/..");
    int rows = screens + 1;
    int cols = 1;
    int chunks = rows * cols ; 

     File[] imgFiles = new File[chunks];
    String files = "";
    for (int i = 0; i < chunks; i++) {
        files = screenNames + i + ".jpg";
        imgFiles[i] = new File(files);          
        System.out.println(screenNames + i + ".jpg"+"\t Screens : "+screens);    

    }

    BufferedImage sample = ImageIO.read(imgFiles[0]);
    //Initializing the final image
    BufferedImage finalImg = new BufferedImage(sample.getWidth() * cols, sample.getHeight() * rows, sample.getType());

    int index = 0;
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            BufferedImage temp = ImageIO.read(imgFiles[index]);
            finalImg.createGraphics().drawImage(temp, sample.getWidth() * j, sample.getHeight() * i, null);
            System.out.println(screenNames + index + ".jpg");
            index++;
        }
    }
    File final_Image = new File("D:\\Screenshots\\FinalImage.jpg");
    ImageIO.write(finalImg, "jpeg", final_Image);

}
收藏
评论

不久前,我也有类似的需求(巨大的图像,而我的情况是16位深度,这是完全不可能的)。我结束了对PNG库的编码以按顺序进行读/写。万一有人觉得有用,就在这里

更新:这是示例代码:

/**
 * Takes several tiles and join them in a single image
 * 
 * @param tiles            Filenames of PNG files to tile
 * @param dest            Destination PNG filename
 * @param nTilesX            How many tiles per row?
 */
public class SampleTileImage {

        public static void doTiling(String tiles[], String dest, int nTilesX) {
                int ntiles = tiles.length;
                int nTilesY = (ntiles + nTilesX - 1) / nTilesX; // integer ceil
                ImageInfo imi1, imi2; // 1:small tile   2:big image
                PngReader pngr = new PngReader(new File(tiles[0]));
                imi1 = pngr.imgInfo;
                PngReader[] readers = new PngReader[nTilesX];
                imi2 = new ImageInfo(imi1.cols * nTilesX, imi1.rows * nTilesY, imi1.bitDepth, imi1.alpha, imi1.greyscale,
                                imi1.indexed);
                PngWriter pngw = new PngWriter(new File(dest), imi2, true);
                // copy palette and transparency if necessary (more chunks?)
                pngw.copyChunksFrom(pngr.getChunksList(), ChunkCopyBehaviour.COPY_PALETTE
                                | ChunkCopyBehaviour.COPY_TRANSPARENCY);
                pngr.readSkippingAllRows(); // reads only metadata             
                pngr.end(); // close, we'll reopen it again soon
                ImageLineInt line2 = new ImageLineInt(imi2);
                int row2 = 0;
                for (int ty = 0; ty < nTilesY; ty++) {
                        int nTilesXcur = ty < nTilesY - 1 ? nTilesX : ntiles - (nTilesY - 1) * nTilesX;
                        Arrays.fill(line2.getScanline(), 0);
                        for (int tx = 0; tx < nTilesXcur; tx++) { // open several readers
                                readers[tx] = new PngReader(new File(tiles[tx + ty * nTilesX]));
                                readers[tx].setChunkLoadBehaviour(ChunkLoadBehaviour.LOAD_CHUNK_NEVER);
                                if (!readers[tx].imgInfo.equals(imi1))
                                        throw new RuntimeException("different tile ? " + readers[tx].imgInfo);
                        }
                        for (int row1 = 0; row1 < imi1.rows; row1++, row2++) {
                                for (int tx = 0; tx < nTilesXcur; tx++) {
                                        ImageLineInt line1 = (ImageLineInt) readers[tx].readRow(row1); // read line
                                        System.arraycopy(line1.getScanline(), 0, line2.getScanline(), line1.getScanline().length * tx,
                                                        line1.getScanline().length);
                                }
                                pngw.writeRow(line2, row2); // write to full image
                        }
                        for (int tx = 0; tx < nTilesXcur; tx++)
                                readers[tx].end(); // close readers
                }
                pngw.end(); // close writer
        }

        public static void main(String[] args) {
                doTiling(new String[] { "t1.png", "t2.png", "t3.png", "t4.png", "t5.png", "t6.png" }, "tiled.png", 2);
                System.out.println("done");
        }
}
收藏
评论

PNG格式不支持切片,因此您无法逃脱至少解压缩和重新压缩数据流。如果所有图像的调色板都相同(或全部不存在),这是您真正需要做的唯一事情。 (我还假设图像不是隔行扫描的。)

您可以通过流方式进行操作,一次只打开一个“行”的PNG,从它们的数据流中读取适当大小的块并将它们写入输出流。这样,您将不需要将整个图像保留在内存中。最有效的方法是自己在libpng上编程。由于像素预测,您可能需要在内存中保留多于一条的像素扫描线。

但是,仅使用ImageMagick,netpbm或类似的命令行实用程序将为您节省大量的开发时间,而收益却很少。

收藏
评论

创建要写入的大图像。根据所需的行数和列数来确定其尺寸。

    BufferedImage result = new BufferedImage(
                               width, height, //work these out
                               BufferedImage.TYPE_INT_RGB);
    Graphics g = result.getGraphics();

现在遍历您的图像并绘制它们:

    for(String image : images){
        BufferedImage bi = ImageIO.read(new File(image));
        g.drawImage(bi, x, y, null);
        x += 256;
        if(x > result.getWidth()){
            x = 0;
            y += bi.getHeight();
        }
    }

最后将其写出到文件中:

    ImageIO.write(result,"png",new File("result.png"));
收藏
评论

正如其他人指出的那样,使用Java不一定是最好的选择。

如果要使用Java,最好的选择是-假设您的内存不足,以至于无法多次将整个数据集读入内存,然后再次将其写出-使用RenderedImage该类将根据需要从磁盘读取PNG。如果您只是创建自己的新BufferedImage,然后尝试将其写出,则PNG编写器将创建数据的额外副本。如果创建自己的RenderedImage,则可以将其传递给ImageIO.write(myImageSet,"png",myFileName) 。您可以从第一个PNG复制SampleModelColorModel信息-希望它们都是一样的。

如果您假装整个图像是多个图块(每个源图像一个图块),则ImageIO.write将创建一个WritableRaster ,其大小为整个图像数据集的大小,并将调用RenderedImage.copyData的实现以将其填充数据。如果您有足够的内存,这是一种简单的方法(因为您可以获得大量的目标数据,并且可以使用setRect(dx,dy,Raster)方法将所有图像数据转储到其中setRect(dx,dy,Raster) ,然后不必再担心它)。我没有测试过是否可以节省内存,但是在我看来应该这样做。

或者,如果您假装整个图像是单个图块,则ImageIO.write将使用getTile(0,0)询问与该整个图像相对应的栅格。因此,您必须创建自己的Raster,这又使您可以创建自己的DataBuffer。当我尝试这种方法时,成功编写15360x25600 RGB PNG的最小内存使用量是-Xmx1700M (顺便说一下,在Scala中),这仅比写入图像的每个像素略多于4个字节,因此在一个完整图像上几乎没有开销。记忆。

PNG数据格式本身并不是一种要求整个图像都在内存中的格式-它可以按块工作-但是,令人遗憾的是,PNG编写器的默认实现假定它将在内存中具有整个像素阵列。

收藏
评论
新手导航
  • 社区规范
  • 提出问题
  • 进行投票
  • 个人资料
  • 优化问题
  • 回答问题

关于我们

常见问题

内容许可

联系我们

@2020 AskGo
京ICP备20001863号