Strings and paths

Prerequisites

Before starting this lesson, you should be familiar with:

Learning Objectives

After completing this lesson, learners should be able to:
  • Construct complex strings, e.g. to produce log messages and create file paths

  • Automatically create paths for saving the results of the analysis of an input image

Motivation

Combining several strings into a larger string is a prevalent operation in scripting. This is useful, e.g., to create file paths and create log messages. Such concatenation of strings is surprisingly error prone and it is thus important to learn it properly and be aware of all the pitfalls. In addition, it is critical to be able to deal with and manipulate file paths, as this is crucial to write code that automatically saves resulting data at appropriate locations.

Concept map

graph TD S("Strings") --> L("Log/debug messages") S --> F("File paths") S --> O("Script output (tables)")



Figure


Examples of string expressions as they may appear in a typical bioimage analysis script (in some hypothetical scripting language).



Creating paths

A frequent operation in bioimage analysis is to create paths to images by concatenating a folder and file name to a full path. Please note that when concatenating a folder and a file name into a full path, you might need to add a so-called file separator between the folder and the file name. This is a character that separates directory names within a path to a particular location on your computer. Different operating systems use different file separators: on Linux and MacOS, this is /, while Windows uses \. To make it worse, when you store a directory you are typically never sure whether the contained string ends on / or \ or does not have the separator in the end, e.g. C:\Users\Data, in which case you have to add it when concatenating a file name). To make it even worse, in some programming langauges the \ character have a special meaning within strings and is thus not simply interpreted as a character and to actually get a backslash you may have to write \\.

If you want to have some “fun” you can read those discussions:

As all of this can quickly become a huge mess, fortunately, scripting languages typically offer special functions to help you write code to create file paths that will run on all operating systems.

String concatenation

String concatenation is the operation of joining multiple substrings.

For example concatenating “Hello “ and “world!” would result into “Hello world!”.




Activities

Creating log messages


Show activity for:  

ImageJ Macro

// create a log message
imageIndex = 11;
numImages = 100;
print("Analyzing image "+imageIndex+"/"+numImages+"...");

python

# %%
# Creating log messages

image_index = 11
num_images = 100

# %%
# strings can be added together with the '+' operator
print("Analyzing image " + str(image_index) + "/" + str(num_images) + "...")

# %%
# An alternative way to combine strings and variables from your code
# are so called f-strings: in f-strings you can add variables (and some
# code) in between curly braces. Types are automatically coerced to strings.
#  The below code achieves the same output.
print(f"Analysing image {image_index}/{num_images}...")



Manipulate file paths

In bioimage analysis, one very often opens one image and then needs to save a segmentation (label mask file) and segmented object measurements (a table file). To this end, it is useful if the output images have similar names than the input image, such that one knows that they belong to each other. Sometimes it is useful for those output files to be placed in the same folder as the input image, but often you want to specify a dedicated output folder.

To manage all these scenarios it is critical to learn how to manipulate file and folder paths in various ways and thereby construct the output paths from the input path and a given output directory.


Show activity for:  

ImageJ Macro

// manipulating file paths
tmpFolder = getDirectory("temp");
print(tmpFolder);
fileName = "nuclei.tif";
path = tmpFolder + fileName;
print(path);

// Let's create a file name for a label image derived from the nuclei.tif
// such the file name will be nuclei_labels.tif.
filenameWithoutExtension = File.getNameWithoutExtension(path);
print(filenameWithoutExtension)
labelsPath = tmpFolder + filenameWithoutExtension + "_labels.tif";
print(labelsPath);

// Now we want to create a file name for a .csv file, that shares the
// file name up to the extension.
csvPath = tmpFolder + filenameWithoutExtension + ".csv";
print(csvPath);

python

# %%
# Manipulating file paths

import pathlib
import tempfile

# %%
# Let's get a folder and a file name to practice with.
# Temporary folder is different on different operating systems.
# We use tempfile.gettempdir() from the standard library as it has all that logic.
tmp_folder = pathlib.Path(tempfile.gettempdir())
file_name = "nuclei.tif"

# %%
# Combine folder and file name.
# pathlib.Path objects support "syntactic sugar", like using '/' as a
# separator when composing paths.
image_path = tmp_folder / file_name
print(image_path)

# %%
# The parent for a Path object gives you the containing folder
parent_folder = image_path.parent
print(parent_folder == tmp_folder)

# %%
# Let's create a file name for a label image derived from the nuclei.tif
# such the file name will be nuclei_labels.tif.
filename_no_extension = image_path.stem
print(filename_no_extension)
labels_path = tmp_folder / f"{filename_no_extension}_labels.tif"

# %%
# Now we want to create a file name for a .csv file, that shares the
# file name up to the extension.
# If we only change the extension, we can use the with_suffix method.
csv_path = image_path.with_suffix(".csv")
print(csv_path)



Exercises

Show exercise/solution for:

ImageJ Macro: Concatenate variables

Open a new script editor

  1. From ImageJ GUI open a script [ File > New > Script…]
  2. Choose the language IJ1 Macro
  3. Define a string variable with the content "nucleus"
  4. Define a numerical variable with the content 6
  5. Concatenate the variables to get the final string "nucleus_6"
  6. Print the final string

Solution

// define variables
str = "nucleus";
num = 6;
// concatenate
concat = str + "_" + num;
// print the string to the log window
print(concat);

ImageJ Macro: Create function arguments

Create a macro that applies a gaussian blur with a certain sigma value to an image.

  1. Open a sample image: [ File > Open samples > Blobs ]
  2. Open the script editor: [ File > New > Script…]
  3. Choose the language IJ1 Macro
  4. Add the line run("Gaussian Blur...", "sigma=6");
    • Tip: In programming copy and paste as much as possible!
  5. Run the macro to see the effect on the image
  6. Define a variable blurSigma with the value 6
  7. Replace the 6 in the run(...) command with this variable.

Solution

blurSigma = 6;
run("Gaussian Blur...", "sigma="+blurSigma );
// Below would also work and is very convenient, but a bit non-standard...
// run("Gaussian Blur...", "sigma=&blurSigma");

ImageJ Macro: Create paths

Concatenatimg folder and file names

  1. [ File › Open Samples › Blobs ]
  2. Save the image onto the Desktop of your computer
  3. Open the script editor: [ File > New > Script…]
  4. Choose the language IJ1 Macro
  5. Define folder as a string variable pointing to your computer’s desktop folder (it should be something like C:\Users\Username\Desktop or /Users/Username/Deskop)
  6. If you are on Windows, watch out: In order to have C:\Users\Username\Desktop you will have to write C:\\Users\\Username\\Desktop, because \ is a special character that needs to be “escaped”
  7. Define fileName as a string representing the file name of the image that you just saved (it should be something like "blobs.tiff")
  8. Concatenate the variables to get a new string (filePath) representing the full file path (folder + File.Separator + fileName).
  9. Use the filePath variable to add code that prints something like “Opening C:\Users\Username\blobs.tiff”
  10. Add open(filePath); to open the image.
  11. Run the macro

Solution

// define the variables
folder = "/Users/Username/Desktop/"; // <= This must be replaced!
fileName = "blobs.tiff"; // <= Be careful: There is ".tiff" and ".tif"
// concatenating, adding the file separator in the middle
filePath = folder + File.separator + fileName;
// print log message
print("Opening " + filePath);
// open the file
open(filePath);



Assessment

Fill in the blanks

  1. In MacOS and Linux sub-folders are separated by the ___ string, whereas on Windows they are separated by the ___ string.
  2. Concatenating "Hello" and "world" one obtains ___.
  3. Concatenation the variables folder = "/Users/Images" and file = "MyImage.tif" one obtains ___.

Solution

  1. MacOs "/", Windows "\"
  2. One would get "Helloworld"; to fix this one needs to add a third " " string in the middle to get "Hello world".
  3. One would obetain "/Users/ImagesMyImage.tif". There are several ways to fix this, depending on the scripting language. A good way is to use functions such as, e.g., os.path.join( folder, file ) in python, because this will work for both cases: folder = "/Users/Images" and folder = "/Users/Images/".




Follow-up material

Recommended follow-up modules:

Learn more: