Napari plugin for scikit-image regionprops

Hi #scikit-image folks,
CC @emmanuelle @jni,

I just started a napari plugin for regionprops in scikit image because the underlying code was removed while refactoring a related project and I need such an interactive way of browsing object properties.

If the scikit-image team has a place for accumulating napari-related stuff (and did not develop the same thing already), I’m happy to contribute that plugin and maintain it mid/long term. If not, I’m happy to deploy/distribute it independently.

Let me know what you think. :slight_smile:

Best,
Robert

5 Likes

This repo by @jluethi seems related:

See the discussion here:

3 Likes

There are a lot of parallel efforts at the moment to make tables showing various layer properties. This repo from @kevinyamauchi, while not specifically for regionprops, has a lot of similar functionality as well: GitHub - kevinyamauchi/napari-properties-viewer: A viewer for napari layer properties

early days for sure, will be good to see what people come up with!

3 Likes

Awesome, great hints @imagejan and @talley :slight_smile:

It looks like @jluethi’s plugin needs to load a dataframe from disk, while mine could write a dataframe to disk.

@jluethi, would you be interested in a PR, which brings my stuff to your repository? Again, I’m happy to help maintaining this mid/long term.

And if we manage to get @kevinyamauchi’s table into it too, we could click on table entries and see which object this corresponds to in the image :smiley:

3 Likes

Looks very cool @haesleinhuepf! And yes, so far, the data input is being handled slightly differently in those 2 approaches. I like that your approach is able to calculate features directly from the plugin. I also want to be able to visualize features that were calculated earlier in other libraries (e.g. 3D feature measurements that take quite a bit longer to run, thus live calculation wouldn’t be feasible) or where e.g. corrections across a whole experiment was applied to the features beforehand (and thus could not be calculated per site).
I also looked into ways of providing the data directly from memory, e.g. from a running jupyter notebook, which I can’t currently make work.

As long as we can keep both ways running without interference, I’d be happy if you want to add the ability to calculate features directly on the image to the plugin! The core part of visualizing features on the label images is the same in both approaches anyway :slight_smile:
I haven’t played with having multiple dock widgets in the same plugin, but that may come in helpful here. E.g. one widget to calculate features (like you show it in your initial post), and the second (the one I currently have) to either select the calculated df or read one in from disk + play with the visualization options like colormaps & rescaling.

I haven’t looked into combining this with showing the feature table or even linking feature table clicks to identify objects, but sure, that would be great too! :slight_smile:

2 Likes

for those playing with tables and using magicgui, just want to mention that magicgui has a Table widget that behaves very much like a dict of lists (where the keys are column names and the lists are column values). It’s poorly documented in the web-based docs, but relatively fully featured. There’s an example here, and a somewhat complete docstring here.

Hey @talley ,

cool! Is this really a QWidget? I just tried to add it to a layout and received an error:

Code

view = Table(value=table)
#  ....
widget.layout().addWidget(view.show())

Error message:

    99     widget.layout().addWidget(view.show())
        widget.layout.addWidget = undefined
        view.show = 
    100 
    101     return widget

TypeError: arguments did not match any overloaded call:
  addWidget(self, QWidget): argument 1 has unexpected type 'Table'
  addWidget(self, QWidget, int, int, alignment: Union[Qt.Alignment, Qt.AlignmentFlag] = Qt.Alignment()): argument 1 has unexpected type 'Table'
  addWidget(self, QWidget, int, int, int, int, alignment: Union[Qt.Alignment, Qt.AlignmentFlag] = Qt.Alignment()): argument 1 has unexpected type 'Table'

Any hint is appreciated!

Is this really a QWidget?

Like all magicgui widgets, it wraps a backend widget… and for all magicgui widgets, you can access the backend widget at mgui_widget.native

If you’d like to combine magicgui widgets and Qt layouts, you can, but you need to add the .native widget, not the magicgui widget.

mgui_table = Table(value=table)
#  ....
widget.layout().addWidget(mgui_table.native)

Thanks @talley , that works like a charm! :slight_smile:

I think for the moment it’s perfectly fine to store the csv-file on disk intermediately and reload it. In that way there is also no interference. And later it might make sense to have a Table parameter in a magicgui-annotated function. I haven’t tested this, but I assume @talley’s table is thought like this.

Awesome! Alright, I sent you a pull-request. You’ll see I was just adding a function and a hook (no widget).

I think regionprops and your feature visualization work nicely together :slight_smile:

And I think in a future version of napari your widget and my function will show up in the same menu.

image

1 Like

yep, that’s exactly the plan. you should be able to do:

@magicgui(result_widget=True)
def somefunc(...) -> Table:
    ...

and have it append the table to the gui… hooking up interactive events there still needs work though

2 Likes

Of course. Good things take while :slightly_smiling_face: How about saving the table or copying it to the clipboard?

you can export the tables to a pandas dataframe, or just the data part to a numpy array… see the bottom part of the example:

# export to numpy or pandas
table.data.to_numpy()
table.to_dataframe()

from that, you can handle clipboard logic, or writing the dataframe to whatever format you want using the pandas API…

(the most useful methods are documented in this part of the docstring)

1 Like

Ah sorry, I was unclear in my question: I was wondering how to do this from the GUI :wink: Because if this would be implemented, I could get rid of these two buttons and maybe the whole method around building that GUI:
image

There’s no button in the Table widget that does what you want ( “click this to pop up an open file dialog and save to CSV”), so what you’re doing there is very reasonable… I would just modify the clicked callbacks to take the data from the table, rather than what’s passed into the function (which has the risk of being stale when the button is actually clicked):

    view = Table(value=table)    
    save_button = QPushButton("Save as csv...")

    @save_button.clicked.connect
    def save_trigger():
        filename, _ = QFileDialog.getSaveFileName(save_button, "Save as csv...", ".", "*.csv")
        view.to_dataframe().to_csv(filename)

1 Like

Great hint. Thanks! :slight_smile:

1 Like

@haesleinhuepf I merged it into the napari-feature-visualization repository now and updated the readme a bit :slight_smile:

Thanks a lot for the contribution! I have not released this plugin via pipy yet, as some of it depends on napar/magicgui code that is not in a released version as of now. After that, I think we can make an initial release of the plugin to make it discoverable within napari and go from there.

2 Likes

Awesome, thanks @jluethi ! Looking forward to the release. There is indeed some cool stuff coming with the next napari, so worth waiting a moment. It’s a pleasure working with you! Let me know if I can do anything :sun_with_face:

It’s a pleasure from my side as well! :slight_smile:
Well, I’m not actively using the functionality, so would be cool if you look through and decide if there’s something to be added. Feel free to open issues in the repository for things that should be added. E.g. I created one for 3D support for the regionsprops part. At least for my test case, that wasn’t working. Not urgent, but then we collect what could be added :slight_smile:

Also, maybe I’m doing something wrong, but I can’t find the plugin anymore in the napari interface in the master version I have running in my main env (but it’s still fine in an env running the 0.4.7 napari version). Can you reproduce that? We can also wait until the release to see. Do you know if the function plugins were moved? Do we need to do something different than in the past to expose the widget to napari?

1 Like

Hehe, in my case it’s the opposite: I can see the plugins in napari 0.4.8.dev84+g097dca87 (current master) but I don’t see it in napari 0.4.7. The menu Plugins > Plugin Errors allow you to read issues that happened during plugin initialization. Maybe there was an error?

This is how I see it currently:

anytime your plugin doesn’t seem to be detected by napari, try running

napari --plugin-info -v

usually, it will have a useful traceback

2 Likes