mostly done examples

This commit is contained in:
John Cupitt 2017-04-04 17:14:17 +01:00
parent f7b01ed930
commit d5858efd74
3 changed files with 198 additions and 160 deletions

View File

@ -9,7 +9,11 @@
<refpurpose>Introduction to `vipsthumbnail`, with examples</refpurpose> <refpurpose>Introduction to `vipsthumbnail`, with examples</refpurpose>
</refnamediv> </refnamediv>
This page shows a few examples of using VIPS from Python. This page shows a few libvips examples using Python. They will work with small syntax
changes in any language with a libvips binding.
The libvips test suite is written in Python and exercises every operation in the API.
It's also a useful source of examples.
# Average a region of interest box on an image # Average a region of interest box on an image
@ -21,205 +25,237 @@ import gi
gi.require_version('Vips', '8.0') gi.require_version('Vips', '8.0')
from gi.repository import Vips from gi.repository import Vips
roix = 10 left = 10
roiy = 10 top = 10
roiw = 64 width = 64
roih = 64 height = 64
image = Vips.Image.new_from_file(sys.argv[1]) image = Vips.Image.new_from_file(sys.argv[1])
roi = image.crop(roix, roiy, roiw, roih) roi = image.crop(left, top, width, height)
print 'average: ', roi.avg() print 'average:', roi.avg()
``` ```
# VIPS and PIL # libvips and numpy
This script moves an image between PIL and VIPS. You can use `Vips.Image.new_from_memory_copy()` to make a vips image from an area of
memory. The memory array needs to be laid out band-interleaved, as a set of scanlines,
with no padding between lines.
``` python This example moves an image from numpy to vips, but it's simple to move the other way
(use `Vips.Image.write_to_memory()`) to to move images into or out of PIL.
```python
#!/usr/bin/python #!/usr/bin/python
import numpy
import scipy.ndimage
import gi
gi.require_version('Vips', '8.0')
from gi.repository import Vips
def np_dtype_to_vips_format(np_dtype):
'''
Map numpy data types to VIPS data formats.
Parameters
----------
np_dtype: numpy.dtype
Returns
-------
gi.overrides.Vips.BandFormat
'''
lookup = {
numpy.dtype('int8'): Vips.BandFormat.CHAR,
numpy.dtype('uint8'): Vips.BandFormat.UCHAR,
numpy.dtype('int16'): Vips.BandFormat.SHORT,
numpy.dtype('uint16'): Vips.BandFormat.USHORT,
numpy.dtype('int32'): Vips.BandFormat.INT,
numpy.dtype('float32'): Vips.BandFormat.FLOAT,
numpy.dtype('float64'): Vips.BandFormat.DOUBLE
}
return lookup[np_dtype]
def np_array_to_vips_image(array):
'''
Convert a `numpy` array to a `Vips` image object.
Parameters
----------
nparray: numpy.ndarray
Returns
-------
gi.overrides.Vips.image
'''
# Look up what VIPS format corresponds to the type of this np array
vips_format = np_dtype_to_vips_format(array.dtype)
dims = array.shape
height = dims[0]
width = 1
bands = 1
if len(dims) > 1:
width = dims[1]
if len(dims) > 2:
bands = dims[2]
img = Vips.Image.new_from_memory_copy(array.data,
width, height, bands, vips_format)
return img
array = numpy.random.random((10,10))
vips_image = np_array_to_vips_image(array)
print 'avg =', vips_image.avg()
array = scipy.ndimage.imread("test.jpg")
vips_image = np_array_to_vips_image(array)
print 'avg =', vips_image.avg()
vips_image.write_to_file("test2.jpg")
```
# Watermarking
This example renders a simple watermark on an image. Use it like this:
```
./watermark.py somefile.png output.jpg "hello <i>world</i>"
```
The text is rendered in transparent red pixels all over the image. It knows about
transparency, CMYK, and 16-bit images.
```python
#!/usr/bin/python
import sys import sys
import gi
gi.require_version('Vips', '8.0')
from gi.repository import Vips
im = Vips.Image.new_from_file(sys.argv[1], access = Vips.Access.SEQUENTIAL)
text = Vips.Image.text(sys.argv[3], width = 500, dpi = 300)
text = (text * 0.3).cast("uchar")
text = text.embed(100, 100, text.width + 200, text.width + 200)
text = text.replicate(1 + im.width / text.width, 1 + im.height / text.height)
text = text.crop(0, 0, im.width, im.height)
from vipsCC import * # we want to blend into the visible part of the image and leave any alpha
import Image # channels untouched ... we need to split im into two parts
# try this 1,000 times and check for leaks # 16-bit images have 65535 as white
for i in range (0,1000): if im.format == Vips.BandFormat.USHORT:
vim = VImage.VImage (sys.argv[1]) white = 65535
else:
white = 255
# do some processing in vips ... cut out a small piece of image # guess how many bands from the start of im contain visible colour information
vim = vim.extract_area (500, 500, 100, 100) if im.bands >= 4 and im.interpretation == Vips.Interpretation.CMYK:
# cmyk image ... put the white into the magenta channel
n_visible_bands = 4
text_colour = [0, white, 0, 0]
elif im.bands >= 3:
# colour image ... put the white into the red channel
n_visible_bands = 3
text_colour = [white, 0, 0]
else:
# mono image
n_visible_bands = 1
text_colour = white
# make a PIL image # split into image and alpha
# we use Image.frombuffer (), so PIL is using vim's memory if im.bands - n_visible_bands > 0:
# you need to be very careful not to destroy vim until you're done with pim alpha = im.extract_band(n_visible_bands, n = im.bands - n_visible_bands)
# ideally you should make a proxy class that wraps this lifetime problem up im = im.extract_band(0, n = n_visible_bands)
mode = VImage.PIL_mode_from_vips (vim) else:
size = (vim.Xsize (), vim.Ysize ()) alpha = None
data = vim.tobuffer ()
pim = Image.frombuffer (mode, size, data, 'raw', mode, 0, 1)
# rotate 12 degrees with PIL # blend means do a smooth fade using the 0 - 255 values in the condition channel
pim = pim.rotate (12, Image.BILINEAR, 1) # (test in this case) ... this will render the anit-aliasing
im = text.ifthenelse(text_colour, im, blend = True)
# back to vips again # reattach alpha
# PIL doesn't have a tobuffer method, so we have to use tostring to copy the if alpha:
# data out of PIL and then fromstring to copy back into VIPS im = im.bandjoin(alpha)
str = pim.tostring ()
bands, format, type = VImage.vips_from_PIL_mode (pim.mode) im.write_to_file(sys.argv[2])
width, height = pim.size
vim2 = VImage.VImage.fromstring (str, width, height, bands, format)
# finally write from vips
vim2.write (sys.argv[2])
``` ```
Leak testing # Build huge image mosaic
------------
This loads an image, does some simple processing, and saves again. Handy for leak testing. This makes a 100,000 x 100,000 black image, then inserts all the images you pass on the
command-line into it at random positions. libvips is able to run this program in
sequential mode: it'll open all the input images at the same time, and stream pixels from
them as it needs them to generate the output.
``` python To test it, first make a large 1-bit image. This command will take the green channel and
#!/usr/bin/python write as a 1-bit fax image. `wtc.jpg` is a test 10,000 x 10,000 jpeg:
import sys ```
$ vips extract_band wtc.jpg x.tif[squash,compression=ccittfax4,strip] 1
# just need this for leaktesting
import gc
from vipsCC import *
if len (sys.argv) != 3:
print 'usage:', sys.argv[0], 'inputimage outputimage'
sys.exit (1)
try:
a = VImage.VImage (sys.argv[1])
b = a.invert ()
c = b.lin ([1,2,3],[4,5,6])
m = VMask.VIMask (3, 3, 1, 0,
[-1, -1, -1,
-1, 8, -1,
-1, -1, -1])
d = a.conv (m)
d.write (sys.argv[2])
except VError.VError, e:
e.perror (sys.argv[0])
# we can get properties of VImage too
print 'inputimage is', a.Xsize (), 'pixels across'
print 'starting shutdown ...'
del b
del a
del c
del d
del m
# sometimes have to do several GCs to get them all, not sure why
for i in range(10):
gc.collect ()
print 'shutdown!'
print 'leaked IMAGEs:'
VImage.im__print_all ()
print 'done ... hopefully you saw no leaks'
``` ```
Build image mosaic Now make 1,000 copies of that image in a subdirectory:
------------------
This loads a lot of images (RGB or greyscale) and pastes them at random positions in a 10,000 by 10,000 pixel output image. 8-bit only, but it'd be easy to change that. ```
$ mkdir test
$ for i in {1..1000}; do cp x.tif test/$i.tif; done
```
And run this Python program on them:
```
$ time ./try255.py x.tif[squash,compression=ccittfax4,strip,bigtif] test/*
real 1m59.924s
user 4m5.388s
sys 0m8.936s
```
It completes in just under two minutes on this laptop, and needs about
7gb of RAM to run. It would need about the same amount of memory for a
full-colour RGB image, I was just keen to keep disc usage down.
If you wanted to handle transparency, or if you wanted mixed CMYK and RGB images, you'd
need to do some more work to convert them all into the same colourspace before
inserting them.
``` python ``` python
#!/usr/bin/python #!/usr/bin/env python
import sys import sys
import random import random
from vipsCC import *
# the size of the image we build import gi
size = 10000 gi.require_version('Vips', '8.0')
from gi.repository import Vips
try: # turn on progress reporting
if len(sys.argv) < 3: Vips.progress_set(True)
print 'usage:', sys.argv[0], 'outfile infile1 ...'
sys.exit (1)
# make the background image # this makes a 8-bit, mono image of 100,000 x 100,000 pixels, each pixel zero
bg = VImage.VImage.black (size, size, 3) im = Vips.Image.black(100000, 100000)
# paste each argument in for filename in sys.argv[2:]:
for file in sys.argv[2:]: tile = Vips.Image.new_from_file(filename, access = Vips.Access.SEQUENTIAL)
im = VImage.VImage (file)
# is this a mono image? convert to RGB by joining three of them im = im.insert(tile,
# together random.randint(0, im.width - tile.width),
if im.Bands() == 1: random.randint(0, im.height - tile.height))
im = im.bandjoin (im).bandjoin (im)
x = random.randint (0, size - im.Xsize () - 1) im.write_to_file(sys.argv[1])
y = random.randint (0, size - im.Ysize () - 1)
bg = bg.insert_noexpand (im, x, y)
# write result
bg.write (sys.argv[1])
except VError.VError, e:
e.perror (sys.argv[0])
``` ```
Build image pyramid # Rename DICOM images using header fields
-------------------
This makes a tiled image pyramid, with each tile in a separate 512x512 pixel file. DICOM images commonly come in an awful directory hierarchy named as something
like `images/a/b/e/z04`. There can be thousands of files and it can be very
hard to find the one you want.
``` python This utility copies files to a single flat directory, naming them using
#!/usr/bin/python fields from the DICOM header. You can actually find stuff! Useful.
import sys
from vipsCC import *
tilesize = 512
maxlevel = 100
try:
im = VImage.VImage (sys.argv[1])
for level in range (maxlevel, -1, -1):
print "Creating tiles for level", level
# loop to create the tiles
for y in range (0, im.Ysize(), tilesize):
for x in range (0, im.Xsize(), tilesize):
filename = '%dx%d_y%d.jpg' % (level, x / tilesize, y / tilesize)
# clip tilesize against image size
width = min (im.Xsize () - x, tilesize)
height = min (im.Ysize () - y, tilesize)
# expand edge tiles up to the full tilesize ... Google maps likes this
# im.extract_area (x, y, width, height).embed(0, 0, 0, tilesize, tilesize).write(filename)
# let edge tiles be smaller than the full tile size, tiff tiling prefers this
im.extract_area (x, y, width, height).write (filename)
# was there only a single tile? we are done
if im.Xsize() <= tilesize and im.Ysize() <= tilesize:
break
# create next pyramid level in RAM
shrink = im.rightshift_size (1, 1, im.BandFmt())
im = shrink.write (VImage.VImage ("temp", "t"))
except VError.VError, e:
e.perror (sys.argv[0])
```
Rename DICOM images using header fields
---------------------------------------
DICOM images commonly come in an awful directory hierarchy named as something like images/a/b/e/z04. There can be thousands of files and it can be very hard to find the one you want.
This utility copies files to a single flat directory, naming them using fields from the DICOM header. You can actually find stuff! Useful.
``` python ``` python
#!/usr/bin/python #!/usr/bin/python

View File

@ -146,6 +146,7 @@ markdown_content_files = \
How-it-works.md \ How-it-works.md \
Using-vipsthumbnail.md \ Using-vipsthumbnail.md \
How-it-opens-files.md \ How-it-opens-files.md \
Examples.md \
Making-image-pyramids.md Making-image-pyramids.md
# converted to xml in this dir by pandoc # converted to xml in this dir by pandoc

View File

@ -43,6 +43,7 @@
<xi:include href="xml/How-it-opens-files.xml"/> <xi:include href="xml/How-it-opens-files.xml"/>
<xi:include href="xml/Making-image-pyramids.xml"/> <xi:include href="xml/Making-image-pyramids.xml"/>
<xi:include href="xml/Using-vipsthumbnail.xml"/> <xi:include href="xml/Using-vipsthumbnail.xml"/>
<xi:include href="xml/Examples.xml"/>
</chapter> </chapter>
<chapter> <chapter>