Setting scale bar units in other than pixels (real coordinates)

Hi!

I apologise if the answer to this question is obvious - Is there a way to change the scale of the scale bar (pun intended)? I am trying to display it in real-world units (um) instead of pixels given that I know the pixel size of the image but I haven´t found how to access this value yet. So far I figured out that there a number of fields in the EventedModel representation of the scale:

napari.Viewer.scale_bar

that apparently are actually the ones accessed in the view → scale bar in the GUI. However, I can´t neither retrieve nor change the value of the scale from there. Where in the code should I be looking at? Any pointers will be very welcome, thanks (and congrats) to all the napari team!

Hi @Diego_AC adding support for physical units is being actively discussed here Allow scale and translation parameters to have units · Issue #1701 · napari/napari · GitHub and in a couple other places on the repo. We don’t have support for it yet, but there are a couple folks working on it! cc @jni

1 Like

@Diego_AC even though we don’t have real units support, we do have support for a “unitless” scale for the image. Depending on how you are calling napari, you should be able to set the scale in the layer with:

microns_per_pixel = [0.45, 0.45]  # one number per dimension
viewer.add_image(
    image_array, scale=microns_per_pixel
)

Or, opening the console:

layer = viewer.layers[0]  # or whichever layer your image is in
layer.scale = [0.45, 0.45]  # one number per dimension

Then the scale bar will show the appropriate number of micrometers, it just won’t say (or know!) that it’s micrometers.

I hope this helps while we work on adding actual physical units!

2 Likes

Thank you both @jni @sofroniewn this is exactly what I needed :slight_smile:

2 Likes

Hi, I’d like to add that to improve user experience, it is possible to monkey-patch the scale bar to add units manually until physical units are supported:

def add_scale_bar_units(viewer: napari.Viewer, units: str) -> None:
    scale_bar: VispyScaleBarVisual = viewer.window.qt_viewer.scale_bar

    old_on_zoom_change = scale_bar._on_zoom_change

    def _on_zoom_change(event: Event):
        old_on_zoom_change(event)
        # scale_bar.text_node.text += units
        if not scale_bar.text_node.text.endswith(units):
            scale_bar.text_node.text += units

    scale_bar._on_zoom_change = _on_zoom_change

Edit: fixed bug where units str is appended over and over on drag.

2 Likes

This is awesome @ahn ! I did not know about the text attribute, that helps a lot!