tests.test_events

  1import os
  2import time
  3
  4import cv2
  5import numpy as np
  6import pandas as pd
  7
  8from csi_images import csi_events, csi_tiles, csi_scans
  9
 10
 11if os.environ.get("DEBIAN_FRONTEND") == "noninteractive":
 12    SHOW_PLOTS = False
 13else:
 14    # Change this to your preference for local testing, but commit as True
 15    SHOW_PLOTS = True
 16
 17
 18def test_getting_event():
 19    scan = csi_scans.Scan.load_txt("tests/data")
 20    tile = csi_tiles.Tile(scan, 1000)
 21    event = csi_events.Event(
 22        scan,
 23        tile,
 24        515,
 25        411,
 26    )
 27    images = event.extract_images()
 28    assert len(images) == 4
 29    images = event.extract_images(crop_size=100, in_pixels=True)
 30    assert images[0].shape == (100, 100)
 31    images = event.extract_images(crop_size=50, in_pixels=True)
 32    assert images[0].shape == (50, 50)
 33
 34    if SHOW_PLOTS:
 35        for image in images:
 36            cv2.imshow("Bright DAPI event in the center", image)
 37            cv2.waitKey(0)
 38        cv2.destroyAllWindows()
 39
 40    # Test a corner event
 41    event = csi_events.Event(
 42        scan,
 43        tile,
 44        2,
 45        1000,
 46    )
 47    images = event.extract_images()
 48    assert len(images) == 4
 49    images = event.extract_images(crop_size=100, in_pixels=True)
 50    assert images[0].shape == (100, 100)
 51    images = event.extract_images(crop_size=200, in_pixels=True)
 52    assert images[0].shape == (200, 200)
 53
 54    if SHOW_PLOTS:
 55        for image in images:
 56            cv2.imshow("Events in the corner of a tile", image)
 57            cv2.waitKey(0)
 58        cv2.destroyAllWindows()
 59
 60
 61def test_getting_many_events():
 62    scan = csi_scans.Scan.load_txt("tests/data")
 63    tile = csi_tiles.Tile(scan, 1000)
 64    tile2 = csi_tiles.Tile(scan, 0)
 65    events = [
 66        csi_events.Event(scan, tile, 515, 411),
 67        csi_events.Event(scan, tile2, 2, 1000),
 68        csi_events.Event(scan, tile, 1000, 1000),
 69        csi_events.Event(scan, tile, 87, 126),
 70        csi_events.Event(scan, tile, 1000, 2),
 71        csi_events.Event(scan, tile2, 800, 800),
 72        csi_events.Event(scan, tile, 1000, 662),
 73    ]
 74    # Test time to extract images sequentially
 75    start_time = time.time()
 76    images_1 = []
 77    for event in events:
 78        images_1.append(event.extract_images())
 79    sequential_time = time.time() - start_time
 80
 81    # Test time to extract images in parallel
 82    start_time = time.time()
 83    images_2 = csi_events.Event.extract_images_for_list(events, crop_size=50)
 84    parallel_time = time.time() - start_time
 85    assert parallel_time < sequential_time
 86    for list_a, list_b in zip(images_1, images_2):
 87        assert len(list_a) == len(list_b)
 88        for image_a, image_b in zip(list_a, list_b):
 89            assert np.array_equal(image_a, image_b)
 90
 91    # Test that it works after converting to EventArray and back
 92    event_array = csi_events.EventArray.from_events(events)
 93    remade_events = event_array.to_events(
 94        [scan], ignore_metadata=True, ignore_features=True
 95    )
 96    images_3 = csi_events.Event.extract_images_for_list(
 97        remade_events,
 98        crop_size=50,
 99    )
100
101
102def test_event_coordinates_for_bzscanner():
103    scan = csi_scans.Scan.load_txt("tests/data")
104    # Origin
105    tile = csi_tiles.Tile(scan, 0)
106    event = csi_events.Event(scan, tile, 0, 0)
107    scan_origin = event.get_scan_position()
108    assert 2500 <= scan_origin[0] <= 3500
109    assert 2500 <= scan_origin[1] <= 3500
110    scan_origin_on_slide = event.get_slide_position()
111    assert 71500 <= scan_origin_on_slide[0] <= 72500
112    assert 21500 <= scan_origin_on_slide[1] <= 22500
113    # Within the same tile, "bottom-right corner"
114    event = csi_events.Event(scan, tile, 1000, 1000)
115    scan_position = event.get_scan_position()
116    assert scan_origin[0] <= scan_position[0]
117    assert scan_origin[1] <= scan_position[1]
118    slide_position = event.get_slide_position()
119    assert slide_position[0] <= scan_origin_on_slide[0]
120    assert slide_position[1] <= scan_origin_on_slide[1]
121
122    # Next row, opposite side
123    tile = csi_tiles.Tile(scan, (scan.roi[0].tile_cols - 1, 1))
124    event = csi_events.Event(scan, tile, 1000, 1000)
125    scan_position = event.get_scan_position()
126    assert scan_origin[0] <= scan_position[0]
127    assert scan_origin[1] <= scan_position[1]
128    slide_position = event.get_slide_position()
129    assert slide_position[0] <= scan_origin_on_slide[0]
130    assert slide_position[1] <= scan_origin_on_slide[1]
131
132    # Opposite corner
133    tile = csi_tiles.Tile(scan, (scan.roi[0].tile_cols - 1, scan.roi[0].tile_rows - 1))
134    event = csi_events.Event(scan, tile, 1361, 1003)
135    scan_position = event.get_scan_position()
136    assert 21500 <= scan_position[0] <= 22500
137    assert 58500 <= scan_position[1] <= 60500
138    slide_position = event.get_slide_position()
139    assert 14500 <= slide_position[0] <= 15500
140    assert 2500 <= slide_position[1] <= 3500
141
142
143def test_event_coordinates_for_axioscan():
144    scan = csi_scans.Scan.load_yaml("tests/data")
145    # Origin
146    tile = csi_tiles.Tile(scan, 0)
147    event = csi_events.Event(scan, tile, 0, 0)
148    scan_position = event.get_scan_position()
149    assert -59000 <= scan_position[0] < -55000
150    assert 0 <= scan_position[1] < 4000
151    slide_position = event.get_slide_position()
152    assert 16000 <= slide_position[0] < 20000
153    assert scan_position[1] == slide_position[1]
154
155    # Opposite corner
156    tile = csi_tiles.Tile(scan, (scan.roi[0].tile_cols - 1, scan.roi[0].tile_rows - 1))
157    event = csi_events.Event(scan, tile, 2000, 2000)
158    scan_position = event.get_scan_position()
159    assert -4000 <= scan_position[0] <= 0
160    assert 21000 <= scan_position[1] <= 25000
161    slide_position = event.get_slide_position()
162    assert 71000 <= slide_position[0] <= 75000
163    assert scan_position[1] == slide_position[1]
164
165
166def test_eventarray_conversions():
167    scan = csi_scans.Scan.load_yaml("tests/data")
168    # Origin
169    tile = csi_tiles.Tile(scan, 0)
170    event0 = csi_events.Event(scan, tile, 0, 0)
171    event1 = csi_events.Event(scan, tile, 1000, 1000)
172    event2 = csi_events.Event(scan, tile, 2000, 2000)
173
174    event_array = csi_events.EventArray.from_events([event0, event1, event2])
175
176    assert len(event_array) == 3
177    assert event_array.metadata is None
178    assert event_array.features is None
179
180    event0.metadata = pd.Series({"event0": 0})
181
182    try:
183        event_array = csi_events.EventArray.from_events([event0, event1, event2])
184        # Should throw error
185        assert False
186    except ValueError:
187        pass
188
189    event1.metadata = pd.Series({"event0": 1})
190    event2.metadata = pd.Series({"event0": 2})
191
192    event_array = csi_events.EventArray.from_events([event0, event1, event2])
193
194    assert len(event_array) == 3
195
196    events_df = event_array.to_dataframe()
197
198    assert len(events_df) == 3
199
200    assert event_array == csi_events.EventArray.from_dataframe(events_df)
201
202    # Test saving and loading
203    assert event_array.save_csv("tests/data/events.csv")
204    assert event_array == csi_events.EventArray.load_csv("tests/data/events.csv")
205    os.remove("tests/data/events.csv")
206
207    assert event_array.save_hdf5("tests/data/events.h5")
208    assert event_array == csi_events.EventArray.load_hdf5("tests/data/events.h5")
209    os.remove("tests/data/events.h5")
def test_getting_event():
19def test_getting_event():
20    scan = csi_scans.Scan.load_txt("tests/data")
21    tile = csi_tiles.Tile(scan, 1000)
22    event = csi_events.Event(
23        scan,
24        tile,
25        515,
26        411,
27    )
28    images = event.extract_images()
29    assert len(images) == 4
30    images = event.extract_images(crop_size=100, in_pixels=True)
31    assert images[0].shape == (100, 100)
32    images = event.extract_images(crop_size=50, in_pixels=True)
33    assert images[0].shape == (50, 50)
34
35    if SHOW_PLOTS:
36        for image in images:
37            cv2.imshow("Bright DAPI event in the center", image)
38            cv2.waitKey(0)
39        cv2.destroyAllWindows()
40
41    # Test a corner event
42    event = csi_events.Event(
43        scan,
44        tile,
45        2,
46        1000,
47    )
48    images = event.extract_images()
49    assert len(images) == 4
50    images = event.extract_images(crop_size=100, in_pixels=True)
51    assert images[0].shape == (100, 100)
52    images = event.extract_images(crop_size=200, in_pixels=True)
53    assert images[0].shape == (200, 200)
54
55    if SHOW_PLOTS:
56        for image in images:
57            cv2.imshow("Events in the corner of a tile", image)
58            cv2.waitKey(0)
59        cv2.destroyAllWindows()
def test_getting_many_events():
 62def test_getting_many_events():
 63    scan = csi_scans.Scan.load_txt("tests/data")
 64    tile = csi_tiles.Tile(scan, 1000)
 65    tile2 = csi_tiles.Tile(scan, 0)
 66    events = [
 67        csi_events.Event(scan, tile, 515, 411),
 68        csi_events.Event(scan, tile2, 2, 1000),
 69        csi_events.Event(scan, tile, 1000, 1000),
 70        csi_events.Event(scan, tile, 87, 126),
 71        csi_events.Event(scan, tile, 1000, 2),
 72        csi_events.Event(scan, tile2, 800, 800),
 73        csi_events.Event(scan, tile, 1000, 662),
 74    ]
 75    # Test time to extract images sequentially
 76    start_time = time.time()
 77    images_1 = []
 78    for event in events:
 79        images_1.append(event.extract_images())
 80    sequential_time = time.time() - start_time
 81
 82    # Test time to extract images in parallel
 83    start_time = time.time()
 84    images_2 = csi_events.Event.extract_images_for_list(events, crop_size=50)
 85    parallel_time = time.time() - start_time
 86    assert parallel_time < sequential_time
 87    for list_a, list_b in zip(images_1, images_2):
 88        assert len(list_a) == len(list_b)
 89        for image_a, image_b in zip(list_a, list_b):
 90            assert np.array_equal(image_a, image_b)
 91
 92    # Test that it works after converting to EventArray and back
 93    event_array = csi_events.EventArray.from_events(events)
 94    remade_events = event_array.to_events(
 95        [scan], ignore_metadata=True, ignore_features=True
 96    )
 97    images_3 = csi_events.Event.extract_images_for_list(
 98        remade_events,
 99        crop_size=50,
100    )
def test_event_coordinates_for_bzscanner():
103def test_event_coordinates_for_bzscanner():
104    scan = csi_scans.Scan.load_txt("tests/data")
105    # Origin
106    tile = csi_tiles.Tile(scan, 0)
107    event = csi_events.Event(scan, tile, 0, 0)
108    scan_origin = event.get_scan_position()
109    assert 2500 <= scan_origin[0] <= 3500
110    assert 2500 <= scan_origin[1] <= 3500
111    scan_origin_on_slide = event.get_slide_position()
112    assert 71500 <= scan_origin_on_slide[0] <= 72500
113    assert 21500 <= scan_origin_on_slide[1] <= 22500
114    # Within the same tile, "bottom-right corner"
115    event = csi_events.Event(scan, tile, 1000, 1000)
116    scan_position = event.get_scan_position()
117    assert scan_origin[0] <= scan_position[0]
118    assert scan_origin[1] <= scan_position[1]
119    slide_position = event.get_slide_position()
120    assert slide_position[0] <= scan_origin_on_slide[0]
121    assert slide_position[1] <= scan_origin_on_slide[1]
122
123    # Next row, opposite side
124    tile = csi_tiles.Tile(scan, (scan.roi[0].tile_cols - 1, 1))
125    event = csi_events.Event(scan, tile, 1000, 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 slide_position[0] <= scan_origin_on_slide[0]
131    assert slide_position[1] <= scan_origin_on_slide[1]
132
133    # Opposite corner
134    tile = csi_tiles.Tile(scan, (scan.roi[0].tile_cols - 1, scan.roi[0].tile_rows - 1))
135    event = csi_events.Event(scan, tile, 1361, 1003)
136    scan_position = event.get_scan_position()
137    assert 21500 <= scan_position[0] <= 22500
138    assert 58500 <= scan_position[1] <= 60500
139    slide_position = event.get_slide_position()
140    assert 14500 <= slide_position[0] <= 15500
141    assert 2500 <= slide_position[1] <= 3500
def test_event_coordinates_for_axioscan():
144def test_event_coordinates_for_axioscan():
145    scan = csi_scans.Scan.load_yaml("tests/data")
146    # Origin
147    tile = csi_tiles.Tile(scan, 0)
148    event = csi_events.Event(scan, tile, 0, 0)
149    scan_position = event.get_scan_position()
150    assert -59000 <= scan_position[0] < -55000
151    assert 0 <= scan_position[1] < 4000
152    slide_position = event.get_slide_position()
153    assert 16000 <= slide_position[0] < 20000
154    assert scan_position[1] == slide_position[1]
155
156    # Opposite corner
157    tile = csi_tiles.Tile(scan, (scan.roi[0].tile_cols - 1, scan.roi[0].tile_rows - 1))
158    event = csi_events.Event(scan, tile, 2000, 2000)
159    scan_position = event.get_scan_position()
160    assert -4000 <= scan_position[0] <= 0
161    assert 21000 <= scan_position[1] <= 25000
162    slide_position = event.get_slide_position()
163    assert 71000 <= slide_position[0] <= 75000
164    assert scan_position[1] == slide_position[1]
def test_eventarray_conversions():
167def test_eventarray_conversions():
168    scan = csi_scans.Scan.load_yaml("tests/data")
169    # Origin
170    tile = csi_tiles.Tile(scan, 0)
171    event0 = csi_events.Event(scan, tile, 0, 0)
172    event1 = csi_events.Event(scan, tile, 1000, 1000)
173    event2 = csi_events.Event(scan, tile, 2000, 2000)
174
175    event_array = csi_events.EventArray.from_events([event0, event1, event2])
176
177    assert len(event_array) == 3
178    assert event_array.metadata is None
179    assert event_array.features is None
180
181    event0.metadata = pd.Series({"event0": 0})
182
183    try:
184        event_array = csi_events.EventArray.from_events([event0, event1, event2])
185        # Should throw error
186        assert False
187    except ValueError:
188        pass
189
190    event1.metadata = pd.Series({"event0": 1})
191    event2.metadata = pd.Series({"event0": 2})
192
193    event_array = csi_events.EventArray.from_events([event0, event1, event2])
194
195    assert len(event_array) == 3
196
197    events_df = event_array.to_dataframe()
198
199    assert len(events_df) == 3
200
201    assert event_array == csi_events.EventArray.from_dataframe(events_df)
202
203    # Test saving and loading
204    assert event_array.save_csv("tests/data/events.csv")
205    assert event_array == csi_events.EventArray.load_csv("tests/data/events.csv")
206    os.remove("tests/data/events.csv")
207
208    assert event_array.save_hdf5("tests/data/events.h5")
209    assert event_array == csi_events.EventArray.load_hdf5("tests/data/events.h5")
210    os.remove("tests/data/events.h5")