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;
}