Skip to content

Improve handling of segmentation LUTs#51

Merged
finsberg merged 5 commits into
mainfrom
segmentation-lut
May 12, 2026
Merged

Improve handling of segmentation LUTs#51
finsberg merged 5 commits into
mainfrom
segmentation-lut

Conversation

@Erasdna
Copy link
Copy Markdown
Collaborator

@Erasdna Erasdna commented May 8, 2026

Changed some of how luts are handled in the segmentation classes:

  • Changed luts to index on roi (makes for slightly easier lookup)
  • Changed default lut format to csv (easier to use in freeview)
  • Added a function for generating custom luts with colors from a freeview-compatible colormap (this function could also be improved)

Also modified some statistics computations. Using pandas is faster when computing multiple statistics I believe.

@Erasdna Erasdna requested review from cdaversin and finsberg May 8, 2026 12:28
Copy link
Copy Markdown
Member

@finsberg finsberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job @Erasdna. Also very nice with the updated tests. I have a few suggestions for improvements.

Comment thread src/mritk/segmentation.py Outdated
Comment thread src/mritk/segmentation.py Outdated
Comment on lines +131 to +133
def _preprocess_lut(self, lut: pd.DataFrame) -> pd.DataFrame:
# dummy function for subclasses to override if they need to preprocess the LUT after loading
return lut
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we should like to send in a different lut than the one that is saved on the instance (i.e self.lut), if not we can probably change the signature of the method to

def _preprocess_lut(self) -> None: ...

and modify self.lut inplace.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option could be that you can pass a standalone function with the signature

def preprocess_lut(lut: pd.DataFrame) -> pd.DataFrame: ...

as an argument to the class initializer. That would also make it more customisable.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think probably the first option is better. In the second case you may get unexpected behavior from your lut (like how it is indexed etc)

Comment thread src/mritk/segmentation.py Outdated
Comment on lines +157 to +158
if lut_path is None and seg_path.with_suffix(".csv").exists():
lut_path = seg_path.with_suffix(".csv")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am OK with using .csv suffix, but perhaps the user can provide this suffix as an argument instead, so that it also works with .json. Perhaps we want to support other formats in the future as well?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why not. My main goal here was to be consistent with freesurfer (i.e make it easy to use a lut you can directly load the lut into freeview). I struggled a lot with .json. I believe the code assumed .json previously

Comment thread src/mritk/segmentation.py
Comment thread src/mritk/segmentation.py Outdated
self.lut = lut.set_index(label_column)
self.label_name = label_column
else:
self.label_name = "label" if "label" in self.lut.columns else self.lut.columns[0]
Copy link
Copy Markdown
Member

@finsberg finsberg May 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So use "label" if that is in one of the columns, otherwise use the first column. Is "use the current index" the same? (ref docstrings)

Comment thread src/mritk/segmentation.py Outdated
# Get evenly spaced values between 0 and 1 based on the number of labels
color_indices = np.linspace(0, 0.95, len(labels))
# Sample a colormap
rgb_float = plt.get_cmap(cmap)(color_indices)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is the only reason we need to bring in matplotlib as a dependency. I am on board with that, but it is a heavy dependency so might consider adding it as an optional dependency and move the import inside the function call.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will modify this to use colorsys by default and matplotlib optional

Comment thread tests/test_mri_stats.py
Comment thread pyproject.toml Outdated
"scikit-image",
"pydicom",
"dcm2niix",
"matplotlib>=3.10.9",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not set a lower bound, unless we need to. The will make the library less flexible and harder to install.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was uv, forgot to check this!

Erasdna and others added 3 commits May 11, 2026 08:25
Co-authored-by: Henrik Finsberg <henrikfinsberg@hotmail.com>
@finsberg
Copy link
Copy Markdown
Member

Looks good to me. Thanks @Erasdna

@finsberg finsberg merged commit 7873271 into main May 12, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants