Skip to content

Commit 3c1892d

Browse files
committed
Add tests for ROI manipulation: copy/paste, import/export functionality
1 parent 553cebb commit 3c1892d

File tree

2 files changed

+529
-0
lines changed

2 files changed

+529
-0
lines changed
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
2+
3+
"""Image ROI manipulation application test (copy/paste, import/export)"""
4+
5+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
6+
# guitest: show
7+
8+
from __future__ import annotations
9+
10+
import os
11+
import tempfile
12+
from typing import TYPE_CHECKING
13+
14+
from sigima.io import read_roi
15+
from sigima.objects import NewImageParam, create_image_roi
16+
from sigima.tests.data import create_multigaussian_image
17+
18+
from datalab.env import execenv
19+
from datalab.objectmodel import get_uuid
20+
from datalab.tests import datalab_test_app_context
21+
22+
if TYPE_CHECKING:
23+
from datalab.gui.panel.image import ImagePanel
24+
25+
SIZE = 200
26+
27+
# Image ROIs:
28+
IROI1 = [100, 100, 75, 100] # Rectangle
29+
IROI2 = [66, 100, 50] # Circle
30+
IROI3 = [100, 100, 100, 150, 150, 133] # Polygon
31+
32+
33+
def test_image_roi_copy_paste():
34+
"""Test image ROI copy and paste functionality"""
35+
with datalab_test_app_context(console=False) as win:
36+
execenv.print("Image ROI Copy/Paste test:")
37+
panel: ImagePanel = win.imagepanel
38+
param = NewImageParam.create(height=SIZE, width=SIZE)
39+
40+
# Create first image with ROI
41+
ima1 = create_multigaussian_image(param)
42+
ima1.title = "Image with ROI"
43+
roi1 = create_image_roi("rectangle", IROI1)
44+
roi1.add_roi(create_image_roi("circle", IROI2))
45+
ima1.roi = roi1
46+
panel.add_object(ima1)
47+
48+
# Create second image without ROI
49+
ima2 = create_multigaussian_image(param)
50+
ima2.title = "Image without ROI"
51+
panel.add_object(ima2)
52+
53+
# Create third image without ROI
54+
ima3 = create_multigaussian_image(param)
55+
ima3.title = "Image without ROI 2"
56+
panel.add_object(ima3)
57+
58+
execenv.print(" Initial state:")
59+
execenv.print(f" Image 1 ROI: {ima1.roi is not None}")
60+
execenv.print(f" Image 2 ROI: {ima2.roi is not None}")
61+
execenv.print(f" Image 3 ROI: {ima3.roi is not None}")
62+
63+
# Select first image and copy its ROI
64+
panel.objview.set_current_item_id(get_uuid(ima1))
65+
panel.copy_roi()
66+
execenv.print(" Copied ROI from Image 1")
67+
68+
# Select second image and paste ROI
69+
panel.objview.set_current_item_id(get_uuid(ima2))
70+
panel.paste_roi()
71+
execenv.print(" Pasted ROI to Image 2")
72+
73+
# Verify that ima2 now has the same ROI as ima1
74+
assert ima2.roi is not None, "Image 2 should have ROI after paste"
75+
assert len(ima2.roi) == len(ima1.roi), "ROI should have same number of regions"
76+
execenv.print(f" Image 2 now has {len(ima2.roi)} ROI regions")
77+
78+
# Select third image and paste ROI (should create new ROI)
79+
panel.objview.set_current_item_id(get_uuid(ima3))
80+
panel.paste_roi()
81+
execenv.print(" Pasted ROI to Image 3")
82+
83+
assert ima3.roi is not None, "Image 3 should have ROI after paste"
84+
assert len(ima3.roi) == len(ima1.roi), "ROI should have same number of regions"
85+
execenv.print(f" Image 3 now has {len(ima3.roi)} ROI regions")
86+
87+
# Test pasting to image that already has ROI (should combine)
88+
panel.objview.set_current_item_id(get_uuid(ima2))
89+
panel.copy_roi()
90+
execenv.print(" Copied ROI from Image 2")
91+
92+
# Add a different ROI to ima1
93+
roi_new = create_image_roi("polygon", IROI3)
94+
ima1.roi.add_roi(roi_new)
95+
original_roi_count = len(ima1.roi)
96+
execenv.print(f" Image 1 now has {original_roi_count} ROI regions")
97+
98+
# Paste the ROI from ima2 into ima1 (should combine)
99+
panel.objview.set_current_item_id(get_uuid(ima1))
100+
panel.paste_roi()
101+
execenv.print(" Pasted ROI to Image 1 (should combine)")
102+
103+
# Get fresh reference to ima1 from panel
104+
ima1_updated = panel.objmodel[get_uuid(ima1)]
105+
assert ima1_updated.roi is not None, "Image 1 should still have ROI"
106+
# After combining, ima1 should have more regions than before
107+
assert len(ima1_updated.roi) >= original_roi_count, (
108+
f"Expected at least {original_roi_count} ROI regions, "
109+
f"got {len(ima1_updated.roi)}"
110+
)
111+
execenv.print(
112+
f" Image 1 now has {len(ima1_updated.roi)} ROI regions (combined)"
113+
)
114+
115+
execenv.print(" ✓ Image ROI copy/paste test passed")
116+
117+
118+
def test_image_roi_copy_paste_multiple_selection():
119+
"""Test image ROI paste to multiple selected images"""
120+
with datalab_test_app_context(console=False) as win:
121+
execenv.print("Image ROI Copy/Paste with multiple selection test:")
122+
panel: ImagePanel = win.imagepanel
123+
param = NewImageParam.create(height=SIZE, width=SIZE)
124+
125+
# Create source image with ROI
126+
ima_src = create_multigaussian_image(param)
127+
ima_src.title = "Source with ROI"
128+
roi = create_image_roi("rectangle", IROI1)
129+
roi.add_roi(create_image_roi("circle", IROI2))
130+
ima_src.roi = roi
131+
panel.add_object(ima_src)
132+
133+
# Create multiple target images without ROI
134+
target_images = []
135+
for i in range(3):
136+
ima = create_multigaussian_image(param)
137+
ima.title = f"Target image {i + 1}"
138+
panel.add_object(ima)
139+
target_images.append(ima)
140+
141+
execenv.print(f" Created {len(target_images)} target images")
142+
143+
# Copy ROI from source
144+
panel.objview.set_current_item_id(get_uuid(ima_src))
145+
panel.copy_roi()
146+
execenv.print(" Copied ROI from source image")
147+
148+
# Select all target images
149+
target_uuids = [get_uuid(img) for img in target_images]
150+
panel.objview.set_current_item_id(target_uuids[0])
151+
for uuid in target_uuids[1:]:
152+
panel.objview.set_current_item_id(uuid, extend=True)
153+
154+
execenv.print(f" Selected {len(target_uuids)} target images")
155+
156+
# Paste ROI to all selected images
157+
panel.paste_roi()
158+
execenv.print(" Pasted ROI to all selected images")
159+
160+
# Verify all target images have ROI
161+
for i, img in enumerate(target_images):
162+
assert img.roi is not None, f"Target image {i + 1} should have ROI"
163+
assert len(img.roi) == len(ima_src.roi), (
164+
f"Target image {i + 1} should have {len(ima_src.roi)} ROI regions"
165+
)
166+
execenv.print(f" Target image {i + 1}: {len(img.roi)} ROI regions ✓")
167+
168+
execenv.print(" ✓ Multiple selection paste test passed")
169+
170+
171+
def test_image_roi_import_export():
172+
"""Test image ROI import and export to/from file functionality"""
173+
with datalab_test_app_context(console=False) as win:
174+
execenv.print("Image ROI Import/Export test:")
175+
panel: ImagePanel = win.imagepanel
176+
param = NewImageParam.create(height=SIZE, width=SIZE)
177+
178+
# Create first image with ROI
179+
ima1 = create_multigaussian_image(param)
180+
ima1.title = "Image with ROI"
181+
roi1 = create_image_roi("rectangle", IROI1)
182+
roi1.add_roi(create_image_roi("circle", IROI2))
183+
roi1.add_roi(create_image_roi("polygon", IROI3))
184+
ima1.roi = roi1
185+
panel.add_object(ima1)
186+
187+
original_roi_count = len(ima1.roi)
188+
execenv.print(f" Image 1 has {original_roi_count} ROI regions")
189+
190+
# Export ROI to file
191+
roi_file = tempfile.mktemp(suffix=".dlabroi")
192+
try:
193+
execenv.print(" Exporting ROI to temporary file")
194+
195+
# Select first image and export its ROI
196+
panel.objview.set_current_item_id(get_uuid(ima1))
197+
panel.export_roi_to_file(roi_file)
198+
execenv.print(" ✓ ROI exported")
199+
200+
# Verify file was created
201+
assert os.path.exists(roi_file), "ROI file should have been created"
202+
203+
# Read the exported ROI directly to verify content
204+
exported_roi = read_roi(roi_file)
205+
assert len(exported_roi) == original_roi_count, (
206+
f"Exported ROI should have {original_roi_count} regions"
207+
)
208+
execenv.print(f" ✓ Exported ROI has {len(exported_roi)} regions")
209+
210+
# Create second image without ROI
211+
ima2 = create_multigaussian_image(param)
212+
ima2.title = "Image without ROI"
213+
panel.add_object(ima2)
214+
assert ima2.roi is None, "Image 2 should not have ROI initially"
215+
216+
# Import ROI from file to second image
217+
panel.objview.set_current_item_id(get_uuid(ima2))
218+
panel.import_roi_from_file(roi_file)
219+
execenv.print(" Imported ROI to Image 2")
220+
221+
# Get fresh reference to ima2 from panel
222+
ima2_updated = panel.objmodel[get_uuid(ima2)]
223+
assert ima2_updated.roi is not None, "Image 2 should have ROI after import"
224+
assert len(ima2_updated.roi) == original_roi_count, (
225+
f"Imported ROI should have {original_roi_count} regions"
226+
)
227+
execenv.print(f" ✓ Image 2 now has {len(ima2_updated.roi)} ROI regions")
228+
229+
# Test importing ROI to image that already has ROI (should combine)
230+
ima3 = create_multigaussian_image(param)
231+
ima3.title = "Image with existing ROI"
232+
roi3 = create_image_roi("circle", [150, 150, 40])
233+
ima3.roi = roi3
234+
panel.add_object(ima3)
235+
initial_roi_count = len(ima3.roi)
236+
execenv.print(f" Image 3 has {initial_roi_count} ROI region initially")
237+
238+
# Import ROI (should combine with existing)
239+
panel.objview.set_current_item_id(get_uuid(ima3))
240+
panel.import_roi_from_file(roi_file)
241+
execenv.print(" Imported ROI to Image 3 (should combine)")
242+
243+
# Get fresh reference to ima3 from panel
244+
ima3_updated = panel.objmodel[get_uuid(ima3)]
245+
assert ima3_updated.roi is not None, "Image 3 should still have ROI"
246+
# After combining, should have more regions
247+
assert len(ima3_updated.roi) >= initial_roi_count, (
248+
f"Expected at least {initial_roi_count} ROI regions, "
249+
f"got {len(ima3_updated.roi)}"
250+
)
251+
execenv.print(
252+
f" ✓ Image 3 now has {len(ima3_updated.roi)} ROI regions (combined)"
253+
)
254+
finally:
255+
# Clean up temporary file
256+
if os.path.exists(roi_file):
257+
try:
258+
os.unlink(roi_file)
259+
except (PermissionError, OSError):
260+
pass # Ignore cleanup errors on Windows
261+
262+
execenv.print(" ✓ Image ROI import/export test passed")
263+
264+
265+
if __name__ == "__main__":
266+
test_image_roi_copy_paste()
267+
test_image_roi_copy_paste_multiple_selection()
268+
test_image_roi_import_export()

0 commit comments

Comments
 (0)