Using custom C code in a task type

Custom C code (or custom code in any other language) can be used in a task type.

The native should usually be deployed to the worker via a custom dependency package. Small portable executable that don’t require any installation or configuration could also be included in the task type package itself.

Example.C.DivideByTwo wraps a custom small C program.

The main script.py contains the boilerplate XamFlow input and output handling:

import aimio
import lupoutil
import omni
import runutil
import xfworkerutil

import numpy


worker = xfworkerutil.XFWorkerJob()

inputs = worker.job['inputs']

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

result_omni_path = worker.create_related_path(image.metadata_path, '_filt.omni')
result_raw_path = worker.create_related_path(image.metadata_path, '_filt.raw')
result_aim_path = worker.create_related_path(image.metadata_path, '_filt.aim')

# Extract RAW pixel data
temp_folder_path = worker.working_dir.path / "temp"
temp_folder_path.mkdir(parents=True, exist_ok=False)

if image.pixel_type != 'int16' or image.pixels_compression != omni.Image.PIXELS_COMPRESSION_UNCOMPRESSED:
    worker.error(f"Image of type {image.pixel_type} / {image.pixels_compression} not supported.")

input_raw_path = lupoutil.get_or_extract_raw_pixels_file(image, temp_folder_path / "input.raw")

# Call C tool
runutil.run("C Tool", [
        "example.exe",  # Compile and include the executable in this task type package (or create a dependency package)
        str(input_raw_path),
        str(result_raw_path),
        str(image.size[0]),
        str(image.size[1]),
        str(image.size[2]),
        str(image.pixel_size_mm[0]),
        str(image.pixel_size_mm[1]),
        str(image.pixel_size_mm[2]),
    ])

if inputs['convert_back_to_aim']:
    # Upload result image in AIM format
    shape = tuple(reversed(image.size))
    pixels = numpy.fromfile(str(result_raw_path), dtype=numpy.int16).reshape(shape)
    res_nano = (int(image.pixel_size_mm[0] * 1_000_000), int(image.pixel_size_mm[1] * 1_000_000), int(image.pixel_size_mm[2] * 1_000_000))
    aimio.save_aim(str(result_aim_path), pixels, res_nano=res_nano)

    # Upload output files
    worker.upload_output_file(result_aim_path)

    filt_image_output = worker.create_output_image(result_aim_path.name, image.size)

else:
    # Upload result image in OMNI format (raw pixels files and JSON metadata file)

    # Create metadata file for output image
    metadata = {}
    if image.pixel_min_value is not None:
        metadata['pixel_min_value'] = image.pixel_min_value // 2
    if image.pixel_max_value is not None:
        metadata['pixel_max_value'] = image.pixel_max_value // 2
    filt_image = image.save_derived_image(result_omni_path, result_raw_path, metadata)

    # Upload output files
    filt_image_output = worker.upload_omni_output(filt_image)

# Save outputs
outputs = {
    "filt_image": filt_image_output,
}
worker.finish(outputs)

The example.c code implements a trivial image processing operation:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main(int argc, char *argv[])
{
    if (argc != 9)
    {
        fprintf(stderr, "Expected arguments: <input filename> <output filename> <x pixel size> <y pixel size> <z pixel size> <x mm/pixel> <y mm/pixel> <z mm/pixel>\n");
        return EXIT_FAILURE;
    }
    char *input_filename = argv[1];
    char *output_filename = argv[2];
    int x_pixel_size = atoi(argv[3]);
    int y_pixel_size = atoi(argv[4]);
    int z_pixel_size = atoi(argv[5]);
    double x_mm_size = atof(argv[6]);
    double y_mm_size = atof(argv[7]);
    double z_mm_size = atof(argv[8]);
    printf("Pixel size: %i, %i, %i\n", x_pixel_size, y_pixel_size, z_pixel_size);
    printf("Millimeter size per pixel: %lf, %lf, %lf\n", x_mm_size, y_mm_size, z_mm_size);
    printf("Input filename: %s\n", input_filename);
    printf("Output filename: %s\n", output_filename);

    FILE *input_fh = fopen(input_filename, "rb");
    if (input_fh == NULL)
    {
        fprintf(stderr, "Could not open input file %s\n", input_filename);
        return EXIT_FAILURE;
    }

    FILE *output_fh = fopen(output_filename, "wb");
    if (output_fh == NULL)
    {
        fprintf(stderr, "Could not open output file %s\n", output_filename);
        fclose(input_fh);
        return EXIT_FAILURE;
    }

    while (1)
    {
        enum { buffer_size = 1024 };
        uint16_t input_buffer[buffer_size];
        uint16_t output_buffer[buffer_size];

        const int n = fread(input_buffer, sizeof(uint16_t), buffer_size, input_fh);
        if (n == 0) break;

        for(int i = 0; i < n; ++i)
        {
            output_buffer[i] = input_buffer[i] / 2;
        }

        fwrite(output_buffer, sizeof(uint16_t), n, output_fh);
    }

    fclose(input_fh);
    fclose(output_fh);

    return EXIT_SUCCESS;
}