VIGRA/Interactive Tutorial

Contents

This is a simple sketchpad which shall serve as a minimal introduction into the VIGRA Python bindings.

If you do not know Python yet, I suggest skimming through the official Python Tutorial, which allows you to lern basic Python stuff in maybe 30 minutes. (OTOH, it is really worth reading and is kept up-to-date with new Python features, so reading it carefully from start to end gives you a very good overview of what Python offers.) After the tutorial, you should definitely bookmark the Library Reference, which is the documentation on Python.

How to start VIGRA/interactive

Actually, you can use the VIGRA functionality by simply importing the vigra module within Python. That is (after module add vigra of course, if you work at KOGS) you can start python interactively or non-interactively and try sth. like:

Python 2.4.4 (#1, Feb 15 2007, 18:34:47)
[GCC 4.1.2 20061115 (prerelease) (SUSE Linux)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from vigra import *
>>> img = readImage("example.jpg")
>>> w = showImage(img)

However, the preferred way is to use an advance Python console. I prefer IPython, but beginners should probably try PyTerm first. Simply enter vigra at the command line to start the equivalent of pyterm vigra.

The advantage of PyTerm is that there is a VIGRA menu which helps you in finding the right functions for processing/analysing images:

screenshot of PyTerm with the VIGRA menu opened

Later, I suggest you try IPython, which is a really advanced, flexible, great Python terminal. In order to be able to use functions like showImage which open Qt windows, you need to start ipython with the -qthread commandline switch. I prefer using a "vigra profile": Simply put the following into a new file ~/.ipython/ipythonrc-vigra:

include ipythonrc

import_all math vigra
import_mod Gnuplot

Then, start vigra with:

# ipython -qthread -p vigra

And you can start using the VIGRA functionality rightaway.

BTW: I suggest you make some adjustments in your ~/.ipython/ipythonrc, too, for example:

# I use konsole with a white background -> uncomment the following:
colors LightBG

# I often use stuff from these modules:
import_mod os sys pickle

# I like the ``path`` class for quick filesystem stuff in Python:
import_some path path

Nowadays, this way of changing settings is deprecated (in favor of new files that may contain any Python code), but it works for me(TM), so why should I change it? ;-)

Access to Image Data

Each image offers access to its pixel data via the obvious Python syntax:

pos = (10, 10)
value = img[pos] # read pixel value
img[pos] = newValue # write new value

pos can be a tuple or e.g. a Point2D. Note that Python allows you to write tuples in an abbreviated way:

g = GrayImage(100, 100)
g[10, 10] = 42.5 # for single-band images

rgb = RGBImage(100, 100)
rgb[10, 10] = Pixel(1, 2, 3) # 'Pixel' and 'Vector' are synonyms

grad = gaussianGradientAsVector(g, 1.5)
grad[10, 10] = Vector(12, 23)

Each PythonImage can have any number of bands, e.g. 5 in this example:

img = PythonImage(5, rgb.size())
assert g.bands() == 1
assert rgb.bands() == 3
assert grad.bands() == 2
assert img.bands() == 5

You can get a "view" for a single band of a multi-band image like this:

red = rgb.subImage(0)
red = rgb[0] # shortcut for the above
assert red.bands() == 1

Furthermore, you can access a ROI (region of interest) in the same way:

roi = Rect2D(Point2D(10, 10), Point2D(20, 20))
roiImg = img.subImage(roi)

Rect2D can be initialized from two points or a point and a size:

firstLineRect = Rect2D(Point2D(0, 0), Size2D(img.width(), 1))

Caution!

The Vector objects returned by e.g.

color = rgb[10, 10]

are copies of the pixel values in the image. That means, the following code will not modify the original image:

rgb[10, 10][1] = newGreenValue # rgb is not changed!
# explanation: the line above is equivalent to:
color = rgb[10, 10]
color[1] = newGreenValue

This works however:

rgb[1][10, 10] = newGreenValue
# explanation: the line above is equivalent to:
greenChannel = rgb[1]
greenChannel[10, 10] = newGreenValue

Processing each Pixel

The obvious way to process each pixel is this:

for y in range(img.height()):
    for x in range(img.width()):
        img[x, y] = 255 - img[x, y]

There is also the meshIter function which creates an iterator over all points in a given range:

for p in meshIter(img.size()):
    img[p] = 255 - img[p]

# change only an ROI, assuming roi is a Rect2D object:
for p in meshIter(roi):
    img[p] = 255 - img[p]

Unfortunately, the latter is slower than the first, obviously due to the creation of the Point2D objects.

However, for the above pointwise transformation, you would use transformImage anyways. This is its (formatted) docstring (unfortunately, not many functions have comparable documentation):

The transformImage() function can be used for point transformations involving arbitary many images. Its last argument must be a functor, all other arguments must be images, for example:

result = transformImage(image1, functor)
result = transformImage(image1, image2, functor)
result = transformImage(image1, image2, image3, functor)

The functor may either be an arbitrary callable Python function (including bound member functions and lambda expressions), or a VIGRA functor. In case of a multi-band image, functors are applied band-wise, unless they explicitly require a multi-band argument (such as an indexing functor). For example, you can add two images with arbitrary (but matching) number of bands by means of a lambda expression like this:

result = transformImage(image1, image2, lambda x,y: x + y)

Using Python functions is extremely flexible, but also relatively slow. For faster operation, VIGRA functors are recommended. You create a VIGRA functor (which is evaluated in C++) by enclosing the lambda expression in quotes:

result = transformImage(image1, image2, "lambda x,y: x + y")

The following operations may be used in a VIGRA functor expression:

Arithmetic operators:
+ (unary and binary), - (unary and binary), *, /, %, ** (power)
Logical operations:
==, !=, <, <=, >, >=, &&, and, ||, or, !, not
Algebraic functions:
abs, min, max, pow, sqrt, sq (square), exp, log, log10, sin, asin, cos, acos, tan, atan, atan2, floor, ceil, erf (error function), BrightnessContrast (see below)
Ternary if-then-else operator:
condition ? true-answer : false-answer
Vector operations:
Vector (constructor), [] (index operator), magnitude, norm (same as magnitude), dot (dot product)
Literals:
integer and float numbers
Variables:
if they are in the current scope (see examples below)

Finally, "lambda" can be abbreviated as "\l". Here are some typical usage examples:

# add two images
result = transformImage(image1, image2, "\l x,y: x + y")

# transform a color image into a gray image
# x[0] is the red channel, x[1] the green channel etc.
result = transformImage(image, "\l x: 0.3*x[0] + 0.59*x[1] + 0.11*x[2]")

# threshold an image
threshold = 24
result = transformImage(image, "\l x: x < threshold ? 0 : 255")

# transform the pixels of a two-band image into polar coordinates
result = transformImage(image, "\l x: Vector(sqrt(sq(x[0])+sq(x[1])), atan2(x[1], x[0]))")
# ..the same, this time using the norm() shortcut:
result = transformImage(image, "\l x: Vector(norm(x), atan2(x[1], x[0]))")

# change brightness and contrast of an image.
# A parameter value of 1 means 'no change', If the value is
# between 0 and 1, brightness or contrast are decreased, otherwise
# they are increased.
brightness = 1.4 # make image brighter
contrast = 0.8    # decrease contrast
result = transformImage(image, "\l x: BrightnessContrast(x, brightness, contrast)")

Automatic Type Conversion

There are automatic converters installed which make it possible to call functions which expect e.g. a Size2D object with a Python tuple. The same holds for Point2D, Vector etc.:

mi = meshIter((100, 200)) # pass image size as tuple
rgb[10,10] = (12, 23, 34) # pass pixel value as tuple

Valid XHTML 1.0! Valid CSS!
This page was last modified: Wednesday, May 09, 2007 hacker emblem