This is the first tutorial about Python and GeeXLab. The purpose of this series is to do an overview of the Python libraries that may be useful for GeeXLab demos AND that work with GeeXLab.

GeeXLab has a very simple way of working. Roughly speaking, a demo is made up of an initialization script (INIT, executed once) and a per frame script (FRAME, executed every frame). These scripts can be programmed either in Lua or in Python. In a script you can code what you want with both languages. There is no restriction: GeeXLab can be seen as a virtual machine for Lua and Python. That’s why, most the Python packages available will work with GeeXLab.

And to interact with GeeXLab scene data (textures, GLSL shaders, meshes, etc.) there is the Host-API which is simply GeeXLab API for Lua and Python. GeeXLab Host-API functions description is available HERE.

This quick description of GeeXLab scripting is important for this series of tutorials about Python libraries because I’m going to focuse on the functionalities offered by those libraries and I’ll try to limit the use of GeeXLab functions to the bare minimum. That way, you will be able to quickly re-use all code snippets in your own python projects.

That said, let’s talk about the first Python lib: PIL.

1 – PIL: Python Imaging Library

PIL or Python Imaging Library is a package that exposes many functions to manipulate images from a Python script. PIL official homepage is HERE. The current version of PIL is PIL 1.1.7 and is available for Python 2.3 up to Python 2.7. I’ll use PIL 1.1.7 for Python 2.6 in this article.

Under Windows (XP, Vista or Seven) the installation of PIL is rather simple: just launch the PIL Windows installer and you’re ok. Of course you need a valid Python 2.6.6 installation before.

PIL documentation is available here:

2 – Loading an image

Here is a small INIT script that uses PIL to load an image and display it. If you need it, PIL version can be found in the Image.VERSION variable.

import HYP_Utils import sys from PIL import Image scriptDir = HYP_Utils.GetDemoDir() PIL_Version = Image.VERSION img_filename = "%s/flower.jpg" % scriptDir im = Image.open(img_filename) im.show()

Under Windows, the Image.show() function saves the image to a temporary file and calls the default image viewer utility. On my system, Irfanview is called to display the image:





Demo_Python_PIL_01.xml

3 – Saving an image

Just call the Image.save() function. You want to save to JPEG format? Just add the .jpg extension to your image filename… Same thing for the other formats.

Formats supported in reading AND writing: *.bmp, *.gif, *.jpg, *.msp, *.pcx, *.png, *.ppm, *.tiff and .xbm.

Here is a simple JPG to BMP converter:

import HYP_Utils from PIL import Image scriptDir = HYP_Utils.GetDemoDir() PIL_Version = Image.VERSION img_filename = "%s/flower.jpg" % scriptDir im = Image.open(img_filename) im.save("%s/flower.bmp" % scriptDir)

4 – Reading the pixels

There are two functions that make it possible to read the pixmap (or pixel data): Image.getpixel() and Image.getdata().

Image.getpixel() returns the value of a single pixel. Just give a tuple with the X and Y coordinates and getpixel() returns a 3-tuple RGB for a RGB image or a single value for a luminance image. Image.getdata() returns the complete pixmap. You need the Python function list() to create the pixmap list of RGB tuples.

Here is a code snippet that loads an image with PIL, creates a texture object with GeeXLab Python API and fills the texture with image pixels.

import HYP_Utils import HYP_Texture import HYP_Material import sys from PIL import Image scriptDir = HYP_Utils.GetDemoDir() PIL_Version = Image.VERSION img_filename = "%s/flower.jpg" % scriptDir im = Image.open(img_filename) imageW = im.size[0] imageH = im.size[1] TEXTURE_2D = 2 RGB_BYTE = 2 texId = HYP_Texture.Create(TEXTURE_2D, RGB_BYTE, imageW, imageH, 0) matId = HYP_Material.GetId("plane1_mat") HYP_Material.AddTexture(matId, texId) if (im.mode == "RGB"): for y in range(0, imageH): for x in range(0, imageW): offset = y*imageW + x xy = (x, y) rgb = im.getpixel(xy) HYP_Texture.SetValueTex2DByteRgb(texId, offset, rgb[0], rgb[1], rgb[2]) elif (imout.mode == "L"): for y in range(0, imageH): for x in range(0, imageW): offset = y*imageW + x xy = (x, y) rgb = im.getpixel(xy) HYP_Texture.SetValueTex2DByteRgb(texId, offset, rgb, rgb, rgb)

With Image.getdata(), the last lines of the previous script would be:

pixels = list(im.getdata()) if (im.mode == "RGB"): for y in range(0, imageH): for x in range(0, imageW): offset = y*imageW + x rgb = pixels[offset] HYP_Texture.SetValueTex2DByteRgb(texId, offset, rgb[0], rgb[1], rgb[2]) elif (imout.mode == "L"): for y in range(0, imageH): for x in range(0, imageW): offset = y*imageW + x rgb = pixels[offset] HYP_Texture.SetValueTex2DByteRgb(texId, offset, rgb, rgb, rgb)



Demo_Python_PIL_02.xml

5 – Image Processing

You can easily apply common image filters with PIL: blur, emboss, sharpen, etc. Just import the ImageFilter module:

from PIL import Image from PIL import ImageFilter ... i = Image.open(img_filename) im = i.filter(ImageFilter.EMBOSS) #im = i.filter(ImageFilter.FIND_EDGES) ...

Predefined filters are: BLUR, CONTOUR, DETAIL, EDGE_ENHANCE, EDGE_ENHANCE_MORE, EMBOSS, FIND_EDGES, SMOOTH, SMOOTH_MORE, and SHARPEN.



Demo_Python_PIL_03.xml – EMBOSS

Demo_Python_PIL_03.xml – FIND_EDGES

There is also a module called ImageOps that exposes image processing functions such as colorize(), flip(), grayscale(), invert(), mirror(), solarize(), or posterize().





Demo_Python_PIL_04.xml – solarise()

Demo_Python_PIL_04.xml – posterize()

6 – Adding a Watermark

ImageDraw and ImageFont give to PIL the capability to write text on an image as well as to draw lines or points. Here is a code snippet that shows a simple batch converter with PIL: it reads all jpg files of a folder, adds the watermark (a cross and the “GEEXLAB” string) and saves the images with the gxl_ prefix.

import HYP_Utils import os, glob from PIL import Image from PIL import ImageDraw from PIL import ImageFont scriptDir = HYP_Utils.GetDemoDir() ft = ImageFont.load("timR24.pil") os.chdir(scriptDir) file_list = glob.glob("*.jpg") for f in file_list: im = Image.open(scriptDir + str(f)) draw = ImageDraw.Draw(im) draw.line((0, 0) + im.size, fill=(255, 255, 255)) draw.line((0, im.size[1], im.size[0], 0), fill=(255, 255, 255)) wh = ft.getsize("G E E X L A B") draw.text((im.size[0]/2 - wh[0]/2, im.size[1]/2 + 20), "G E E X L A B",\ fill=(255, 255, 0), font=ft) draw.text((im.size[0]/2 - wh[0]/2, im.size[1]/2 - 60), "G E E X L A B",\ fill=(255, 255, 0), font=ft) del draw im.save(scriptDir + "gxl_" + str(f))

The file timR24.pil comes from a PIL fonts package. You can download it HERE.



Demo_Python_PIL_05.xml

There is also a nice function ImageFont.truetype() but it didn’t work on my system because this function relies on the _imagingft.pyd library (actually a DLL) that could not be loaded due to a Visual C runtime problem. Here is the error in GeeXLab console:

# ERROR: Python – Script [initScene] has a runtime error. Error line: 29 – Error object: – Error data: DLL load failed: The application has failed to start because its side-by-side configuration is incorrect. Please see the application event log or use the command-line sxstrace.exe tool for more detail.. Script disabled.

This link has some details about this problem.

7 – Downloads

If you want to play with the GeeXLab demos, you can find them in the code samples demo pack available HERE. The demos related to PIL are in the Python_PIL/ folder.

Here is a patch for GeeXLab 0.2.5 + Python 2.6.6 that you must use to run the demos. Just unzip the archive and copy the DLL into GeeXLab_Python_Lua/ folder.

PATCH DOWNLOAD: GeeXLabCore.dll