-
Notifications
You must be signed in to change notification settings - Fork 7
Cellpose normalization #738
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cellpose normalization #738
Conversation
Thanks a lot for the PR @fstur ! Very useful functionality to have indeed. As discussed today, @lorenzocerrone and I refactored the Cellpose input models to handle the normalization aspects better: Normalization can now be specified when selecting the channel. This needed a bit of reworking to make it work with the Pydantic models afterwards. Things that remain:
Unrelated (cc @tcompa ):
It's also what made the checks above fail before. I also added an exception for flake8 W503, which conflicts with black in how to format the cellpose tests. |
Agreed. This handling of
Sure, nothing against it. It's not like pre-commit takes very long ( |
Hey @fstur Can you check whether this version now works for you? I'd be happy with merging this into fractal tasks core & then providing the normalization approach for both channels. @tcompa Can you review this PR? One of the slightly weird things I'm doing is making @lorenzocerrone If you also want to have a look, open for further feedback :) But not a requirement that you review the PR. |
This comment is not relevant any more as of b63547f, right? |
Right! Now I made the normalize part of channel have a default. Would be great if you could review if the use of Field with default_factory mode is a good approach for this |
Re: I could not find a better way for making Higher-level: I agree about
and this is what blocks us towards a clean version where
I think it's worth continuing this discussion (in a different issue, indeed), because it sounds like a possibly frequent pattern. Note that the outcome in principle could also look like "refactor the [continues below, concerning |
Re: Using
The main difference is that the produced JSON Schemas are different. In the example below, I reproduce a minimal example with two function arguments (one always using Python from typing import Optional, Literal
from pydantic import BaseModel, Field
class Normalizer(BaseModel):
"""
Description of Normalizer
Attributes:
type: Description of type
time: Description of time
"""
type: Literal["option_a", "option_b"] = "option_a"
class MyModelDefaultFactory(BaseModel):
"""
Description of ChannelModel
Attributes:
normalizer: Description of normalizer.
time: Description of time.
attr: Description of attr1.
"""
normalizer: Normalizer = Field(default_factory=Normalizer)
time: float = Field(default_factory=time.perf_counter)
attr: Optional[str] = None
class MyModelObjectInit(BaseModel):
"""
Description of ChannelModel
Attributes:
normalizer: Description of normalizer.
time: Description of time.
attr: Description of attr1.
"""
normalizer: Normalizer = Normalizer()
time: float = time.perf_counter()
attr: Optional[str] = None
@validate_arguments
def function(
arg_default_factory: MyModelDefaultFactory = Field(default_factory=MyModelDefaultFactory),
arg2: MyModelObjectInit = MyModelObjectInit(),
):
"""
Description of function
Args:
arg_default_factory: Description of arg_default_factory
arg2: Description of arg2
"""
pass which leads to {
"title": "Function",
"type": "object",
"properties": {
"arg_default_factory": {
"$ref": "#/definitions/MyModelDefaultFactory",
"title": "Arg_Default_Factory",
"description": "Description of arg_default_factory"
},
"arg2": {
"title": "Arg2",
"default": {
"normalizer": {
"type": "option_a"
},
"time": 24457.131491715,
"attr": null
},
"allOf": [
{
"$ref": "#/definitions/MyModelObjectInit"
}
],
"description": "Description of arg2"
}
},
"additionalProperties": false,
"definitions": {
"Normalizer": {
"title": "Normalizer",
"description": "Description of Normalizer",
"type": "object",
"properties": {
"type": {
"title": "Type",
"default": "option_a",
"enum": [
"option_a",
"option_b"
],
"type": "string"
}
}
},
"MyModelDefaultFactory": {
"title": "MyModelDefaultFactory",
"description": "Description of ChannelModel",
"type": "object",
"properties": {
"normalizer": {
"$ref": "#/definitions/Normalizer",
"title": "Normalizer"
},
"time": {
"title": "Time",
"type": "number"
},
"attr": {
"title": "Attr",
"type": "string"
}
}
},
"MyModelObjectInit": {
"title": "MyModelObjectInit",
"description": "Description of ChannelModel",
"type": "object",
"properties": {
"normalizer": {
"title": "Normalizer",
"default": {
"type": "option_a"
},
"allOf": [
{
"$ref": "#/definitions/Normalizer"
}
]
},
"time": {
"title": "Time",
"default": 24457.131491715,
"type": "number"
},
"attr": {
"title": "Attr",
"type": "string"
}
}
}
}
} The main difference here is the presence of "default": {
"normalizer": {
"type": "option_a"
},
"time": 24457.131491715,
"attr": null
}, for Does this matter for
|
TL;DR re:
If I were to choose, I would replace channel2: CellposeChannel2InputModel = Field(default_factory=CellposeChannel2InputModel) with channel2: CellposeChannel2InputModel = CellposeChannel2InputModel() which creates a more transparent JSON Schema that includes - "$ref": "#/definitions/CellposeChannel2InputModel",
"title": "Channel2",
+ "default": {
+ "wavelength_id": null,
+ "label": null,
+ "normalize": {
+ "type": "default",
+ "lower_percentile": null,
+ "upper_percentile": null,
+ "lower_bound": null,
+ "upper_bound": null
+ }
+ },
+ "allOf": [
+ {
+ "$ref": "#/definitions/CellposeChannel2InputModel"
+ }
+ ], but this is just personal preference and I have no specific reasons for it. |
This closes my feedback on the arguments/schema-related part of the PR. I can still review the rest of the PR, if it's useful, but that will be a bit later. |
Thanks for the review & the reasoning on trade-offs @tcompa . We'll stay with this version for the time being and will evaluate later whether we'll have a strong opinion one way or the other for Fractal tasks. |
Thanks @fstur for starting this work, I'm now merging this PR so we have the updated version of the Cellpose task with easier input handling & normalize options for both channels available in main :) |
Checklist before merging
CHANGELOG.md
The cellpose_segmentation task currently accepts a custom normalization option. The problem is, that if two channels are used for the cellpose segmentation, both channels are normalized with the same
CellposeCustomNormalizer
, but one might want to normalize them with different parameters.I added an optional
normalize2
argument tocellpose_segmentation
, and updatedsegement_ROI
accordingly.No tests are implemented yet.