4

I'm trying to figure out how to join multiple images with vips via python. I have in a folder lets say 30 ( but can be more than 600 ) png files that are stripes, they have resolution 854x289920 ( all the same resolution )...

PIL in python will immediately die if I try join them together horizontally with MemmoryError. So I google around and found VIPS which can do both things I need join the images and make deep zoom image from result.

Unfortunately I'm not sure how to correctly join them horizontally in python.

I have in array a list of images from folder, but how would I loop through them and sequentially write joined image out to disk ?

VladoPortos
  • 563
  • 5
  • 21

3 Answers3

7

Just for reference, you can also do this at the command line. Try:

vips arrayjoin "a.png b.png c.png" mypyr.dz --across 3

Will join three PNG images horizontally and save the result as a deepzoom pyramid called mypyr. The arrayjoin docs have all of the options:

https://www.libvips.org/API/current/libvips-conversion.html#vips-arrayjoin

You can give the pyramid builder parameters by enclosing them in square brackets after the .dz.

vips arrayjoin "a.png b.png c.png" mypyr.dz[overlap=0,container=zip] --across 3

On Windows, deepzoom pyramids can be very slow to write since Windows hates creating files, and hates huge directories. If you write with container=zip, vips will directly create a .zip file containing the pyramid. This makes pyramid creation around 4x faster.

JulesDoe
  • 143
  • 9
jcupitt
  • 10,213
  • 2
  • 23
  • 39
1

This also seems to be working fine for opening large number of images and doing joinarray on them so they are next to each other. Thanks @user894763

import os
import pyvips
# natsort helps with sorting the list logically
from natsort import natsorted

source = r"E:/pics/"
output = r"E:/out/"
save_to = output + 'final' + '.tif'

# define list of pictures we are going to get from folder
list_of_pictures = []
# get the 
for x in os.listdir(source):
    list_of_pictures.append(source + x)

# list_of_pictures now contains all the images from folder including full path
# since os.listdir will not guarantee proper order of files we use natsorted to do it
list_of_pictures = natsorted(list_of_pictures)

array_images = []
image = None
# lets create array of the images for joining, using sequential so it use less ram
for i in list_of_pictures:
    tile = pyvips.Image.new_from_file(i, access="sequential")
    array_images.append(tile)

# Join them, across is how many pictures there be next to each other, so i just counted all pictures in array with len 
out = pyvips.Image.arrayjoin(array_images, across=len(list_of_pictures))
# write it out to file....
out.write_to_file(save_to, Q=95, compression="lzw", bigtiff=True)
VladoPortos
  • 563
  • 5
  • 21
  • 1
    The `Q` param on save is just for JPG compression, it won't have any effect to LZW. You'll probably see the best results with `compression="deflate"` and `predictor="horizontal"`. As I said though, I would write the pyramid directly and not save the intermediate, it'll be much faster. – jcupitt Mar 25 '18 at 14:16
0

I figured it out with some questions still:

import pyvips

list_of_pictures = []
for x in os.listdir(source):
    list_of_pictures.append(source + x)

image = None
for i in list_of_pictures:
    tile = pyvips.Image.new_from_file(i, access="sequential")
    image = tile if not image else image.join(tile, "horizontal")

image.write_to_file(save_to)

which yes, produce tif with joined pictures... but holly cow ! the original pictures are png (30x ) all together 4.5GB the result tiff is 25GB ! what gives, why is there such a huge size difference ?

VladoPortos
  • 563
  • 5
  • 21
  • ok there are some options here for compression: `image.write_to_file(save_to, Q=95, compression="lzw", bigtiff=True)` – VladoPortos Mar 24 '18 at 05:43
  • where valid options are (jpeg, deflate, packbits, ccittfax4, lzw) `jpeg = fails if its get over 4GB, deflate = crash, packbits = 13.2GB ( made in 45s), ccittfax4 = wbuffer_write: write failed, lzw = 5.88GB (made in 151s)` So far lzw compression seems to provide the best results size vise, and I have not noticed any image degradation in the result – VladoPortos Mar 24 '18 at 05:49
  • 1
    You can use `arrayjoin` to join an array of images in a single step. It'll work better for very large numbers of source images. You can write directly to a deepzoom pyramid, just swap your last line for `image.dzsave("mypyr")`. – jcupitt Mar 24 '18 at 11:50
  • You can also write a deepzoom pyramid with `write_to_file`: just use `.dz` as the suffix. For example: `image.write_to_file("mypyr.dz[suffix=.png]")` – jcupitt Mar 24 '18 at 11:54
  • @user894763 thanks, I think I will first generate the huge one image and continue from there... but the arrayjoin looks interesting, I'm just not sure if I understand the parameters correctly. Lets say I have 30 images and want to have them array joined next to each other would I do: `pyvips.Image.arrayjoin(list_of_images, across = 30)` ? The across parameter is what I'm referring to. – VladoPortos Mar 24 '18 at 18:58
  • That's it. There's no benefit to generating the huge image and then making the pyramid -- I'd just generate the pyramid directly. Up to you, of course! – jcupitt Mar 25 '18 at 14:09
  • @user894763 true I think your approach is correct and would save me a step, but I made it finally, this took so long to finish :D its till uploading but results can be seen here: http://deepzoom.reverz.sk/ all .sk domains in one picture – VladoPortos Mar 26 '18 at 05:01
  • Haha that's hilarious! You sorted by dominant colour I guess? Nice job anyway. – jcupitt Mar 26 '18 at 08:31
  • @user894763 yea I have tried, there is some issues with the process not producing nice rainbow output... I know what the issue is, but no idea how to fix it :D since technically its sorted by dominant colors ( actually by first 3 dominant colors ) but visually I would move lots of the pictures to different places :D – VladoPortos Mar 26 '18 at 10:57