spacr 0.2.46__py3-none-any.whl → 0.2.56__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/core.py +306 -21
- spacr/deep_spacr.py +101 -41
- spacr/gui.py +1 -3
- spacr/gui_core.py +78 -65
- spacr/gui_elements.py +437 -152
- spacr/gui_utils.py +84 -73
- spacr/io.py +14 -7
- spacr/measure.py +196 -145
- spacr/plot.py +2 -42
- spacr/resources/font/open_sans/OFL.txt +93 -0
- spacr/resources/font/open_sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
- spacr/resources/font/open_sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
- spacr/resources/font/open_sans/README.txt +100 -0
- spacr/resources/font/open_sans/static/OpenSans-Bold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-BoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-ExtraBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-ExtraBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-Italic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-Light.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-LightItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-Medium.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-MediumItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-Regular.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-SemiBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-SemiBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Bold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-BoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Italic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Light.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-LightItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Medium.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-MediumItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Regular.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Bold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-BoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Italic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Light.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-LightItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Medium.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-MediumItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Regular.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf +0 -0
- spacr/sequencing.py +481 -587
- spacr/settings.py +197 -122
- spacr/utils.py +21 -13
- {spacr-0.2.46.dist-info → spacr-0.2.56.dist-info}/METADATA +7 -4
- spacr-0.2.56.dist-info/RECORD +100 -0
- spacr-0.2.46.dist-info/RECORD +0 -60
- {spacr-0.2.46.dist-info → spacr-0.2.56.dist-info}/LICENSE +0 -0
- {spacr-0.2.46.dist-info → spacr-0.2.56.dist-info}/WHEEL +0 -0
- {spacr-0.2.46.dist-info → spacr-0.2.56.dist-info}/entry_points.txt +0 -0
- {spacr-0.2.46.dist-info → spacr-0.2.56.dist-info}/top_level.txt +0 -0
spacr/measure.py
CHANGED
@@ -13,6 +13,8 @@ from skimage.feature import graycomatrix, graycoprops
|
|
13
13
|
from mahotas.features import zernike_moments
|
14
14
|
from skimage import morphology, measure, filters
|
15
15
|
from skimage.util import img_as_bool
|
16
|
+
import matplotlib.pyplot as plt
|
17
|
+
from math import ceil, sqrt
|
16
18
|
|
17
19
|
from .logger import log_function_call
|
18
20
|
|
@@ -582,6 +584,113 @@ def _intensity_measurements(cell_mask, nucleus_mask, pathogen_mask, cytoplasm_ma
|
|
582
584
|
|
583
585
|
return pd.concat(cell_dfs, axis=1), pd.concat(nucleus_dfs, axis=1), pd.concat(pathogen_dfs, axis=1), pd.concat(cytoplasm_dfs, axis=1)
|
584
586
|
|
587
|
+
def save_and_add_image_to_grid(png_channels, img_path, grid, plot=False):
|
588
|
+
"""
|
589
|
+
Add an image to a grid and save it as PNG.
|
590
|
+
|
591
|
+
Args:
|
592
|
+
png_channels (ndarray): The array representing the image channels.
|
593
|
+
img_path (str): The path to save the image as PNG.
|
594
|
+
grid (list): The grid of images to be plotted later.
|
595
|
+
|
596
|
+
Returns:
|
597
|
+
grid (list): Updated grid with the new image added.
|
598
|
+
"""
|
599
|
+
|
600
|
+
# Save the image as a PNG
|
601
|
+
cv2.imwrite(img_path, png_channels)
|
602
|
+
|
603
|
+
if plot:
|
604
|
+
|
605
|
+
# Ensure the image is in uint8 format for cv2 functions
|
606
|
+
if png_channels.dtype == np.uint16:
|
607
|
+
png_channels = (png_channels / 256).astype(np.uint8)
|
608
|
+
|
609
|
+
# Get the filename without the extension
|
610
|
+
filename = os.path.splitext(os.path.basename(img_path))[0]
|
611
|
+
|
612
|
+
# Add the label to the image
|
613
|
+
#labeled_image = cv2.putText(png_channels.copy(), filename, (10, 30),
|
614
|
+
# cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
|
615
|
+
|
616
|
+
# Add the labeled image to the grid
|
617
|
+
grid.append(png_channels)
|
618
|
+
|
619
|
+
return grid
|
620
|
+
|
621
|
+
def img_list_to_grid(grid, titles=None):
|
622
|
+
"""
|
623
|
+
Plot a grid of images with optional titles.
|
624
|
+
|
625
|
+
Args:
|
626
|
+
grid (list): List of images to be plotted.
|
627
|
+
titles (list): List of titles for the images.
|
628
|
+
|
629
|
+
Returns:
|
630
|
+
fig (Figure): The matplotlib figure object containing the image grid.
|
631
|
+
"""
|
632
|
+
n_images = len(grid)
|
633
|
+
grid_size = ceil(sqrt(n_images))
|
634
|
+
|
635
|
+
fig, axs = plt.subplots(grid_size, grid_size, figsize=(15, 15), facecolor='black')
|
636
|
+
|
637
|
+
for i, ax in enumerate(axs.flat):
|
638
|
+
if i < n_images:
|
639
|
+
image = grid[i]
|
640
|
+
ax.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
|
641
|
+
ax.axis('off')
|
642
|
+
ax.set_facecolor('black')
|
643
|
+
|
644
|
+
if titles:
|
645
|
+
# Determine text size
|
646
|
+
img_height, img_width = image.shape[:2]
|
647
|
+
text_size = max(min(img_width / (len(titles[i]) * 1.5), img_height / 10), 4)
|
648
|
+
ax.text(5, 5, titles[i], color='white', fontsize=text_size, ha='left', va='top', fontweight='bold')
|
649
|
+
else:
|
650
|
+
fig.delaxes(ax)
|
651
|
+
|
652
|
+
# Adjust spacing
|
653
|
+
plt.subplots_adjust(wspace=0.05, hspace=0.05)
|
654
|
+
plt.tight_layout(pad=0.1)
|
655
|
+
return fig
|
656
|
+
|
657
|
+
def filepaths_to_database(img_paths, settings, source_folder, crop_mode):
|
658
|
+
from. utils import _map_wells_png
|
659
|
+
png_df = pd.DataFrame(img_paths, columns=['png_path'])
|
660
|
+
|
661
|
+
png_df['file_name'] = png_df['png_path'].apply(lambda x: os.path.basename(x))
|
662
|
+
|
663
|
+
parts = png_df['file_name'].apply(lambda x: pd.Series(_map_wells_png(x, timelapse=settings['timelapse'])))
|
664
|
+
|
665
|
+
columns = ['plate', 'row', 'col', 'field']
|
666
|
+
|
667
|
+
if settings['timelapse']:
|
668
|
+
columns = columns + ['time_id']
|
669
|
+
|
670
|
+
columns = columns + ['prcfo']
|
671
|
+
|
672
|
+
if crop_mode == 'cell':
|
673
|
+
columns = columns + ['cell_id']
|
674
|
+
|
675
|
+
if crop_mode == 'nucleus':
|
676
|
+
columns = columns + ['nucleus_id']
|
677
|
+
|
678
|
+
if crop_mode == 'pathogen':
|
679
|
+
columns = columns + ['pathogen_id']
|
680
|
+
|
681
|
+
if crop_mode == 'cytoplasm':
|
682
|
+
columns = columns + ['cytoplasm_id']
|
683
|
+
|
684
|
+
png_df[columns] = parts
|
685
|
+
|
686
|
+
try:
|
687
|
+
conn = sqlite3.connect(f'{source_folder}/measurements/measurements.db', timeout=5)
|
688
|
+
png_df.to_sql('png_list', conn, if_exists='append', index=False)
|
689
|
+
conn.commit()
|
690
|
+
except sqlite3.OperationalError as e:
|
691
|
+
print(f"SQLite error: {e}", flush=True)
|
692
|
+
traceback.print_exc()
|
693
|
+
|
585
694
|
#@log_function_call
|
586
695
|
def _measure_crop_core(index, time_ls, file, settings):
|
587
696
|
|
@@ -603,20 +712,15 @@ def _measure_crop_core(index, time_ls, file, settings):
|
|
603
712
|
A list of cropped images.
|
604
713
|
"""
|
605
714
|
|
606
|
-
from .io import _create_database
|
607
715
|
from .plot import _plot_cropped_arrays
|
608
716
|
from .utils import _merge_overlapping_objects, _filter_object, _relabel_parent_with_child_labels, _exclude_objects, normalize_to_dtype
|
609
|
-
from .utils import _merge_and_save_to_database, _crop_center, _find_bounding_box, _generate_names, _get_percentiles
|
610
|
-
|
717
|
+
from .utils import _merge_and_save_to_database, _crop_center, _find_bounding_box, _generate_names, _get_percentiles
|
718
|
+
|
719
|
+
figs = {}
|
720
|
+
grid = []
|
611
721
|
start = time.time()
|
612
722
|
try:
|
613
723
|
source_folder = os.path.dirname(settings['src'])
|
614
|
-
#if not os.path.basename(source_folder).endswith('merged'):
|
615
|
-
# source_folder = os.path.join(source_folder, 'merged')
|
616
|
-
# print(f'changed source_folder to {source_folder}')
|
617
|
-
|
618
|
-
#if not os.path.exists(source_folder):
|
619
|
-
# return
|
620
724
|
|
621
725
|
file_name = os.path.splitext(file)[0]
|
622
726
|
data = np.load(os.path.join(settings['src'], file))
|
@@ -628,18 +732,13 @@ def _measure_crop_core(index, time_ls, file, settings):
|
|
628
732
|
if settings['verbose']:
|
629
733
|
print(f'Converted data from {data_type_before} to {data_type}')
|
630
734
|
|
631
|
-
if settings['
|
632
|
-
os.makedirs(source_folder+'/measurements', exist_ok=True)
|
633
|
-
_create_database(source_folder+'/measurements/measurements.db')
|
634
|
-
|
635
|
-
if settings['plot_filtration']:
|
636
|
-
|
735
|
+
if settings['plot']:
|
637
736
|
if len(data.shape) == 3:
|
638
737
|
figuresize = data.shape[2]*10
|
639
738
|
else:
|
640
739
|
figuresize = 10
|
641
|
-
|
642
|
-
|
740
|
+
fig = _plot_cropped_arrays(data, file, figuresize)
|
741
|
+
figs[f'{file_name}__before_filtration'] = fig
|
643
742
|
|
644
743
|
channel_arrays = data[:, :, settings['channels']].astype(data_type)
|
645
744
|
if settings['cell_mask_dim'] is not None:
|
@@ -714,9 +813,9 @@ def _measure_crop_core(index, time_ls, file, settings):
|
|
714
813
|
if settings['cytoplasm']:
|
715
814
|
data = np.concatenate((data, cytoplasm_mask[:, :, np.newaxis]), axis=2)
|
716
815
|
|
717
|
-
if settings['
|
718
|
-
_plot_cropped_arrays(data, file, figuresize)
|
719
|
-
|
816
|
+
if settings['plot']:
|
817
|
+
fig = _plot_cropped_arrays(data, file, figuresize)
|
818
|
+
figs[f'{file_name}__after_filtration'] = fig
|
720
819
|
|
721
820
|
if settings['save_measurements']:
|
722
821
|
|
@@ -837,53 +936,12 @@ def _measure_crop_core(index, time_ls, file, settings):
|
|
837
936
|
if png_channels.shape[2] == 2:
|
838
937
|
dummy_channel = np.zeros_like(png_channels[:,:,0]) # Create a 2D zero array with same shape as one channel
|
839
938
|
png_channels = np.dstack((png_channels, dummy_channel))
|
840
|
-
|
939
|
+
grid = save_and_add_image_to_grid(png_channels, img_path, grid, settings['plot'])
|
841
940
|
else:
|
842
|
-
|
941
|
+
grid = save_and_add_image_to_grid(png_channels, img_path, grid, settings['plot'])
|
843
942
|
|
844
943
|
if len(img_paths) == len(objects_in_image):
|
845
|
-
|
846
|
-
png_df = pd.DataFrame(img_paths, columns=['png_path'])
|
847
|
-
|
848
|
-
png_df['file_name'] = png_df['png_path'].apply(lambda x: os.path.basename(x))
|
849
|
-
|
850
|
-
parts = png_df['file_name'].apply(lambda x: pd.Series(_map_wells_png(x, timelapse=settings['timelapse'])))
|
851
|
-
|
852
|
-
columns = ['plate', 'row', 'col', 'field']
|
853
|
-
|
854
|
-
if settings['timelapse']:
|
855
|
-
columns = columns + ['time_id']
|
856
|
-
|
857
|
-
columns = columns + ['prcfo']
|
858
|
-
|
859
|
-
if crop_mode == 'cell':
|
860
|
-
columns = columns + ['cell_id']
|
861
|
-
|
862
|
-
if crop_mode == 'nucleus':
|
863
|
-
columns = columns + ['nucleus_id']
|
864
|
-
|
865
|
-
if crop_mode == 'pathogen':
|
866
|
-
columns = columns + ['pathogen_id']
|
867
|
-
|
868
|
-
if crop_mode == 'cytoplasm':
|
869
|
-
columns = columns + ['cytoplasm_id']
|
870
|
-
|
871
|
-
png_df[columns] = parts
|
872
|
-
|
873
|
-
try:
|
874
|
-
conn = sqlite3.connect(f'{source_folder}/measurements/measurements.db', timeout=5)
|
875
|
-
png_df.to_sql('png_list', conn, if_exists='append', index=False)
|
876
|
-
conn.commit()
|
877
|
-
except sqlite3.OperationalError as e:
|
878
|
-
print(f"SQLite error: {e}", flush=True)
|
879
|
-
traceback.print_exc()
|
880
|
-
|
881
|
-
if settings['plot']:
|
882
|
-
if len(png_channels.shape) == 3:
|
883
|
-
figuresize = png_channels.shape[2]*10
|
884
|
-
else:
|
885
|
-
figuresize = 10
|
886
|
-
_plot_cropped_arrays(png_channels, img_name, figuresize, threshold=1)
|
944
|
+
filepaths_to_database(img_paths, settings, source_folder, crop_mode)
|
887
945
|
|
888
946
|
if settings['save_arrays']:
|
889
947
|
row_idx, col_idx = np.where(region)
|
@@ -891,21 +949,12 @@ def _measure_crop_core(index, time_ls, file, settings):
|
|
891
949
|
array_folder = f"{fldr}/region_array/"
|
892
950
|
os.makedirs(array_folder, exist_ok=True)
|
893
951
|
np.save(os.path.join(array_folder, img_name), region_array)
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
if not settings['save_arrays'] and not settings['save_png'] and settings['plot']:
|
902
|
-
row_idx, col_idx = np.where(region)
|
903
|
-
region_array = data[row_idx.min():row_idx.max()+1, col_idx.min():col_idx.max()+1, :]
|
904
|
-
if len(png_channels.shape) == 3:
|
905
|
-
figuresize = png_channels.shape[2]*10
|
906
|
-
else:
|
907
|
-
figuresize = 10
|
908
|
-
_plot_cropped_arrays(png_channels, file, figuresize, threshold=1)
|
952
|
+
|
953
|
+
grid = save_and_add_image_to_grid(png_channels, img_path, grid, settings['plot'])
|
954
|
+
|
955
|
+
img_paths.append(img_path)
|
956
|
+
if len(img_paths) == len(objects_in_image):
|
957
|
+
filepaths_to_database(img_paths, settings, source_folder, crop_mode)
|
909
958
|
|
910
959
|
cells = np.unique(cell_mask)
|
911
960
|
except Exception as e:
|
@@ -917,7 +966,10 @@ def _measure_crop_core(index, time_ls, file, settings):
|
|
917
966
|
duration = end-start
|
918
967
|
time_ls.append(duration)
|
919
968
|
average_time = np.mean(time_ls) if len(time_ls) > 0 else 0
|
920
|
-
|
969
|
+
if settings['plot']:
|
970
|
+
fig = img_list_to_grid(grid)
|
971
|
+
figs[f'{file_name}__pngs'] = fig
|
972
|
+
return index, average_time, cells, figs
|
921
973
|
|
922
974
|
#@log_function_call
|
923
975
|
def measure_crop(settings):
|
@@ -932,10 +984,9 @@ def measure_crop(settings):
|
|
932
984
|
None
|
933
985
|
"""
|
934
986
|
|
935
|
-
from .io import _save_settings_to_db
|
936
|
-
from .timelapse import _timelapse_masks_to_gif
|
937
|
-
from .
|
938
|
-
from .utils import _list_endpoint_subdirectories, _generate_representative_images, measure_test_mode, print_progress
|
987
|
+
from .io import _save_settings_to_db, _create_database
|
988
|
+
from .timelapse import _timelapse_masks_to_gif
|
989
|
+
from .utils import measure_test_mode, print_progress
|
939
990
|
from .settings import get_measure_crop_settings
|
940
991
|
|
941
992
|
settings = get_measure_crop_settings(settings)
|
@@ -943,9 +994,14 @@ def measure_crop(settings):
|
|
943
994
|
|
944
995
|
src_fldr = settings['src']
|
945
996
|
if not os.path.basename(src_fldr).endswith('merged'):
|
946
|
-
print(f"WARNING: Source folder, settings: src: {src_fldr} should end with 'merged'")
|
997
|
+
print(f"WARNING: Source folder, settings: src: {src_fldr} should end with '/merged'")
|
947
998
|
src_fldr = os.path.join(src_fldr, 'merged')
|
948
999
|
print(f"Changed source folder to: {src_fldr}")
|
1000
|
+
|
1001
|
+
if settings['save_measurements']:
|
1002
|
+
source_folder = os.path.dirname(settings['src'])
|
1003
|
+
os.makedirs(source_folder+'/measurements', exist_ok=True)
|
1004
|
+
_create_database(source_folder+'/measurements/measurements.db')
|
949
1005
|
|
950
1006
|
if settings['cell_mask_dim'] is None:
|
951
1007
|
settings['include_uninfected'] = True
|
@@ -957,7 +1013,15 @@ def measure_crop(settings):
|
|
957
1013
|
settings['cytoplasm'] = True
|
958
1014
|
else:
|
959
1015
|
settings['cytoplasm'] = False
|
960
|
-
|
1016
|
+
|
1017
|
+
spacr_cores = int(mp.cpu_count() - 6)
|
1018
|
+
if spacr_cores <= 2:
|
1019
|
+
spacr_cores = 1
|
1020
|
+
|
1021
|
+
if settings['n_jobs'] > spacr_cores:
|
1022
|
+
print(f'Warning reserving 6 CPU cores for other processes, setting n_jobs to {spacr_cores}')
|
1023
|
+
settings['n_jobs'] = spacr_cores
|
1024
|
+
|
961
1025
|
dirname = os.path.dirname(settings['src'])
|
962
1026
|
settings_df = pd.DataFrame(list(settings.items()), columns=['Key', 'Value'])
|
963
1027
|
settings_csv = os.path.join(dirname,'settings','measure_crop_settings.csv')
|
@@ -994,76 +1058,63 @@ def measure_crop(settings):
|
|
994
1058
|
|
995
1059
|
_save_settings_to_db(settings)
|
996
1060
|
files = [f for f in os.listdir(settings['src']) if f.endswith('.npy')]
|
997
|
-
n_jobs = settings['n_jobs']
|
1061
|
+
n_jobs = settings['n_jobs']
|
998
1062
|
print(f'using {n_jobs} cpu cores')
|
1063
|
+
print_progress(files_processed=0, files_to_process=len(files), n_jobs=n_jobs, time_ls=[], operation_type='Measure and Crop')
|
1064
|
+
|
1065
|
+
def job_callback(result):
|
1066
|
+
completed_jobs.add(result[0])
|
1067
|
+
process_meassure_crop_results([result], settings)
|
1068
|
+
files_processed = len(completed_jobs)
|
1069
|
+
files_to_process = len(files)
|
1070
|
+
print_progress(files_processed, files_to_process, n_jobs, time_ls=time_ls, operation_type='Measure and Crop')
|
1071
|
+
if files_processed >= files_to_process:
|
1072
|
+
pool.terminate()
|
1073
|
+
|
999
1074
|
with mp.Manager() as manager:
|
1000
1075
|
time_ls = manager.list()
|
1076
|
+
completed_jobs = set() # Set to keep track of completed jobs
|
1077
|
+
|
1001
1078
|
with mp.Pool(n_jobs) as pool:
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
files_processed = len(time_ls)
|
1008
|
-
files_to_process = len(files)
|
1009
|
-
print_progress(files_processed, files_to_process, n_jobs, time_ls=time_ls, operation_type='Measure and Crop')
|
1010
|
-
result.get()
|
1011
|
-
|
1012
|
-
if settings['representative_images']:
|
1013
|
-
if settings['save_png']:
|
1014
|
-
img_fldr = os.path.join(os.path.dirname(settings['src']), 'data')
|
1015
|
-
sc_img_fldrs = _list_endpoint_subdirectories(img_fldr)
|
1016
|
-
|
1017
|
-
for i, well_src in enumerate(sc_img_fldrs):
|
1018
|
-
if len(os.listdir(well_src)) < 16:
|
1019
|
-
nr_imgs = len(os.listdir(well_src))
|
1020
|
-
standardize = False
|
1021
|
-
else:
|
1022
|
-
nr_imgs = 16
|
1023
|
-
standardize = True
|
1024
|
-
try:
|
1025
|
-
all_folders = len(sc_img_fldrs)
|
1026
|
-
_save_scimg_plot(src=well_src, nr_imgs=nr_imgs, channel_indices=settings['png_dims'], um_per_pixel=0.1, scale_bar_length_um=10, standardize=standardize, fontsize=12, show_filename=True, channel_names=['red','green','blue'], dpi=300, plot=False, i=i, all_folders=all_folders)
|
1027
|
-
|
1028
|
-
except Exception as e:
|
1029
|
-
print(f"Unable to generate figure for folder {well_src}: {e}", end='\r', flush=True)
|
1030
|
-
#traceback.print_exc()
|
1031
|
-
|
1032
|
-
if settings['save_measurements']:
|
1033
|
-
db_path = os.path.join(os.path.dirname(settings['src']), 'measurements', 'measurements.db')
|
1034
|
-
channel_indices = settings['png_dims']
|
1035
|
-
channel_indices = [min(value, 2) for value in channel_indices]
|
1036
|
-
_generate_representative_images(db_path,
|
1037
|
-
cells=settings['cells'],
|
1038
|
-
cell_loc=settings['cell_loc'],
|
1039
|
-
pathogens=settings['pathogens'],
|
1040
|
-
pathogen_loc=settings['pathogen_loc'],
|
1041
|
-
treatments=settings['treatments'],
|
1042
|
-
treatment_loc=settings['treatment_loc'],
|
1043
|
-
channel_of_interest=settings['channel_of_interest'],
|
1044
|
-
compartments = settings['compartments'],
|
1045
|
-
measurement = settings['measurement'],
|
1046
|
-
nr_imgs=settings['nr_imgs'],
|
1047
|
-
channel_indices=channel_indices,
|
1048
|
-
um_per_pixel=settings['um_per_pixel'],
|
1049
|
-
scale_bar_length_um=10,
|
1050
|
-
plot=False,
|
1051
|
-
fontsize=12,
|
1052
|
-
show_filename=True,
|
1053
|
-
channel_names=None)
|
1079
|
+
for index, file in enumerate(files):
|
1080
|
+
pool.apply_async(_measure_crop_core, args=(index, time_ls, file, settings), callback=job_callback)
|
1081
|
+
|
1082
|
+
pool.close()
|
1083
|
+
pool.join()
|
1054
1084
|
|
1055
1085
|
if settings['timelapse']:
|
1056
1086
|
if settings['timelapse_objects'] == 'nucleus':
|
1057
1087
|
folder_path = settings['src']
|
1058
|
-
mask_channels = [settings['nucleus_mask_dim'], settings['pathogen_mask_dim'],settings['cell_mask_dim']]
|
1059
|
-
object_types = ['nucleus','pathogen','cell']
|
1088
|
+
mask_channels = [settings['nucleus_mask_dim'], settings['pathogen_mask_dim'], settings['cell_mask_dim']]
|
1089
|
+
object_types = ['nucleus', 'pathogen', 'cell']
|
1060
1090
|
_timelapse_masks_to_gif(folder_path, mask_channels, object_types)
|
1061
1091
|
|
1062
|
-
#if settings['save_png']:
|
1063
|
-
img_fldr = os.path.join(os.path.dirname(settings['src']), 'data')
|
1064
|
-
sc_img_fldrs = _list_endpoint_subdirectories(img_fldr)
|
1065
|
-
_scmovie(sc_img_fldrs)
|
1066
1092
|
print("Successfully completed run")
|
1093
|
+
|
1094
|
+
def process_meassure_crop_results(partial_results, settings):
|
1095
|
+
"""
|
1096
|
+
Process the results, display, and optionally save the figures.
|
1097
|
+
|
1098
|
+
Args:
|
1099
|
+
partial_results (list): List of partial results.
|
1100
|
+
settings (dict): Settings dictionary.
|
1101
|
+
save_figures (bool): Flag to save figures or not.
|
1102
|
+
"""
|
1103
|
+
for result in partial_results:
|
1104
|
+
if result is None:
|
1105
|
+
continue
|
1106
|
+
index, avg_time, cells, figs = result
|
1107
|
+
if figs is not None:
|
1108
|
+
for key, fig in figs.items():
|
1109
|
+
part_1, part_2 = key.split('__')
|
1110
|
+
save_dir = os.path.join(os.path.dirname(settings['src']), 'results', f"{part_1}")
|
1111
|
+
os.makedirs(save_dir, exist_ok=True)
|
1112
|
+
fig_path = os.path.join(save_dir, f"{part_2}.pdf")
|
1113
|
+
fig.savefig(fig_path)
|
1114
|
+
plt.figure(fig.number)
|
1115
|
+
plt.show()
|
1116
|
+
plt.close(fig)
|
1117
|
+
result = (index, None, None, None)
|
1067
1118
|
|
1068
1119
|
def generate_cellpose_train_set(folders, dst, min_objects=5):
|
1069
1120
|
os.makedirs(dst, exist_ok=True)
|
spacr/plot.py
CHANGED
@@ -908,49 +908,9 @@ def _plot_cropped_arrays(stack, filename, figuresize=20, cmap='inferno', thresho
|
|
908
908
|
fig, axs = plt.subplots(1, num_channels, figsize=(figuresize, figuresize))
|
909
909
|
for channel in range(num_channels):
|
910
910
|
plot_single_array(stack[:, :, channel], axs[channel], f'C. {channel}', plt.get_cmap(cmap))
|
911
|
-
fig.tight_layout()
|
912
|
-
plt.show()
|
913
|
-
|
914
|
-
#stop = time.time()
|
915
|
-
#duration = stop - start
|
916
|
-
#print('plot_cropped_arrays', duration)
|
911
|
+
fig.tight_layout()
|
917
912
|
print(f'{filename}')
|
918
|
-
|
919
|
-
def _plot_cropped_arrays_v1(stack, figuresize=20, cmap='inferno'):
|
920
|
-
"""
|
921
|
-
Plot cropped arrays.
|
922
|
-
|
923
|
-
Args:
|
924
|
-
stack (ndarray): The array to be plotted.
|
925
|
-
figuresize (int, optional): The size of the figure. Defaults to 20.
|
926
|
-
cmap (str, optional): The colormap to be used. Defaults to 'inferno'.
|
927
|
-
|
928
|
-
Returns:
|
929
|
-
None
|
930
|
-
"""
|
931
|
-
start = time.time()
|
932
|
-
dim = stack.shape
|
933
|
-
channel=min(dim)
|
934
|
-
if len(stack.shape) == 2:
|
935
|
-
f, a = plt.subplots(1, 1,figsize=(figuresize,figuresize))
|
936
|
-
a.imshow(stack, cmap=plt.get_cmap(cmap))
|
937
|
-
a.set_title('Channel one',size=18)
|
938
|
-
a.axis('off')
|
939
|
-
f.tight_layout()
|
940
|
-
plt.show()
|
941
|
-
if len(stack.shape) > 2:
|
942
|
-
anr = stack.shape[2]
|
943
|
-
f, a = plt.subplots(1, anr,figsize=(figuresize,figuresize))
|
944
|
-
for channel in range(anr):
|
945
|
-
a[channel].imshow(stack[:,:,channel], cmap=plt.get_cmap(cmap))
|
946
|
-
a[channel].set_title('Channel '+str(channel),size=18)
|
947
|
-
a[channel].axis('off')
|
948
|
-
f.tight_layout()
|
949
|
-
plt.show()
|
950
|
-
stop = time.time()
|
951
|
-
duration = stop - start
|
952
|
-
print('plot_cropped_arrays', duration)
|
953
|
-
return
|
913
|
+
return fig
|
954
914
|
|
955
915
|
def _visualize_and_save_timelapse_stack_with_tracks(masks, tracks_df, save, src, name, plot, filenames, object_type, mode='btrack', interactive=False):
|
956
916
|
"""
|
@@ -0,0 +1,93 @@
|
|
1
|
+
Copyright 2020 The Open Sans Project Authors (https://github.com/googlefonts/opensans)
|
2
|
+
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
5
|
+
https://openfontlicense.org
|
6
|
+
|
7
|
+
|
8
|
+
-----------------------------------------------------------
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
10
|
+
-----------------------------------------------------------
|
11
|
+
|
12
|
+
PREAMBLE
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
14
|
+
development of collaborative font projects, to support the font creation
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
17
|
+
with others.
|
18
|
+
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
24
|
+
however, cannot be released under any other type of license. The
|
25
|
+
requirement for fonts to remain under this license does not apply
|
26
|
+
to any document created using the fonts or their derivatives.
|
27
|
+
|
28
|
+
DEFINITIONS
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
31
|
+
include source files, build scripts and documentation.
|
32
|
+
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
34
|
+
copyright statement(s).
|
35
|
+
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
37
|
+
distributed by the Copyright Holder(s).
|
38
|
+
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
42
|
+
new environment.
|
43
|
+
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
45
|
+
writer or other person who contributed to the Font Software.
|
46
|
+
|
47
|
+
PERMISSION & CONDITIONS
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
51
|
+
Software, subject to the following conditions:
|
52
|
+
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
55
|
+
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
58
|
+
contains the above copyright notice and this license. These can be
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
62
|
+
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
66
|
+
presented to the users.
|
67
|
+
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
72
|
+
permission.
|
73
|
+
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
75
|
+
must be distributed entirely under this license, and must not be
|
76
|
+
distributed under any other license. The requirement for fonts to
|
77
|
+
remain under this license does not apply to any document created
|
78
|
+
using the Font Software.
|
79
|
+
|
80
|
+
TERMINATION
|
81
|
+
This license becomes null and void if any of the above conditions are
|
82
|
+
not met.
|
83
|
+
|
84
|
+
DISCLAIMER
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
Binary file
|