I have approx. 6000 PNG files (256*256 pixels) and want to combine them into a big PNG holding all of them programmatically.
What\'s the best/fastest way to do that?
simple python script for joining tiles into one big image:
import Image
TILESIZE = 256
ZOOM = 15
def merge_images( xmin, xmax, ymin, ymax, output) :
out = Image.new( 'RGB', ((xmax-xmin+1) * TILESIZE, (ymax-ymin+1) * TILESIZE) )
imx = 0;
for x in range(xmin, xmax+1) :
imy = 0
for y in range(ymin, ymax+1) :
tile = Image.open( "%s_%s_%s.png" % (ZOOM, x, y) )
out.paste( tile, (imx, imy) )
imy += TILESIZE
imx += TILESIZE
out.save( output )
run:
merge_images(18188, 18207, 11097, 11111, "output.png")
works for files named like %ZOOM_%XCORD_%YCORD.png , for example 15_18188_11097.png
Use imagemagick's montage like this:
montage *.png montage.png
You can find more information on the parameters here
Good luck
I had some similar need some time ago (huge images -and, I my case with 16 bitdepth- to have them fully in memory was not an option). And I ended coding a PNG library to do the read/write in a sequential way. In case someone find it useful, it's here.
Updated: here's a sample code:
/**
* 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");
}
}