tests.test_events

  1import os
  2import time
  3
  4import pytest
  5
  6import cv2
  7import numpy as np
  8import pandas as pd
  9
 10from csi_images.csi_scans import Scan
 11from csi_images.csi_tiles import Tile
 12from csi_images.csi_events import Event, EventArray
 13from csi_images import csi_images
 14
 15if os.environ.get("DEBIAN_FRONTEND") == "noninteractive":
 16    SHOW_PLOTS = False
 17else:
 18    # Change this to your preference for local testing, but commit as True
 19    SHOW_PLOTS = True
 20
 21
 22@pytest.fixture
 23def bzscan():
 24    return Scan.load_txt("tests/data")
 25
 26
 27@pytest.fixture
 28def axscan():
 29    return Scan.load_yaml("tests/data")
 30
 31
 32def test_get_crops(bzscan):
 33    tile = Tile(bzscan, 1000)
 34    event = Event(tile, 400, 350)
 35
 36    # Test a regular event
 37    images = event.get_crops()
 38    assert len(images) == 5
 39    images = event.get_crops(crop_size=50)
 40    assert images[0].shape == (50, 50)
 41    images = event.get_crops(crop_size=100)
 42    assert images[0].shape == (100, 100)
 43
 44    if SHOW_PLOTS:
 45        for image in images:
 46            cv2.imshow("Bright DAPI event in the center", image)
 47            cv2.waitKey(0)
 48        cv2.destroyAllWindows()
 49
 50    # Test a corner event
 51    event = Event(tile, 1350, 2)
 52    images = event.get_crops()
 53    assert len(images) == 5
 54    images = event.get_crops(crop_size=200)
 55    assert images[0].shape == (200, 200)
 56    images = event.get_crops(crop_size=100)
 57    assert images[0].shape == (100, 100)
 58
 59    if SHOW_PLOTS:
 60        for image in images:
 61            cv2.imshow("Events in the top-right corner of a tile", image)
 62            cv2.waitKey(0)
 63        cv2.destroyAllWindows()
 64
 65    # Test many events
 66    tile2 = Tile(bzscan, 500)
 67    events = [
 68        Event(tile, 515, 411),
 69        Event(tile2, 2, 1000),
 70        Event(tile, 1000, 1000),
 71        Event(tile, 87, 126),
 72        Event(tile, 1000, 2),
 73        Event(tile2, 800, 800),
 74        Event(tile, 1000, 662),
 75    ]
 76
 77    # Test time to extract images sequentially
 78    start_time = time.time()
 79    images_1 = []
 80    for event in events:
 81        images_1.append(event.get_crops())
 82    sequential_time = time.time() - start_time
 83
 84    # Test time to extract images in parallel
 85    start_time = time.time()
 86    images_2 = Event.get_many_crops(events, crop_size=100)
 87    parallel_time = time.time() - start_time
 88    assert parallel_time < sequential_time
 89    for list_a, list_b in zip(images_1, images_2):
 90        assert len(list_a) == len(list_b)
 91        for image_a, image_b in zip(list_a, list_b):
 92            assert np.array_equal(image_a, image_b)
 93
 94    # Test that it works after converting to EventArray and back
 95    event_array = EventArray.from_events(events)
 96    remade_events = event_array.to_events(
 97        [bzscan], ignore_metadata=True, ignore_features=True
 98    )
 99    images_3 = Event.get_many_crops(remade_events, crop_size=100)
100    for list_a, list_b in zip(images_1, images_3):
101        assert len(list_a) == len(list_b)
102        for image_a, image_b in zip(list_a, list_b):
103            assert np.array_equal(image_a, image_b)
104
105
106def test_event_coordinates_for_bzscanner(bzscan):
107    # Origin
108    tile = Tile(bzscan, 0)
109    event = Event(tile, 0, 0)
110    scan_origin = event.get_scan_position()
111    assert 2500 <= scan_origin[0] <= 3500
112    assert 2500 <= scan_origin[1] <= 3500
113    scan_origin_on_slide = event.get_slide_position()
114    assert 71500 <= scan_origin_on_slide[0] <= 72500
115    assert 21500 <= scan_origin_on_slide[1] <= 22500
116    # Within the same tile, "top-right corner"
117    event = Event(tile, 1000, 0)
118    scan_position = event.get_scan_position()
119    assert scan_origin[0] < scan_position[0]
120    assert scan_origin[1] == scan_position[1]
121    slide_position = event.get_slide_position()
122    assert scan_origin_on_slide[0] == slide_position[0]
123    assert scan_origin_on_slide[1] > slide_position[1]
124    # Within the same tile, "bottom-left corner"
125    event = Event(tile, 0, 1000)
126    scan_position = event.get_scan_position()
127    assert scan_origin[0] == scan_position[0]
128    assert scan_origin[1] < scan_position[1]
129    slide_position = event.get_slide_position()
130    assert scan_origin_on_slide[0] > slide_position[0]
131    assert scan_origin_on_slide[1] == slide_position[1]
132
133    # Next row, opposite side
134    tile = Tile(bzscan, (bzscan.roi[0].tile_cols - 1, 1))
135    event = Event(tile, 1000, 1000)
136    scan_position = event.get_scan_position()
137    assert scan_origin[0] <= scan_position[0]
138    assert scan_origin[1] <= scan_position[1]
139    slide_position = event.get_slide_position()
140    assert slide_position[0] <= scan_origin_on_slide[0]
141    assert slide_position[1] <= scan_origin_on_slide[1]
142
143    # Opposite corner
144    tile = Tile(bzscan, (bzscan.roi[0].tile_cols - 1, bzscan.roi[0].tile_rows - 1))
145    event = Event(tile, 1361, 1003)
146    scan_position = event.get_scan_position()
147    assert 21500 <= scan_position[0] <= 22500
148    assert 58500 <= scan_position[1] <= 60500
149    slide_position = event.get_slide_position()
150    assert 14500 <= slide_position[0] <= 15500
151    assert 2500 <= slide_position[1] <= 3500
152
153
154def test_event_coordinates_for_axioscan(axscan):
155    # Origin
156    tile = Tile(axscan, 0)
157    event = Event(tile, 0, 0)
158    scan_position = event.get_scan_position()
159    assert -59000 <= scan_position[0] < -55000
160    assert 0 <= scan_position[1] < 4000
161    slide_position = event.get_slide_position()
162    assert 16000 <= slide_position[0] < 20000
163    assert scan_position[1] == slide_position[1]
164
165    # Opposite corner
166    tile = Tile(axscan, (axscan.roi[0].tile_cols - 1, axscan.roi[0].tile_rows - 1))
167    event = Event(tile, 2000, 2000)
168    scan_position = event.get_scan_position()
169    assert -4000 <= scan_position[0] <= 0
170    assert 21000 <= scan_position[1] <= 25000
171    slide_position = event.get_slide_position()
172    assert 71000 <= slide_position[0] <= 75000
173    assert scan_position[1] == slide_position[1]
174
175
176def test_eventarray_conversions(axscan):
177    # Origin
178    tile = Tile(axscan, 0)
179    event0 = Event(tile, 0, 0)
180    event1 = Event(tile, 1000, 1000)
181    event2 = Event(tile, 2000, 2000)
182
183    event_array = EventArray.from_events([event0, event1, event2])
184
185    assert len(event_array) == 3
186    assert event_array.metadata is None
187    assert event_array.features is None
188
189    event0.metadata = pd.Series({"event0": 0})
190
191    try:
192        event_array = EventArray.from_events([event0, event1, event2])
193        # Should throw error
194        assert False
195    except ValueError:
196        pass
197
198    event1.metadata = pd.Series({"event0": 1})
199    event2.metadata = pd.Series({"event0": 2})
200
201    event_array = EventArray.from_events([event0, event1, event2])
202
203    assert len(event_array) == 3
204
205    events_df = event_array.to_dataframe()
206
207    assert len(events_df) == 3
208
209    assert event_array == EventArray.from_dataframe(events_df)
210
211    # Test adding different dtypes and converting back and forth
212    event_array.features = pd.DataFrame(
213        {"feature1": [1, 2, 3], "feature2": [4.0, 5.0, 6.0]}
214    )
215    remade_event_list = event_array.to_events([axscan])
216    assert len(remade_event_list) == 3
217    remade_event_array = EventArray.from_events(remade_event_list)
218    assert event_array == remade_event_array
219    # Test saving and loading
220    assert event_array.save_csv("tests/data/events.csv")
221    assert event_array == EventArray.load_csv("tests/data/events.csv")
222    os.remove("tests/data/events.csv")
223
224    assert event_array.save_hdf5("tests/data/events.h5")
225    assert event_array == EventArray.load_hdf5("tests/data/events.h5")
226    os.remove("tests/data/events.h5")
227
228
229# @pytest.mark.skip(reason="No longer required.")
230def test_ocular_conversions():
231    input_path = "/mnt/HDSCA_Development/DZ/0B68818/ocular"
232    result = EventArray.load_ocular(input_path)
233    # For the purposes of this test, we will manually relabel "clust" == nan to 0
234    # These come from ocular_interesting.rds, which does not have clusters
235    result.metadata["clust"] = result.metadata["clust"].fillna(0)
236    result.metadata["hcpc"] = result.metadata["hcpc"].fillna(0)
237    result.save_ocular("tests/data")
238    new_result = EventArray.load_ocular("tests/data")
239    # # Sort them so that they are in the same order
240    result = result.sort(["tile", "x", "y"])
241    new_result = new_result.sort(["tile", "x", "y"])
242    # Note: hcpc method within ocularr and here are different
243    result.metadata["hcpc"] = new_result.metadata["hcpc"].copy()
244    assert result == new_result
245    # Clean up
246    os.remove("tests/data/rc-final.csv")
247    os.remove("tests/data/rc-final.rds")
248    os.remove("tests/data/rc-final1.rds")
249    os.remove("tests/data/rc-final2.rds")
250    os.remove("tests/data/rc-final3.rds")
251    os.remove("tests/data/rc-final4.rds")
252
253    # Try it with "others" files
254    result = EventArray.load_ocular(input_path, event_type="others")
255    result.save_ocular("tests/data", event_type="others")
256    new_result = EventArray.load_ocular("tests/data", event_type="others")
257    result = result.sort(["tile", "x", "y"])
258    new_result = new_result.sort(["tile", "x", "y"])
259    # Note: hcpc method within ocularr and here are different
260    result.metadata["hcpc"] = new_result.metadata["hcpc"].copy()
261    assert result == new_result
262    # Clean up
263    os.remove("tests/data/others-final.csv")
264    os.remove("tests/data/others-final.rds")
265    os.remove("tests/data/others-final1.rds")
266    os.remove("tests/data/others-final2.rds")
267    os.remove("tests/data/others-final3.rds")
268    os.remove("tests/data/others-final4.rds")
269
270
271def test_copy_sort_rows_get(axscan):
272    # Origin
273    tile = Tile(axscan, 0)
274    events = [
275        Event(tile, 0, 100),
276        Event(tile, 0, 0),
277        Event(tile, 1000, 1000),
278        Event(tile, 1000, 1),
279        Event(tile, 2000, 2000),
280    ]
281
282    events = EventArray.from_events(events)
283
284    # Copy
285    events_copy = events.copy()
286    events_copy.info["x"] = np.uint16(1)
287    # Check that changes to the copy did not change the original
288    assert events_copy.info["x"].equals(pd.Series([1, 1, 1, 1, 1], dtype=np.uint16))
289    assert events.info["x"].equals(pd.Series([0, 0, 1000, 1000, 2000], dtype=np.uint16))
290
291    # Sort
292    events = events.sort(["x", "y"], ascending=[False, True])
293    assert events.info["x"].equals(pd.Series([2000, 1000, 1000, 0, 0], dtype=np.uint16))
294    assert events.info["y"].equals(pd.Series([2000, 1, 1000, 0, 100], dtype=np.uint16))
295
296    # Get
297    events_get = events.get(["x", "y"])
298    assert events_get["x"].equals(pd.Series([2000, 1000, 1000, 0, 0], dtype=np.uint16))
299    assert events_get["y"].equals(pd.Series([2000, 1, 1000, 0, 100], dtype=np.uint16))
300    assert events_get.columns.equals(pd.Index(["x", "y"]))
301
302    # Rows
303    events_get = events.rows([0, 1, 3])
304    assert len(events_get) == 3
305    assert events_get.info["x"].equals(pd.Series([2000, 1000, 0], dtype=np.uint16))
306    assert events_get.info["y"].equals(pd.Series([2000, 1, 0], dtype=np.uint16))
307    events_get = events.rows([True, False, False, True, True])
308    assert len(events_get) == 3
309    assert events_get.info["x"].equals(pd.Series([2000, 0, 0], dtype=np.uint16))
310    assert events_get.info["y"].equals(pd.Series([2000, 0, 100], dtype=np.uint16))
311
312
313def test_event_montages(bzscan):
314    tile = Tile(bzscan, 1000)
315    event = Event(tile, 515, 411)
316    images = event.get_crops(crop_size=100)
317
318    montage = csi_images.make_montage(
319        images,
320        [0, 1, 4, 2],
321        {0: (0, 0, 1), 1: (1, 0, 0), 2: (0, 1, 0), 4: (1, 1, 1)},
322        labels=["RGB", "DAPI", "AF555", "AF488", "AF647"],
323    )
324    if SHOW_PLOTS:
325        cv2.imshow(
326            "Full, classic montage with labels",
327            cv2.cvtColor(montage, cv2.COLOR_RGB2BGR),
328        )
329        cv2.waitKey(0)
330        cv2.destroyAllWindows()
331
332
333def test_saving_crops_and_montages(bzscan):
334    tile = Tile(bzscan, 1000)
335    tile2 = Tile(bzscan, 0)
336    events = [
337        Event(tile, 515, 411),
338        Event(tile2, 2, 1000),
339        Event(tile, 1000, 1000),
340        Event(tile2, 800, 800),
341        Event(tile, 1000, 662),
342    ]
343
344    # Get all crops and montages
345    serial_crops = []
346    serial_montages = []
347    for event in events:
348        serial_crops.append(event.get_crops())
349        serial_montages.append(event.get_montage())
350
351    # Save crops and montages
352    Event.get_and_save_many_crops(events, "temp", bzscan.get_channel_names())
353    Event.get_and_save_many_montages(events, "temp")
354
355    saved_crops = []
356    saved_montages = []
357    for event in events:
358        crops = event.load_crops("temp")
359        saved_crops.append([crops[c] for c in bzscan.get_channel_names()])
360        saved_montages.append(event.load_montage("temp"))
361
362    # Make sure crops are identical
363    for a, b in zip(serial_crops, saved_crops):
364        for a_img, b_img in zip(a, b):
365            assert np.array_equal(a_img, b_img)
366
367    # Montages got JPEG compressed, so
368    # Size comparison:
369    for a, b in zip(serial_montages, saved_montages):
370        assert a.shape == b.shape
371
372    # Visual inspection
373    if SHOW_PLOTS:
374        cv2.imshow("Original", cv2.cvtColor(serial_montages[0], cv2.COLOR_RGB2BGR))
375        cv2.imshow("Saved", cv2.cvtColor(saved_montages[0], cv2.COLOR_RGB2BGR))
376        cv2.waitKey(0)
377        cv2.destroyAllWindows()
378
379    # Clean up
380    for file in os.listdir("temp"):
381        os.remove(os.path.join("temp", file))
382    os.rmdir("temp")
@pytest.fixture
def bzscan():
23@pytest.fixture
24def bzscan():
25    return Scan.load_txt("tests/data")
@pytest.fixture
def axscan():
28@pytest.fixture
29def axscan():
30    return Scan.load_yaml("tests/data")
def test_get_crops(bzscan):
 33def test_get_crops(bzscan):
 34    tile = Tile(bzscan, 1000)
 35    event = Event(tile, 400, 350)
 36
 37    # Test a regular event
 38    images = event.get_crops()
 39    assert len(images) == 5
 40    images = event.get_crops(crop_size=50)
 41    assert images[0].shape == (50, 50)
 42    images = event.get_crops(crop_size=100)
 43    assert images[0].shape == (100, 100)
 44
 45    if SHOW_PLOTS:
 46        for image in images:
 47            cv2.imshow("Bright DAPI event in the center", image)
 48            cv2.waitKey(0)
 49        cv2.destroyAllWindows()
 50
 51    # Test a corner event
 52    event = Event(tile, 1350, 2)
 53    images = event.get_crops()
 54    assert len(images) == 5
 55    images = event.get_crops(crop_size=200)
 56    assert images[0].shape == (200, 200)
 57    images = event.get_crops(crop_size=100)
 58    assert images[0].shape == (100, 100)
 59
 60    if SHOW_PLOTS:
 61        for image in images:
 62            cv2.imshow("Events in the top-right corner of a tile", image)
 63            cv2.waitKey(0)
 64        cv2.destroyAllWindows()
 65
 66    # Test many events
 67    tile2 = Tile(bzscan, 500)
 68    events = [
 69        Event(tile, 515, 411),
 70        Event(tile2, 2, 1000),
 71        Event(tile, 1000, 1000),
 72        Event(tile, 87, 126),
 73        Event(tile, 1000, 2),
 74        Event(tile2, 800, 800),
 75        Event(tile, 1000, 662),
 76    ]
 77
 78    # Test time to extract images sequentially
 79    start_time = time.time()
 80    images_1 = []
 81    for event in events:
 82        images_1.append(event.get_crops())
 83    sequential_time = time.time() - start_time
 84
 85    # Test time to extract images in parallel
 86    start_time = time.time()
 87    images_2 = Event.get_many_crops(events, crop_size=100)
 88    parallel_time = time.time() - start_time
 89    assert parallel_time < sequential_time
 90    for list_a, list_b in zip(images_1, images_2):
 91        assert len(list_a) == len(list_b)
 92        for image_a, image_b in zip(list_a, list_b):
 93            assert np.array_equal(image_a, image_b)
 94
 95    # Test that it works after converting to EventArray and back
 96    event_array = EventArray.from_events(events)
 97    remade_events = event_array.to_events(
 98        [bzscan], ignore_metadata=True, ignore_features=True
 99    )
100    images_3 = Event.get_many_crops(remade_events, crop_size=100)
101    for list_a, list_b in zip(images_1, images_3):
102        assert len(list_a) == len(list_b)
103        for image_a, image_b in zip(list_a, list_b):
104            assert np.array_equal(image_a, image_b)
def test_event_coordinates_for_bzscanner(bzscan):
107def test_event_coordinates_for_bzscanner(bzscan):
108    # Origin
109    tile = Tile(bzscan, 0)
110    event = Event(tile, 0, 0)
111    scan_origin = event.get_scan_position()
112    assert 2500 <= scan_origin[0] <= 3500
113    assert 2500 <= scan_origin[1] <= 3500
114    scan_origin_on_slide = event.get_slide_position()
115    assert 71500 <= scan_origin_on_slide[0] <= 72500
116    assert 21500 <= scan_origin_on_slide[1] <= 22500
117    # Within the same tile, "top-right corner"
118    event = Event(tile, 1000, 0)
119    scan_position = event.get_scan_position()
120    assert scan_origin[0] < scan_position[0]
121    assert scan_origin[1] == scan_position[1]
122    slide_position = event.get_slide_position()
123    assert scan_origin_on_slide[0] == slide_position[0]
124    assert scan_origin_on_slide[1] > slide_position[1]
125    # Within the same tile, "bottom-left corner"
126    event = Event(tile, 0, 1000)
127    scan_position = event.get_scan_position()
128    assert scan_origin[0] == scan_position[0]
129    assert scan_origin[1] < scan_position[1]
130    slide_position = event.get_slide_position()
131    assert scan_origin_on_slide[0] > slide_position[0]
132    assert scan_origin_on_slide[1] == slide_position[1]
133
134    # Next row, opposite side
135    tile = Tile(bzscan, (bzscan.roi[0].tile_cols - 1, 1))
136    event = Event(tile, 1000, 1000)
137    scan_position = event.get_scan_position()
138    assert scan_origin[0] <= scan_position[0]
139    assert scan_origin[1] <= scan_position[1]
140    slide_position = event.get_slide_position()
141    assert slide_position[0] <= scan_origin_on_slide[0]
142    assert slide_position[1] <= scan_origin_on_slide[1]
143
144    # Opposite corner
145    tile = Tile(bzscan, (bzscan.roi[0].tile_cols - 1, bzscan.roi[0].tile_rows - 1))
146    event = Event(tile, 1361, 1003)
147    scan_position = event.get_scan_position()
148    assert 21500 <= scan_position[0] <= 22500
149    assert 58500 <= scan_position[1] <= 60500
150    slide_position = event.get_slide_position()
151    assert 14500 <= slide_position[0] <= 15500
152    assert 2500 <= slide_position[1] <= 3500
def test_event_coordinates_for_axioscan(axscan):
155def test_event_coordinates_for_axioscan(axscan):
156    # Origin
157    tile = Tile(axscan, 0)
158    event = Event(tile, 0, 0)
159    scan_position = event.get_scan_position()
160    assert -59000 <= scan_position[0] < -55000
161    assert 0 <= scan_position[1] < 4000
162    slide_position = event.get_slide_position()
163    assert 16000 <= slide_position[0] < 20000
164    assert scan_position[1] == slide_position[1]
165
166    # Opposite corner
167    tile = Tile(axscan, (axscan.roi[0].tile_cols - 1, axscan.roi[0].tile_rows - 1))
168    event = Event(tile, 2000, 2000)
169    scan_position = event.get_scan_position()
170    assert -4000 <= scan_position[0] <= 0
171    assert 21000 <= scan_position[1] <= 25000
172    slide_position = event.get_slide_position()
173    assert 71000 <= slide_position[0] <= 75000
174    assert scan_position[1] == slide_position[1]
def test_eventarray_conversions(axscan):
177def test_eventarray_conversions(axscan):
178    # Origin
179    tile = Tile(axscan, 0)
180    event0 = Event(tile, 0, 0)
181    event1 = Event(tile, 1000, 1000)
182    event2 = Event(tile, 2000, 2000)
183
184    event_array = EventArray.from_events([event0, event1, event2])
185
186    assert len(event_array) == 3
187    assert event_array.metadata is None
188    assert event_array.features is None
189
190    event0.metadata = pd.Series({"event0": 0})
191
192    try:
193        event_array = EventArray.from_events([event0, event1, event2])
194        # Should throw error
195        assert False
196    except ValueError:
197        pass
198
199    event1.metadata = pd.Series({"event0": 1})
200    event2.metadata = pd.Series({"event0": 2})
201
202    event_array = EventArray.from_events([event0, event1, event2])
203
204    assert len(event_array) == 3
205
206    events_df = event_array.to_dataframe()
207
208    assert len(events_df) == 3
209
210    assert event_array == EventArray.from_dataframe(events_df)
211
212    # Test adding different dtypes and converting back and forth
213    event_array.features = pd.DataFrame(
214        {"feature1": [1, 2, 3], "feature2": [4.0, 5.0, 6.0]}
215    )
216    remade_event_list = event_array.to_events([axscan])
217    assert len(remade_event_list) == 3
218    remade_event_array = EventArray.from_events(remade_event_list)
219    assert event_array == remade_event_array
220    # Test saving and loading
221    assert event_array.save_csv("tests/data/events.csv")
222    assert event_array == EventArray.load_csv("tests/data/events.csv")
223    os.remove("tests/data/events.csv")
224
225    assert event_array.save_hdf5("tests/data/events.h5")
226    assert event_array == EventArray.load_hdf5("tests/data/events.h5")
227    os.remove("tests/data/events.h5")
def test_ocular_conversions():
231def test_ocular_conversions():
232    input_path = "/mnt/HDSCA_Development/DZ/0B68818/ocular"
233    result = EventArray.load_ocular(input_path)
234    # For the purposes of this test, we will manually relabel "clust" == nan to 0
235    # These come from ocular_interesting.rds, which does not have clusters
236    result.metadata["clust"] = result.metadata["clust"].fillna(0)
237    result.metadata["hcpc"] = result.metadata["hcpc"].fillna(0)
238    result.save_ocular("tests/data")
239    new_result = EventArray.load_ocular("tests/data")
240    # # Sort them so that they are in the same order
241    result = result.sort(["tile", "x", "y"])
242    new_result = new_result.sort(["tile", "x", "y"])
243    # Note: hcpc method within ocularr and here are different
244    result.metadata["hcpc"] = new_result.metadata["hcpc"].copy()
245    assert result == new_result
246    # Clean up
247    os.remove("tests/data/rc-final.csv")
248    os.remove("tests/data/rc-final.rds")
249    os.remove("tests/data/rc-final1.rds")
250    os.remove("tests/data/rc-final2.rds")
251    os.remove("tests/data/rc-final3.rds")
252    os.remove("tests/data/rc-final4.rds")
253
254    # Try it with "others" files
255    result = EventArray.load_ocular(input_path, event_type="others")
256    result.save_ocular("tests/data", event_type="others")
257    new_result = EventArray.load_ocular("tests/data", event_type="others")
258    result = result.sort(["tile", "x", "y"])
259    new_result = new_result.sort(["tile", "x", "y"])
260    # Note: hcpc method within ocularr and here are different
261    result.metadata["hcpc"] = new_result.metadata["hcpc"].copy()
262    assert result == new_result
263    # Clean up
264    os.remove("tests/data/others-final.csv")
265    os.remove("tests/data/others-final.rds")
266    os.remove("tests/data/others-final1.rds")
267    os.remove("tests/data/others-final2.rds")
268    os.remove("tests/data/others-final3.rds")
269    os.remove("tests/data/others-final4.rds")
def test_copy_sort_rows_get(axscan):
272def test_copy_sort_rows_get(axscan):
273    # Origin
274    tile = Tile(axscan, 0)
275    events = [
276        Event(tile, 0, 100),
277        Event(tile, 0, 0),
278        Event(tile, 1000, 1000),
279        Event(tile, 1000, 1),
280        Event(tile, 2000, 2000),
281    ]
282
283    events = EventArray.from_events(events)
284
285    # Copy
286    events_copy = events.copy()
287    events_copy.info["x"] = np.uint16(1)
288    # Check that changes to the copy did not change the original
289    assert events_copy.info["x"].equals(pd.Series([1, 1, 1, 1, 1], dtype=np.uint16))
290    assert events.info["x"].equals(pd.Series([0, 0, 1000, 1000, 2000], dtype=np.uint16))
291
292    # Sort
293    events = events.sort(["x", "y"], ascending=[False, True])
294    assert events.info["x"].equals(pd.Series([2000, 1000, 1000, 0, 0], dtype=np.uint16))
295    assert events.info["y"].equals(pd.Series([2000, 1, 1000, 0, 100], dtype=np.uint16))
296
297    # Get
298    events_get = events.get(["x", "y"])
299    assert events_get["x"].equals(pd.Series([2000, 1000, 1000, 0, 0], dtype=np.uint16))
300    assert events_get["y"].equals(pd.Series([2000, 1, 1000, 0, 100], dtype=np.uint16))
301    assert events_get.columns.equals(pd.Index(["x", "y"]))
302
303    # Rows
304    events_get = events.rows([0, 1, 3])
305    assert len(events_get) == 3
306    assert events_get.info["x"].equals(pd.Series([2000, 1000, 0], dtype=np.uint16))
307    assert events_get.info["y"].equals(pd.Series([2000, 1, 0], dtype=np.uint16))
308    events_get = events.rows([True, False, False, True, True])
309    assert len(events_get) == 3
310    assert events_get.info["x"].equals(pd.Series([2000, 0, 0], dtype=np.uint16))
311    assert events_get.info["y"].equals(pd.Series([2000, 0, 100], dtype=np.uint16))
def test_event_montages(bzscan):
314def test_event_montages(bzscan):
315    tile = Tile(bzscan, 1000)
316    event = Event(tile, 515, 411)
317    images = event.get_crops(crop_size=100)
318
319    montage = csi_images.make_montage(
320        images,
321        [0, 1, 4, 2],
322        {0: (0, 0, 1), 1: (1, 0, 0), 2: (0, 1, 0), 4: (1, 1, 1)},
323        labels=["RGB", "DAPI", "AF555", "AF488", "AF647"],
324    )
325    if SHOW_PLOTS:
326        cv2.imshow(
327            "Full, classic montage with labels",
328            cv2.cvtColor(montage, cv2.COLOR_RGB2BGR),
329        )
330        cv2.waitKey(0)
331        cv2.destroyAllWindows()
def test_saving_crops_and_montages(bzscan):
334def test_saving_crops_and_montages(bzscan):
335    tile = Tile(bzscan, 1000)
336    tile2 = Tile(bzscan, 0)
337    events = [
338        Event(tile, 515, 411),
339        Event(tile2, 2, 1000),
340        Event(tile, 1000, 1000),
341        Event(tile2, 800, 800),
342        Event(tile, 1000, 662),
343    ]
344
345    # Get all crops and montages
346    serial_crops = []
347    serial_montages = []
348    for event in events:
349        serial_crops.append(event.get_crops())
350        serial_montages.append(event.get_montage())
351
352    # Save crops and montages
353    Event.get_and_save_many_crops(events, "temp", bzscan.get_channel_names())
354    Event.get_and_save_many_montages(events, "temp")
355
356    saved_crops = []
357    saved_montages = []
358    for event in events:
359        crops = event.load_crops("temp")
360        saved_crops.append([crops[c] for c in bzscan.get_channel_names()])
361        saved_montages.append(event.load_montage("temp"))
362
363    # Make sure crops are identical
364    for a, b in zip(serial_crops, saved_crops):
365        for a_img, b_img in zip(a, b):
366            assert np.array_equal(a_img, b_img)
367
368    # Montages got JPEG compressed, so
369    # Size comparison:
370    for a, b in zip(serial_montages, saved_montages):
371        assert a.shape == b.shape
372
373    # Visual inspection
374    if SHOW_PLOTS:
375        cv2.imshow("Original", cv2.cvtColor(serial_montages[0], cv2.COLOR_RGB2BGR))
376        cv2.imshow("Saved", cv2.cvtColor(saved_montages[0], cv2.COLOR_RGB2BGR))
377        cv2.waitKey(0)
378        cv2.destroyAllWindows()
379
380    # Clean up
381    for file in os.listdir("temp"):
382        os.remove(os.path.join("temp", file))
383    os.rmdir("temp")