spacr 0.0.71__py3-none-any.whl → 0.0.81__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
spacr/measure.py CHANGED
@@ -626,7 +626,13 @@ def _measure_crop_core(index, time_ls, file, settings):
626
626
  _create_database(source_folder+'/measurements/measurements.db')
627
627
 
628
628
  if settings['plot_filtration']:
629
- _plot_cropped_arrays(data)
629
+
630
+ if len(data.shape) == 3:
631
+ figuresize = data.shape[2]*10
632
+ else:
633
+ figuresize = 10
634
+ print('')
635
+ _plot_cropped_arrays(data, file, figuresize)
630
636
 
631
637
  channel_arrays = data[:, :, settings['channels']].astype(data_type)
632
638
  if settings['cell_mask_dim'] is not None:
@@ -652,7 +658,6 @@ def _measure_crop_core(index, time_ls, file, settings):
652
658
  data[:, :, settings['nucleus_mask_dim']] = nucleus_mask
653
659
  save_folder = settings['input_folder']
654
660
  np.save(os.path.join(save_folder, file), data)
655
-
656
661
  else:
657
662
  nucleus_mask = np.zeros_like(data[:, :, 0])
658
663
 
@@ -703,7 +708,8 @@ def _measure_crop_core(index, time_ls, file, settings):
703
708
  data = np.concatenate((data, cytoplasm_mask[:, :, np.newaxis]), axis=2)
704
709
 
705
710
  if settings['plot_filtration']:
706
- _plot_cropped_arrays(data)
711
+ _plot_cropped_arrays(data, file, figuresize)
712
+ #_plot_cropped_arrays(data)
707
713
 
708
714
  if settings['save_measurements']:
709
715
 
@@ -792,23 +798,25 @@ def _measure_crop_core(index, time_ls, file, settings):
792
798
  if settings['save_png']:
793
799
  fldr_type = f"{crop_mode}_png/"
794
800
  png_folder = os.path.join(fldr,fldr_type)
795
-
796
801
  img_path = os.path.join(png_folder, img_name)
797
-
802
+ img_paths.append(img_path)
803
+
798
804
  png_channels = data[:, :, settings['png_dims']].astype(data_type)
799
805
 
800
806
  if settings['normalize_by'] == 'fov':
801
- percentiles_list = _get_percentiles(png_channels, settings['normalize'][0],q2=settings['normalize'][1])
807
+ if not settings['normalize'] is False:
808
+ percentile_list = _get_percentiles(png_channels, settings['normalize'][0], settings['normalize'][1])
802
809
 
803
810
  png_channels = _crop_center(png_channels, region, new_width=width, new_height=height)
804
-
805
811
  if isinstance(settings['normalize'], list):
806
812
  if settings['normalize_by'] == 'png':
807
- png_channels = normalize_to_dtype(png_channels, q1=settings['normalize'][0],q2=settings['normalize'][1])
813
+ png_channels = normalize_to_dtype(png_channels, settings['normalize'][0], settings['normalize'][1])
808
814
 
809
815
  if settings['normalize_by'] == 'fov':
810
- png_channels = normalize_to_dtype(png_channels, q1=settings['normalize'][0],q2=settings['normalize'][1], percentiles=percentiles_list)
811
-
816
+ png_channels = normalize_to_dtype(png_channels, settings['normalize'][0], settings['normalize'][1], percentile_list=percentile_list)
817
+ else:
818
+ png_channels = normalize_to_dtype(png_channels, 0, 100)
819
+
812
820
  os.makedirs(png_folder, exist_ok=True)
813
821
 
814
822
  if png_channels.shape[2] == 2:
@@ -818,8 +826,6 @@ def _measure_crop_core(index, time_ls, file, settings):
818
826
  else:
819
827
  cv2.imwrite(img_path, png_channels)
820
828
 
821
- img_paths.append(img_path)
822
-
823
829
  if len(img_paths) == len(objects_in_image):
824
830
 
825
831
  png_df = pd.DataFrame(img_paths, columns=['png_path'])
@@ -858,7 +864,11 @@ def _measure_crop_core(index, time_ls, file, settings):
858
864
  traceback.print_exc()
859
865
 
860
866
  if settings['plot']:
861
- _plot_cropped_arrays(png_channels)
867
+ if len(png_channels.shape) == 3:
868
+ figuresize = png_channels.shape[2]*10
869
+ else:
870
+ figuresize = 10
871
+ _plot_cropped_arrays(png_channels, img_name, figuresize, threshold=1)
862
872
 
863
873
  if settings['save_arrays']:
864
874
  row_idx, col_idx = np.where(region)
@@ -867,12 +877,20 @@ def _measure_crop_core(index, time_ls, file, settings):
867
877
  os.makedirs(array_folder, exist_ok=True)
868
878
  np.save(os.path.join(array_folder, img_name), region_array)
869
879
  if settings['plot']:
870
- _plot_cropped_arrays(region_array)
880
+ if len(png_channels.shape) == 3:
881
+ figuresize = png_channels.shape[2]*10
882
+ else:
883
+ figuresize = 10
884
+ _plot_cropped_arrays(png_channels, img_name, figuresize, threshold=1)
871
885
 
872
886
  if not settings['save_arrays'] and not settings['save_png'] and settings['plot']:
873
887
  row_idx, col_idx = np.where(region)
874
888
  region_array = data[row_idx.min():row_idx.max()+1, col_idx.min():col_idx.max()+1, :]
875
- _plot_cropped_arrays(region_array)
889
+ if len(png_channels.shape) == 3:
890
+ figuresize = png_channels.shape[2]*10
891
+ else:
892
+ figuresize = 10
893
+ _plot_cropped_arrays(png_channels, file, figuresize, threshold=1)
876
894
 
877
895
  cells = np.unique(cell_mask)
878
896
  except Exception as e:
@@ -899,47 +917,17 @@ def measure_crop(settings):
899
917
  None
900
918
  """
901
919
 
902
- if settings.get('test_mode', False):
903
- if not os.basename(settings['src']) == 'test':
904
- src = os.path.join(src, 'test')
905
- settings['src'] = src
906
- print(f'Changed source folder to {src} for test mode')
907
- else:
908
- print(f'Test mode enabled, using source folder {settings["src"]}')
909
-
910
920
  from .io import _save_settings_to_db
911
921
  from .timelapse import _timelapse_masks_to_gif, _scmovie
912
922
  from .plot import _save_scimg_plot
913
- from .utils import _list_endpoint_subdirectories, _generate_representative_images
914
-
915
- #general settings
916
- settings['merge_edge_pathogen_cells'] = True
917
- settings['radial_dist'] = True
918
- settings['calculate_correlation'] = True
919
- settings['manders_thresholds'] = [15,85,95]
920
- settings['homogeneity'] = True
921
- settings['homogeneity_distances'] = [8,16,32]
922
- settings['save_arrays'] = False
923
-
924
- settings['dialate_pngs'] = False
925
- settings['dialate_png_ratios'] = [0.2]
926
- settings['timelapse'] = False
927
- settings['representative_images'] = False
928
- settings['timelapse_objects'] = 'cell'
929
- settings['max_workers'] = os.cpu_count()-2
930
- settings['experiment'] = 'test'
931
- settings['cells'] = 'HeLa'
932
- settings['cell_loc'] = None
933
- settings['pathogens'] = ['ME49Dku80WT', 'ME49Dku80dgra8:GRA8', 'ME49Dku80dgra8', 'ME49Dku80TKO']
934
- settings['pathogen_loc'] = [['c1', 'c2', 'c3', 'c4', 'c5', 'c6'], ['c7', 'c8', 'c9', 'c10', 'c11', 'c12'], ['c13', 'c14', 'c15', 'c16', 'c17', 'c18'], ['c19', 'c20', 'c21', 'c22', 'c23', 'c24']]
935
- settings['treatments'] = ['BR1', 'BR2', 'BR3']
936
- settings['treatment_loc'] = [['c1', 'c2', 'c7', 'c8', 'c13', 'c14', 'c19', 'c20'], ['c3', 'c4', 'c9', 'c10', 'c15', 'c16', 'c21', 'c22'], ['c5', 'c6', 'c11', 'c12', 'c17', 'c18', 'c23', 'c24']]
937
- settings['channel_of_interest'] = 2
938
- settings['compartments'] = ['pathogen', 'cytoplasm']
939
- settings['measurement'] = 'mean_intensity'
940
- settings['nr_imgs'] = 32
941
- settings['um_per_pixel'] = 0.1
942
- settings['center_crop'] = True
923
+ from .utils import _list_endpoint_subdirectories, _generate_representative_images, get_measure_crop_settings, measure_test_mode
924
+
925
+ settings = get_measure_crop_settings(settings)
926
+ settings = measure_test_mode(settings)
927
+
928
+ if not os.path.exists(settings['input_folder']):
929
+ print(f"Error: {settings['input_folder']} does not exist")
930
+ return
943
931
 
944
932
  if settings['cell_mask_dim'] is None:
945
933
  settings['include_uninfected'] = True
@@ -951,8 +939,6 @@ def measure_crop(settings):
951
939
  settings['cytoplasm'] = True
952
940
  else:
953
941
  settings['cytoplasm'] = False
954
-
955
- #settings = {**settings, **annotation_settings, **advanced_settings}
956
942
 
957
943
  dirname = os.path.dirname(settings['input_folder'])
958
944
  settings_df = pd.DataFrame(list(settings.items()), columns=['Key', 'Value'])
@@ -970,10 +956,11 @@ def measure_crop(settings):
970
956
  if isinstance(settings['normalize'], bool) and settings['normalize']:
971
957
  print(f'WARNING: to notmalize single object pngs set normalize to a list of 2 integers, e.g. [1,99] (lower and upper percentiles)')
972
958
  return
973
-
974
- if settings['normalize_by'] not in ['png', 'fov']:
975
- print("Warning: normalize_by should be either 'png' to notmalize each png to its own percentiles or 'fov' to normalize each png to the fov percentiles ")
976
- return
959
+
960
+ if isinstance(settings['normalize'], list) or isinstance(settings['normalize'], bool) and settings['normalize']:
961
+ if settings['normalize_by'] not in ['png', 'fov']:
962
+ print("Warning: normalize_by should be either 'png' to notmalize each png to its own percentiles or 'fov' to normalize each png to the fov percentiles ")
963
+ return
977
964
 
978
965
  if not all(isinstance(settings[key], int) or settings[key] is None for key in int_setting_keys):
979
966
  print(f"WARNING: {int_setting_keys} must all be integers")
spacr/plot.py CHANGED
@@ -12,17 +12,13 @@ import imageio.v2 as imageio
12
12
  from IPython.display import display
13
13
  from skimage.segmentation import find_boundaries
14
14
  from skimage import measure
15
+ from skimage.measure import find_contours, label, regionprops
15
16
 
16
17
  from ipywidgets import IntSlider, interact
17
18
  from IPython.display import Image as ipyimage
18
19
 
19
20
  from .logger import log_function_call
20
21
 
21
-
22
- #from .io import _save_figure
23
- #from .timelapse import _save_mask_timelapse_as_gif
24
- #from .utils import normalize_to_dtype, _remove_outside_objects, _remove_multiobject_cells, _find_similar_sized_images, _remove_noninfected
25
-
26
22
  def plot_masks(batch, masks, flows, cmap='inferno', figuresize=20, nr=1, file_type='.npz', print_object_number=True):
27
23
  """
28
24
  Plot the masks and flows for a given batch of images.
@@ -193,7 +189,7 @@ def _get_colours_merged(outline_color):
193
189
  outline_colors = [[1, 0, 0], [0, 0, 1], [0, 1, 0]] # rbg
194
190
  return outline_colors
195
191
 
196
- def plot_images_and_arrays(folders, lower_percentile=1, upper_percentile=99, threshold=1000, extensions=['.npy', '.tif', '.tiff', '.png']):
192
+ def plot_images_and_arrays(folders, lower_percentile=1, upper_percentile=99, threshold=1000, extensions=['.npy', '.tif', '.tiff', '.png'], overlay=False, max_nr=None, randomize=True):
197
193
  """
198
194
  Plot images and arrays from the given folders.
199
195
 
@@ -203,6 +199,7 @@ def plot_images_and_arrays(folders, lower_percentile=1, upper_percentile=99, thr
203
199
  upper_percentile (int, optional): The upper percentile for image normalization. Defaults to 99.
204
200
  threshold (int, optional): The threshold for determining whether to display an image as a mask or normalize it. Defaults to 1000.
205
201
  extensions (list, optional): A list of file extensions to consider. Defaults to ['.npy', '.tif', '.tiff', '.png'].
202
+ overlay (bool, optional): If True, overlay the outlines of the objects on the image. Defaults to False.
206
203
  """
207
204
 
208
205
  def normalize_image(image, lower=1, upper=99):
@@ -226,7 +223,7 @@ def plot_images_and_arrays(folders, lower_percentile=1, upper_percentile=99, thr
226
223
  filtered_dict = {k: v for k, v in file_dict.items() if len(v) == len(folders)}
227
224
  return filtered_dict
228
225
 
229
- def plot_from_file_dict(file_dict, threshold=1000, lower_percentile=1, upper_percentile=99):
226
+ def plot_from_file_dict(file_dict, threshold=1000, lower_percentile=1, upper_percentile=99, overlay=False, save=False):
230
227
  """
231
228
  Plot images and arrays from the given file dictionary.
232
229
 
@@ -235,18 +232,14 @@ def plot_images_and_arrays(folders, lower_percentile=1, upper_percentile=99, thr
235
232
  threshold (int, optional): The threshold for determining whether to display an image as a mask or normalize it. Defaults to 1000.
236
233
  lower_percentile (int, optional): The lower percentile for image normalization. Defaults to 1.
237
234
  upper_percentile (int, optional): The upper percentile for image normalization. Defaults to 99.
235
+ overlay (bool, optional): If True, overlay the outlines of the objects on the image. Defaults to False.
238
236
  """
239
237
 
240
238
  for filename, folder_paths in file_dict.items():
241
- num_files = len(folder_paths)
242
- fig, axes = plt.subplots(1, num_files, figsize=(15, 5))
243
- #fig.suptitle(filename)
244
-
245
- # Ensure axes is always a list
246
- if num_files == 1:
247
- axes = [axes]
239
+ image_data = None
240
+ mask_data = None
248
241
 
249
- for i, (folder, path) in enumerate(folder_paths.items()):
242
+ for folder, path in folder_paths.items():
250
243
  if path.endswith('.npy'):
251
244
  data = np.load(path)
252
245
  elif path.endswith('.tif') or path.endswith('.tiff'):
@@ -254,25 +247,57 @@ def plot_images_and_arrays(folders, lower_percentile=1, upper_percentile=99, thr
254
247
  else:
255
248
  continue
256
249
 
257
- ax = axes[i]
258
250
  unique_values = np.unique(data)
251
+
259
252
  if len(unique_values) > threshold:
260
- # Normalize image to percentiles
261
- data = normalize_image(data, lower_percentile, upper_percentile)
262
- ax.imshow(data, cmap='gray')
253
+ image_data = normalize_image(data, lower_percentile, upper_percentile)
263
254
  else:
264
- # Display as mask with random colormap
265
- cmap = random_cmap(num_objects=len(unique_values))
266
- ax.imshow(data, cmap=cmap)
267
-
268
- ax.set_title(f"{os.path.basename(folder)}: {os.path.basename(path)}")
269
- ax.axis('off')
270
- plt.tight_layout
271
- plt.subplots_adjust(wspace=0.01, hspace=0.01)
272
- plt.show()
273
-
255
+ mask_data = data
256
+
257
+ if image_data is not None and mask_data is not None:
258
+ fig, axes = plt.subplots(1, 2, figsize=(15, 7))
259
+
260
+ # Display the mask with random colormap
261
+ cmap = random_cmap(num_objects=len(np.unique(mask_data)))
262
+ axes[0].imshow(mask_data, cmap=cmap)
263
+ axes[0].set_title(f"{filename} - Mask")
264
+ axes[0].axis('off')
265
+
266
+ # Display the normalized image
267
+ axes[1].imshow(image_data, cmap='gray')
268
+ if overlay:
269
+ labeled_mask = label(mask_data)
270
+ for region in regionprops(labeled_mask):
271
+ if region.image.shape[0] >= 2 and region.image.shape[1] >= 2:
272
+ contours = find_contours(region.image, 0.75)
273
+ for contour in contours:
274
+ # Adjust contour coordinates relative to the full image
275
+ contour[:, 0] += region.bbox[0]
276
+ contour[:, 1] += region.bbox[1]
277
+ axes[1].plot(contour[:, 1], contour[:, 0], linewidth=2, color='magenta')
278
+
279
+ axes[1].set_title(f"{filename} - Normalized Image")
280
+ axes[1].axis('off')
281
+
282
+ plt.tight_layout()
283
+ plt.show()
284
+
285
+ if save:
286
+ save_path = os.path.join(folder,f"{filename}.png")
287
+ plt.savefig(save_path)
288
+
289
+ if overlay:
290
+ print(f'Overlay will only work on the first two folders in the list')
291
+
274
292
  file_dict = find_files(folders, extensions)
275
- plot_from_file_dict(file_dict, threshold, lower_percentile, upper_percentile)
293
+ items = list(file_dict.items())
294
+ if randomize:
295
+ random.shuffle(items)
296
+ if isinstance(max_nr, (int, float)):
297
+ items = items[:int(max_nr)]
298
+ file_dict = dict(items)
299
+
300
+ plot_from_file_dict(file_dict, threshold, lower_percentile, upper_percentile, overlay, save=False)
276
301
  return
277
302
 
278
303
  def _filter_objects_in_plot(stack, cell_mask_dim, nucleus_mask_dim, pathogen_mask_dim, mask_dims, filter_min_max, include_multinucleated, include_multiinfected):
@@ -739,7 +764,53 @@ def _save_scimg_plot(src, nr_imgs=16, channel_indices=[0,1,2], um_per_pixel=0.1,
739
764
 
740
765
  return
741
766
 
742
- def _plot_cropped_arrays(stack, figuresize=20,cmap='inferno'):
767
+ def _plot_cropped_arrays(stack, filename, figuresize=20, cmap='inferno', threshold=500):
768
+ """
769
+ Plot cropped arrays.
770
+
771
+ Args:
772
+ stack (ndarray): The array to be plotted.
773
+ figuresize (int, optional): The size of the figure. Defaults to 20.
774
+ cmap (str, optional): The colormap to be used. Defaults to 'inferno'.
775
+ threshold (int, optional): The threshold for the number of unique intensity values. Defaults to 1000.
776
+
777
+ Returns:
778
+ None
779
+ """
780
+ #start = time.time()
781
+ dim = stack.shape
782
+
783
+ def plot_single_array(array, ax, title, chosen_cmap):
784
+ unique_values = np.unique(array)
785
+ num_unique_values = len(unique_values)
786
+
787
+ if num_unique_values <= threshold:
788
+ chosen_cmap = _generate_mask_random_cmap(array)
789
+ title = f'{title}, {num_unique_values} (obj.)'
790
+
791
+ ax.imshow(array, cmap=chosen_cmap)
792
+ ax.set_title(title, size=18)
793
+ ax.axis('off')
794
+
795
+ if len(dim) == 2:
796
+ fig, ax = plt.subplots(1, 1, figsize=(figuresize, figuresize))
797
+ plot_single_array(stack, ax, 'Channel one', plt.get_cmap(cmap))
798
+ fig.tight_layout()
799
+ plt.show()
800
+ elif len(dim) > 2:
801
+ num_channels = dim[2]
802
+ fig, axs = plt.subplots(1, num_channels, figsize=(figuresize, figuresize))
803
+ for channel in range(num_channels):
804
+ plot_single_array(stack[:, :, channel], axs[channel], f'C. {channel}', plt.get_cmap(cmap))
805
+ fig.tight_layout()
806
+ plt.show()
807
+
808
+ #stop = time.time()
809
+ #duration = stop - start
810
+ #print('plot_cropped_arrays', duration)
811
+ print(f'{filename}')
812
+
813
+ def _plot_cropped_arrays_v1(stack, figuresize=20, cmap='inferno'):
743
814
  """
744
815
  Plot cropped arrays.
745
816
 
@@ -1192,56 +1263,6 @@ def _plot_plates(df, variable, grouping, min_max, cmap, min_count=0):
1192
1263
  plt.show()
1193
1264
  return fig
1194
1265
 
1195
- #def plate_heatmap(src, variable='recruitment', grouping='mean', min_max='allq', cmap='viridis', channel_of_interest=3, min_count=25, verbose=False):
1196
- # db_loc = [src+'/measurements/measurements.db']
1197
- # tables = ['cell', 'nucleus', 'pathogen','cytoplasm']
1198
- # include_multinucleated, include_multiinfected, include_noninfected = True, 2.0, True
1199
- # df, _ = spacr.io._read_and_merge_data(db_loc,
1200
- # tables,
1201
- # verbose=verbose,
1202
- # include_multinucleated=include_multinucleated,
1203
- # include_multiinfected=include_multiinfected,
1204
- # include_noninfected=include_noninfected)
1205
- #
1206
- # df['recruitment'] = df[f'pathogen_channel_{channel_of_interest}_outside_75_percentile']/df[f'cytoplasm_channel_{channel_of_interest}_mean_intensity']
1207
- #
1208
- # spacr.plot._plot_plates(df, variable, grouping, min_max, cmap, min_count)
1209
- # #display(df)
1210
- # #for col in df.columns:
1211
- # # print(col)
1212
- # return
1213
-
1214
- #from finetune cellpose
1215
- #def plot_arrays(src, figuresize=50, cmap='inferno', nr=1, normalize=True, q1=1, q2=99):
1216
- # paths = []
1217
- # for file in os.listdir(src):
1218
- # if file.endswith('.tif') or file.endswith('.tiff'):
1219
- # path = os.path.join(src, file)
1220
- # paths.append(path)
1221
- # paths = random.sample(paths, nr)
1222
- # for path in paths:
1223
- # print(f'Image path:{path}')
1224
- # img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
1225
- # if normalize:
1226
- # img = normalize_to_dtype(array=img, q1=q1, q2=q2)
1227
- # dim = img.shape
1228
- # if len(img.shape) > 2:
1229
- # array_nr = img.shape[2]
1230
- # fig, axs = plt.subplots(1, array_nr, figsize=(figuresize, figuresize))
1231
- # for channel in range(array_nr):
1232
- # i = np.take(img, [channel], axis=2)
1233
- # axs[channel].imshow(i, cmap=plt.get_cmap(cmap))
1234
- # axs[channel].set_title('Channel '+str(channel), size=24)
1235
- # axs[channel].axis('off')
1236
- # else:
1237
- # fig, ax = plt.subplots(1, 1, figsize=(figuresize, figuresize))
1238
- # ax.imshow(img, cmap=plt.get_cmap(cmap))
1239
- # ax.set_title('Channel 0', size=24)
1240
- # ax.axis('off')
1241
- # fig.tight_layout()
1242
- # plt.show()
1243
- # return
1244
-
1245
1266
  def print_mask_and_flows(stack, mask, flows, overlay=False):
1246
1267
  fig, axs = plt.subplots(1, 3, figsize=(30, 10)) # Adjust subplot layout
1247
1268
 
@@ -1434,3 +1455,18 @@ def plot_comparison_results(comparison_results):
1434
1455
  plt.tight_layout()
1435
1456
  plt.show()
1436
1457
  return fig
1458
+
1459
+ def plot_object_outlines(src, objects=['nucleus','cell','pathogen'], channels=[0,1,2], max_nr=10):
1460
+
1461
+ for object_, channel in zip(objects, channels):
1462
+ folders = [os.path.join(src, 'norm_channel_stack', f'{object_}_mask_stack'),
1463
+ os.path.join(src,f'{channel+1}')]
1464
+ print(folders)
1465
+ plot_images_and_arrays(folders,
1466
+ lower_percentile=2,
1467
+ upper_percentile=99.5,
1468
+ threshold=1000,
1469
+ extensions=['.npy', '.tif', '.tiff', '.png'],
1470
+ overlay=True,
1471
+ max_nr=10,
1472
+ randomize=True)