mostly done examples
This commit is contained in:
parent
f7b01ed930
commit
d5858efd74
356
doc/Examples.md
356
doc/Examples.md
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user