Skip to content
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

Improve labels transfer to ImageJ #324

Merged
merged 2 commits into from
Mar 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dev-environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ dependencies:
- numpy
- openjdk=11
- pandas
- pyimagej >= 1.4.1
- pyimagej >= 1.5.0
- scyjava >= 1.9.1
- superqt >= 0.7.0
- xarray < 2024.10.0
Expand Down
2 changes: 1 addition & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ dependencies:
- numpy
- openjdk=11
- pandas
- pyimagej >= 1.4.1
- pyimagej >= 1.5.0
- scyjava >= 1.9.1
- superqt >= 0.7.0
- xarray < 2024.10.0
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ dependencies = [
"napari >= 0.4.17",
"numpy",
"pandas",
"pyimagej >= 1.4.1",
"pyimagej >= 1.5.0",
"scyjava >= 1.9.1",
"superqt >= 0.7.0",
"xarray < 2024.10.0",
Expand Down
12 changes: 10 additions & 2 deletions src/napari_imagej/widgets/widget_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from logging import getLogger
from typing import List

from imagej.convert import index_img_to_roi_tree
from jpype import JArray, JByte
from scyjava import jvm_started
from magicgui import magicgui
Expand Down Expand Up @@ -156,7 +157,7 @@ def __init__(self, title: str, error_message: str, *args, **kwargs):


_IMAGE_LAYER_TYPES = (Image, Labels)
_ROI_LAYER_TYPES = (Points, Shapes)
_ROI_LAYER_TYPES = (Labels, Points, Shapes)


class LayerComboBox(QWidget):
Expand Down Expand Up @@ -261,7 +262,7 @@ def __init__(self, viewer: Viewer):
for layer in viewer.layers:
if isinstance(layer, _IMAGE_LAYER_TYPES):
self.imgs.append(layer)
elif isinstance(layer, _ROI_LAYER_TYPES):
if isinstance(layer, _ROI_LAYER_TYPES):
self.rois.append(layer)

# Add combo boxes
Expand Down Expand Up @@ -298,11 +299,18 @@ def pass_to_ij():
j_img = nij.ij.py.to_java(
img, dim_order=self.dims_container.provided_labels()
)
# Labels layers should display the index image
if isinstance(j_img, jc.ImgLabeling):
j_img = nij.ij.py.to_dataset(j_img.getIndexImg())
j_img.setName(img.name)
if roi:
if isinstance(roi, Points):
j_point = nij.ij.py.to_java(roi)
j_roi = jc.DefaultROITree()
j_roi.addROIs(jc.ArrayList([j_point]))
elif isinstance(roi, Labels):
j_label = nij.ij.py.to_java(roi)
j_roi = index_img_to_roi_tree(nij.ij, j_label.getIndexImg())
else:
j_roi = nij.ij.py.to_java(roi)
j_img.getProperties().put("rois", j_roi)
Expand Down
116 changes: 115 additions & 1 deletion tests/widgets/test_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import numpy
import pytest
from napari import Viewer
from napari.layers import Image, Layer, Shapes
from napari.layers import Image, Labels, Layer, Shapes
from napari.viewer import current_viewer
from qtpy.QtCore import QRunnable, Qt, QThreadPool
from qtpy.QtGui import QPixmap
Expand Down Expand Up @@ -395,6 +395,120 @@ def check_active_display():
asserter(check_active_display)


def test_labels_transfer_as_data(
popup_handler, asserter, ij, gui_widget: NapariImageJMenu
):
"""Tests the detailed image exporter"""
if settings.headless():
pytest.skip("Only applies when not running headlessly")

button: ToIJDetailedButton = gui_widget.to_ij_detail
assert not button.isEnabled()

# Add some labels to the viewer
sample_data = numpy.ones((50, 100), dtype=numpy.uint8)
labels = Labels(data=sample_data, name="test_labels")
current_viewer().add_layer(labels)
asserter(lambda: button.isEnabled())

def handle_transfer(widget: QDialog) -> bool:
if not isinstance(widget, DetailExportDialog):
print("Not an AdvancedExportDialog")
return False
if not widget.img_container.combo.currentText() == "test_labels":
print(widget.img_container.combo.currentText())
print("Unexpected starting image text")
return False
if not widget.roi_container.combo.currentText() == "--------":
print("Unexpected starting roi text")
return False
if labels not in widget.roi_container.choices:
print("Labels layer not available")
return False

ok_button = widget.buttons.button(QDialogButtonBox.Ok)
ok_button.clicked.emit()
return True

popup_handler(button.clicked.emit, handle_transfer)

# Assert that the data is now in Fiji
def check_active_display():
if not ij.display().getActiveDisplay():
return False
dataset = ij.display().getActiveDisplay().getActiveView().getData()
if not dataset.getName() == "test_labels":
return False

return True

asserter(check_active_display)


def test_labels_transfer_as_rois(
popup_handler, asserter, ij, gui_widget: NapariImageJMenu
):
"""Tests the detailed image exporter"""
if settings.headless():
pytest.skip("Only applies when not running headlessly")

button: ToIJDetailedButton = gui_widget.to_ij_detail
assert not button.isEnabled()

# Add an image to the viewer
sample_data = numpy.ones((50, 100, 3), dtype=numpy.uint8)
# NB: Unnatural data order used for testing in handler
dims = ("X", "Y", "Z")
sample_data = DataArray(data=sample_data, dims=dims)
image: Image = Image(data=sample_data, name="test_to")
current_viewer().add_layer(image)
asserter(lambda: button.isEnabled())

# Add some labels to the viewer
sample_data = numpy.ones((50, 100), dtype=numpy.uint8)
labels = Labels(data=sample_data, name="test_labels")
current_viewer().add_layer(labels)

def handle_transfer(widget: QDialog) -> bool:
if not isinstance(widget, DetailExportDialog):
print("Not an AdvancedExportDialog")
return False
# Note test_labels is the active layer - it will be the current text
if not widget.img_container.combo.currentText() == "test_labels":
print(widget.img_container.combo.currentText())
print("Unexpected starting image text")
return False
if not widget.roi_container.combo.currentText() == "--------":
print("Unexpected starting roi text")
return False
if labels not in widget.roi_container.choices:
print("Labels layer not available")
return False

widget.img_container.combo.setCurrentText("test_to")
widget.roi_container.combo.setCurrentText("test_labels")

ok_button = widget.buttons.button(QDialogButtonBox.Ok)
ok_button.clicked.emit()
return True

popup_handler(button.clicked.emit, handle_transfer)

# Assert that the data is now in Fiji
def check_active_display():
if not ij.display().getActiveDisplay():
return False
dataset = ij.display().getActiveDisplay().getActiveView().getData()
if not dataset.getName() == "test_to":
return False
if dataset.getProperties().get("rois") is None:
return False

return True

asserter(check_active_display)


def test_settings_no_change(gui_widget: NapariImageJMenu):
"""Ensure that no changes are made when there is no change from the defaults"""
button: SettingsButton = gui_widget.settings_button
Expand Down
Loading