diff --git a/autogen.sh b/autogen.sh index 076f9e02..208a1bf2 100755 --- a/autogen.sh +++ b/autogen.sh @@ -27,6 +27,8 @@ find doc -depth \( \ -o -path 'doc/images/*' \ -o -name '*.xml' ! -name libvips-docs.xml ! -path 'doc/xml/*' \ -o -name '*.py' \ + -o -name '*.md' \ + -o -name '*.docbook' \ \) -prune -or \( \ -type f \ -o -type d -empty \ diff --git a/doc/.gitignore b/doc/.gitignore deleted file mode 100644 index 258d81a5..00000000 --- a/doc/.gitignore +++ /dev/null @@ -1 +0,0 @@ -How-it-works.xml diff --git a/doc/How-it-works.xml b/doc/How-it-works.xml new file mode 100644 index 00000000..5b4064dc --- /dev/null +++ b/doc/How-it-works.xml @@ -0,0 +1,287 @@ + + + + + + How-it-works.md + 3 + libvips + + + + libvips + How-it-works.md + + + + + Compared to most image processing libraries, VIPS needs little RAM and runs quickly, especially on machines with more than one CPU. VIPS achieves this improvement by only keeping the pixels currently being processed in RAM and by having an efficient, threaded image IO system. This page explains how these features are implemented. + + + Images + + VIPS images have three dimensions: width, height and bands. Bands usually (though not always) represent colour. These three dimensions can be any size up to 2 ** 31 elements. Every band element in an image has to have the same format. A format is an 8-, 16- or 32-bit int, signed or unsigned, 32- or 64-bit float, and 64- or 128-bit complex. + + + + Regions + + An image can be very large, much larger than the available memory, so you can’t just access pixels with a pointer *. + + + Instead, you read pixels from an image with a region. This is a rectangular sub-area of an image. In C, the API looks like: + + +VipsImage *image = vips_image_new_from_file( filename, NULL ); +VipsRegion *region = vips_region_new( image ); + +// ask for a 100x100 pixel region at 0x0 (top left) +VipsRect r = { left: 0, top: 0, width: 100, height: 100 }; +if( vips_region_prepare( region, &r ) ) + vips_error( ... ); + +// get a pointer to the pixel at x, y, where x, y must +// be within the region + +// as long as you stay within the valid area for the region, +// you can address pixels with regular pointer arithmetic + +// compile with -DDEBUG and the macro will check bounds for you + +// add VIPS_REGION_LSKIP() to move down a line +VipsPel *pixel = VIPS_REGION_ADDR( region, x, y ); + +// you can call vips_region_prepare() many times + +// everything in libvips is a GObject ... when you're done, +// just free with +g_object_unref( region ); + + + The action that vips_region_prepare() takes varies with the type of image. If the image is a file on disc, for example, then VIPS will arrange for a section of the file to be read in. + + + (* there is an image access mode where you can just use a pointer, but it’s rarely used) + + + + Partial images + + A partial image is one where, instead of storing a value for each pixel, VIPS stores a function which can make any rectangular area of pixels on demand. + + + If you use vips_region_prepare() on a region created on a partial image, VIPS will allocate enough memory to hold the pixels you asked for and use the stored function to calculate values for just those pixels *. + + + The stored function comes in three parts: a start function, a generate function and a stop function. The start function creates a state, the generate function uses the state plus a requested area to calculate pixel values and the stop function frees the state again. Breaking the stored function into three parts is good for SMP scaling: resource allocation and synchronisation mostly happens in start functions, so generate functions can run without having to talk to each other. + + + VIPS makes a set of guarantees about parallelism that make this simple to program. Start and stop functions are mutually exclusive and a state is never used by more than one generate. In other words, a start / generate / generate / stop sequence works like a thread. + +
+ + + + + + +
+ + (* in fact VIPS keeps a cache of calculated pixel buffers and will return a pointer to a previously-calculated buffer if it can) + +
+ + Operations + + VIPS operations read input images and write output images, performing some transformation on the pixels. When an operation writes to an image the action it takes depends upon the image type. For example, if the image is a file on disc then VIPS will start a data sink to stream pixels to the file, or if the image is a partial one then it will just attach start / generate / stop functions. + + + Like most threaded image processing systems, all VIPS operations have to be free of side-effects. In other words, operations cannot modify images, they can only create new images. This could result in a lot of copying if an operation is only making a small change to a large image so VIPS has a set of mechanisms to copy image areas by just adjusting pointers. Most of the time no actual copying is necessary and you can perform operations on large images at low cost. + + + + Run-time code generation + + VIPS uses Orc, a run-time compiler, to generate code for some operations. For example, to compute a convolution on an 8-bit image, VIPS will examine the convolution matrix and the source image and generate a tiny program to calculate the convolution. This program is then compiled to the vector instruction set for your CPU, for example SSE3 on most x86 processors. + + + Run-time vector code generation typically speeds operations up by a factor of three or four. + + + + Joining operations together + + The region create / prepare / prepare / free calls you use to get pixels from an image are an exact parallel to the start / generate / generate / stop calls that images use to create pixels. In fact, they are the same: a region on a partial image holds the state created by that image for the generate function that will fill the region with pixels. + +
+ + + + + + +
+ + VIPS joins image processing operations together by linking the output of one operation (the start / generate / stop sequence) to the input of the next (the region it uses to get pixels for processing). This link is a single function call, and very fast. Additionally, because of the the split between allocation and processing, once a pipeline of operations has been set up, VIPS is able to run without allocating and freeing memory. + + + This graph (generated by vipsprofile, the vips profiler) shows memory use over time for a vips pipeline running on a large image. The bottom trace shows total memory, the upper traces show threads calculating useful results (green), threads blocked on synchronisation (red) and memory allocations (white ticks). + +
+ + + + + + +
+ + Because the intermediate image is just a small region in memory, a pipeline of operations running together needs very little RAM. In fact, intermediates are small enough that they can fit in L2 cache on most machines, so an entire pipeline can run without touching main memory. And finally, because each thread runs a very cheap copy of just the writeable state of the entire pipeline, threads can run with few locks. VIPS needs just four lock operations per output tile, regardless of the pipeline length or complexity. + +
+ + Data sources + + VIPS has data sources which can supply pixels for processing from a variety of sources. VIPS can stream images from files in VIPS native format, from tiled TIFF files, from binary PPM/PGM/PBM/PFM, from Radiance (HDR) files, from FITS images and from tiled OpenEXR images. VIPS will automatically unpack other formats to temporary disc files for you but this can obviously generate a lot of disc traffic. It also has a special sequential mode for streaming operations on non-random-access formats. A post on the libvips blog explains how libvips opens a file. One of the sources uses the ImageMagick (or optionally GraphicsMagick) library, so VIPS can read any image format that these libraries can read. + + + VIPS images are held on disc as a 64-byte header containing basic image information like width, height, bands and format, then the image data as a single large block of pixels, left-to-right and top-to-bottom, then an XML extension block holding all the image metadata, such as ICC profiles and EXIF blocks. + + + When reading from a large VIPS image (or any other format with the same structure on disc, such as binary PPM), VIPS keeps a set of small rolling windows into the file, some small number of scanlines in size. As pixels are demanded by different threads VIPS will move these windows up and down the file. As a result, VIPS can process images much larger than RAM, even on 32-bit machines. + + + + Data sinks + + In a demand-driven system, something has to do the demanding. VIPS has a variety of data sinks that you can use to pull image data though a pipeline in various situations. There are sinks that will build a complete image in memory, sinks to draw to a display, sinks to loop over an image (useful for statistical operations, for example) and sinks to stream an image to disc. + + + The disc sink looks something like this: + +
+ + + + + + +
+ + The sink keeps two buffers*, each as wide as the image. It starts threads as rapidly as it can up to the concurrency limit, filling each buffer with tiles** of calculated pixels, each thread calculating one tile at once. A separate background thread watches each buffer and, as soon as the last tile in a buffer finishes, writes that complete set of scanlines to disc using whatever image write library is appropriate. VIPS can write with libjpeg, libtiff, libpng and others. It then wipes the buffer and repositions it further down the image, ready for the next set of tiles to stream in. + + + These features in combination mean that, once a pipeline of image processing operations has been built, VIPS can run almost lock-free. This is very important for SMP scaling: you don’t want the synchronization overhead to scale with either the number of threads or the complexity of the pipeline of operations being performed. As a result, VIPS scales almost linearly with increasing numbers of threads: + +
+ + + + + + +
+ + Number of CPUs is on the horizontal axis, speedup is on the vertical axis. Taken from the [[Benchmarks]] page. + + + (* there can actually be more than one, it allocate enough buffers to ensure that there are at least two tiles for every thread) + + + (** tiles can be any shape and size, VIPS has a tile hint system that operations use to tell sinks what tile geometry they prefer) + +
+ + Operation cache + + Because VIPS operations are free of side-effects*, you can cache them. Every time you call an operation, VIPS searches the cache for a previous call to the same operation with the same arguments. If it finds a match, you get the previous result again. This can give a huge speedup. + + + By default, VIPS caches the last 1,000 operation calls. You can also control the cache size by memory use or by files opened. + + + (* Some vips operations DO have side effects, for example, vips_draw_circle() will draw a circle on an image. These operations emit an invalidate signal on the image they are called on and this signal makes all downstream operations and caches drop their contents.) + + + + Operation database and APIs + + VIPS has around 300 image processing operations written in this style. Each operation is a GObject class. You can use the standard GObject calls to walk the class hierarchy and discover operations, and libvips adds a small amount of extra introspection metadata to handle things like optional arguments. + + + The C API is a set of simple wrappers which create class instances for you. The C++ API is a little fancier and adds things like automatic object lifetime management. The command-line interface uses introspection to run any vips operation in the class hierarchy. + + + The Python API is built on top of gobject-introspection. It is written in Python, so as long as you can get gobject-introspection working, you should be able to use vips. It supports python2 and python3 and works on Linux, OS X and Windows. + + + + Snip + + The VIPS GUI, nip2, has its own scripting language called Snip. Snip is a lazy, higher-order, purely functional, object oriented language. Almost all of nip2’s menus are implemented in it, and nip2 workspaces are Snip programs. + + + VIPS operations listed in the operation database appear as Snip functions. For example, abs can be used from Snip as: + + +// absolute value of image b +a = vips_call "abs" [b] []; + + + However, abs won’t work on anything except the primitive vips image type. It can’t be used on any class, or list or number. Definitions in _stdenv.dev wrap each VIPS operation as a higher level Snip operation. For example: + + +abs x + = oo_unary_function abs_op x, is_class x + = vips_call "abs" [x] [], is_image x + = abs_cmplx x, is_complex x + = abs_num x, is_real x + = abs_list x, is_real_list x + = abs_list (map abs_list x), is_matrix x + = error (_ "bad arguments to " ++ "abs") +{ + abs_op = Operator "abs" abs Operator_type.COMPOUND false; + + abs_list l = (sum (map square l)) ** 0.5; + + abs_num n + = n, n >= 0 + = -n; + + abs_cmplx c = ((re c)**2 + (im c)**2) ** 0.5; +} + + + This defines the behaviour of abs for the base Snip types (number, list, matrix, image and so on), then classes will use that to define operator behaviour on higher-level objects. + + + Now you can use: + + +// absolute value of anything +a = abs b; + + + and you ought to get sane behaviour for any object, including things like the Matrix class. + + + You can write Snip classes which present functions to the user as menu items. For example, Math.def has this: + + +Math_arithmetic_item = class + Menupullright "_Arithmetic" "basic arithmetic for objects" { + + Absolute_value_item = class + Menuaction "A_bsolute Value" "absolute value of x" { + action x = map_unary abs x; + } +} + + + Now the user can select an object and click Math / Abs to find the absolute value of that object. + + + + +
diff --git a/doc/Makefile.am b/doc/Makefile.am index 8284af6a..77c58cc8 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -141,7 +141,9 @@ HTML_IMAGES = \ # Our markdown source files markdown_content_files = \ - How-it-works.md + How-it-works.md \ + Using-vipsthumbnail.md \ + Making-image-pyramids.md # converted to xml in this dir by pandoc markdown_content_files_docbook = $(markdown_content_files:.md=.xml) diff --git a/doc/Making-image-pyramids.md b/doc/Making-image-pyramids.md new file mode 100644 index 00000000..084ed2d5 --- /dev/null +++ b/doc/Making-image-pyramids.md @@ -0,0 +1,191 @@ +libvips includes `vips_dzsave()`, an operation that can build image pyramids +compatible with [DeepZoom](http://en.wikipedia.org/wiki/Deep_Zoom), Zoomify +and [Google Maps](https://developers.google.com/maps/) image viewers. It's +fast and can generate pyramids for large images using only a small amount +of memory. + +The TIFF writer, `vips_tiffsave()` can also build tiled pyramidal TIFF images, +but that's very simple to use. This page concentrates on the DeepZoom builder. + +Run dzsave with no arguments to see a summary: + +``` +$ vips dzsave +save image to deepzoom file +usage: + dzsave in filename +where: + in - Image to save, input VipsImage + filename - Filename to save to, input gchararray +optional arguments: + basename - Base name to save to, input gchararray + 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 +operation flags: sequential nocache +``` + +You can also call `vips_dzsave()` from any language with a libvips binding, or +by using `.dz` or `.szi` as an output file suffix. + +### Writing [DeepZoom](http://en.wikipedia.org/wiki/Deep_Zoom) 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](https://developers.google.com/maps/) pyramids + +Use `--layout google` to write Google maps-style pyramids. These are +compatible with the [NYU Pathology pyramid +builder](http://code.google.com/p/virtualmicroscope/wiki/SlideTiler). +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. + +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. You can use `.szi` as a suffix to +enable zip output as well. + +Use `--properties` to output an XML file called `vips-properties.xml`. This +contains a dump of all the metadata vips has about the image as a set of +name-value pairs. It's handy with openslide image sources. + +#### Preprocessing images + +You can use `.dz` as a filename suffix, meaning send the image to +`vips_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 `vips_dzsave()` to work. + + diff --git a/doc/Making-image-pyramids.xml b/doc/Making-image-pyramids.xml new file mode 100644 index 00000000..13f4952c --- /dev/null +++ b/doc/Making-image-pyramids.xml @@ -0,0 +1,188 @@ + + + + + + Making-image-pyramids.md + 3 + libvips + + + + libvips + Making-image-pyramids.md + + + + + libvips includes vips_dzsave(), an operation that can build image pyramids compatible with DeepZoom, Zoomify and Google Maps image viewers. It’s fast and can generate pyramids for large images using only a small amount of memory. + + + The TIFF writer, vips_tiffsave() can also build tiled pyramidal TIFF images, but that’s very simple to use. This page concentrates on the DeepZoom builder. + + + Run dzsave with no arguments to see a summary: + + +$ vips dzsave +save image to deepzoom file +usage: + dzsave in filename +where: + in - Image to save, input VipsImage + filename - Filename to save to, input gchararray +optional arguments: + basename - Base name to save to, input gchararray + 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 +operation flags: sequential nocache + + + You can also call vips_dzsave() from any language with a libvips binding, or by using .dz or .szi as an output file suffix. + + + Writing <ulink url="http://en.wikipedia.org/wiki/Deep_Zoom">DeepZoom</ulink> 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 <ulink url="https://developers.google.com/maps/">Google Maps</ulink> 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. + + + 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. You can use .szi as a suffix to enable zip output as well. + + + Use --properties to output an XML file called vips-properties.xml. This contains a dump of all the metadata vips has about the image as a set of name-value pairs. It’s handy with openslide image sources. + + + + Preprocessing images + + You can use .dz as a filename suffix, meaning send the image to vips_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 vips_dzsave() to work. + + + + + + diff --git a/doc/Using-vipsthumbnail.md b/doc/Using-vipsthumbnail.md new file mode 100644 index 00000000..91391338 --- /dev/null +++ b/doc/Using-vipsthumbnail.md @@ -0,0 +1,342 @@ + +libvips has shipped with a handy thumbnail maker for a while now. I +thought a post of tips and tricks might be useful. Scroll all the way to +the bottom for a summary and recommended usage. + +### Why use vipsthumbnail?  + +It’s fast and uses little memory. For example, here’s ImageMagick with +`wtc.tif`, a 10,000 x 10,000 pixel RGB tiff image: + +``` +$ time convert wtc.tif -resize 128 tn_wtc.jpg +peak RSS: 705m +real 0m2.639s +user 0m4.036s +sys 0m0.516s +``` + +And here’s `vipsthumbnail`: + +``` +$ time vipsthumbnail wtc.tif +peak RSS: 52mb +real 0m0.239s +user 0m0.168s +sys 0m0.072s +``` + +So `vipsthumbnail` +is about 11 times faster and needs 1 / 13th of the memory. + +`vipsthumbnail` +and `convert` +are using the same downsizing algorithm: a fast box filter for +large-scale reduction, and a high-quality lanczos3 interpolator for the +final 200%. + +You see similar improvements with png images, but much less with jpeg. +This is because libjpeg includes support for shrink-during-load, so the +image processing system has much less effect. + +``` +$ time convert -define jpeg:size=256x256 wtc.jpg -resize 128 +tn_wtc.jpg +peak rss: 19mb +real 0m0.259s +user 0m0.284s +sys 0m0.004s +$ time vipsthumbnail wtc.jpg +peak rss: 30mb +real 0m0.268s +user 0m0.256s +sys 0m0.016s +``` + + +The `define` argument makes `convert` +load the image at twice the target size, then use a high-quality +downsampler to get to the exact output dimensions. If you don’t leave +this headroom you can get bad aliasing artifacts. `vipsthumbnail` +does exactly this automatically. + +At larger output sizes you start to see a difference, since there are +actually some pixels being processed: + +``` +$ time convert -define jpeg:size=4000x4000 wtc.jpg -resize 2000 +tn_wtc.jpg +peak rss: 285mb +real 0m1.126s +user 0m2.508s +sys 0m0.240s +$ time vipsthumbnail wtc.jpg -s 2000 +peak rss: 47mb +real 0m0.499s +user 0m0.928s +sys 0m0.028s +``` + +### libvips options + +`vipsthumbnail` supports the usual range of vips command-line options. A +few of them are useful: + +`--vips-cache-trace` shows each operation as libvips starts it. It can be +handy to see exactly what operations `vipsthumbnail` is running for you. + +`--vips-leak` turns on the libvips memory leak checker. As well as reporting +leaks (hopefully there are none) it also tracks and reports peak memory use. + +`--vips-progress` runs a progress indicator during computation. It can be +useful to see where libvips is looping and how often. + +`--vips-info` shows a higher level view of the operations that `vipsthumbnail` +is running.  + +### Looping + +vipsthumbnail can process many images in one operation. For example: + +``` +$ vipsthumbnail *.jpg +``` + +will make a thumbnail for every jpeg in the current directory.  See the +**Output directory** section below to see how to change where thumbnails +are written. + +`vipsthumbnail` will process images one after the other. You can get a good +speedup by running several `vipsthumbnail`s in parallel, depending on how +much load you want to put on your system. + +### Thumbnail size + +You can set the bounding box of the generated thumbnail with the `--size` +option. For example: + +``` +$ vipsthumbnail shark.jpg --size 200x100 +``` + +Use a single number to set a square bounding box. You can omit either number +but keep the x to mean resize just based on that axis, for example: + +``` +$ vipsthumbnail shark.jpg --size 200x +``` + +Will resize to 200 pixels across, no matter what the height of the input image +is. + +You can append `<` or `>` to mean only resize if the image is smaller or larger +than the target. + +### Cropping + +`vipsthumbnail` normally shrinks images to fit within the box set by `--size`. +You can use the `--smartcrop` option to crop to fill the box instead. Excess +pixels are trimmed away using the strategy you set. For example: + +``` +$ vipsthumbnail owl.jpg --smartcrop attention -s 128 +``` + +Where `owl.jpg` is an off-centre composition: + +![](owl.jpg) + +Gives this result: + +![](tn_owl.jpg) + +First it shrinks the image to get the vertical axis to 128 pixels, then crops +down to 128 pixels across using the `attention` strategy. This one searches +the image for features which might catch a human eye, see `vips_smartcrop()` +for details. + +### Linear light + +Shrinking images involves combining many pixels into one. Arithmetic +averaging really ought to be in terms of the number of photons, but (for +historical reasons) the values stored in image files are usually related +to the voltage that should be applied to a CRT electron gun. + +`vipsthumbnail` has an option to perform image shrinking in linear space, that +is, a colourspace where values are proportional to photon numbers. For example: + +``` +$ vipsthumbnail fred.jpg --linear +``` + +The downside is that in linear mode, none of the very fast shrink-on-load +tricks that `vipsthumbnail` normally uses are possible, since the shrinking +done by the image libraries is done at encode time, and done in +terms of CRT voltage, not light. This can make linear light thumbnailing of +large images extremely slow. + +### Output directory + +You set the thumbnail write parameters with the `-o` +option. This is a pattern which the input filename is pasted into to +produce the output filename. For example: + +``` +$ vipsthumbnail fred.jpg jim.tif -o tn_%s.jpg +``` + +For each of the files to be thumbnailed, `vipsthumbnail` +will drop the extension (`.jpg` and `.tif` +in this case) and then substitute the name into the `-o` +option, replacing the `%s` +So this example will write thumbnails to `tn_fred.jpg` and `tn_jim.jpg`. + +If the pattern given to `-o` +is an absolute path, any path components are dropped from the input +filenames. This lets you write all of your thumbnails to a specific +directory, if you want. For example: + +``` +$ vipsthumbnail fred.jpg ../jim.tif -o /mythumbs/tn_%s.jpg +``` + +Now both thumbnails will be written to `/mythumbs`, +even though the source images are in different directories. + +Conversely, if `-o` +is set to a relative path, any path component from the input file is +prepended. For example: + +``` +$ vipsthumbnail fred.jpg ../jim.tif -o mythumbs/tn_%s.jpg +``` + +Now both input files will have thumbnails written to a subdirectory of +their current directory. + +### Output format and options + +You can use `-o` +to specify the thumbnail image format too. For example:  + +``` +$ vipsthumbnail fred.jpg ../jim.tif -o tn_%s.png +``` + +Will write thumbnails in PNG format. + +You can give options to the image write operation as a list of +comma-separated arguments in square brackets. For example: + +``` +$ vipsthumbnail fred.jpg ../jim.tif -o > tn_%s.jpg[Q=90,optimize_coding] +``` + +will write jpeg images with quality 90, and will turn on the libjpeg +coding optimizer. + +Check the image write operations to see all the possible options. For +example: + +``` +$ vips jpegsave +save image to jpeg file +usage: + jpegsave in filename +where: + in - Image to save, input VipsImage + filename - Filename to save to, input gchararray +optional arguments: + Q - Q factor, input gint + default: 75 + min: 1, max: 100 + profile - ICC profile to embed, input gchararray + optimize-coding - Compute optimal Huffman coding tables, input gboolean + default: false + interlace - Generate an interlaced (progressive) jpeg, input gboolean + default: false + no-subsample - Disable chroma subsample, input gboolean + default: false + trellis-quant - Apply trellis quantisation to each 8x8 block, input gboolean + default: false + overshoot-deringing - Apply overshooting to samples with extreme values, input gboolean + default: false + optimize-scans - Split the spectrum of DCT coefficients into separate scans, input gboolean + default: false + quant-table - Use predefined quantization table with given index, input gint + default: 0 + min: 0, max: 8 + strip - Strip all metadata from image, input gboolean + default: false + background - Background value, input VipsArrayDouble +``` + +The `strip` option is especially useful. Many image have very large IPCT, ICC or +XMP metadata items embedded in them, and removing these can give a large +saving. + +For example: + +``` +$ vipsthumbnail 42-32157534.jpg +$ ls -l tn_42-32157534.jpg +-rw-r–r– 1 john john 6682 Nov 12 21:27 tn_42-32157534.jpg +``` + +`strip` almost halves the size of the thumbnail: + +``` +$ vipsthumbnail 42-32157534.jpg -o x.jpg[optimize_coding,strip] +$ ls -l x.jpg +-rw-r–r– 1 john john 3600 Nov 12 21:27 x.jpg +``` + +### Colour management + +`vipsthumbnail` will optionally put images through LittleCMS for you. You can +use this to move all thumbnails to the same colour space. All web browsers +assume that images without an ICC profile are in sRGB colourspace, so if +you move your thumbnails to sRGB, you can strip all the embedded profiles. +This can save several kb per thumbnail. + +For example: + +``` +$ vipsthumbnail shark.jpg +$ ls -l tn_shark.jpg +-rw-r–r– 1 john john 7295 Nov  9 14:33 tn_shark.jpg +``` + +Now encode with sRGB and delete any embedded profile: + +``` +$ vipsthumbnail shark.jpg --eprofile /usr/share/color/icc/sRGB.icc --delete +$ ls -l tn_shark.jpg +-rw-r–r– 1 john john 4229 Nov  9 14:33 tn_shark.jpg +``` + +It’ll look identical to a user, but be almost half the size. + +You can also specify a fallback input profile to use if the image has no +embedded one, but this is less useful. + +### Auto-rotate + +Many JPEG files have a hint set in the header giving the image orientation. If +you strip out the metadata, this hint will be lost, and the image will appear +to be rotated. + +If you use the `--rotate` option, `vipsthumbnail` examines the image header and +if there's an orientation tag, applies and removes it. + +### Final suggestion + +Putting all this together, I suggest this as a sensible set of options: + +``` +$ vipsthumbnail fred.jpg \ + --size 128 \ + -o tn_%s.jpg[optimize_coding,strip] \ + --eprofile /usr/share/color/icc/sRGB.icc \ + --rotate +``` diff --git a/doc/images/owl.jpg b/doc/images/owl.jpg new file mode 100644 index 00000000..e1441475 Binary files /dev/null and b/doc/images/owl.jpg differ diff --git a/doc/images/tn_owl.jpg b/doc/images/tn_owl.jpg new file mode 100644 index 00000000..fecbb4d5 Binary files /dev/null and b/doc/images/tn_owl.jpg differ diff --git a/doc/images/x.jpg b/doc/images/x.jpg new file mode 100644 index 00000000..4269c821 Binary files /dev/null and b/doc/images/x.jpg differ diff --git a/doc/libvips-docs.xml.in b/doc/libvips-docs.xml.in index f5a4a430..828859d9 100644 --- a/doc/libvips-docs.xml.in +++ b/doc/libvips-docs.xml.in @@ -40,6 +40,8 @@ + + diff --git a/tools/vipsthumbnail.c b/tools/vipsthumbnail.c index 2a4fe18f..5c5f5f00 100644 --- a/tools/vipsthumbnail.c +++ b/tools/vipsthumbnail.c @@ -156,9 +156,10 @@ static GOptionEntry options[] = { { "linear", 'a', 0, G_OPTION_ARG_NONE, &linear_processing, N_( "process in linear space" ), NULL }, - { "smartcrop", 'c', 0, + { "smartcrop", 'm', 0, G_OPTION_ARG_STRING, &smartcrop_image, - N_( "crop exactly to SIZE" ), NULL }, + N_( "shrink and crop to fill SIZE using STRATEGY" ), + N_( "STRATEGY" ) }, { "rotate", 't', 0, G_OPTION_ARG_NONE, &rotate_image, N_( "auto-rotate" ), NULL },