ALBULA  4.0.1
ALBULA API

Introduction

ALBULA API is a general purpose library, which lets you manipulate, analyse and visualize Dectris detector images. The core of the framework is the class DImage providing functionality to manipulate detector images. Use DImageSeries in order to gain access to series of image files through DImage objects. Class DImageWriter allows you to write DImage objects to various formats. Please note ALBULA API is not available for Macintosh or Windows.

Getting Started

Prerequisites:

  • Linux: ALBULA API exists only for Linux.
  • Python 3.6 or 3.7 (CPython); needs to be compiled with "––enable-unicode=ucs4" (Default for most Linux distributions)
  • NumPy (>= 1.4)
  • Eventually run the ALBULA installer with the option "––python=</path/to/python_interpreter>". This will additionally install ALBULA into the site-package directory of your Python interpreter. (Optional)

Your python script has to include the module dectris.albula. The module dectris.albula must be imported before any other module that links against libstdc++.so.6. This does not pose any problem for standard python modules. However, third party libraries like e.g. matplotlib or PyQt must be imported after dectris.albula.

By default the module dectris.albula resides in /opt/dectris/albula/<version>/python, so e.g. for version 4.0 do

import sys
sys.path.insert(0,"/opt/dectris/albula/4.0/python") # or wherever you've installed ALBULA

Hint: Inserting the install directory to sys.path is not necessary if the ALBULA installer has been run with the option "––python=</path/to/python_interpreter>".

Hint: If you get ImportError: /usr/lib64/libstdc++.so.6: version ‘GLIBCXX_3.4.15’ not found, this means there has been some module imported before dectris.albula that links against libstdc++.so.6. Import dectris.albula as the first module.

Changes in version 3

Starting from version 3 ALBULA is subject to many non backwards compatible changes as described below:

  • class DImage replaces the classes DImageInt and DImageFloat. class DImage accomodates both integer and float type data. The datatype may be defined by passing to the constructor one of the strings: "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float32", "float64". dataType() returns the datatype string.
  • data() returns a copy of the image data as a numpy array. In earlier versions data() used to return a reference to its data. Passing a numeric numpy array to the constructor is the preferred way create a DImage from a numeric numpy array.
  • class DImageSeries replaces the classes DImageIntContainer, DImageFloatContainer, DHdf5IntContainer and DHdf5FloatContainer. DImageSeries determines automatically the image type (e.g. cbf or hdf5) and data type (e.g. 32 bit integer or 32 bit float).
  • classes DNeXusGroup, DNeXusData and DNeXusAttribute replace the generic class DNeXusNode. You will face no changes if you gain accessed to header data only via class DImageOptionalData.

Access to image series

DImageSeries gives you access to series of image file or hdf5 container files. The images themselves are represented by DImage objects.

import sys
sys.path.insert(0,"/opt/dectris/albula/4.0/python") # or wherever you've installed ALBULA
# In order to access a series of cbf or tif images do open() on an arbitrary image file
series.open("/path/to/dataset/image_01234.cbf")
img = series[12] # access image_00012.cbf, returns DImage object
# analogously with tif images
series.open("/path/to/dataset/image_02344.tif")
# open hdf5 file
series.open("/path/to/hdf_master.h5")

Access to image data

Image data may be accessed via DImage.pixel(x,y), DImage.setPixel(x,y) or as a NumPy array DImage.data(). Note that NumPy arrays are in row major order i.e. the y (x) position is accessed by the first (second) index. Starting from 3.0 DImage.data() returns a copy of the image data. So manipulating the returned numpy array do no longer affect the DImage itself. You may pass a numpy array to the DImage constructor.

img.setPixel(5,8,100) # set pixel at x=5, y=8 to 100
p = img.pixel(5,8) # p = 100
n = img.data() # n is a numpy array in row major order i.e. n[8,5] = 100

Writing images to files

After we did something more or less useful with img we write it back to /tmp/img.tif and /tmp/img.cbf:

# img is an DImage object that has been opened before. See example above.
# write image back to /tmp/img.tif
# write image back to /tmp/img.cbf

Displaying images

The viewer window consists of a DMainFrame that may contain multiple DSubFrames. Create and display a DMainFrame object and open one DSubFrame inside the DMainFrame

s = m.openSubFrame()

An image may be displayed in a DSubFrame by either opening a file

s.loadFile("/data/img_000.cbf")

or by passing a DImage object to the DSubFrame

s.loadImage(img)

Starting from version 2.2 you may achieve the same in one line of code:

mainFrame, subFrame = dectris.albula.display("/data/img_000.cbf")

For further useful commands please refer to the documentation of DSubFrame.

Working with Hdf5 files

Starting from 3.0 there is just one class that gives you access to both Hdf5 container files and series of single image files. Class DImageSeries turns the single frames in hdf5 files into DImage objects. Let's say you have in your current working directory the master file "series_16_master.h5" and mulitple data files "series_16_data_000000.h5", "series_16_data_000001.h5",... The only thing you have to do is to open the master file.

h5cont = dectris.albula.DImageSeries("series_16_master.h5")

The frames of the image series may be accessed via

# loop over the frames and display them in albula
for i in range(h5cont.first(), h5cont.first() + h5cont.size()):
img = h5cont[i]

All images of the series provide access to the full hdf5 header data, e.g.

img = h5cont[0]
# read header items using convenience functions
optData = img.optionalData() # DImageOptionalData object
# e.g. wavelength
wavelength = optData.wavelength()
# threshold energy
threshold_energy = optData.threshold_energy()

It is a common pitfall to merge line 1 and 3 of the example above into one expression:

optData = h5cont[0].optionalData() # not valid code -> segmentation fault

Accessing optData leads to a segmentation fault. This is because optionalData() returns a reference to an internal DImageOptionalData object, which is destroyed as soon as the temporary DImage object returned by h5Cont[0] is garbage collected.

You may also access the NeXus tree from the DImageSeries :

# Define convenience function to iterate over the NeXus tree
def iterateChildren(node,nodeList=[]):
""" iterates over the children of a neXus node """
if node.type() == dectris.albula.DNeXusNode.GROUP:
for kid in node.children():
nodeList = iterateChildren(kid,nodeList)
else:
nodeList.append(node)
return nodeList
# Read the header item directly from the DImageSeries
neXusHeader = h5cont.neXus() # NeXus tree of a hdf5 container
neXusRoot = neXusHeader.root() # Gain access to the root element of the NeXus tree
# print all header item names with path
for kid in iterateChildren(neXusRoot):
print kid.path()
# extract wavelength
wavelength = neXusRoot.childElement('/entry/instrument/beam/incident_wavelength')
print "wavelength value: ",wavelength.value()
#print attributes
for attr in wavelength.attributes():
print attr.name(), attr.value()
threshold_energy = neXusRoot.childElement('/entry/instrument/detector/threshold_energy')
print "threshold_energy value: ",threshold_energy.value()
# print attributes
for attr in threshold_energy.attributes():
print attr.name(), attr.value()

The following lines let you write the image data to an hdf5 file

# write the (uncompressed) images and the neXus header to a new HDF5 file
hdf5Writer = dectris.albula.DHdf5Writer("testContainer.h5",1000, neXusHeader)
for i in range(h5cont.first(), h5cont.first() + h5cont.size()):
img = h5cont[i]
hdf5Writer.write(img)
# flushing closes the master and the data files
hdf5Writer.flush()

After we have worked through the examples above, it is a simple execise to write hdf5 data to a series of cbf files. Here is the solution:

# Write the images in the cbf format.
# Be careful: Header information will be lost!
for i in range(h5cont.first(), h5cont.first() + h5cont.size()):
img = h5cont[i]
dectris.albula.DImageWriter.write(img, "testImage_{0:05d}.cbf".format(i))
# Write the images in the tif format.
# Be careful: Header information will be lost!
for i in range(h5cont.first(), h5cont.first() + h5cont.size()):
img = h5cont[i]
dectris.albula.DImageWriter.write(img, "testImage_{0:05d}.tif".format(i))

See also DImage, DImageSeries, DImageOptionalData, DNeXusTree, DNeXusNode.

dectris.albula.DImageSeries
class DImageSeries provides access to image series, which may reside in a series of single image file...
Definition: __init__.py:685
dectris.albula
Definition: __init__.py:1
dectris.albula.DImageWriter.write
def write(image)
Write image to fileName() with the given format.
Definition: __init__.py:1040
dectris.albula.openMainFrame
def openMainFrame(width=-1, height=-1, hideToolBar=False, disableClose=False, enableGLSL=True)
Creates and displays new DMainFrame.
Definition: __init__.py:262
dectris.albula.display
def display(image, width=-1, height=-1, hideToolBar=False, disableClose=False, hideToolButtons=False, title=None)
Displays image in new DMainFrame.
Definition: __init__.py:277
dectris.albula.DHdf5Writer
Class DHdf5Writer writes DImage objects to an HDF5 container file.
Definition: __init__.py:593