NOTE: This feature has not been merged into the production branch yet.
I have incorporated a class called Stitcher
, which acts as a wrapper for the ASHLAR image-stitching software running in its own thread. An instance of Stitcher
assumes it is receiving tiles which are each the sole data for their specific X-Y coordinate, so one should instantiate a Stitcher
for each Z
-level and each configuration taken at this Z
-level. If using this class from within a multipoint_custom_script_entry
, the dictionary mulitPointWorker.multiPointController.tile_stitchers
is provided as a suggested place to store these. Below is the documentation for initializing a Stitcher
instance.
class Stitcher():
"""
:brief: Spawns a process for combining tiles as they are acquired
into an OME TIFF file, followed by running ashlar to stitch them
together.
"""
def __init__(self, tiled_file_path="", stitched_file_path="", ashlar_args = {},
auto_run_ashlar = False, image_reader = default_image_reader):
"""
:brief: Initializes class, creates control variables.
:param tiled_file_path: output file to write the tiled OME-TIFF to, to
pass to ashlar as an input
:param stitched_file_path: output file to write ashlar's stitching to
:param ashlar_args: dict of additional arguments to ashlar besides I/O file,
will be unpacked and passed to ashlar wrapper. can also put
stdin/stdout/stderr for the ashlar process here
:param auto_run_ashlar: Whether to automatically run ashlar after all tiles
are acquired
:param image_reader: function to pass to queued_ometiff_writer as an image reader
"""
As an example, to either initialize or do operations on the relevant Stitcher
for a given Z
-level/configuration combination, first from control.stitcher import Stitcher
at the start of your code, and then:
for k in range(multiPointWorker.NZ):
...
for config in multiPointWorker.selected_configurations:
stitcher_dict = multiPointWorker.multiPointController.tile_stitchers
stitcher_key = str(config.name)+"_Z_"+str(k)
stitcher_to_use = None
image = multiPointWorker.camera.read_frame()
#... (additional processing) ...
saving_path = #(your method for generating individual tile filepaths here)
cv2.imwrite(saving_path, image)
try:
stitcher_to_use = stitcher_dict[stitcher_key]
except: # create stitcher if necessary
stitcher_tiled_file_path = #(your method for generating a filename from Z-level + config name)
stitcher_stitched_file_path #(same method, but with "ashlar_" prepended)
#arguments to pass to the ashlar terminal command to run when stitching.
#argument names should have dashes replaced with underscores and have leading
#dashes removed before being written as kwargs
stitcher_default_options = {'align_channel':0, 'maximum_shift':int(min(multiPointWorker.crop_width,multPointWorker.crop_height)*0.05), 'filter_sigma':1}
stitcher_to_use = Stitcher(stitcher_tiled_file_path, stitcher_stitched_file_path, stitcher_default_options, auto_run_ashlar=True, image_reader = multiPointWorker.multiPointController.stitcher_image_reader)
multiPointWorker.multiPointController.tile_stitchers[stitcher_key] = stitcher_to_use
stitcher_to_use.start_ometiff_writer()
tile_metadata = { 'Plane': { # necessary positioning data that ashlar needs to know
'PositionX':int((j if multiPointWorker.x_scan_direction==1 else multiPointWorker.NX-1-j)*multiPointWorker.crop_width)
'PositionY':int(i*multiPointWorker.crop_height)
}
stitcher_to_use.add_tile(saving_path, tile_metadata)
In this example, each Z
-level/config combo will get an ASHLAR-stitched OME-TIFF image of the tiles captured with that configuration saved to its respective stitcher_stitched_file_path
once all FOVs at that level have been captured.
For reference on how to pass the ashlar_args
, here is the Python ASHLAR wrapper’s docstring:
def ashlar(inputpath, outputpath, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, **kwargs):
"""
:brief Uses subprocess Popen to call ashlar with arguments
:param inputpath: String, path to input file, assumed multipage TIFF
with metadata Plane {PositionX/PositionY} defined
:param outputpath: String, path to output file
:param kwargs: For other arguments to Ashlar, see labsyspharm/ashlar
on Github. All arguments must be given in their "word" form, omitting
the double dash at the start and replacing internal dashes with underscores.
Only use 0 and 1 for False and True values
:param stdin: stdin to pass to subprocess handler, defaults to dev null
:param stdout: stdout to pass to subprocess handler, defaults to dev null
:param stderr: stderr to pass to subprocess handler, defaults to dev null
:return: A subprocess.Popen object representing the ongoing ashlar process.
Can be polled or waited for.
"""