Usage in Python#

You can interact with Imaging Server Kit algorithms in Python to access all of the main functionalities:

  • Interact with remote (served) as well as local algorithms.

  • Run algorithms in a regular or tile-by-tile fashion.

  • Collect intermediate (streamed) outputs.

  • Load samples and access algorithm documentation.

Running algorithms#

Let’s consider our usual thresholding algorithm:

import imaging_server_kit as sk
import skimage.data

@sk.algorithm(
    name="Intensity threshold",
    parameters={"threshold": sk.Integer(name="Threshold", min=0, max=255, default=128)},
    samples=[{"image" : skimage.data.coins()}],
)
def threshold_algo(image, threshold):
    mask = image > threshold
    return sk.Mask(mask, name="Binary mask")

Our function turned algorithm still “behaves” similarly to the original Python function. If you try this:

image = skimage.data.coins()

mask = threshold_algo(image, threshold=100)
print(mask)

You will get a standard Numpy array for mask.

However, one notable difference is that parameters are validated at runtime according to the annotations you provided.

Because of that, and your annotation of min=0 for the threshold parameter, you won’t be able to run the algorithm with negative threshold values:

mask_invalid = threshold_algo(image, threshold=-1)  # Will raise a `ValidationError`

In this case, a ValidationError is raised with the message indicating that the threshold input should be greater than or equal to zero.

Moreover, the algorithm object has gained a few extra methods. You can execute computations via .run():

results = threshold_algo.run(image, threshold=100)

print(results)

In this case, the returned results is an instance of a Results object from imaging-server-kit. Results encapsulate a stack of data layers. In this case, the results include one layer containing the segmentation mask.

Data layers in a Results object can be accessed either by name via .read() or by indexing (results[0]).

mask_result = results.read("Binary mask")  # <- Equivalent to accessing `results[0]`
print(mask_result)

Here, mask_result is a Mask object, which is an instance of sk.DataLayer. You can retreive the segmentation mask as its data attribute.

mask = mask_result.data  # Numpy array
print(mask.shape)

All data layers have a data attribute, as well as other attributes that you can explore:

  • .name: A unique name identifying the layer in the stack.

  • .data: The returned data, such as a Numpy array.

  • .meta: A dictionary of metadata about the layer.

Using sk.Client#

Note

You need to run the “server-side” code from this section in a Python script (not a Jupyter notebook) as explained in Serving algorithms via:

...

if __name__ == "__main__":
  sk.serve(threshold_algo)

One of the main advantages of using .run() is that it will work in the same way when applied to a local algorithm and when applied to a client instance connected to an algorithm server.

To demonstrate this, let’s serve our threshold algorithm so that it becomes available on http://localhost:8000. Once the server is available, you can connect to it from Python via a sk.Client() instance:

import imaging_server_kit as sk

# Connect to the server
client = sk.Client("http://localhost:8000")

# Send the image and parameters, computation runs on the server, results are retrieved:
results = client.run(image, threshold=0.5)
print(results)

# Mask data layer
mask_result = results[0]
print(mask_result)

# Segmentation mask
mask = results[0].data
print(mask.shape)

Moreover, you can retreive samples using the .get_sample() method on an algorithm, or client, and passing the index of the sample:

n_samples = client.get_n_samples()  # <- Returns the number of samples available
sample = client.get_sample(idx=0)  # <- Retreive the first sample.

# Results from running the algo with the given sample
results = client.run(**sample.to_params_dict())

Finally, using .info() on an algorithm, or a connected client, will open the algo docs page in a web browser:

client.info()

Working with algorithm collections#

Algorithm collections expose the same methods as standalone algorithms, except that you need to provide a value for the algorithm parameter, which corresponds to the name of the algorithm.

On the server side, combine two algorithms and serve them:

import imaging_server_kit as sk
import skimage.data
from skimage.filters import threshold_otsu, threshold_li

@sk.algorithm(
    name="intensity-threshold",
    parameters={"threshold": sk.Integer(name="Threshold", min=0, max=255, default=128)},
    samples=[{"image" : skimage.data.coins()}],
)
def threshold_algo(image, threshold):
    mask = image > threshold
    return sk.Mask(mask, name="Binary mask")


@sk.algorithm(
  name="automatic-threshold",
  parameters={"method": sk.Choice(name="Method", items=["otsu", "li"], default="otsu")},
  samples=[{"image" : skimage.data.coins()}],
)
def auto_threshold(image, method):
  if method == "otsu":
    mask = image > threshold_otsu(image)
  elif method == "li":
    mask = image > threshold_li(image)
  return sk.Mask(mask, name="Binary mask")

# Combine the algorithms
threshold_algos = sk.combine([threshold_algo, auto_threshold], name="threshold-algos")

if __name__ == "__main__":
  sk.serve(threshold_algos)

Then, on the client side:

client = sk.Client("http://localhost:8000")

# List the available algorithms
print(client.algorithms)  # ["intensity-threshold", "automatic-threshold"]

thresh_results = client.run(algorithm="intensity-threshold", image=image, threshold=0.3)
print(thresh_results)

auto_results = client.run(algorithm="automatic-threshold", image=image, method="otsu")
print(auto_results)

Tiled inference#

To run an algorithm tile-by-tile, you can set tiled=True and specify the tile_size_px, overlap_percent, randomize and delay_sec parameters:

results = client.run(
  image=image,
  tiled=True,  # <- Enable tiled inference
  tile_size_px=64,  #  (64, 64) sized tiles
  overlap_percent=0.1,  # 10% overlap
  randomize=True,  # Process the tiles in a random order
  delay_sec=0.0,  # (Optional) Add a little time delay betwen each tile
)

Summary#

  • Use sk.Client() to connect to algorithm servers from Python.

  • Use .run() on a client, or the original algorithm, to retreive a Results object.

  • Results contain a stack of data layers.

  • You can access the data, name and meta attributes of data layers.

  • Parameters are validated (invalid parameters raise a ValidationError).

  • Use .get_sample() to retreive samples

  • Use .info() to access documentation.

  • Use tiled=True for tiled inference.

Next steps#

Next, we’ll take a look at how you can serve Server Kit algorithms in Docker containers.