class scale_range(SharedProcessingSchema): bioimageio_description = "Scale with percentiles." mode = fields.ProcMode(required=True, valid_modes=("per_dataset", "per_sample")) axes = fields.Axes( required=True, valid_axes="czyx", bioimageio_description="The subset of axes to normalize jointly. For example xy to normalize the two image " "axes for 2d data jointly. The batch axis (b) is not valid here.", ) min_percentile = fields.Float( default=0, validate=field_validators.Range(0, 100, min_inclusive=True, max_inclusive=False), bioimageio_description="The lower percentile used for normalization, in range 0 to 100. Default value: 0.", ) max_percentile = fields.Float( default=100, validate=field_validators.Range(1, 100, min_inclusive=False, max_inclusive=True), bioimageio_description="The upper percentile used for normalization, in range 1 to 100. Has to be bigger " "than min_percentile. Default value: 100. The range is 1 to 100 instead of 0 to 100 to avoid mistakenly " "accepting percentiles specified in the range 0.0 to 1.0.", ) eps = fields.Float( missing=1e-6, bioimageio_description="Epsilon for numeric stability: " "`out = (tensor - v_lower) / (v_upper - v_lower + eps)`; " "with `v_lower,v_upper` values at the respective percentiles. Default value: 10^-6.", ) @validates_schema def min_smaller_max(self, data, **kwargs): min_p = data.get("min_percentile", 0) max_p = data.get("max_percentile", 100) if min_p >= max_p: raise ValidationError(f"min_percentile {min_p} >= max_percentile {max_p}")
class scale_linear(SharedProcessingSchema): bioimageio_description = "Fixed linear scaling." axes = fields.Axes( required=True, valid_axes="czyx", bioimageio_description="The subset of axes to scale jointly. " "For example xy to scale the two image axes for 2d data jointly. " "The batch axis (b) is not valid here.", ) gain = fields.Array( fields.Float(), missing=fields.Float(missing=1.0), bioimageio_description="multiplicative factor" ) # todo: check if gain match input axes offset = fields.Array( fields.Float(), missing=fields.Float(missing=0.0), bioimageio_description="additive term" ) # todo: check if offset match input axes @validates_schema def either_gain_or_offset(self, data, **kwargs): gain = data["gain"] if isinstance(gain, (float, int)): gain = [gain] offset = data["offset"] if isinstance(offset, (float, int)): offset = [offset] if all(g == 1.0 for g in gain) and all(off == 0 for off in offset): raise ValidationError("Specify gain!=1.0 or offset!=0.0")
class zero_mean_unit_variance(SharedProcessingSchema): bioimageio_description = "Subtract mean and divide by variance." mode = fields.ProcMode(required=True) axes = fields.Axes( required=True, valid_axes="czyx", bioimageio_description="The subset of axes to normalize jointly. For example xy to normalize the two image " "axes for 2d data jointly. The batch axis (b) is not valid here.", ) mean = fields.Array( fields.Float(), bioimageio_description="The mean value(s) to use for `mode == fixed`. For example `[1.1, 2.2, 3.3]` in the " "case of a 3 channel image where the channels are not normalized jointly.", ) # todo: check if means match input axes (for mode 'fixed') std = fields.Array( fields.Float(), bioimageio_description="The standard deviation values to use for `mode == fixed`. Analogous to mean.", ) eps = fields.Float( missing=1e-6, bioimageio_description="epsilon for numeric stability: `out = (tensor - mean) / (std + eps)`. " "Default value: 10^-6.", ) @validates_schema def mean_and_std_match_mode(self, data, **kwargs): if data["mode"] == "fixed" and ("mean" not in data or "std" not in data): raise ValidationError( "`kwargs` for 'zero_mean_unit_variance' preprocessing with `mode` 'fixed' require additional " "`kwargs`: `mean` and `std`." ) elif data["mode"] != "fixed" and ("mean" in data or "std" in data): raise ValidationError( "`kwargs`: `mean` and `std` for 'zero_mean_unit_variance' preprocessing are only valid for `mode` 'fixed'." )
class _TensorBase(_BioImageIOSchema): name = fields.String( required=True, validate=field_validators.Predicate("isidentifier"), bioimageio_description="Tensor name. No duplicates are allowed.", ) description = fields.String() axes = fields.Axes( required=True, bioimageio_description= """Axes identifying characters from: bitczyx. Same length and order as the axes in `shape`. | character | description | | --- | --- | | b | batch (groups multiple samples) | | i | instance/index/element | | t | time | | c | channel | | z | spatial dimension z | | y | spatial dimension y | | x | spatial dimension x |""", ) data_type = fields.String( required=True, bioimageio_description= "The data type of this tensor. For inputs, only `float32` is allowed and the consumer " "software needs to ensure that the correct data type is passed here. For outputs can be any of `float32, " "float64, (u)int8, (u)int16, (u)int32, (u)int64`. The data flow in bioimage.io models is explained " "[in this diagram.](https://docs.google.com/drawings/d/1FTw8-Rn6a6nXdkZ_SkMumtcjvur9mtIhRqLwnKqZNHM/edit).", ) data_range = fields.Tuple( (fields.Float(allow_nan=True), fields.Float(allow_nan=True)), bioimageio_description= "Tuple `(minimum, maximum)` specifying the allowed range of the data in this tensor. " "If not specified, the full data range that can be expressed in `data_type` is allowed.", ) shape: fields.Union processing_name: str @validates_schema def validate_processing_kwargs(self, data, **kwargs): axes = data.get("axes", []) processing_list = data.get(self.processing_name, []) for processing in processing_list: kwargs = processing.kwargs or {} kwarg_axes = kwargs.get("axes", "") if any(a not in axes for a in kwarg_axes): raise ValidationError( "`kwargs.axes` needs to be subset of axes")
class ImplicitOutputShape(SharedBioImageIOSchema): reference_tensor = fields.String(required=True, bioimageio_description="Name of the reference tensor.") scale = fields.List( fields.Float(), required=True, bioimageio_description="'output_pix/input_pix' for each dimension." ) offset = fields.List( fields.Float(), required=True, bioimageio_description="Position of origin wrt to input. Multiple of 0.5." ) @validates_schema def matching_lengths(self, data, **kwargs): scale = data["scale"] offset = data["offset"] if len(scale) != len(offset): raise ValidationError(f"scale {scale} has to have same length as offset {offset}!") @validates("offset") def double_offset_is_int(self, value: List[float]): for v in value: if 2 * v != int(2 * v): raise ValidationError(f"offset {v} in {value} not a multiple of 0.5!")
class clip(SharedProcessingSchema): bioimageio_description = "Set tensor values below min to min and above max to max." min = fields.Float(required=True, bioimageio_description="minimum value for clipping") max = fields.Float(required=True, bioimageio_description="maximum value for clipping")
class binarize(SharedProcessingSchema): bioimageio_description = ( "Binarize the tensor with a fixed threshold, values above the threshold will be set to one, values below " "the threshold to zero." ) threshold = fields.Float(required=True, bioimageio_description="The fixed threshold")