Wednesday 6 March 2013

Making DeepZoom, Zoomify and Google Maps image pyramids with vips

UPDATE

This page has been reworked into a chapter in the official libvips documentation. 

https://libvips.github.io/libvips/API/current/Making-image-pyramids.md.html

Please see that page rather than this one.

If you have any questions, please ask on the libvips issue tracker:

https://github.com/libvips/libvips/issues
 
Here's the original text of this page, for reference:






A new version of libvips has just been released and it includes an updated version of the pyramid builder. I thought I'd write a short introduction and show some benchmarks.

Here's the Zoomify viewer working on a large image. These things are very easy to make: you convert your source image into a pyramid of tiles, then copy them to your webserver together with a scrap of javascript.

Building the image pyramid can be slow. For example, Zoomify Image is a popular Zoomify pyramid builder and wtc.tif is a 10,000 by 10,000 pixel RGB image:
$ time python ZoomifyFileProcessor.py ~/pics/wtc.tif
real    0m22.520s
user    0m17.035s
sys    0m5.419s
Peak mem 400MB
vips is almost 10 times faster:
$ time vips dzsave wtc.tif x --layout zoomify
real    0m2.581s
user    0m7.675s
sys    0m3.534s
Peak mem 120MB
vips can load images using the openslide library. This means you can make pyramids of medical images very quickly. For example, openslide includes a DeepZoom tiler written in Python, and 54479.svs is a 45,000 by 45,000 pixel slide compressed with a jpeg2000 codec:
$ time python deepzoom_tile.py ~/pics/jp2k/54479.svs
Tiling slide: wrote 36663/36663 tiles
real    56m4.753s
user    100m22.116s
sys     3m1.863s
vips is around 7 times faster:
$ time vips dzsave 54479.svs x
real    7m56.462s
user    9m9.018s
sys     0m42.475s
Though this is more than a little unfair, the openslide DeepZoom writer is not primarily designed for batch use.

vips memory use scales with image width, not number of pixels, so it can process very large images without using much memory. I've made pyramids of 150,000 by 150,000 pixel images with no problems, and it should go quite a bit larger than that.

Run dzsave with no arguments to see a summary:
$ vips dzsave
save image to deep zoom format
usage:
   dzsave in filename
where:
   in           - Image to save, input VipsImage
   filename     - Filename to save to, input gchararray
optional arguments:
   layout       - Directory layout, input VipsForeignDzLayout
   default: dz
   allowed: dz, zoomify, google
   suffix       - Filename suffix for tiles, input gchararray
   overlap      - Tile overlap in pixels, input gint
   default: 1
   min: 0, max: 8192
   tile-size    - Tile size in pixels, input gint
   default: 254
   min: 1, max: 8192
   centre       - Center image in tile, input gboolean
   default: false
   depth        - Pyramid depth, input VipsForeignDzDepth
   default: onepixel
   allowed: onepixel, onetile, one
   angle        - Rotate image during save, input VipsAngle
   default: d0
   allowed: d0, d90, d180, d270
   container    - Pyramid container type, input VipsForeignDzContainer
   default: fs
   allowed: fs, zip
   properties   - Write a properties file to the output directory, input gboolean
   default: false
   compression  - ZIP deflate compression level, input gint
   default: 0
   min: -1, max: 9
   strip        - Strip all metadata from image, input gboolean
   default: false
   background   - Background value, input VipsArrayDouble
There are also the API docs.

Writing DeepZoom pyramids

The --layout option sets the basic mode of operation. With no --layout, dzsave writes DeepZoom pyramids. For example:
$ vips dzsave huge.tif mydz
This will create a directory called mydz_files containing the image tiles, and write a file called mydz.dzi containing the image metadata. 

You can use the --suffix option to control how tiles are written. For example:
$ vips dzsave huge.tif mydz --suffix .jpg[Q=90]
will write JPEG tiles with the quality factor set to 90. You can set any format write options you like, see the API docs for vips_jpegsave() for details.

Writing Zoomify pyramids

Use --layout zoomify to put dzsave into zoomify mode. For example:
$ vips dzsave huge.tif myzoom --layout zoomify
This will create a directory called myzoom containing a file called ImageProperties.xml with the image metadata in, and a series of directories called TileGroupn, each containing 256 image tiles.

As with DeepZoom, you can use --suffix to set jpeg quality.

Writing Google Maps pyramids

Use --layout google to write Google maps-style pyramids. These are compatible with the NYU Pathology pyramid builder. For example:
$ vips dzsave wtc.tif gmapdir --layout google
Will create a directory called gmapdir containing blank.png, the file to display for blank tiles, and a set of numbered directories, one for each zoom level. The pyramid can be sparse (blank tiles are not written).

As with DeepZoom, you can use --suffix to set jpeg quality.

Use --background to set the background colour. This is the colour displayed for bits of the pyramid not in the image (image edges, for example). By default, the image background is white.

Use --centre to add a border to the image large enough to centre the image within the lowest resolution tile. By default, images are not centred.

For example:
$ vips dzsave wtc.tif gmapdir --layout google --background 0 --centre

Other options

You can use --tile-size and --overlap to control how large the tiles are and how they overlap (obviously). They default to the correct values for the selected layout.

You can use --depth to control how deep the pyramid should be. Possible values are onepixel, onetile and one. onepixel means the image is shrunk until it fits within a single pixel. onetile means shrink until it fits with a tile. one means only write one pyramid layer (the highest resolution one). It defaults to the correct value for the selected layout. --depth one is handy for slicing up a large image into tiles (rather than a pyramid).

You can use --angle to do a 90, 180 or 270 degree rotate of an image during pyramid write.

With 7.40 and later, you can use --container to set the container type. Normally dzsave will write a tree of directories, but with --container zip you'll get a zip file instead. Use .zip as the directory suffix to turn on zip format automatically:
$ vips dzsave wtc.tif mypyr.zip
to write a zipfile containing the tiles.

Preprocessing images

libvips 7.36 and later let you use .dz as a filename suffix, meaning send the image to dzsave. This means you can write the output of any vips operation to a pyramid. For example:
$ vips extract_area huge.svs mypy.dz[layout=google] 100 100 10000 10000
The arguments to extract_area are image-in, image-out, left, top, width, height. So this command will cut out a 10,000 by 10,000 pixel area from near the top-left-hand corner of an Aperio slide image, then build a pyramid in Google layout using just those pixels.

If you are working from OpenSlide images, you can use the shrink-on-load feature of many of those formats. For example:
$ vips dzsave CMU-1.mrxs[level=1] x
Will pull out level 1 (the half-resolution level of an MRXS slide) and make a pyramid from that.

Troubleshooting

If you are building vips from source you do need to check the summary at the end of configure carefully. You must have the libgsf-1-dev package for dzsave to work.

45 comments:

  1. I didn't see anything in the API but are there any settings I have to use to make jpeg2000 work? I am having trouble tiling this test file http://openslide.cs.cmu.edu/download/openslide-testdata/Aperio/CMU-1-JP2K-33005.svs

    ReplyDelete
  2. Hi, it should just work. Have you built libvips yourself? Perhaps you're missing openslide? Open an issue on the vips issue tracker on github:

    https://github.com/jcupitt/libvips/issues

    ReplyDelete
  3. I tried your image on this (very slow) machine and it works OK for me:

    $ time vips dzsave CMU-1-JP2K-33005.svs x
    real 9m58.659s
    user 12m29.699s
    sys 0m51.943s
    $

    If you built libvips from source, check the configure output and make sure everything looks OK. Make sure it's found your openslide. Also check for openjpeg, the jp2k decoder library.

    ReplyDelete
  4. Thanks for this post, its really helpful for processing Aperio files.

    One question though, is there any easy way to import a slide thats split across many .TIFF files from the command line? Right now I'm using a script that does it using the MS Image Compositing Editor but its slow and memory inefficient because it loads the entire file into memory at once.

    ReplyDelete
  5. You can use "vips insert" to paste two images together:

    http://www.vips.ecs.soton.ac.uk/supported/7.32/doc/html/libvips/libvips-conversion.html#vips-insert

    At the command-line, it's something like:

    vips insert big.tif small.tif out.tif 200 100 --expand --background 255

    will make out.tif, a copy of big.tif with small.tif pasted in at 200, 100, expanded if necessary, and with new background pixels set to 255.

    There are other operators which will do a feathered join or search for features to find the overlap distance. You can use the GUI too.

    ReplyDelete
  6. I see, thanks. One other question, does doing a join like this require loading the entire image into RAM at once or is it possible to stream it from disk while tiling?

    ReplyDelete
  7. Yes, "vips insert" will stream the image.

    ReplyDelete
  8. the output of the --layout google is a directory structure based on zoom levels with numbered jpegs in folders and a blank.png in the home folder, however, on the NYUVM site they say their slide tiling script for use with their virtual microscope produces tiles with a naming convention "tile_z_x_y.jpg". i was never able to get their slide tiling script to work with my aperio slides but wondered if this vips google output would still be compatible with their viewer?

    ReplyDelete
    Replies
    1. I'm told it works with at least one of the GM viewers. There seem to be several viewers all with slightly different tile naming conventions. You might need to write a tiny script to rename the tiles.

      Delete
    2. Looking at the viewer example here:

      http://code.google.com/p/virtualmicroscope/wiki/simpleviewerexample

      You just need to change the customGetTileUrl() function.

      Change it to be something like:

      return "http://mysite.com/images/slide14/" + zoom + "/" + coord.y + "/" + coord.x + ".jpg"

      Delete
    3. Does it have any adapter that alows to store result tiles in S3 buckets?

      Delete
    4. As it is, dzsave will only write to a filesystem.

      You'd need to add some sort of swappable backend I guess. It shouldn't be too hard, sources here if you're curious:

      https://github.com/jcupitt/libvips/blob/master/libvips/foreign/dzsave.c

      strip_work() calls vips_image_write_to_file() to save each tile. You'd just need to change that to call vips_jpegsave_buffer() instead (write an image to memory in jpeg format) then send that buffer to S3. There are a couple of other bits of dzsave you'd need to adapt as well, but nothing difficult.

      Delete
    5. vips-7.40 and later use libgsf to write the tiles. This lets you send the output to any structured storage, for example to a zip file:

      vips dzsave huge.tif mypyr.zip

      will writes the whole pyramid directly to a zip file.

      Delete
  9. Hi all,

    I try to use vips and the dzsave option (on Ubuntu LT12.04). I successfully installed vips and modules (I think). Here my .configure output:

    * general build options
    native win32: no
    native OS X: no
    open files in binary mode: no
    enable debug: no
    build C++ components: yes
    build docs with gtkdoc: no
    install docs: yes
    gobject introspection: no

    * optional packages and modules
    use fftw3 for FFT: yes
    Magick package: MagickWand
    file import with libMagick: yes
    accelerate loops with orc: yes
    (requires orc-0.4.11 or later)
    ICC profile support with lcms: yes (lcms2)
    file import with OpenEXR: yes
    file import with OpenSlide: yes
    (requires openslide-3.3.0 or later)
    file import with matio: yes
    file import with cfitsio: yes
    file import/export with libwebp: no
    text rendering with pangoft2: yes
    file import/export with libpng: yes (pkg-config libpng >= 1.2.9)
    (requires libpng-1.2.9 or later)
    file import/export with libtiff: yes (found by search)
    file import/export with libjpeg: yes
    image pyramid export: yes
    (requires libgsf-1 1.14.27 or later)
    use libexif to load/save JPEG metadata: yes
    build Python binding: yes

    Unfornutately, I don't have the dzsave option. Could you help me?

    ReplyDelete
    Replies
    1. Your configure output looks OK. Perhaps it's a problem with paths or building? Please open an issue on the vips bugtracker and I'll help fix it:

      https://github.com/jcupitt/libvips/issues

      Delete
    2. FYI, I found a post to add more librairies as follow:
      apt-get install -qq automake gobject-introspection gtk-doc-tools libfftw3-dev libglib2.0-dev libjpeg-turbo8-dev libpng12-dev libwebp-dev libtiff4-dev libxml2-dev swig libmagick++-dev bc libgsf-1-dev libcfitsio3-dev libgsl0-dev libmatio-dev

      Now, I'm able to use vips dzsave perfectly. Probably a missing library...

      Delete
  10. Is it possible to use vips to generate tiles with transparent background and png format? If so can someone point me to an example? Thanx

    ReplyDelete
    Replies
    1. Yes, you just need to pick png as your output image format. Example:

      $ vips dzsave lion.png x --suffix .png
      $ ls x_files/10/
      0_0.png 0_2.png 1_0.png 1_2.png 2_0.png 2_2.png
      0_1.png 0_3.png 1_1.png 1_3.png 2_1.png 2_3.png

      Beware that png is very slow and needs a lot of disk space.

      Delete
  11. I'm trying to scan slides (similar to the NYU virtual slides) for use in Google Maps. I have everything working, but I'm wondering if, since the Google Maps tiles can be sparse, is there a way I can systematically delete all of the extra white tiles that you usually see in a slide (example here: http://education.med.nyu.edu/virtualmicroscope/v/1540/)? It could really reduce total file size to just have the smaller white tiles removed and the blank.png image replacing them as needed, but it would be challenging to manually remove every white or close-to-white image in those folders.

    Also, thank you so much for doing this. Vips is a phenomenal tool.

    ReplyDelete
    Replies
    1. There was an issue about this on the vips tracker:

      https://github.com/jcupitt/libvips/issues/352

      With some code to automatically remove background tiles, but it was never merged because we were unable to test it. If you post on that issue, we could try again.

      Delete
    2. (and thanks for your kind words about vips)

      Delete
    3. I'll give it a shot! I'll check out that link.

      Delete
    4. This feature was added late last year, so pyramids are all sparse now.

      Delete
  12. Hi,
    I want to generate deep zoom tiles for an image of size 55,000 x 55,000 pixels. I tried using deep zoom software's but they keep loading the file without any success. Now i have downloaded vips on windows and from the command line I am trying to generate deep zoom tiles. The command i am using is
    vips dzsave huge.tif mydz --suffix .png
    but it is not giving me good quality. I have currently tried small files to firts test the result. I have couple of questions. First, would it work on such large file? secondly, how to improve the quality?
    Thanks

    ReplyDelete
  13. Hi, yes, I regularly make 200,000 x 200,000 pyramids on my small laptop, it should be fine. What do you mean by bad quality? What fault are you seeing?

    I would open an issue on the libvips tracker. You can post sample images and it's a much better way to debug issues.

    https://github.com/jcupitt/libvips/issues

    ReplyDelete
  14. Thanks John. I used vips and it worked as a charm. I still have a question about quality. For the same image, if i generate deep zoom tiles through deep zoom composer i am getting images in the tiles with bit depth of 32 bits. However for the same image vips is generating tile images with bit depth of 24 bits. So colors look bit bad as compare to deep zoom composer tiles images. Why is it so? How can i generate deep zoom tiles with better bit rate? Thanks

    ReplyDelete
  15. The 24/32 thing is the alpha channel: Deep Zoom Composer is adding an extra alpha channel you don't need. If colours don't look the same, it's probably because of an ICC profile. Make an issue on libvips github and we can track down the problem.

    ReplyDelete
  16. I want know how to convert a BMP image to JP2. faster than kakadu on windows OS.
    I need to create multilevel zoom image (pyramid).
    Kakadu creates jp2 with factor of 2. is it possible to create more immediate layers?

    ReplyDelete
  17. Hi, vips doesn't really support jp2. There are no good, free libraries for the format and it's very little used.

    ReplyDelete
  18. Hi John,
    is there any way to tile a PDF?
    and if yes how much time does VIPS takes to tile 25mb pdf?
    Thanks
    Manjinder

    ReplyDelete
  19. Hello, sure, should be fine. It uses poppler to render PDFs, so it ought to be quick, but it does depend a bit on what's in the document. On this laptop, I see:

    $ time vips dzsave nipguide.pdf[page=10,dpi=1200] x
    real 0m9.640s
    user 0m16.528s
    sys 0m2.440s

    That's rendering a page from the nip2 manual containing a mixture of text and graphics at 1200 DPI to make a 9,000 x 15,000 pixel image and saving as a pyramid.

    ReplyDelete
  20. Hello John,
    I am trying to config vips on my PC. that is windows PC. I want to use VIPS on command prompt of window. For this I download ni2-x.x.x from download link you provided. And also try some other settings and follow other links, but I am not able to run vips commands. Please help me in configurations of VIPS on windows 7 and upper system.
    Example I want to run same command you mentioned above.
    "time vips dzsave nipguide.pdf[page=10,dpi=1200] "
    How can I get this on windows?
    Thanks,
    Manjinder Singh

    ReplyDelete
  21. Hi, download vips-dev-w64-all-8.5.4.zip (the current latest version) from here:

    https://github.com/jcupitt/libvips/releases

    unzip somewhere, then cd to the vips-8.5.4/bin folder, and enter

    vips.exe dzsave huge.tif x

    or whatever. If you want to be able to run vips.exe from anywhere on your system, you will need to set your PATH.

    ReplyDelete
  22. Hi Mr. Cuppit, I'm working on a virtual microscope and I got the .svs files, but when I use the dzsave with google layout some images are left with 7 layers and others with 9, why is that?, Also is there any way I can get just 7 layers always?.

    in advance thanks for your help, And thanks a lot for taking the time to develop this library it's really awesome and usefull

    ReplyDelete
    Replies
    1. It depends on the size of the slide and the magnification you use. If you use one of the standard viewers, like openseadragon, it shouldn't matter, the viewer will just pull down the bits it needs.

      I suppose you could resize to a standard size before converting with dzsave.

      Delete
    2. I'm using the google maps API, does the amount of layer depend on the image resolution?

      Delete
    3. Yes. Each layer is half the size of the layer below, so the number of layers depends on how large the original image is. For example, if you have a 1024 x 1024 pixel original, the layers will be:

      1024 x 1024
      512 x 512
      256 x 256
      128 x 128

      So four layers. If you have a 8192 x 8192 original, you'll have seven layers.

      Delete
  23. Hi Mr. Cuppit, I'm working on a virtual microscope and I got the .svs files, but when I use the dzsave with google layout some images are left with 7 layers and others with 9, why is that?, Also is there any way I can get just 7 layers always?.

    in advance thanks for your help, And thanks a lot for taking the time to develop this library it's really awesome and usefull

    ReplyDelete
  24. Just to say: I discover your tool while working on a webgame powered by leaflet. It's great. Fast, and light, perfect !

    I haven't managed to define properly the zoom level. I did :
    vips dzsave HUGE_PIC.png mapTiles --layout google --suffix .png --depth onetile

    And I get, from a 32768x32768 px picture, zoomlevels from 0 to 7. I would like to have from 0 to 10 (or whatever) - how can I reach that ?

    (For now, I use the resize tools to create gigantic pictures, and then I can have more zoom levels).

    ReplyDelete
  25. Hi,
    First of all, thank you for your tool ! It save me a lot of time and is powerfull and ligth.
    I'm looking for a way to change the number of zoomlevel to genereate with dzsave.

    I did :
    vips dzsave PIC.png mapFolder --layout google --suffix .png --depth onetile
    And it create from a 32768x32768 px picture a tileset allowing zoom from 0 to 7...
    I notice that if I work with bigger or smaller picture, it did different level of zoom according to this.

    I make a game, and I need 0 to 10 levels of zoom - for now, I'm resize the original pic bigger and bigger, but is there an option to juste change the level of zoom to generate from a specific piture ?

    Thank again for your work.

    ReplyDelete
  26. Hello, it always shrinks the image by x2, and stops when the image fits in one tile. This means the number of layers is ((log2(image size) - log2(tile size)).

    You can size the image up in libvips, for example:

    vips resize PIC.png mapFolder.dz[layout=google,suffix=.png,depth=onetile] 8

    and it'll make the image 8x larger before building the pyramid. If the image is 8x larger, it will need log2(8), or three more layers.

    ReplyDelete
  27. Hi John,
    First of all, thank you for very much writing this amazing tool and still supporting it after all these years!

    I have a strange behaviour that I wanted to consult you about:
    While trying to convert an mrxs file with 22 levels (0-21), the result stops after 12 levels (0-11).

    Here's the command I'm using
    vips dzsave 1.mrxs dz1

    Am I missing something?
    Thanks

    ReplyDelete
    Replies
    1. It should work. Open an issue on the libvips tracker and I'll help debug your problem:

      https://github.com/jcupitt/libvips/issues

      Delete
  28. Hi John -
    Mirroring everyone else's comments - thanks for this work. I have used vips for years now.

    I have a new scanner to create tiff formats, when I try and run them through vips using:

    vips dzsave 84045T_001.tif dzs/84045001 --background 0 --centre --layout google

    I am getting errors I've never seen:

    (vips:7861): VIPS-WARNING **: 09:24:01.682: error in tile 1792 x 0
    TIFFFillTile: 0: Invalid tile byte count, tile 7

    (repeating errors for subsequent 7 tiles (7-14)) ...

    Can you point me in the direction to fix this error?

    Thanks
    Zack

    ReplyDelete
    Replies
    1. Hello, open an issue on the libvips tracker, it's a better forum for chasing bugs.

      https://github.com/libvips/libvips/issues

      Delete