Huygens Python scripting
By clicking the "Python shell" tab above a scripting box in Huygens Localizer or Huygens Professional, you get access to a fully functional Python interpreter. This allows you to manipulate images and perform operations just as in the Tcl interpreter. Furthermore, Python modules can be imported for even more functionality. Note that a local Python installation is not required to use the Python interpreter.The image class
An image class was created to represent images in Huygens. Images act like mutable objects and can be created from scratch, opened from file, or adopted from the Tcl interpreter / main window. See the following examples:# Create a new image with default parameters. a = image() # Create a new image called "foo" with specific dimensions and dataType (more options exist). a = image("foo", dim = [500, 500, 50], chan = 2) # Open an image from file. a = image(path = "/home/user/foo.ics") # Adopt an image from the main window / Tcl interpreter by passing the image name. a = image(tclName = "psf")
Note that images are automatically destroyed when their last reference disappears, except when adopted from Tcl / the main window since it is assumed they are still serving a purpose there.
# The image "foo" is implictly destroyed. a = image("foo") a = 7 # The image "foo" is retained since another reference exists. a = image("foo") b = a a = 7
The image class contains many image processing methods. All the operations that are available in the Tcl interpreter are included. The names of these operations and their options are the same as in Tcl, with a few exceptions we'll cover later. The mandatory arguments appear in the same order, and the optional arguments have become keyword arguments. In case of an operation with a destination, there are a couple of different options. You can pass an existing image object, in which case this will be the destination. You can also pass a string, in which case a new image will be created with this name. Finally, you can completely omit the destination (make sure to still provide all the other mandatory arguments), in which case a new destination image will be created with a default name. Note that the destination images are always returned by the method, in addition to any other return values (these then form a tuple). In case of an operation with additional non-dest images, these can be specified either by passing an image object or by passing a string containing the literal name of the image.
# Mirror the image "a" in the x direction. This is an in-place operation with no destination. a.mir("x") # Perform a cmle deconvolution on "a" with an existing image called "psf" and 30 iterations. A result image called "cmle" is created and stored in "b". b = a.cmle("psf", it = 30) # Perform a cmle deconvolution on "a" with an existing psf image (object) and automatic background subtraction. A result image called "newName" is created and stored in "b". b = a.cmle(psf, "newName", bgMode = "auto") # Perform a gmle deconvolution, using an existing result image b which already had the name "foo". a.gmle(psf, b) → foo
The image class contains arithmetic operations which work as expected. Furthermore, you can index into an image to manipulate voxel values. A single number is interpreted as a linear index whereas a tuple is interpreted as raw coordinates. Some examples:
# Add 7 to each voxel in a. a = a + 7 # Subtract image b from image a and divide the result by 2. c = (a - b) / 2 # Multiply a complex image by a complex phase of pi / 4. import cmath a *= cmath.exp(0.25j * cmath.pi) # Multiply a specific voxel value by 2: a[5, 6, 7] *= 2 # Create a smooth gradient in the y direction. for inx in a: a[inx] = inx[1] # Perform a custom filtering operation from image a to image b. In this case a sum-filter in the x direction of size 3. for inx in b: neighbours = [inx] neighbours.append((inx[0] + 1, inx[1], inx[2], inx[3], inx[4], inx[5])) neighbours.append((inx[0] - 1, inx[1], inx[2], inx[3], inx[4], inx[5])) b[inx] = 0 for n in neighbours: if n in a: b[inx] += a[n]
The following operations have been renamed to avoid conflicts:
Tcl operation | Python operation |
+ | add |
- | sub |
/ | div |
* | mul |
*cj | mulcj |
-> | copy |
del | delete |
The first four of these are already available as overloaded artihmetic operators so there is not much reason to use them in this form (although you can do something like a.add(b, c) if you want). The copy command is quite useful though and works both with and without an explicit destination:
# Make a new copy of image a called "copy" and store in variable b. b = a.copy() # Make a new copy of image a called "foo" and store in variable b. b = a.copy("foo") # Copy image a to image b (which already had the name "foo"). Note that b must be an existing image object. a.copy(b) → foo
Using the explicit delete command is not recommended as there may be other references to the images. If possible it's best to use "del a" instead, which deletes the image if a is the last reference to it.
The table class
Single molecule localization tables can also be manipulated in the Python interpreter. They have their own class which works just like the image class. Localization tables for individual frames can be read and overwritten. Table objects can be created from scratch, opened from file, or adopted from the Tcl interpreter / main window. See the following examples:# Create a new table called "foo" with specific column types. a = table("foo", columns = ["intensity", "x", "y", "z", "fwhmX", "fwhmY", "offset", "uncertainty"], channels = 1, frames = 1000) # Open a table from file. a = table(path = "/home/user/foo.csv") # Adopt a table from the main window / Tcl interpreter by passing the image name. a = table(tclName = "psf")
Note that tables are automatically destroyed when their last reference disappears, except when adopted from Tcl / the main window since it is assumed they are still serving a purpose there.
# The table "foo" is implictly destroyed. a = table("foo") a = 7 # The table "foo" is retained since another reference exists. a = table("foo") b = a a = 7
Tables can be manipulated by (channel, frame) index. With this you can get and set localization information for a specific frame (and channel). The channel index can also be omitted: in the case channel 0 will be assumed. Note that the format must be a list of lists, where each list contains the values of the columns in the table. Some examples:
# Get the localization information in frame 31 (channel 0 explicit): a[0, 31] → [[400, 8, 5, 2.3, 0.1, 0.2, 100, 0.010], [300, 3, 2, 2.1, 0.2, 0.2, 120, 0.015], [200, 2, 2, 1.7, 0.2, 0.1, 130, 0.012]] # Remove the localizations in frame 31 (channel 0 implicit): a[31] = [] # Set the localizations in frame 31 (channel 0 explicit): a[0, 31] = [[400, 8, 5, 2.3, 0.1, 0.2, 100, 0.010], [300, 3, 2, 2.1, 0.2, 0.2, 120, 0.015], [200, 2, 2, 1.7, 0.2, 0.1, 130, 0.012]] # Copy the localizations in a range of frames from table b to table a (channel 0 implicit): for fr in range(30, 41): a[fr] = b[fr] # Copy the localizations in a range of frame from table b to table a, using the faster cp command: b.cp(a, srco = [0, 30], desto = [0, 30], span = [0, 10]) # Remove all localizations from a table with an intensity smaller than 10000: for fr in a: a[fr] = [x for x in a[fr] if x[0] < 10000]
Tables can be saved as .csv or as .txt, with the main difference being that values are separated by commas in a csv and by spaces in a .txt file.
# Save a table as a .csv file. a.save("/home/user/newTableFile", type = "csv") → /home/user/newTableFile.csv # Save a table as a .txt file, with values separated by spaces. a.save("/home/user/newTableFile", type = "txt") → /home/user/newTableFile.txt
Other commands
Commands which are not directly related to images and / or tables are available in module-like structures. The naming of these modules and functions is once again identical to the names used in the Tcl interpreter. For example:# Print a message to the task report window. huOpt.report("Hello") # Disable GPU acceleration huOpt.gpuSet(enabled = 0)
Importing Python modules from outside Huygens.
Huygens comes with all the usual Python modules built in. However, additional modules from other Python installations can also be used, as long as the versions are compatible. To do this, additional paths must be specified from which the modules can be loaded. This is usually a local Python installation folder. The paths can be added using the preference Window found in Edit -> Preferences -> Directories -> Python module paths in Huygens Localizer and Huygens Professional. To find which paths to add, run the Python version from which you want to adopt the module and import it. After importing, check module.__file__ to see where the main module files are located. For example:# Note we are running this in the local Python installation, not Huygens. import numpy numpy.__file__ → '/home/username/.local/lib/python3.7/site-packages/numpy/__init__.py'
In this case we would see that we need to add "/home/username/.local/lib/python3.7/site-packages/" to the Huygens Python module directories in the preferences. After adding a path to the preferences, Huygens must be restarted before the new modules can be used. Note that some modules may depend on other modules which can have their own path. Usually a couple of paths are enough to be able to use all the modules from another Python installation. Keep in mind that Huygens uses a specific Python version so only modules that are compatible with that version can be used. To check which Python version Huygens is on, simply run:
# Check the Huygens Python version number. import sys sys.version → 3.7.8
Creating and running Python scripts.
Python scripts can be created and run just like Tcl scripts: Either by passing the script path to the command line arguments of Huygens or by using the source() function in the Python interpreter.# Run Python script from the command line. huygenspro /home/username/testScript.py # Run Python script from Python. source("/home/username/testScript.py")