Saving output in a task type

Primitive parameters

Primitive outputs (of type Int, Float, String or Bool; see Parameter Types) can be stored directly via the Python module xfworkerutil.

For example Lucid.Statistics.ImagePixelsMinMax stores minimum and maximum output parameters:

import json

import lupoutil
import xfworkerutil


worker = xfworkerutil.XFWorkerJob()

inputs = worker.job['inputs']
calibration_name = inputs['calibration']

# Download input files
image = worker.get_omni_image(inputs['image'])
mask = worker.get_omni_image(inputs['mask']) if inputs['mask'] is not None else None

dir_path = worker.working_dir.path
result_json_path = worker.create_temp_path('minmax.json')

# LUPO

input_float32_raw_path = lupoutil.get_or_extract_raw_float32_pixels_file(image, dir_path / "extract")

element_count = image.pixels_count

if mask is not None:
    # Min/max of masked pixels
    if mask.size_3d_tuple != image.size_3d_tuple:
        worker.error(f"Mask size {mask.size_3d_tuple} does not match image size {image.size_3d_tuple}.")
    mask_raw_path = lupoutil.get_or_extract_raw_uint8_pixels_file(mask, dir_path / "extract_mask")
    masked_raw_path = worker.create_temp_path('masked.raw')
    lupoutil.run_lupo([
        'mask-sparse',
        str(input_float32_raw_path),
        str(0),
        str(mask_raw_path),
        str(0),
        str(element_count),
        str(masked_raw_path),
    ])
    input_float32_raw_path = masked_raw_path
    element_count = masked_raw_path.stat().st_size // 4
    # Avoid NaN from division-by-zero:
    if element_count == 0:
        raise Exception("Can not calculate min / max because the mask is empty.")

lupoutil.run_lupo([
    'min-max',
    str(input_float32_raw_path),
    str(0),
    str(element_count),
    str(result_json_path),
])
result = json.loads(result_json_path.read_text())
min_value = result['min']
max_value = result['max']

if calibration_name is not None:
    calibrations = image.pixel_calibrations_of_unit_type(calibration_name)
    if len(calibrations) == 0:
        worker.error(f"Image is missing calibration metadata of type {calibration_name}")
    calibration = calibrations[0]
    min_value = calibration.raw_to_calibrated(min_value)
    max_value = calibration.raw_to_calibrated(max_value)

# Save outputs
outputs = {
    "min": min_value,
    "max": max_value,
}
worker.finish(outputs)

Bulk data

Composite parameters like Image refer to bulk data that should be stored separately on FSS.

OMNI image data can be stored using the xfworkerutil.XFWorkerJob.upload_omni_output(). The returned value should be passed to xfworkerutil.XFWorkerJob.finish() for the respective output key. For example Lucid.Core.Threshold does this as follows:

import xfworkerutil

import thres

worker = xfworkerutil.XFWorkerJob()

inputs = worker.job['inputs']
threshold_lower = inputs['threshold_lower0']
threshold_upper = inputs['threshold_upper0']
calibration_name = inputs['calibration']

# Download input files
image = worker.get_omni_image(inputs['image'])

seg_image_omni_path = worker.create_related_path(image.metadata_path, '_seg.omni')
seg_image_pixels_path = worker.create_related_path(image.metadata_path, '_seg.raw')
thres_temp_dir_path = worker.working_dir.path / "thres"

if calibration_name is not None:
    calibrations = image.pixel_calibrations_of_unit_type(calibration_name)
    if len(calibrations) == 0:
        worker.error(f"Image is missing calibration metadata of type {calibration_name}")
    calibration = calibrations[0]
    threshold_lower = calibration.calibrated_to_raw(threshold_lower) if threshold_lower is not None else None
    threshold_upper = calibration.calibrated_to_raw(threshold_upper) if threshold_upper is not None else None

# LUPO
seg_image = thres.thres_image(
    image,
    threshold_lower,
    threshold_upper,
    seg_image_omni_path,
    seg_image_pixels_path,
    thres_temp_dir_path)

# Upload output files
seg_image_output = worker.upload_omni_output(seg_image)

# Save outputs
outputs = {
    "segments": seg_image_output,
}
worker.finish(outputs)

See also Saving an OMNI Image.

For other data files xfworkerutil.XFWorkerJob.upload_output_file() can be used instead. The value to be passed to xfworkerutil.XFWorkerJob.finish() for the respective output key can be created using e.g. xfworkerutil.XFWorkerJob.create_output_image() or similar methods. See Using IPL in a task type for an example.