diff --git a/README.md b/README.md index e06ef255..147efb47 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,10 @@ The model specification and its validation tools can be found at None: `path` must contain `{member_id}` and may contain `{sample_id}`, which are resolved with the `sample` object. """ - path = str(path).format(sample_id=sample.id) if "{member_id}" not in path: raise ValueError(f"missing `{{member_id}}` in path {path}") + path = str(path).format(sample_id=sample.id, member_id="{member_id}") + for m, t in sample.members.items(): save_tensor(Path(path.format(member_id=m)), t) diff --git a/bioimageio/core/prediction.py b/bioimageio/core/prediction.py index c9058f0b..8656a24c 100644 --- a/bioimageio/core/prediction.py +++ b/bioimageio/core/prediction.py @@ -39,7 +39,7 @@ def predict( model: Union[ PermissiveFileSource, v0_4.ModelDescr, v0_5.ModelDescr, PredictionPipeline ], - inputs: PerMember[Union[Tensor, xr.DataArray, NDArray[Any], Path]], + inputs: Union[Sample, PerMember[Union[Tensor, xr.DataArray, NDArray[Any], Path]]], sample_id: Hashable = "sample", blocksize_parameter: Optional[ Union[ @@ -56,7 +56,7 @@ def predict( Args: model: model to predict with. May be given as RDF source, model description or prediction pipeline. - inputs: the named input(s) for this model as a dictionary + inputs: the input sample or the named input(s) for this model as a dictionary sample_id: the sample id. blocksize_parameter: (optional) tile the input into blocks parametrized by blocksize according to any parametrized axis sizes defined in the model RDF @@ -82,9 +82,12 @@ def predict( pp = create_prediction_pipeline(model) - sample = create_sample_for_model( - pp.model_description, inputs=inputs, sample_id=sample_id - ) + if isinstance(inputs, Sample): + sample = inputs + else: + sample = create_sample_for_model( + pp.model_description, inputs=inputs, sample_id=sample_id + ) if blocksize_parameter is None: output = pp.predict_sample_without_blocking( diff --git a/bioimageio/core/proc_setup.py b/bioimageio/core/proc_setup.py index 947ea0c2..9cc5f734 100644 --- a/bioimageio/core/proc_setup.py +++ b/bioimageio/core/proc_setup.py @@ -11,10 +11,18 @@ from typing_extensions import assert_never +from bioimageio.core.common import MemberId +from bioimageio.core.digest_spec import get_member_ids from bioimageio.spec.model import AnyModelDescr, v0_4, v0_5 from bioimageio.spec.model.v0_5 import TensorId -from .proc_ops import AddKnownDatasetStats, Processing, UpdateStats, get_proc_class +from .proc_ops import ( + AddKnownDatasetStats, + EnsureDtype, + Processing, + UpdateStats, + get_proc_class, +) from .sample import Sample from .stat_calculators import StatsCalculator from .stat_measures import DatasetMeasure, Measure, MeasureValue @@ -87,12 +95,8 @@ def _prepare_setup_pre_and_postprocessing(model: AnyModelDescr) -> _SetupProcess pre_measures: Set[Measure] = set() post_measures: Set[Measure] = set() - if isinstance(model, v0_4.ModelDescr): - input_ids = {TensorId(str(d.name)) for d in model.inputs} - output_ids = {TensorId(str(d.name)) for d in model.outputs} - else: - input_ids = {d.id for d in model.inputs} - output_ids = {d.id for d in model.outputs} + input_ids = set(get_member_ids(model.inputs)) + output_ids = set(get_member_ids(model.outputs)) def prepare_procs(tensor_descrs: Sequence[TensorDescr]): procs: List[Processing] = [] diff --git a/example/dataset_creation.ipynb b/example/dataset_creation.ipynb deleted file mode 100644 index 392c96b8..00000000 --- a/example/dataset_creation.ipynb +++ /dev/null @@ -1,125 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# ⚠️ this notebook needs to be updated " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from bioimageio.spec.pretty_validation_errors import (\n", - " enable_pretty_validation_errors_in_ipynb,\n", - ")\n", - "\n", - "enable_pretty_validation_errors_in_ipynb()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from bioimageio.spec.dataset.v0_3 import (\n", - " Author,\n", - " CiteEntry,\n", - " Dataset,\n", - " HttpUrl,\n", - " RelativeFilePath,\n", - ")\n", - "\n", - "nuclei_broad_data = Dataset(\n", - " name=\"Kaggle 2018 Data Science Bowl\",\n", - " description=\"This image data set contains a large number of segmented nuclei images and was created for the Kaggle \"\n", - " \"2018 Data Science Bowl sponsored by Booz Allen Hamilton with cash prizes. The image set was a testing ground \"\n", - " \"for the application of novel and cutting edge approaches in computer vision and machine learning to the \"\n", - " \"segmentation of the nuclei belonging to cells from a breadth of biological contexts.\",\n", - " documentation=RelativeFilePath(\"README.md\"),\n", - " covers=(\n", - " HttpUrl(\n", - " \"https://data.broadinstitute.org/bbbc/BBBC038/BBBC038exampleimage1.png\"\n", - " ),\n", - " HttpUrl(\n", - " \"https://data.broadinstitute.org/bbbc/BBBC038/BBBC038exampleimage2.png\"\n", - " ),\n", - " HttpUrl(\n", - " \"https://data.broadinstitute.org/bbbc/BBBC038/BBBC038exampleimage3.png\"\n", - " ),\n", - " HttpUrl(\n", - " \"https://data.broadinstitute.org/bbbc/BBBC038/BBBC038exampleimage4.png\"\n", - " ),\n", - " HttpUrl(\n", - " \"https://data.broadinstitute.org/bbbc/BBBC038/BBBC038exampleimage5.png\"\n", - " ),\n", - " ),\n", - " authors=(\n", - " Author(\n", - " name=\"Fynn Beuttenmueller\",\n", - " affiliation=\"EMBL\",\n", - " github_user=\"fynnbe\",\n", - " orcid=\"0000-0002-8567-6389\",\n", - " ),\n", - " ),\n", - " source=HttpUrl(\"https://bbbc.lbroadinstitute.org/BBBC038/\"),\n", - " cite=(\n", - " CiteEntry(\n", - " text=\"Caicedo, J.C., Goodman, A., Karhohs, K.W. et al. Nucleus segmentation across imaging experiments: \"\n", - " \"the 2018 Data Science Bowl. Nat Methods 16, 1247–1253 (2019).\",\n", - " url=\"10.1038/s41592-019-0612-7\",\n", - " ),\n", - " CiteEntry(\n", - " text=\"Allen Goodman, Anne Carpenter, Elizabeth Park, jlefman-nvidia, Josette_BoozAllen, Kyle, Maggie, \"\n", - " \"Nilofer, Peter Sedivec, Will Cukierski. (2018). 2018 Data Science Bowl . Kaggle.\",\n", - " url=\"https://kaggle.com/competitions/data-science-bowl-2018\",\n", - " ),\n", - " ),\n", - " license=\"CC0-1.0\",\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "nuclei_broad_data.source" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "bio38", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.17" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/example/model_usage.ipynb b/example/model_usage.ipynb index ae76cdfd..3b40c523 100644 --- a/example/model_usage.ipynb +++ b/example/model_usage.ipynb @@ -5,15 +5,9 @@ "id": "0daf2216", "metadata": {}, "source": [ - "# bioimageio.core usage examples" - ] - }, - { - "cell_type": "markdown", - "id": "275284b2", - "metadata": {}, - "source": [ - "# ⚠️ this notebook needs to be updated " + "# bioimageio.core usage examples\n", + "\n", + "Checkout [load_model_and_create_your_own.ipynb](https://github.com/bioimage-io/spec-bioimage-io/blob/main/example/) for examples on model creation, loading and inspection." ] }, { @@ -23,17 +17,10 @@ "metadata": {}, "outputs": [], "source": [ - "import hashlib\n", "import os\n", "\n", - "import imageio\n", - "\n", - "# we use napari for visualising images, you can install it via `pip install napari` or`conda install napari`\n", - "import napari\n", - "import numpy as np\n", - "import xarray as xr\n", - "\n", - "import bioimageio.core" + "if os.getenv(\"COLAB_RELEASE_TAG\"):\n", + " %pip install bioimageio.core \"napari[all]\" pytorch onnxruntime" ] }, { @@ -44,12 +31,23 @@ "outputs": [], "source": [ "# helper function for showing multiple images in napari\n", - "def show_images(*images, names=None):\n", + "from bioimageio.core import Tensor\n", + "from typing import Any, Dict, Union\n", + "from pathlib import Path\n", + "\n", + "import napari\n", + "\n", + "from numpy.typing import NDArray\n", + "\n", + "\n", + "def show_images(images: Dict[str, Union[Tensor, NDArray[Any], Path]]):\n", " v = napari.Viewer()\n", - " for i, im in enumerate(images):\n", - " name = None if names is None else names[i]\n", - " if isinstance(im, str):\n", + " for name, im in images.items():\n", + " if isinstance(im, Path):\n", " im = imageio.imread(im)\n", + " elif isinstance(im, Tensor):\n", + " im = im.data\n", + " print(f\"napari viewer: adding {name}\")\n", " v.add_image(im, name=name)" ] }, @@ -60,97 +58,51 @@ "source": [ "## Loading a model\n", "\n", - "We will use a model that predicts foreground and boundaries in images of nuclei from the [kaggle nucles segmentation challenge](https://www.kaggle.com/c/data-science-bowl-2018).\n", - "Find the model on bioimage.io here: https://bioimage.io/#/?id=10.5281%2Fzenodo.5764892\n", - "\n", - "First, we will use `bioimageio.core.load_resource_description` to load the model and inspec the obtained model resource." + "We will use a model that predicts boundaries in images of plant cells [kaggle nucles segmentation challenge](https://www.kaggle.com/c/data-science-bowl-2018).\n", + "Find the model on bioimage.io here: [\"affable-shark](https://bioimage.io/#/?id=10.5281%2Fzenodo.5764892)" ] }, { "cell_type": "code", "execution_count": null, - "id": "2de51dae", + "id": "96ea5f9a", "metadata": {}, "outputs": [], "source": [ - "# the model can be loaded using different representations:\n", - "\n", - "# the doi of the zenodo entry corresponding to the model\n", - "rdf_doi = \"10.5281/zenodo.6287342\"\n", + "from bioimageio.spec import load_description\n", "\n", - "# the url of the yaml file containing the model resource description\n", - "rdf_url = \"https://zenodo.org/record/6287342/files/rdf.yaml\"\n", - "\n", - "# filepath to the downloaded model (either zipped package or yaml)\n", - "# to download it from the website:\n", - "# - go to https://bioimage.io/#/?id=10.5281%2Fzenodo.5764892%2F5764893\n", - "# - click the download icon\n", - "# - select \"ilastik\" weight format\n", - "rdf_path = (\n", - " \"/home/pape/Downloads/nuclei-segmentation-boundarymodel_pytorch_state_dict.zip\"\n", - ")" + "model = load_description(\"affable-shark/draft\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "ce884f8c", - "metadata": {}, - "outputs": [], - "source": [ - "# load model from link to rdf.yaml\n", - "model_resource = bioimageio.core.load_resource_description(rdf_url)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c1230b2d", + "id": "2de51dae", "metadata": {}, "outputs": [], "source": [ - "# load model from doi\n", - "model_resource = bioimageio.core.load_resource_description(rdf_doi)" + "# model alternative\n", + "from bioimageio.spec import load_description\n", + "\n", + "model = load_description(\"emotional-cricket/draft\")" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "8329315c", + "cell_type": "markdown", + "id": "2a46da8c", "metadata": {}, - "outputs": [], "source": [ - "# load model from path to the zipped model files\n", - "model_resource = bioimageio.core.load_resource_description(rdf_path)" + "Let's briefly checkout the validation summary created upon loading the description" ] }, { "cell_type": "code", "execution_count": null, - "id": "c23b9f9d", + "id": "ce884f8c", "metadata": {}, "outputs": [], "source": [ - "# the \"model_resource\" instance returned by load_resource_description\n", - "# contains the information stored in the resource description (see https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/model_spec_latest.md)\n", - "\n", - "# we can e.g. check what weight formats are available in the model (pytorch_state_dict for the model used here)\n", - "print(\"Available weight formats for this model:\", model_resource.weights.keys())\n", - "# or where the (downloaded) weight files are stored\n", - "print(\n", - " \"Pytorch state dict weights are stored at:\",\n", - " model_resource.weights[\"pytorch_state_dict\"].source,\n", - ")\n", - "print()\n", - "# or what inputs the model expects\n", - "print(\"The model requires as inputs:\")\n", - "for inp in model_resource.inputs:\n", - " print(\"Input with axes:\", inp.axes, \"and shape\", inp.shape)\n", - "print()\n", - "# and what the model outputs are\n", - "print(\"The model returns the following outputs:\")\n", - "for out in model_resource.outputs:\n", - " print(\"Output with axes:\", out.axes, \"and shape\", out.shape)" + "model.validation_summary.display()" ] }, { @@ -164,17 +116,10 @@ "# including running prediction for the test input(s) and checking that they agree with the test output(s)\n", "# before using a model, it is recommended to check that it properly works with this function\n", "# 'test_model' returns a dict with 'status'='passed'/'failed' and more detailed information\n", - "from bioimageio.core.resource_tests import test_model\n", + "from bioimageio.core import test_model\n", "\n", - "test_result = test_model(model_resource)\n", - "if test_result[\"status\"] == \"failed\":\n", - " print(\"model test:\", test_result[\"name\"])\n", - " print(\"The model test failed with:\", test_result[\"error\"])\n", - " print(\"with the traceback:\")\n", - " print(\"\".join(test_result[\"traceback\"]))\n", - "else:\n", - " test_result[\"status\"] == \"passed\"\n", - " print(\"The model passed all tests\")" + "test_summary = test_model(model)\n", + "test_summary.display()" ] }, { @@ -184,8 +129,8 @@ "source": [ "## Prediction with the model\n", "\n", - "`bioimageio.core` implements functionality to run prediction with models in the `bioimage.io` format.\n", - "This includes functions to run prediction with `xarray.DataArrays` as input and convenience functions to run predictions for images stored on disc." + "`bioimageio.core` implements functionality to run prediction with models desribed in the `bioimage.io` format.\n", + "This includes functions to run prediction on `numpy.ndarray`s/`xarray.DataArrays` as input and convenience functions to run predictions for images stored on disc." ] }, { @@ -196,7 +141,12 @@ "outputs": [], "source": [ "# Load the example image for this model, which is stored in numpy file format.\n", - "input_image = np.load(model_resource.test_inputs[0])" + "from bioimageio.spec.utils import load_array\n", + "from bioimageio.spec.model import v0_5\n", + "\n", + "assert isinstance(model, v0_5.ModelDescr)\n", + "input_image = load_array(model.inputs[0].test_tensor)\n", + "print(f\"array shape: {input_image.shape}\")" ] }, { @@ -206,232 +156,182 @@ "metadata": {}, "outputs": [], "source": [ - "# Create an xarray.DataArray from the input image.\n", - "# DataArrays are like numpy arrays, but they have annotated axes.\n", - "# The axes are used to validate that the axes of the input image match the axes expected by a model.\n", - "input_array = xr.DataArray(input_image, dims=tuple(model_resource.inputs[0].axes))\n", - "# print the axis annotations ('dims') and the shape of the input array\n", - "print(input_array.dims)\n", - "print(input_array.shape)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "808e2ca7", - "metadata": {}, - "outputs": [], - "source": [ - "# Next, create a 'prediction_pipeline'. The prediction_pipeline is used to run prediction with a given model.\n", - "# This means it applies the preprocessing, runs inference with the model and applies the postprocessing.\n", - "\n", - "# The 'devices' argument can be used to specify which device(s) to use for inference with the model.\n", - "# Hence it can be used to specify whether to use the cpu, a single gpu or multiple gpus (not implemented yet).\n", - "# By default (devices=None) a gpu will be used if available and otherwise the cpu will be used.\n", - "devices = None\n", + "from bioimageio.core import Sample, Tensor\n", "\n", - "# The 'weight_format' argument can be used to specify which weight format available in the model to use.\n", - "# By default (weight_format=None) the weight format with highest priority (as defined by bioimageio.core) will be used.\n", - "weight_format = None\n", + "# Create a `Tensor` (light wrapper around an `xarray.DataArray`) from the test input image.\n", + "# `bioimageio.core.Tensors/xarray.DataArrays` are like numpy arrays, but they have annotated axes.\n", + "# The axes are used to validate that the axes of the input image match the axes expected by a model.\n", + "test_input_tensor = Tensor.from_numpy(input_image, dims=model.inputs[0].axes)\n", "\n", - "prediction_pipeline = bioimageio.core.create_prediction_pipeline(\n", - " model_resource, devices=devices, weight_format=weight_format\n", - ")" + "# print the axis annotations ('dims') and the shape of the input array\n", + "print(f\"tensor shape: {test_input_tensor.tagged_shape}\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "13c73742", + "id": "998a5c86", "metadata": {}, "outputs": [], "source": [ - "# Use the prediction pipeline to run prediction for the image we loaded before.\n", - "# The prediction pipeline always returns a tuple (even if the model only has a single output tensor).\n", - "# So we access the first element of the prediction to get the predicted tensor.\n", - "prediction = prediction_pipeline(input_array)[0]\n", - "show_images(\n", - " input_image, prediction, names=[\"image\", \"prediction\"]\n", - ") # show the prediction result" + "# now we can create a sample --- a collection of tensors.\n", + "# In this case our model only has one input, but for models with multiple inputs a `Sample` includes a tensor for each input.\n", + "sample = Sample(members={\"raw\": test_input_tensor}, stat=None, id=\"sample-from-numpy\")\n", + "sample" ] }, { "cell_type": "code", "execution_count": null, - "id": "4f235e9a", + "id": "786dce2c", "metadata": {}, "outputs": [], "source": [ - "# The prediction pipeline expects inputs to have a shape that fits the model exactly.\n", - "# So if the input does not fit the expected input shape the prediction will fail.\n", - "# E.g. if we crop the input to shape [1, 1, 250, 250] it will not work for our example model,\n", - "# which expects a spatial shape that is a multiple of 16\n", - "cropped_image = input_image[:, :, :250, :250]\n", - "cropped_array = xr.DataArray(cropped_image, dims=tuple(model_resource.inputs[0].axes))" + "# shortcut: helper function `create_sample_for_model` to create a sample for a given model directly\n", + "\n", + "from bioimageio.core.digest_spec import create_sample_for_model\n", + "from bioimageio.spec.utils import download\n", + "\n", + "input_paths = {ipt.id: download(ipt.test_tensor).path for ipt in model.inputs}\n", + "print(f\"input paths: {input_paths}\")\n", + "assert isinstance(model, v0_5.ModelDescr)\n", + "sample = create_sample_for_model(\n", + " model=model, inputs=input_paths, sample_id=\"my_demo_sample\"\n", + ")\n", + "sample" ] }, { "cell_type": "code", "execution_count": null, - "id": "f476af51", + "id": "2ff89c9e", "metadata": {}, "outputs": [], "source": [ - "# Applying the prediction pipeline to an image with the wrong shape will fail!\n", - "prediction_pipeline(cropped_array)" + "# shortcut: get test input sample for a given model\n", + "from bioimageio.core.digest_spec import get_test_inputs\n", + "\n", + "test_sample = get_test_inputs(model)\n", + "test_sample" ] }, { "cell_type": "code", "execution_count": null, - "id": "580b0a36", + "id": "808e2ca7", "metadata": {}, "outputs": [], "source": [ - "# Instead, we can use the function `predict_with_padding`, which will pad the image to a shape that fits the model.\n", - "prediction = bioimageio.core.predict_with_padding(prediction_pipeline, cropped_array)\n", - "show_images(\n", - " cropped_image, prediction, names=[\"image\", \"prediction\"]\n", - ") # show the prediction result" + "from bioimageio.core import create_prediction_pipeline\n", + "\n", + "# Next, create a 'prediction_pipeline'. The prediction_pipeline is used to run prediction with a given model.\n", + "# This means it applies the preprocessing, runs inference with the model and applies the postprocessing.\n", + "\n", + "# The 'devices' argument can be used to specify which device(s) to use for inference with the model.\n", + "# Hence it can be used to specify whether to use the cpu, a single gpu or multiple gpus (not implemented yet).\n", + "# By default (devices=None) a gpu will be used if available and otherwise the cpu will be used.\n", + "devices = None\n", + "\n", + "# The 'weight_format' argument can be used to specify which weight format available in the model to use.\n", + "# By default (weight_format=None) the weight format with highest priority (as defined by bioimageio.core) will be used.\n", + "weight_format = None\n", + "\n", + "prediction_pipeline = create_prediction_pipeline(\n", + " model, devices=devices, weight_format=weight_format\n", + ")" ] }, { "cell_type": "code", "execution_count": null, - "id": "b2d6472e", + "id": "13c73742", "metadata": {}, "outputs": [], "source": [ - "# There is also the function `predict_with_tiling`, which will run prediction for patches in a sliding window fashion.\n", - "# This is especially helpful for large inputs that do not fit into the model as a single input.\n", - "\n", - "# The `tiling` argument is used to specify the tile size and the `halo`, which is the part of the patch\n", - "# that is cropped in order to reduce boundary artifacts.\n", - "# Alternatively, `tiling` can also be set to `True`, than the tile size and halo will be deduced from the model config\n", - "# (this is also the default behavior when the `tiling` parameter is not passed).\n", - "tiling = {\n", - " \"tile\": {\"x\": 128, \"y\": 128},\n", - " \"halo\": {\"x\": 16, \"y\": 16},\n", - "} # use a tile size of 128x128 and crop a halo of 16 pixels\n", + "# Use the prediction pipeline to run prediction for the image we loaded before.\n", + "# The prediction pipeline returns a `Sample` object.\n", + "prediction: Sample = prediction_pipeline.predict_sample_without_blocking(sample)\n", "\n", - "# if `verbose` is set to True a progress bar will be printed\n", - "prediction = bioimageio.core.predict_with_tiling(\n", - " prediction_pipeline, cropped_array, tiling=tiling, verbose=True\n", - ")\n", - "show_images(cropped_image, prediction, names=[\"image\", \"prediction\"])" + "# show the prediction result\n", + "show_images({**sample.members, **prediction.members})" ] }, { "cell_type": "markdown", - "id": "4ba91499", + "id": "44b102c2", "metadata": {}, "source": [ - "### Convenience prediction functions\n", - "\n", - "`bioimageio.core` also contains a few convenience functions to directly predict images that are stored on disc:\n", - "- `predict_image` can be used to run prediction for a single image\n", - "- `predict_images` to run prediction for many images" + "there are convenience functions `predict` and `predict_many` that can be used to predict images without explicit creation of a `PredictionPipeline`... " ] }, { "cell_type": "code", "execution_count": null, - "id": "58f6c459", + "id": "34be13c4", "metadata": {}, "outputs": [], "source": [ - "# The convenience function `predict_image` can be used to run prediction for an image stored on disc.\n", - "from bioimageio.core.prediction import predict_image\n", + "from bioimageio.core import predict # , predict_many\n", "\n", - "# The filepath where the output should be stored; supports most common image formats as well as npy fileformat.\n", - "outputs = [\"prediction.tif\"]\n", - "predict_image(model_resource, model_resource.test_inputs, outputs)\n", - "\n", - "# The output tensor contains 2 channels, which is not supported by normal tif.\n", - "# Thus, these 2 channels are stored as 2 separate images.\n", - "fg_pred = imageio.imread(\"prediction-c0.tif\")\n", - "bd_pred = imageio.imread(\"prediction-c1.tif\")\n", - "show_images(\n", - " input_image,\n", - " fg_pred,\n", - " bd_pred,\n", - " names=[\"image\", \"foreground-prediction\", \"boundary-prediction\"],\n", - ")" + "predict(model=model, inputs=sample)\n", + "# predict_many(model=model, inputs=[sample])" + ] + }, + { + "cell_type": "markdown", + "id": "0fae04a0", + "metadata": {}, + "source": [ + "# parts below are not up to date yet" ] }, { "cell_type": "code", "execution_count": null, - "id": "879618df", + "id": "4f235e9a", "metadata": {}, "outputs": [], "source": [ - "# The convenience function `predict_images` can be use to run prediction for many images stored on disc\n", - "# Note: this only works for models which have a single input and output!\n", - "# Here we use a small subset of the dsb challenge data for prediction.\n", - "# The original data is available at https://github.com/stardist/stardist/releases/download/0.1.0/dsb2018.zip.\n", - "# We have added a few images to the repository so that the notebook runs out of the box.\n", - "# Get all paths to the images in the \"example-images\" folder.\n", - "from glob import glob\n", - "\n", - "from bioimageio.core.prediction import predict_images\n", - "\n", - "inputs = glob(\"./example-images/*.png\")\n", - "\n", - "# Create an output folder and specify the output path for each image.\n", - "output_folder = \"./predictions\"\n", - "os.makedirs(output_folder, exist_ok=True)\n", - "outputs = [os.path.join(output_folder, os.path.split(inp)[1]) for inp in inputs]\n", + "# The `PredictionPipeline.predict_sample_without_blocking` and `.predict_sample_block` expects the tensor members of the given sample to have a shape that can be processed by the model exactly.\n", + "# So if the input does not fit the expected input shape the prediction might fail.\n", + "# If the model specifies that an input is `concatenatable`, then we can create tiles/blocks that fit the model description and stitch (possibly overlaying) output tiles/blocks together.\n", + "# Do demonstrate this we load the sample image.\n", + "from pprint import pprint\n", "\n", - "print(len(inputs), \"images for prediction were found\")" + "large_input_sample = create_sample_for_model(\n", + " model=model,\n", + " inputs={ipt.id: ipt.sample_tensor.download().path for ipt in model.inputs},\n", + " sample_id=\"sample input\",\n", + ")\n", + "pprint({m: t.tagged_shape for m, t in large_input_sample.members.items()})" ] }, { "cell_type": "code", "execution_count": null, - "id": "cd7cf46a", + "id": "f476af51", "metadata": {}, "outputs": [], "source": [ - "# The model at hand can only predict images which have a spatial shape that is\n", - "# a multiple of 16. To run with images of other sizes we pass the `padding`\n", - "# argument to `predict_images` and specify that the input is padded to the next bigger\n", - "# size that is divisible by 16 (mode: dynamic).\n", - "# As an alternative `\"mode\": \"fixed\"` will pad to a fixed shape, e.g.\n", - "# `{\"x\": 512, \"y\": 512, \"mode\": \"fixed\"}` will always pad to a size of 512x512.\n", - "# The padding is cropped again after the prediction to restore the input shape.\n", - "padding = {\"x\": 16, \"y\": 16, \"mode\": \"dynamic\"}\n", - "predict_images(model_resource, inputs, outputs, padding=padding, verbose=True)\n", + "# Applying the prediction pipeline to an image with the wrong shape might fail!\n", "\n", - "# check the first input/output\n", - "show_images(\n", - " inputs[0],\n", - " outputs[0].replace(\".png\", \"-c0.png\"),\n", - " outputs[0].replace(\".png\", \"-c1.png\"),\n", - ")" + "_ = prediction_pipeline.predict_sample_without_blocking(large_input_sample)" ] }, { "cell_type": "code", "execution_count": null, - "id": "5abe0590", + "id": "580b0a36", "metadata": {}, "outputs": [], "source": [ - "# Instead of padding, we can also use tiling.\n", - "# Here, we specify a tile size of 256 and a halo (= what's cropped from the tile on either side) of 16.\n", - "tiling = {\n", - " \"tile\": {\"x\": 256, \"y\": 256},\n", - " \"halo\": {\"x\": 16, \"y\": 16},\n", - "}\n", - "predict_images(model_resource, inputs, outputs, tiling=tiling, verbose=True)\n", + "# Instead, we can use the method `predict_sample_with_blocking`, which will block/pad the image to a shape that fits the model.\n", + "large_output_sample = prediction_pipeline.predict_sample_with_blocking(\n", + " large_input_sample\n", + ")\n", "\n", - "# Check the first input output pair.\n", - "show_images(\n", - " inputs[0],\n", - " outputs[0].replace(\".png\", \"-c0.png\"),\n", - " outputs[0].replace(\".png\", \"-c1.png\"),\n", - ")" + "# show the prediction result\n", + "show_images({**large_input_sample.members, **large_output_sample.members})" ] } ],