tests.test_events
1import os 2import time 3 4import cv2 5import numpy as np 6import pandas as pd 7 8from csi_images.csi_scans import Scan 9from csi_images.csi_tiles import Tile 10from csi_images.csi_events import Event, EventArray 11 12if os.environ.get("DEBIAN_FRONTEND") == "noninteractive": 13 SHOW_PLOTS = False 14else: 15 # Change this to your preference for local testing, but commit as True 16 SHOW_PLOTS = True 17 18 19def test_getting_event(): 20 scan = Scan.load_txt("tests/data") 21 tile = Tile(scan, 1000) 22 event = 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 = 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() 60 61 62def test_getting_many_events(): 63 scan = Scan.load_txt("tests/data") 64 tile = Tile(scan, 1000) 65 tile2 = Tile(scan, 0) 66 events = [ 67 Event(scan, tile, 515, 411), 68 Event(scan, tile2, 2, 1000), 69 Event(scan, tile, 1000, 1000), 70 Event(scan, tile, 87, 126), 71 Event(scan, tile, 1000, 2), 72 Event(scan, tile2, 800, 800), 73 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 = Event.extract_images_for_list(events, crop_size=100) 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 = EventArray.from_events(events) 94 remade_events = event_array.to_events( 95 [scan], ignore_metadata=True, ignore_features=True 96 ) 97 images_3 = Event.extract_images_for_list(remade_events, crop_size=100) 98 for list_a, list_b in zip(images_1, images_3): 99 assert len(list_a) == len(list_b) 100 for image_a, image_b in zip(list_a, list_b): 101 assert np.array_equal(image_a, image_b) 102 103 104def test_event_coordinates_for_bzscanner(): 105 scan = Scan.load_txt("tests/data") 106 # Origin 107 tile = Tile(scan, 0) 108 event = Event(scan, tile, 0, 0) 109 scan_origin = event.get_scan_position() 110 assert 2500 <= scan_origin[0] <= 3500 111 assert 2500 <= scan_origin[1] <= 3500 112 scan_origin_on_slide = event.get_slide_position() 113 assert 71500 <= scan_origin_on_slide[0] <= 72500 114 assert 21500 <= scan_origin_on_slide[1] <= 22500 115 # Within the same tile, "bottom-right corner" 116 event = Event(scan, tile, 1000, 1000) 117 scan_position = event.get_scan_position() 118 assert scan_origin[0] <= scan_position[0] 119 assert scan_origin[1] <= scan_position[1] 120 slide_position = event.get_slide_position() 121 assert slide_position[0] <= scan_origin_on_slide[0] 122 assert slide_position[1] <= scan_origin_on_slide[1] 123 124 # Next row, opposite side 125 tile = Tile(scan, (scan.roi[0].tile_cols - 1, 1)) 126 event = Event(scan, tile, 1000, 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 slide_position[0] <= scan_origin_on_slide[0] 132 assert slide_position[1] <= scan_origin_on_slide[1] 133 134 # Opposite corner 135 tile = Tile(scan, (scan.roi[0].tile_cols - 1, scan.roi[0].tile_rows - 1)) 136 event = Event(scan, tile, 1361, 1003) 137 scan_position = event.get_scan_position() 138 assert 21500 <= scan_position[0] <= 22500 139 assert 58500 <= scan_position[1] <= 60500 140 slide_position = event.get_slide_position() 141 assert 14500 <= slide_position[0] <= 15500 142 assert 2500 <= slide_position[1] <= 3500 143 144 145def test_event_coordinates_for_axioscan(): 146 scan = Scan.load_yaml("tests/data") 147 # Origin 148 tile = Tile(scan, 0) 149 event = Event(scan, tile, 0, 0) 150 scan_position = event.get_scan_position() 151 assert -59000 <= scan_position[0] < -55000 152 assert 0 <= scan_position[1] < 4000 153 slide_position = event.get_slide_position() 154 assert 16000 <= slide_position[0] < 20000 155 assert scan_position[1] == slide_position[1] 156 157 # Opposite corner 158 tile = Tile(scan, (scan.roi[0].tile_cols - 1, scan.roi[0].tile_rows - 1)) 159 event = Event(scan, tile, 2000, 2000) 160 scan_position = event.get_scan_position() 161 assert -4000 <= scan_position[0] <= 0 162 assert 21000 <= scan_position[1] <= 25000 163 slide_position = event.get_slide_position() 164 assert 71000 <= slide_position[0] <= 75000 165 assert scan_position[1] == slide_position[1] 166 167 168def test_eventarray_conversions(): 169 scan = Scan.load_yaml("tests/data") 170 # Origin 171 tile = Tile(scan, 0) 172 event0 = Event(scan, tile, 0, 0) 173 event1 = Event(scan, tile, 1000, 1000) 174 event2 = Event(scan, tile, 2000, 2000) 175 176 event_array = EventArray.from_events([event0, event1, event2]) 177 178 assert len(event_array) == 3 179 assert event_array.metadata is None 180 assert event_array.features is None 181 182 event0.metadata = pd.Series({"event0": 0}) 183 184 try: 185 event_array = EventArray.from_events([event0, event1, event2]) 186 # Should throw error 187 assert False 188 except ValueError: 189 pass 190 191 event1.metadata = pd.Series({"event0": 1}) 192 event2.metadata = pd.Series({"event0": 2}) 193 194 event_array = EventArray.from_events([event0, event1, event2]) 195 196 assert len(event_array) == 3 197 198 events_df = event_array.to_dataframe() 199 200 assert len(events_df) == 3 201 202 assert event_array == EventArray.from_dataframe(events_df) 203 204 # Test adding different dtypes and converting back and forth 205 event_array.features = pd.DataFrame( 206 {"feature1": [1, 2, 3], "feature2": [4.0, 5.0, 6.0]} 207 ) 208 assert event_array == EventArray.from_events(event_array.to_events(scan)) 209 # Test saving and loading 210 assert event_array.save_csv("tests/data/events.csv") 211 assert event_array == EventArray.load_csv("tests/data/events.csv") 212 os.remove("tests/data/events.csv") 213 214 assert event_array.save_hdf5("tests/data/events.h5") 215 assert event_array == EventArray.load_hdf5("tests/data/events.h5") 216 os.remove("tests/data/events.h5") 217 218 219def test_ocular_conversions(): 220 scan = Scan.load_txt("tests/data") 221 input_path = "/mnt/HDSCA_Development/DZ/0B58703/ocular" 222 result = EventArray.load_ocular(input_path) 223 # For the purposes of this test, we will manually relabel "clust" == nan to 0 224 # These come from ocular_interesting.rds, which does not have clusters 225 result.metadata["clust"] = result.metadata["clust"].fillna(0) 226 result.metadata["hcpc"] = result.metadata["hcpc"].fillna(0) 227 result.save_ocular("tests/data") 228 new_result = EventArray.load_ocular("tests/data") 229 # # Sort them so that they are in the same order 230 result = result.sort(["tile", "x", "y"]) 231 new_result = new_result.sort(["tile", "x", "y"]) 232 # Note: hcpc method within ocularr and here are different 233 result.metadata["hcpc"] = new_result.metadata["hcpc"].copy() 234 assert result == new_result 235 # Clean up 236 os.remove("tests/data/rc-final.csv") 237 os.remove("tests/data/rc-final.rds") 238 os.remove("tests/data/rc-final1.rds") 239 os.remove("tests/data/rc-final2.rds") 240 os.remove("tests/data/rc-final3.rds") 241 os.remove("tests/data/rc-final4.rds") 242 243 # Try it with "others" files 244 result = EventArray.load_ocular(input_path, event_type="others") 245 result.save_ocular("tests/data", event_type="others") 246 new_result = EventArray.load_ocular("tests/data", event_type="others") 247 result = result.sort(["tile", "x", "y"]) 248 new_result = new_result.sort(["tile", "x", "y"]) 249 # Note: hcpc method within ocularr and here are different 250 result.metadata["hcpc"] = new_result.metadata["hcpc"].copy() 251 assert result == new_result 252 # Clean up 253 os.remove("tests/data/others-final.csv") 254 os.remove("tests/data/others-final.rds") 255 os.remove("tests/data/others-final1.rds") 256 os.remove("tests/data/others-final2.rds") 257 os.remove("tests/data/others-final3.rds") 258 os.remove("tests/data/others-final4.rds") 259 260 261def test_copy_sort_rows_get(): 262 scan = Scan.load_yaml("tests/data") 263 # Origin 264 tile = Tile(scan, 0) 265 events = [ 266 Event(scan, tile, 0, 100), 267 Event(scan, tile, 0, 0), 268 Event(scan, tile, 1000, 1000), 269 Event(scan, tile, 1000, 1), 270 Event(scan, tile, 2000, 2000), 271 ] 272 273 events = EventArray.from_events(events) 274 275 # Copy 276 events_copy = events.copy() 277 events_copy.info["x"] = 1 278 # Check that changes to the copy did not change the original 279 assert events_copy.info["x"].equals(pd.Series([1, 1, 1, 1, 1])) 280 assert events.info["x"].equals(pd.Series([0, 0, 1000, 1000, 2000])) 281 282 # Sort 283 events = events.sort(["x", "y"], ascending=[False, True]) 284 assert events.info["x"].equals(pd.Series([2000, 1000, 1000, 0, 0])) 285 assert events.info["y"].equals(pd.Series([2000, 1, 1000, 0, 100])) 286 287 # Get 288 events_get = events.get(["x", "y"]) 289 assert events_get["x"].equals(pd.Series([2000, 1000, 1000, 0, 0])) 290 assert events_get["y"].equals(pd.Series([2000, 1, 1000, 0, 100])) 291 assert events_get.columns.equals(pd.Index(["x", "y"])) 292 293 # Rows 294 events_get = events.rows([0, 1, 3]) 295 assert len(events_get) == 3 296 assert events_get.info["x"].equals(pd.Series([2000, 1000, 0])) 297 assert events_get.info["y"].equals(pd.Series([2000, 1, 0])) 298 events_get = events.rows([True, False, False, True, True]) 299 assert len(events_get) == 3 300 assert events_get.info["x"].equals(pd.Series([2000, 0, 0])) 301 assert events_get.info["y"].equals(pd.Series([2000, 0, 100]))
def
test_getting_event():
20def test_getting_event(): 21 scan = Scan.load_txt("tests/data") 22 tile = Tile(scan, 1000) 23 event = Event( 24 scan, 25 tile, 26 515, 27 411, 28 ) 29 images = event.extract_images() 30 assert len(images) == 4 31 images = event.extract_images(crop_size=100, in_pixels=True) 32 assert images[0].shape == (100, 100) 33 images = event.extract_images(crop_size=50, in_pixels=True) 34 assert images[0].shape == (50, 50) 35 36 if SHOW_PLOTS: 37 for image in images: 38 cv2.imshow("Bright DAPI event in the center", image) 39 cv2.waitKey(0) 40 cv2.destroyAllWindows() 41 42 # Test a corner event 43 event = Event( 44 scan, 45 tile, 46 2, 47 1000, 48 ) 49 images = event.extract_images() 50 assert len(images) == 4 51 images = event.extract_images(crop_size=100, in_pixels=True) 52 assert images[0].shape == (100, 100) 53 images = event.extract_images(crop_size=200, in_pixels=True) 54 assert images[0].shape == (200, 200) 55 56 if SHOW_PLOTS: 57 for image in images: 58 cv2.imshow("Events in the corner of a tile", image) 59 cv2.waitKey(0) 60 cv2.destroyAllWindows()
def
test_getting_many_events():
63def test_getting_many_events(): 64 scan = Scan.load_txt("tests/data") 65 tile = Tile(scan, 1000) 66 tile2 = Tile(scan, 0) 67 events = [ 68 Event(scan, tile, 515, 411), 69 Event(scan, tile2, 2, 1000), 70 Event(scan, tile, 1000, 1000), 71 Event(scan, tile, 87, 126), 72 Event(scan, tile, 1000, 2), 73 Event(scan, tile2, 800, 800), 74 Event(scan, tile, 1000, 662), 75 ] 76 # Test time to extract images sequentially 77 start_time = time.time() 78 images_1 = [] 79 for event in events: 80 images_1.append(event.extract_images()) 81 sequential_time = time.time() - start_time 82 83 # Test time to extract images in parallel 84 start_time = time.time() 85 images_2 = Event.extract_images_for_list(events, crop_size=100) 86 parallel_time = time.time() - start_time 87 assert parallel_time < sequential_time 88 for list_a, list_b in zip(images_1, images_2): 89 assert len(list_a) == len(list_b) 90 for image_a, image_b in zip(list_a, list_b): 91 assert np.array_equal(image_a, image_b) 92 93 # Test that it works after converting to EventArray and back 94 event_array = EventArray.from_events(events) 95 remade_events = event_array.to_events( 96 [scan], ignore_metadata=True, ignore_features=True 97 ) 98 images_3 = Event.extract_images_for_list(remade_events, crop_size=100) 99 for list_a, list_b in zip(images_1, images_3): 100 assert len(list_a) == len(list_b) 101 for image_a, image_b in zip(list_a, list_b): 102 assert np.array_equal(image_a, image_b)
def
test_event_coordinates_for_bzscanner():
105def test_event_coordinates_for_bzscanner(): 106 scan = Scan.load_txt("tests/data") 107 # Origin 108 tile = Tile(scan, 0) 109 event = Event(scan, 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, "bottom-right corner" 117 event = Event(scan, tile, 1000, 1000) 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 slide_position[0] <= scan_origin_on_slide[0] 123 assert slide_position[1] <= scan_origin_on_slide[1] 124 125 # Next row, opposite side 126 tile = Tile(scan, (scan.roi[0].tile_cols - 1, 1)) 127 event = Event(scan, tile, 1000, 1000) 128 scan_position = event.get_scan_position() 129 assert scan_origin[0] <= scan_position[0] 130 assert scan_origin[1] <= scan_position[1] 131 slide_position = event.get_slide_position() 132 assert slide_position[0] <= scan_origin_on_slide[0] 133 assert slide_position[1] <= scan_origin_on_slide[1] 134 135 # Opposite corner 136 tile = Tile(scan, (scan.roi[0].tile_cols - 1, scan.roi[0].tile_rows - 1)) 137 event = Event(scan, tile, 1361, 1003) 138 scan_position = event.get_scan_position() 139 assert 21500 <= scan_position[0] <= 22500 140 assert 58500 <= scan_position[1] <= 60500 141 slide_position = event.get_slide_position() 142 assert 14500 <= slide_position[0] <= 15500 143 assert 2500 <= slide_position[1] <= 3500
def
test_event_coordinates_for_axioscan():
146def test_event_coordinates_for_axioscan(): 147 scan = Scan.load_yaml("tests/data") 148 # Origin 149 tile = Tile(scan, 0) 150 event = Event(scan, tile, 0, 0) 151 scan_position = event.get_scan_position() 152 assert -59000 <= scan_position[0] < -55000 153 assert 0 <= scan_position[1] < 4000 154 slide_position = event.get_slide_position() 155 assert 16000 <= slide_position[0] < 20000 156 assert scan_position[1] == slide_position[1] 157 158 # Opposite corner 159 tile = Tile(scan, (scan.roi[0].tile_cols - 1, scan.roi[0].tile_rows - 1)) 160 event = Event(scan, tile, 2000, 2000) 161 scan_position = event.get_scan_position() 162 assert -4000 <= scan_position[0] <= 0 163 assert 21000 <= scan_position[1] <= 25000 164 slide_position = event.get_slide_position() 165 assert 71000 <= slide_position[0] <= 75000 166 assert scan_position[1] == slide_position[1]
def
test_eventarray_conversions():
169def test_eventarray_conversions(): 170 scan = Scan.load_yaml("tests/data") 171 # Origin 172 tile = Tile(scan, 0) 173 event0 = Event(scan, tile, 0, 0) 174 event1 = Event(scan, tile, 1000, 1000) 175 event2 = Event(scan, tile, 2000, 2000) 176 177 event_array = EventArray.from_events([event0, event1, event2]) 178 179 assert len(event_array) == 3 180 assert event_array.metadata is None 181 assert event_array.features is None 182 183 event0.metadata = pd.Series({"event0": 0}) 184 185 try: 186 event_array = EventArray.from_events([event0, event1, event2]) 187 # Should throw error 188 assert False 189 except ValueError: 190 pass 191 192 event1.metadata = pd.Series({"event0": 1}) 193 event2.metadata = pd.Series({"event0": 2}) 194 195 event_array = EventArray.from_events([event0, event1, event2]) 196 197 assert len(event_array) == 3 198 199 events_df = event_array.to_dataframe() 200 201 assert len(events_df) == 3 202 203 assert event_array == EventArray.from_dataframe(events_df) 204 205 # Test adding different dtypes and converting back and forth 206 event_array.features = pd.DataFrame( 207 {"feature1": [1, 2, 3], "feature2": [4.0, 5.0, 6.0]} 208 ) 209 assert event_array == EventArray.from_events(event_array.to_events(scan)) 210 # Test saving and loading 211 assert event_array.save_csv("tests/data/events.csv") 212 assert event_array == EventArray.load_csv("tests/data/events.csv") 213 os.remove("tests/data/events.csv") 214 215 assert event_array.save_hdf5("tests/data/events.h5") 216 assert event_array == EventArray.load_hdf5("tests/data/events.h5") 217 os.remove("tests/data/events.h5")
def
test_ocular_conversions():
220def test_ocular_conversions(): 221 scan = Scan.load_txt("tests/data") 222 input_path = "/mnt/HDSCA_Development/DZ/0B58703/ocular" 223 result = EventArray.load_ocular(input_path) 224 # For the purposes of this test, we will manually relabel "clust" == nan to 0 225 # These come from ocular_interesting.rds, which does not have clusters 226 result.metadata["clust"] = result.metadata["clust"].fillna(0) 227 result.metadata["hcpc"] = result.metadata["hcpc"].fillna(0) 228 result.save_ocular("tests/data") 229 new_result = EventArray.load_ocular("tests/data") 230 # # Sort them so that they are in the same order 231 result = result.sort(["tile", "x", "y"]) 232 new_result = new_result.sort(["tile", "x", "y"]) 233 # Note: hcpc method within ocularr and here are different 234 result.metadata["hcpc"] = new_result.metadata["hcpc"].copy() 235 assert result == new_result 236 # Clean up 237 os.remove("tests/data/rc-final.csv") 238 os.remove("tests/data/rc-final.rds") 239 os.remove("tests/data/rc-final1.rds") 240 os.remove("tests/data/rc-final2.rds") 241 os.remove("tests/data/rc-final3.rds") 242 os.remove("tests/data/rc-final4.rds") 243 244 # Try it with "others" files 245 result = EventArray.load_ocular(input_path, event_type="others") 246 result.save_ocular("tests/data", event_type="others") 247 new_result = EventArray.load_ocular("tests/data", event_type="others") 248 result = result.sort(["tile", "x", "y"]) 249 new_result = new_result.sort(["tile", "x", "y"]) 250 # Note: hcpc method within ocularr and here are different 251 result.metadata["hcpc"] = new_result.metadata["hcpc"].copy() 252 assert result == new_result 253 # Clean up 254 os.remove("tests/data/others-final.csv") 255 os.remove("tests/data/others-final.rds") 256 os.remove("tests/data/others-final1.rds") 257 os.remove("tests/data/others-final2.rds") 258 os.remove("tests/data/others-final3.rds") 259 os.remove("tests/data/others-final4.rds")
def
test_copy_sort_rows_get():
262def test_copy_sort_rows_get(): 263 scan = Scan.load_yaml("tests/data") 264 # Origin 265 tile = Tile(scan, 0) 266 events = [ 267 Event(scan, tile, 0, 100), 268 Event(scan, tile, 0, 0), 269 Event(scan, tile, 1000, 1000), 270 Event(scan, tile, 1000, 1), 271 Event(scan, tile, 2000, 2000), 272 ] 273 274 events = EventArray.from_events(events) 275 276 # Copy 277 events_copy = events.copy() 278 events_copy.info["x"] = 1 279 # Check that changes to the copy did not change the original 280 assert events_copy.info["x"].equals(pd.Series([1, 1, 1, 1, 1])) 281 assert events.info["x"].equals(pd.Series([0, 0, 1000, 1000, 2000])) 282 283 # Sort 284 events = events.sort(["x", "y"], ascending=[False, True]) 285 assert events.info["x"].equals(pd.Series([2000, 1000, 1000, 0, 0])) 286 assert events.info["y"].equals(pd.Series([2000, 1, 1000, 0, 100])) 287 288 # Get 289 events_get = events.get(["x", "y"]) 290 assert events_get["x"].equals(pd.Series([2000, 1000, 1000, 0, 0])) 291 assert events_get["y"].equals(pd.Series([2000, 1, 1000, 0, 100])) 292 assert events_get.columns.equals(pd.Index(["x", "y"])) 293 294 # Rows 295 events_get = events.rows([0, 1, 3]) 296 assert len(events_get) == 3 297 assert events_get.info["x"].equals(pd.Series([2000, 1000, 0])) 298 assert events_get.info["y"].equals(pd.Series([2000, 1, 0])) 299 events_get = events.rows([True, False, False, True, True]) 300 assert len(events_get) == 3 301 assert events_get.info["x"].equals(pd.Series([2000, 0, 0])) 302 assert events_get.info["y"].equals(pd.Series([2000, 0, 100]))