From f7b01ed93004c65680aa7e94da5c92565c93729d Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 4 Apr 2017 10:13:10 +0100 Subject: [PATCH] started examples page --- TODO | 2 + doc/Examples.md | 297 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 299 insertions(+) create mode 100644 doc/Examples.md diff --git a/TODO b/TODO index 36f83c6e..7aa1f7ac 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,5 @@ +- add analytics tags to docs output + - not sure about utf8 error messages on win - strange: diff --git a/doc/Examples.md b/doc/Examples.md new file mode 100644 index 00000000..9d4d0d71 --- /dev/null +++ b/doc/Examples.md @@ -0,0 +1,297 @@ + + Using `vipsthumbnail` + 3 + libvips + + + + `vipsthumbnail` + Introduction to `vipsthumbnail`, with examples + + +This page shows a few examples of using VIPS from Python. + +# Average a region of interest box on an image + +``` python +#!/usr/bin/env python + +import sys +import gi +gi.require_version('Vips', '8.0') +from gi.repository import Vips + +roix = 10 +roiy = 10 +roiw = 64 +roih = 64 + +image = Vips.Image.new_from_file(sys.argv[1]) +roi = image.crop(roix, roiy, roiw, roih) +print 'average: ', roi.avg() +``` + +# VIPS and PIL + +This script moves an image between PIL and VIPS. + +``` python +#!/usr/bin/python + +import sys + +from vipsCC import * +import Image + +# try this 1,000 times and check for leaks +for i in range (0,1000): + vim = VImage.VImage (sys.argv[1]) + + # do some processing in vips ... cut out a small piece of image + vim = vim.extract_area (500, 500, 100, 100) + + # make a PIL image + # we use Image.frombuffer (), so PIL is using vim's memory + # you need to be very careful not to destroy vim until you're done with pim + # ideally you should make a proxy class that wraps this lifetime problem up + mode = VImage.PIL_mode_from_vips (vim) + size = (vim.Xsize (), vim.Ysize ()) + data = vim.tobuffer () + pim = Image.frombuffer (mode, size, data, 'raw', mode, 0, 1) + + # rotate 12 degrees with PIL + pim = pim.rotate (12, Image.BILINEAR, 1) + + # back to vips again + # PIL doesn't have a tobuffer method, so we have to use tostring to copy the + # data out of PIL and then fromstring to copy back into VIPS + str = pim.tostring () + bands, format, type = VImage.vips_from_PIL_mode (pim.mode) + width, height = pim.size + vim2 = VImage.VImage.fromstring (str, width, height, bands, format) + + # finally write from vips + vim2.write (sys.argv[2]) +``` + +Leak testing +------------ + +This loads an image, does some simple processing, and saves again. Handy for leak testing. + +``` python +#!/usr/bin/python + +import sys + +# 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 +------------------ + +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. + +``` python +#!/usr/bin/python + +import sys +import random +from vipsCC import * + +# the size of the image we build +size = 10000 + +try: + if len(sys.argv) < 3: + print 'usage:', sys.argv[0], 'outfile infile1 ...' + sys.exit (1) + + # make the background image + bg = VImage.VImage.black (size, size, 3) + + # paste each argument in + for file in sys.argv[2:]: + im = VImage.VImage (file) + + # is this a mono image? convert to RGB by joining three of them + # together + if im.Bands() == 1: + im = im.bandjoin (im).bandjoin (im) + + x = random.randint (0, size - im.Xsize () - 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 +------------------- + +This makes a tiled image pyramid, with each tile in a separate 512x512 pixel file. + +``` python +#!/usr/bin/python + +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 +#!/usr/bin/python + +import sys +import re +import os +import shutil + +from vipsCC import * + +if len (sys.argv) != 3: + print 'rename DICOM files using tags from the header' + print 'usage:' + print '\t%s srcdir destdir' % sys.argv[0] + print 'the directory tree below srcdir is searched, all files are' + print 'renamed and put into destdir in a flat list' + sys.exit (1) + +srcdir = sys.argv[1] +destdir = sys.argv[2] + +if not os.access (destdir, os.F_OK | os.R_OK | os.W_OK | os.X_OK): + os.mkdir (destdir) + +def get_field (vim, field): + result = vim.meta_get_string (field) + + # remove any \n etc. + result = re.sub ("\n", "", result) + + # remove any leading or trailing spaces + result = re.sub (" $", "", result) + result = re.sub ("^ ", "", result) + + return result + +id_name = "magick-dcm:Patient'sID" +modality_name = "magick-dcm:Modality" +series_name = "magick-dcm:SeriesNumber" +instance_name = "magick-dcm:Instance(formerlyImage)Number" +date_name = "magick-dcm:ImageDate" + +n_processed = 0 + +for (dirpath, dirnames, filenames) in os.walk (srcdir): + for file in filenames: + path = os.path.join (dirpath, file) + + try: + vim = VImage.VImage (path) + except VError.VError, e: + print 'unable to open', path + continue + + try: + id = get_field (vim, id_name) + modality = get_field (vim, modality_name) + series = get_field (vim, series_name) + instance = get_field (vim, instance_name) + date = get_field (vim, date_name) + except VError.VError, e: + print 'unable to get fields from header', path + continue + + match = re.match ("(\d\d\d\d)(\d\d)(\d\d)", date) + date = match.group (1) + "." + match.group (2) + "." + match.group (3) + newname = id + "." + modality + "." + series + "." + instance + "." + date + ".IMA" + + shutil.copyfile(path, os.path.join (destdir, newname)) + + n_processed += 1 + +print '\t(%d files processed)' % n_processed +```