cellects 0.2.7__py3-none-any.whl → 0.3.1__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.
- cellects/core/cellects_threads.py +46 -184
- cellects/core/motion_analysis.py +74 -45
- cellects/core/one_image_analysis.py +233 -528
- cellects/core/program_organizer.py +157 -98
- cellects/core/script_based_run.py +13 -23
- cellects/gui/first_window.py +7 -4
- cellects/gui/image_analysis_window.py +45 -35
- cellects/gui/ui_strings.py +3 -2
- cellects/image_analysis/image_segmentation.py +21 -77
- cellects/image_analysis/morphological_operations.py +9 -13
- cellects/image_analysis/one_image_analysis_threads.py +312 -182
- cellects/image_analysis/shape_descriptors.py +1068 -1067
- cellects/utils/formulas.py +3 -1
- cellects/utils/load_display_save.py +1 -1
- {cellects-0.2.7.dist-info → cellects-0.3.1.dist-info}/METADATA +1 -1
- {cellects-0.2.7.dist-info → cellects-0.3.1.dist-info}/RECORD +20 -20
- {cellects-0.2.7.dist-info → cellects-0.3.1.dist-info}/LICENSE +0 -0
- {cellects-0.2.7.dist-info → cellects-0.3.1.dist-info}/WHEEL +0 -0
- {cellects-0.2.7.dist-info → cellects-0.3.1.dist-info}/entry_points.txt +0 -0
- {cellects-0.2.7.dist-info → cellects-0.3.1.dist-info}/top_level.txt +0 -0
|
@@ -33,7 +33,7 @@ from cellects.image_analysis.shape_descriptors import from_shape_descriptors_cla
|
|
|
33
33
|
from cellects.image_analysis.morphological_operations import create_ellipse, rank_from_top_to_bottom_from_left_to_right, \
|
|
34
34
|
get_quick_bounding_boxes, get_bb_with_moving_centers, get_contours, keep_one_connected_component, box_counting_dimension, prepare_box_counting
|
|
35
35
|
from cellects.image_analysis.progressively_add_distant_shapes import ProgressivelyAddDistantShapes
|
|
36
|
-
from cellects.core.one_image_analysis import OneImageAnalysis
|
|
36
|
+
from cellects.core.one_image_analysis import OneImageAnalysis, init_params
|
|
37
37
|
from cellects.utils.load_display_save import read_and_rotate, video2numpy
|
|
38
38
|
from cellects.image_analysis.morphological_operations import shape_selection, draw_img_with_mask
|
|
39
39
|
|
|
@@ -98,8 +98,8 @@ class ProgramOrganizer:
|
|
|
98
98
|
self.one_arena_done: bool = False
|
|
99
99
|
self.reduce_image_dim: bool = False
|
|
100
100
|
self.first_exp_ready_to_run: bool = False
|
|
101
|
-
self.data_to_save = {'first_image': False, '
|
|
102
|
-
self.sample_number =
|
|
101
|
+
self.data_to_save = {'first_image': False, 'exif': False, 'vars': False}
|
|
102
|
+
self.sample_number = 1
|
|
103
103
|
self.top = None
|
|
104
104
|
self.motion = None
|
|
105
105
|
self.analysis_instance = None
|
|
@@ -121,6 +121,7 @@ class ProgramOrganizer:
|
|
|
121
121
|
self.one_row_per_frame = None
|
|
122
122
|
self.not_analyzed_individuals = None
|
|
123
123
|
self.visualize: bool = True
|
|
124
|
+
self.network_shaped: bool = False
|
|
124
125
|
|
|
125
126
|
def update_variable_dict(self):
|
|
126
127
|
"""
|
|
@@ -243,6 +244,7 @@ class ProgramOrganizer:
|
|
|
243
244
|
self.data_list = insensitive_glob(self.all['radical'] + '*' + self.all['extension']) # Provides a list ordered by last modification date
|
|
244
245
|
self.all['folder_list'] = []
|
|
245
246
|
self.all['folder_number'] = 1
|
|
247
|
+
self.vars['first_detection_frame'] = 0
|
|
246
248
|
if len(self.data_list) > 0:
|
|
247
249
|
self._sort_data_list()
|
|
248
250
|
self.sample_number = self.all['first_folder_sample_number']
|
|
@@ -318,8 +320,7 @@ class ProgramOrganizer:
|
|
|
318
320
|
"""
|
|
319
321
|
Set the analyzed individuals variable in the dataset.
|
|
320
322
|
"""
|
|
321
|
-
|
|
322
|
-
self.vars['analyzed_individuals'] = np.arange(self.sample_number) + 1
|
|
323
|
+
self.vars['analyzed_individuals'] = np.arange(self.sample_number) + 1
|
|
323
324
|
if self.not_analyzed_individuals is not None:
|
|
324
325
|
self.vars['analyzed_individuals'] = np.delete(self.vars['analyzed_individuals'],
|
|
325
326
|
self.not_analyzed_individuals - 1)
|
|
@@ -349,7 +350,10 @@ class ProgramOrganizer:
|
|
|
349
350
|
It updates the state of various attributes based on the loaded data
|
|
350
351
|
and logs appropriate messages.
|
|
351
352
|
"""
|
|
353
|
+
self.analysis_instance = None
|
|
352
354
|
self.first_im = None
|
|
355
|
+
self.first_image = None
|
|
356
|
+
self.last_image = None
|
|
353
357
|
current_global_pathway = self.all['global_pathway']
|
|
354
358
|
folder_number = self.all['folder_number']
|
|
355
359
|
if folder_number > 1:
|
|
@@ -362,7 +366,7 @@ class ProgramOrganizer:
|
|
|
362
366
|
if data_to_run_cellects_quickly is None:
|
|
363
367
|
data_to_run_cellects_quickly = {}
|
|
364
368
|
|
|
365
|
-
if ('validated_shapes' in data_to_run_cellects_quickly) and ('
|
|
369
|
+
if ('validated_shapes' in data_to_run_cellects_quickly) and ('all' in data_to_run_cellects_quickly) and ('bb_coord' in data_to_run_cellects_quickly['all']['vars']):
|
|
366
370
|
logging.info("Success to load Data to run Cellects quickly.pkl from the user chosen directory")
|
|
367
371
|
self.all = data_to_run_cellects_quickly['all']
|
|
368
372
|
# If you want to add a new variable, first run an updated version of all_vars_dict,
|
|
@@ -387,8 +391,7 @@ class ProgramOrganizer:
|
|
|
387
391
|
self.update_folder_id(self.all['sample_number_per_folder'][0], self.all['folder_list'][0])
|
|
388
392
|
self.get_first_image()
|
|
389
393
|
self.get_last_image()
|
|
390
|
-
(ccy1, ccy2, ccx1, ccx2, self.
|
|
391
|
-
'coordinates']
|
|
394
|
+
(ccy1, ccy2, ccx1, ccx2, self.top, self.bot, self.left, self.right) = data_to_run_cellects_quickly['all']['vars']['bb_coord']
|
|
392
395
|
if self.all['automatically_crop']:
|
|
393
396
|
self.first_image.crop_coord = [ccy1, ccy2, ccx1, ccx2]
|
|
394
397
|
logging.info("Crop first image")
|
|
@@ -447,11 +450,6 @@ class ProgramOrganizer:
|
|
|
447
450
|
if self.data_to_save['first_image']:
|
|
448
451
|
data_to_run_cellects_quickly['validated_shapes'] = self.first_image.im_combinations[self.current_combination_id]['binary_image']
|
|
449
452
|
data_to_run_cellects_quickly['shape_number'] = self.first_image.im_combinations[self.current_combination_id]['shape_number']
|
|
450
|
-
# data_to_run_cellects_quickly['converted_image'] = self.first_image.im_combinations[self.current_combination_id]['converted_image']
|
|
451
|
-
if self.data_to_save['coordinates']:
|
|
452
|
-
data_to_run_cellects_quickly['coordinates'] = self._list_coordinates()
|
|
453
|
-
logging.info("When they exist, do overwrite unaltered video")
|
|
454
|
-
self.all['overwrite_unaltered_videos'] = True
|
|
455
453
|
if self.data_to_save['exif']:
|
|
456
454
|
self.vars['exif'] = self.extract_exif()
|
|
457
455
|
self.all['vars'] = self.vars
|
|
@@ -459,7 +457,7 @@ class ProgramOrganizer:
|
|
|
459
457
|
pickle_rick = PickleRick()
|
|
460
458
|
pickle_rick.write_file(data_to_run_cellects_quickly, 'Data to run Cellects quickly.pkl')
|
|
461
459
|
|
|
462
|
-
def
|
|
460
|
+
def list_coordinates(self):
|
|
463
461
|
"""
|
|
464
462
|
Summarize the coordinates of images and video.
|
|
465
463
|
|
|
@@ -475,10 +473,9 @@ class ProgramOrganizer:
|
|
|
475
473
|
|
|
476
474
|
"""
|
|
477
475
|
if self.first_image.crop_coord is None:
|
|
478
|
-
self.first_image.crop_coord = [0, self.first_image.image.shape[0], 0,
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
return videos_coordinates
|
|
476
|
+
self.first_image.crop_coord = [0, self.first_image.image.shape[0], 0, self.first_image.image.shape[1]]
|
|
477
|
+
self.vars['bb_coord'] = self.first_image.crop_coord + [self.top, self.bot, self.left, self.right]
|
|
478
|
+
self.all['overwrite_unaltered_videos'] = True
|
|
482
479
|
|
|
483
480
|
def get_first_image(self, first_im: NDArray=None, sample_number: int=None):
|
|
484
481
|
"""
|
|
@@ -488,24 +485,22 @@ class ProgramOrganizer:
|
|
|
488
485
|
depending on whether the data is an image or a video. It performs necessary
|
|
489
486
|
preprocessing and initializes relevant attributes for subsequent analysis.
|
|
490
487
|
"""
|
|
488
|
+
if sample_number is not None:
|
|
489
|
+
self.sample_number = sample_number
|
|
491
490
|
self.reduce_image_dim = False
|
|
492
491
|
if first_im is not None:
|
|
493
492
|
self.first_im = first_im
|
|
494
|
-
if sample_number is not None:
|
|
495
|
-
self.sample_number = sample_number
|
|
496
493
|
else:
|
|
497
494
|
logging.info("Load first image")
|
|
498
|
-
just_read_image = self.first_im is not None
|
|
499
|
-
# just_read_image = self.analysis_instance is not None
|
|
500
495
|
if self.all['im_or_vid'] == 1:
|
|
501
|
-
if
|
|
496
|
+
if self.analysis_instance is None:
|
|
502
497
|
self.analysis_instance = video2numpy(self.data_list[0])
|
|
503
498
|
self.sample_number = len(self.data_list)
|
|
504
499
|
self.vars['img_number'] = self.analysis_instance.shape[0]
|
|
505
500
|
self.first_im = self.analysis_instance[0, ...]
|
|
501
|
+
self.vars['dims'] = self.analysis_instance.shape[:3]
|
|
506
502
|
else:
|
|
507
503
|
self.first_im = self.analysis_instance[self.vars['first_detection_frame'], ...]
|
|
508
|
-
self.vars['dims'] = self.analysis_instance.shape[:3]
|
|
509
504
|
|
|
510
505
|
else:
|
|
511
506
|
self.vars['img_number'] = len(self.data_list)
|
|
@@ -519,7 +514,8 @@ class ProgramOrganizer:
|
|
|
519
514
|
self.reduce_image_dim = True
|
|
520
515
|
if self.reduce_image_dim:
|
|
521
516
|
self.first_im = self.first_im[:, :, 0]
|
|
522
|
-
|
|
517
|
+
|
|
518
|
+
self.first_image = OneImageAnalysis(self.first_im, self.sample_number)
|
|
523
519
|
self.vars['already_greyscale'] = self.first_image.already_greyscale
|
|
524
520
|
if self.vars['already_greyscale']:
|
|
525
521
|
self.vars["convert_for_origin"] = {"bgr": np.array((1, 1, 1), dtype=np.uint8), "logical": "None"}
|
|
@@ -546,16 +542,7 @@ class ProgramOrganizer:
|
|
|
546
542
|
self.last_im = last_im
|
|
547
543
|
else:
|
|
548
544
|
if self.all['im_or_vid'] == 1:
|
|
549
|
-
|
|
550
|
-
counter = 0
|
|
551
|
-
while cap.isOpened() and counter < self.vars['img_number']:
|
|
552
|
-
ret, frame = cap.read()
|
|
553
|
-
if self.reduce_image_dim:
|
|
554
|
-
frame = frame[:, :, 0]
|
|
555
|
-
self.analysis_instance[-1, ...] = frame
|
|
556
|
-
counter += 1
|
|
557
|
-
self.last_im = frame
|
|
558
|
-
cap.release()
|
|
545
|
+
self.last_im = self.analysis_instance[-1, ...]
|
|
559
546
|
else:
|
|
560
547
|
is_landscape = self.first_image.image.shape[0] < self.first_image.image.shape[1]
|
|
561
548
|
self.last_im = read_and_rotate(self.data_list[-1], self.first_im, self.all['raw_images'], is_landscape)
|
|
@@ -574,7 +561,8 @@ class ProgramOrganizer:
|
|
|
574
561
|
"""
|
|
575
562
|
self.vars['time_step_is_arbitrary'] = True
|
|
576
563
|
if self.all['im_or_vid'] == 1:
|
|
577
|
-
|
|
564
|
+
if not 'dims' in self.vars:
|
|
565
|
+
self.vars['dims'] = self.analysis_instance.shape[:3]
|
|
578
566
|
timings = np.arange(self.vars['dims'][0])
|
|
579
567
|
else:
|
|
580
568
|
timings = np.arange(len(self.data_list))
|
|
@@ -662,15 +650,15 @@ class ProgramOrganizer:
|
|
|
662
650
|
self.first_image.im_combinations[self.current_combination_id]['converted_image'] = bracket_to_uint8_image_contrast(greyscale)
|
|
663
651
|
self.first_image.im_combinations[self.current_combination_id]['shape_number'] = shape_number
|
|
664
652
|
|
|
665
|
-
def fast_last_image_segmentation(self,
|
|
653
|
+
def fast_last_image_segmentation(self, bio_mask: NDArray[np.uint8] = None, back_mask: NDArray[np.uint8] = None):
|
|
666
654
|
"""
|
|
667
655
|
Segment the first or subsequent image in a series for biological and background masks.
|
|
668
656
|
|
|
669
657
|
Parameters
|
|
670
658
|
----------
|
|
671
|
-
|
|
659
|
+
bio_mask : NDArray[np.uint8], optional
|
|
672
660
|
The biological mask to be applied to the image.
|
|
673
|
-
|
|
661
|
+
back_mask : NDArray[np.uint8], optional
|
|
674
662
|
The background mask to be applied to the image.
|
|
675
663
|
|
|
676
664
|
Returns
|
|
@@ -688,14 +676,14 @@ class ProgramOrganizer:
|
|
|
688
676
|
self.vars['convert_for_motion'] = {"logical": 'None', "PCA": np.ones(3, dtype=np.uint8)}
|
|
689
677
|
self.cropping(is_first_image=False)
|
|
690
678
|
self.last_image.convert_and_segment(self.vars['convert_for_motion'], self.vars["color_number"],
|
|
691
|
-
|
|
679
|
+
bio_mask, back_mask, self.first_image.subtract_background,
|
|
692
680
|
self.first_image.subtract_background2,
|
|
693
681
|
rolling_window_segmentation=self.vars["rolling_window_segmentation"],
|
|
694
682
|
filter_spec=self.vars["filter_spec"])
|
|
695
683
|
if self.vars['drift_already_corrected'] and not self.last_image.drift_correction_already_adjusted and not self.vars["rolling_window_segmentation"]['do']:
|
|
696
684
|
self.last_image.check_if_image_border_attest_drift_correction()
|
|
697
685
|
self.last_image.convert_and_segment(self.vars['convert_for_motion'], self.vars["color_number"],
|
|
698
|
-
|
|
686
|
+
bio_mask, back_mask, self.first_image.subtract_background,
|
|
699
687
|
self.first_image.subtract_background2,
|
|
700
688
|
allowed_window=self.last_image.drift_mask_coord,
|
|
701
689
|
filter_spec=self.vars["filter_spec"])
|
|
@@ -713,6 +701,102 @@ class ProgramOrganizer:
|
|
|
713
701
|
greyscale = self.last_image.image
|
|
714
702
|
self.last_image.im_combinations[self.current_combination_id]['converted_image'] = bracket_to_uint8_image_contrast(greyscale)
|
|
715
703
|
|
|
704
|
+
def save_user_masks(self, bio_mask=None, back_mask=None):
|
|
705
|
+
self.all["bio_mask"] = None
|
|
706
|
+
self.all["back_mask"] = None
|
|
707
|
+
if self.all['keep_cell_and_back_for_all_folders']:
|
|
708
|
+
self.all["bio_mask"] = bio_mask
|
|
709
|
+
self.all["back_mask"] = back_mask
|
|
710
|
+
|
|
711
|
+
def full_first_image_segmentation(self, first_param_known: bool, bio_mask: NDArray[np.uint8] = None, back_mask: NDArray[np.uint8] = None):
|
|
712
|
+
if bio_mask.any():
|
|
713
|
+
shape_nb, ordered_image = cv2.connectedComponents((bio_mask > 0).astype(np.uint8))
|
|
714
|
+
shape_nb -= 1
|
|
715
|
+
bio_mask = np.nonzero(bio_mask)
|
|
716
|
+
else:
|
|
717
|
+
shape_nb = 0
|
|
718
|
+
bio_mask = None
|
|
719
|
+
if back_mask.any():
|
|
720
|
+
back_mask = np.nonzero(back_mask)
|
|
721
|
+
else:
|
|
722
|
+
back_mask = None
|
|
723
|
+
self.save_user_masks(bio_mask=bio_mask, back_mask=back_mask)
|
|
724
|
+
if self.visualize or len(self.first_im.shape) == 2:
|
|
725
|
+
if not first_param_known and self.all['scale_with_image_or_cells'] == 0 and self.all["set_spot_size"]:
|
|
726
|
+
self.get_average_pixel_size()
|
|
727
|
+
else:
|
|
728
|
+
self.starting_blob_hsize_in_pixels = None
|
|
729
|
+
self.fast_first_image_segmentation()
|
|
730
|
+
if not self.vars['several_blob_per_arena'] and bio_mask is not None and shape_nb == self.sample_number and self.first_image.im_combinations[self.current_combination_id]['shape_number'] != self.sample_number:
|
|
731
|
+
self.first_image.im_combinations[self.current_combination_id]['shape_number'] = shape_nb
|
|
732
|
+
self.first_image.shape_number = shape_nb
|
|
733
|
+
self.first_image.validated_shapes = (ordered_image > 0).astype(np.uint8)
|
|
734
|
+
self.first_image.im_combinations[self.current_combination_id]['binary_image'] = self.first_image.validated_shapes
|
|
735
|
+
else:
|
|
736
|
+
|
|
737
|
+
params = init_params()
|
|
738
|
+
params['is_first_image'] = True
|
|
739
|
+
params['blob_nb'] = self.sample_number
|
|
740
|
+
if self.vars["color_number"] > 2:
|
|
741
|
+
params['kmeans_clust_nb'] = self.vars["color_number"]
|
|
742
|
+
params['bio_mask'] = self.all["bio_mask"]
|
|
743
|
+
params['back_mask'] = self.all["back_mask"]
|
|
744
|
+
params['filter_spec'] = self.vars["filter_spec"]
|
|
745
|
+
|
|
746
|
+
if first_param_known:
|
|
747
|
+
if self.all['scale_with_image_or_cells'] == 0:
|
|
748
|
+
self.get_average_pixel_size()
|
|
749
|
+
else:
|
|
750
|
+
self.starting_blob_hsize_in_pixels = None
|
|
751
|
+
params['several_blob_per_arena'] = self.vars['several_blob_per_arena']
|
|
752
|
+
params['blob_shape'] = self.all['starting_blob_shape']
|
|
753
|
+
params['blob_size'] = self.starting_blob_hsize_in_pixels
|
|
754
|
+
|
|
755
|
+
self.first_image.find_color_space_combinations(params)
|
|
756
|
+
|
|
757
|
+
def full_last_image_segmentation(self, bio_mask: NDArray[np.uint8] = None, back_mask: NDArray[np.uint8] = None):
|
|
758
|
+
if bio_mask.any():
|
|
759
|
+
bio_mask = np.nonzero(bio_mask)
|
|
760
|
+
else:
|
|
761
|
+
bio_mask = None
|
|
762
|
+
if back_mask.any():
|
|
763
|
+
back_mask = np.nonzero(back_mask)
|
|
764
|
+
else:
|
|
765
|
+
back_mask = None
|
|
766
|
+
if self.last_im is None:
|
|
767
|
+
self.get_last_image()
|
|
768
|
+
self.cropping(False)
|
|
769
|
+
self.get_background_to_subtract()
|
|
770
|
+
if self.visualize or (len(self.first_im.shape) == 2 and not self.network_shaped):
|
|
771
|
+
self.fast_last_image_segmentation(bio_mask=bio_mask, back_mask=back_mask)
|
|
772
|
+
else:
|
|
773
|
+
arenas_mask = None
|
|
774
|
+
if self.all['are_gravity_centers_moving'] != 1:
|
|
775
|
+
cr = [self.top, self.bot, self.left, self.right]
|
|
776
|
+
arenas_mask = np.zeros_like(self.first_image.validated_shapes)
|
|
777
|
+
for _i in np.arange(len(self.vars['analyzed_individuals'])):
|
|
778
|
+
if self.vars['arena_shape'] == 'circle':
|
|
779
|
+
ellipse = create_ellipse(cr[1][_i] - cr[0][_i], cr[3][_i] - cr[2][_i])
|
|
780
|
+
arenas_mask[cr[0][_i]: cr[1][_i], cr[2][_i]:cr[3][_i]] = ellipse
|
|
781
|
+
else:
|
|
782
|
+
arenas_mask[cr[0][_i]: cr[1][_i], cr[2][_i]:cr[3][_i]] = 1
|
|
783
|
+
if self.network_shaped:
|
|
784
|
+
self.last_image.network_detection(arenas_mask, csc_dict=self.vars["convert_for_motion"], lighter_background=None, bio_mask=bio_mask, back_mask=back_mask)
|
|
785
|
+
else:
|
|
786
|
+
ref_image = self.first_image.validated_shapes
|
|
787
|
+
params = init_params()
|
|
788
|
+
params['is_first_image'] = False
|
|
789
|
+
params['several_blob_per_arena'] = self.vars['several_blob_per_arena']
|
|
790
|
+
params['blob_nb'] = self.sample_number
|
|
791
|
+
params['arenas_mask'] = arenas_mask
|
|
792
|
+
params['ref_image'] = ref_image
|
|
793
|
+
params['subtract_background'] = self.first_image.subtract_background
|
|
794
|
+
params['bio_mask'] = bio_mask
|
|
795
|
+
params['back_mask'] = back_mask
|
|
796
|
+
params['filter_spec'] = self.vars["filter_spec"]
|
|
797
|
+
|
|
798
|
+
self.last_image.find_color_space_combinations(params)
|
|
799
|
+
|
|
716
800
|
def cropping(self, is_first_image: bool):
|
|
717
801
|
"""
|
|
718
802
|
Crops the image based on specified conditions and settings.
|
|
@@ -731,19 +815,14 @@ class ProgramOrganizer:
|
|
|
731
815
|
if is_first_image:
|
|
732
816
|
if not self.first_image.cropped:
|
|
733
817
|
if (not self.all['overwrite_unaltered_videos'] and os.path.isfile('Data to run Cellects quickly.pkl')):
|
|
818
|
+
self.first_image.get_crop_coordinates()
|
|
734
819
|
pickle_rick = PickleRick()
|
|
735
820
|
data_to_run_cellects_quickly = pickle_rick.read_file('Data to run Cellects quickly.pkl')
|
|
736
|
-
if data_to_run_cellects_quickly is not None:
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
self.first_image.crop_coord = [ccy1, ccy2, ccx1, ccx2]
|
|
742
|
-
else:
|
|
743
|
-
self.first_image.get_crop_coordinates()
|
|
744
|
-
else:
|
|
745
|
-
self.first_image.get_crop_coordinates()
|
|
746
|
-
|
|
821
|
+
if data_to_run_cellects_quickly is not None and 'bb_coord' in data_to_run_cellects_quickly['all']['vars']:
|
|
822
|
+
logging.info("Get crop coordinates from Data to run Cellects quickly.pkl")
|
|
823
|
+
(ccy1, ccy2, ccx1, ccx2, self.top, self.bot, self.left, self.right) = \
|
|
824
|
+
data_to_run_cellects_quickly['all']['vars']['bb_coord']
|
|
825
|
+
self.first_image.crop_coord = [ccy1, ccy2, ccx1, ccx2]
|
|
747
826
|
else:
|
|
748
827
|
self.first_image.get_crop_coordinates()
|
|
749
828
|
if self.all['automatically_crop']:
|
|
@@ -777,26 +856,27 @@ class ProgramOrganizer:
|
|
|
777
856
|
connectivity=8)
|
|
778
857
|
self.first_image.shape_number -= 1
|
|
779
858
|
if self.all['scale_with_image_or_cells'] == 0:
|
|
780
|
-
self.vars['average_pixel_size'] = np.square(
|
|
781
|
-
|
|
782
|
-
self.first_im.shape[1])
|
|
859
|
+
self.vars['average_pixel_size'] = np.square(self.all['image_horizontal_size_in_mm'] /
|
|
860
|
+
self.first_im.shape[1])
|
|
783
861
|
else:
|
|
784
|
-
self.
|
|
785
|
-
self.all['starting_blob_hsize_in_mm'] /
|
|
786
|
-
|
|
862
|
+
if len(self.first_image.stats[1:, 2]) > 0:
|
|
863
|
+
self.vars['average_pixel_size'] = np.square(self.all['starting_blob_hsize_in_mm'] /
|
|
864
|
+
np.mean(self.first_image.stats[1:, 2]))
|
|
865
|
+
else:
|
|
866
|
+
self.vars['average_pixel_size'] = 1.
|
|
867
|
+
self.vars['output_in_mm'] = False
|
|
868
|
+
|
|
787
869
|
if self.all['set_spot_size']:
|
|
788
|
-
self.starting_blob_hsize_in_pixels = (
|
|
789
|
-
|
|
790
|
-
np.sqrt(self.vars['average_pixel_size']))
|
|
870
|
+
self.starting_blob_hsize_in_pixels = (self.all['starting_blob_hsize_in_mm'] /
|
|
871
|
+
np.sqrt(self.vars['average_pixel_size']))
|
|
791
872
|
else:
|
|
792
873
|
self.starting_blob_hsize_in_pixels = None
|
|
793
874
|
|
|
794
875
|
if self.all['automatic_size_thresholding']:
|
|
795
876
|
self.vars['first_move_threshold'] = 10
|
|
796
877
|
else:
|
|
797
|
-
self.vars['first_move_threshold'] = np.round(
|
|
798
|
-
|
|
799
|
-
self.vars['average_pixel_size']).astype(np.uint8)
|
|
878
|
+
self.vars['first_move_threshold'] = np.round(self.all['first_move_threshold_in_mm²'] /
|
|
879
|
+
self.vars['average_pixel_size']).astype(np.uint8)
|
|
800
880
|
logging.info(f"The average pixel size is: {self.vars['average_pixel_size']} mm²")
|
|
801
881
|
|
|
802
882
|
def get_background_to_subtract(self):
|
|
@@ -905,19 +985,10 @@ class ProgramOrganizer:
|
|
|
905
985
|
- 'continue' (bool): Whether to continue processing.
|
|
906
986
|
- 'message' (str): Informational or error message.
|
|
907
987
|
|
|
908
|
-
Raises
|
|
909
|
-
------
|
|
910
|
-
None
|
|
911
|
-
|
|
912
988
|
Notes
|
|
913
989
|
-----
|
|
914
990
|
This function relies on the existence of certain attributes and variables
|
|
915
991
|
defined in the class instance.
|
|
916
|
-
|
|
917
|
-
Examples
|
|
918
|
-
--------
|
|
919
|
-
>>> self.delineate_each_arena()
|
|
920
|
-
{'continue': True, 'message': ''}
|
|
921
992
|
"""
|
|
922
993
|
analysis_status = {"continue": True, "message": ""}
|
|
923
994
|
if not self.vars['several_blob_per_arena'] and (self.sample_number > 1):
|
|
@@ -927,9 +998,9 @@ class ProgramOrganizer:
|
|
|
927
998
|
pickle_rick = PickleRick()
|
|
928
999
|
data_to_run_cellects_quickly = pickle_rick.read_file('Data to run Cellects quickly.pkl')
|
|
929
1000
|
if data_to_run_cellects_quickly is not None:
|
|
930
|
-
if '
|
|
931
|
-
(ccy1, ccy2, ccx1, ccx2, self.
|
|
932
|
-
data_to_run_cellects_quickly['
|
|
1001
|
+
if 'bb_coord' in data_to_run_cellects_quickly['all']['vars']:
|
|
1002
|
+
(ccy1, ccy2, ccx1, ccx2, self.top, self.bot, self.left, self.right) = \
|
|
1003
|
+
data_to_run_cellects_quickly['all']['vars']['bb_coord']
|
|
933
1004
|
self.first_image.crop_coord = [ccy1, ccy2, ccx1, ccx2]
|
|
934
1005
|
if (self.first_image.image.shape[0] == (ccy2 - ccy1)) and (
|
|
935
1006
|
self.first_image.image.shape[1] == (ccx2 - ccx1)): # maybe useless now
|
|
@@ -940,7 +1011,6 @@ class ProgramOrganizer:
|
|
|
940
1011
|
motion_list = None
|
|
941
1012
|
if self.all['are_gravity_centers_moving']:
|
|
942
1013
|
motion_list = self._segment_blob_motion(sample_size=5)
|
|
943
|
-
# if self.all['im_or_vid'] == 1:
|
|
944
1014
|
self.get_bounding_boxes(are_gravity_centers_moving=self.all['are_gravity_centers_moving'] == 1,
|
|
945
1015
|
motion_list=motion_list, all_specimens_have_same_direction=self.all['all_specimens_have_same_direction'])
|
|
946
1016
|
|
|
@@ -958,6 +1028,8 @@ class ProgramOrganizer:
|
|
|
958
1028
|
self._whole_image_bounding_boxes()
|
|
959
1029
|
self.sample_number = 1
|
|
960
1030
|
self._set_analyzed_individuals()
|
|
1031
|
+
self.vars['arena_coord'] = []
|
|
1032
|
+
self.list_coordinates()
|
|
961
1033
|
return analysis_status
|
|
962
1034
|
|
|
963
1035
|
def _segment_blob_motion(self, sample_size: int) -> list:
|
|
@@ -1220,12 +1292,18 @@ class ProgramOrganizer:
|
|
|
1220
1292
|
logging.info("Create origins and background lists")
|
|
1221
1293
|
if self.top is None:
|
|
1222
1294
|
self._whole_image_bounding_boxes()
|
|
1295
|
+
|
|
1296
|
+
if not self.first_image.validated_shapes.any():
|
|
1297
|
+
if self.vars['convert_for_motion'] is not None:
|
|
1298
|
+
self.vars['convert_for_origin'] = self.vars['convert_for_motion']
|
|
1299
|
+
self.fast_first_image_segmentation()
|
|
1223
1300
|
first_im = self.first_image.validated_shapes
|
|
1224
1301
|
self.vars['origin_list'] = []
|
|
1225
1302
|
self.vars['background_list'] = []
|
|
1226
1303
|
self.vars['background_list2'] = []
|
|
1227
1304
|
for rep in np.arange(len(self.vars['analyzed_individuals'])):
|
|
1228
|
-
|
|
1305
|
+
origin_coord = np.nonzero(first_im[self.top[rep]:self.bot[rep], self.left[rep]:self.right[rep]])
|
|
1306
|
+
self.vars['origin_list'].append(origin_coord)
|
|
1229
1307
|
if self.vars['subtract_background']:
|
|
1230
1308
|
for rep in np.arange(len(self.vars['analyzed_individuals'])):
|
|
1231
1309
|
self.vars['background_list'].append(
|
|
@@ -1610,23 +1688,6 @@ class ProgramOrganizer:
|
|
|
1610
1688
|
self.last_image.bgr = draw_img_with_mask(self.last_image.bgr, self.last_image.bgr.shape[:2], minmax,
|
|
1611
1689
|
self.vars['arena_shape'], last_visualization)
|
|
1612
1690
|
|
|
1613
|
-
# cr = ((self.top[i], self.bot[i]),
|
|
1614
|
-
# (self.left[i], self.right[i]))
|
|
1615
|
-
# if self.vars['arena_shape'] == 'circle':
|
|
1616
|
-
# ellipse = create_ellipse(cr[0][1] - cr[0][0], cr[1][1] - cr[1][0])
|
|
1617
|
-
# ellipse = np.stack((ellipse, ellipse, ellipse), axis=2).astype(np.uint8)
|
|
1618
|
-
# first_visualization *= ellipse
|
|
1619
|
-
# self.first_image.bgr[cr[0][0]:cr[0][1], cr[1][0]:cr[1][1], ...] *= (1 - ellipse)
|
|
1620
|
-
# self.first_image.bgr[cr[0][0]:cr[0][1], cr[1][0]:cr[1][1], ...] += first_visualization
|
|
1621
|
-
# if last_visualization is not None:
|
|
1622
|
-
# last_visualization *= ellipse
|
|
1623
|
-
# self.last_image.bgr[cr[0][0]:cr[0][1], cr[1][0]:cr[1][1], ...] *= (1 - ellipse)
|
|
1624
|
-
# self.last_image.bgr[cr[0][0]:cr[0][1], cr[1][0]:cr[1][1], ...] += last_visualization
|
|
1625
|
-
# else:
|
|
1626
|
-
# self.first_image.bgr[cr[0][0]:cr[0][1], cr[1][0]:cr[1][1], ...] = first_visualization
|
|
1627
|
-
# if last_visualization is not None:
|
|
1628
|
-
# self.last_image.bgr[cr[0][0]:cr[0][1], cr[1][0]:cr[1][1], ...] = last_visualization
|
|
1629
|
-
|
|
1630
1691
|
|
|
1631
1692
|
def save_tables(self, with_last_image: bool=True):
|
|
1632
1693
|
"""
|
|
@@ -1654,13 +1715,11 @@ class ProgramOrganizer:
|
|
|
1654
1715
|
del self.one_row_per_arena
|
|
1655
1716
|
except PermissionError:
|
|
1656
1717
|
logging.error("Never let one_row_per_arena.csv open when Cellects runs")
|
|
1657
|
-
self.message_from_thread.emit(f"Never let one_row_per_arena.csv open when Cellects runs")
|
|
1658
1718
|
try:
|
|
1659
1719
|
self.one_row_per_frame.to_csv("one_row_per_frame.csv", sep=";", index=False, lineterminator='\n')
|
|
1660
1720
|
del self.one_row_per_frame
|
|
1661
1721
|
except PermissionError:
|
|
1662
1722
|
logging.error("Never let one_row_per_frame.csv open when Cellects runs")
|
|
1663
|
-
self.message_from_thread.emit(f"Never let one_row_per_frame.csv open when Cellects runs")
|
|
1664
1723
|
if self.all['extension'] == '.JPG':
|
|
1665
1724
|
extension = '.PNG'
|
|
1666
1725
|
else:
|
|
@@ -1677,11 +1736,11 @@ class ProgramOrganizer:
|
|
|
1677
1736
|
for key in ['analyzed_individuals', 'night_mode', 'expert_mode', 'is_auto', 'arena', 'video_option', 'compute_all_options', 'vars', 'dims', 'origin_list', 'background_list', 'background_list2', 'descriptors', 'folder_list', 'sample_number_per_folder']:
|
|
1678
1737
|
global_settings.pop(key, None)
|
|
1679
1738
|
software_settings.update(global_settings)
|
|
1739
|
+
software_settings.pop('video_list', None)
|
|
1680
1740
|
software_settings = pd.DataFrame.from_dict(software_settings, columns=["Setting"], orient='index')
|
|
1681
1741
|
try:
|
|
1682
1742
|
software_settings.to_csv("software_settings.csv", sep=";")
|
|
1683
1743
|
except PermissionError:
|
|
1684
1744
|
logging.error("Never let software_settings.csv open when Cellects runs")
|
|
1685
|
-
self.message_from_thread.emit(f"Never let software_settings.csv open when Cellects runs")
|
|
1686
1745
|
|
|
1687
1746
|
|
|
@@ -13,6 +13,7 @@ from cellects.utils.utilitarian import insensitive_glob
|
|
|
13
13
|
from cellects.core.motion_analysis import MotionAnalysis
|
|
14
14
|
from cellects.image_analysis.morphological_operations import create_ellipse
|
|
15
15
|
from cellects.image_analysis.image_segmentation import convert_subtract_and_filter_video
|
|
16
|
+
from cellects.core.one_image_analysis import init_params
|
|
16
17
|
from cellects.utils.load_display_save import write_video_sets, readim, display_network_methods
|
|
17
18
|
from cellects.image_analysis.network_functions import NetworkDetection
|
|
18
19
|
|
|
@@ -54,7 +55,9 @@ def run_image_analysis(po, PCA: bool=True, last_im:NDArray=None):
|
|
|
54
55
|
if PCA:
|
|
55
56
|
po.fast_first_image_segmentation()
|
|
56
57
|
else:
|
|
57
|
-
|
|
58
|
+
params = init_params()
|
|
59
|
+
params['is_first_image'] = True
|
|
60
|
+
po.first_image.find_color_space_combinations(params)
|
|
58
61
|
po.cropping(is_first_image=True)
|
|
59
62
|
po.get_average_pixel_size()
|
|
60
63
|
po.delineate_each_arena()
|
|
@@ -113,6 +116,7 @@ def run_all_arenas(po):
|
|
|
113
116
|
po.instantiate_tables()
|
|
114
117
|
for i, arena in enumerate(po.vars['analyzed_individuals']):
|
|
115
118
|
l = [i, arena, po.vars, True, True, False, None]
|
|
119
|
+
# l = [i, arena, po.vars, False, True, False, None]
|
|
116
120
|
analysis_i = MotionAnalysis(l)
|
|
117
121
|
po.add_analysis_visualization_to_first_and_last_images(i, analysis_i.efficiency_test_1,
|
|
118
122
|
analysis_i.efficiency_test_2)
|
|
@@ -124,35 +128,21 @@ def run_all_arenas(po):
|
|
|
124
128
|
po.update_one_row_per_frame(i * po.vars['img_number'],
|
|
125
129
|
arena * po.vars['img_number'],
|
|
126
130
|
analysis_i.one_row_per_frame)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
oscil_i = pd.DataFrame(
|
|
131
|
-
np.c_[np.repeat(arena,
|
|
132
|
-
analysis_i.clusters_final_data.shape[0]), analysis_i.clusters_final_data],
|
|
133
|
-
columns=['arena', 'mean_pixel_period', 'phase', 'cluster_size', 'edge_distance', 'coord_y',
|
|
134
|
-
'coord_x'])
|
|
135
|
-
if po.one_row_per_oscillating_cluster is None:
|
|
136
|
-
po.one_row_per_oscillating_cluster = oscil_i
|
|
137
|
-
else:
|
|
138
|
-
po.one_row_per_oscillating_cluster = pd.concat((po.one_row_per_oscillating_cluster, oscil_i))
|
|
131
|
+
# Keep the tables
|
|
132
|
+
one_row_per_arena = po.one_row_per_arena
|
|
133
|
+
one_row_per_frame = po.one_row_per_frame
|
|
139
134
|
po.save_tables()
|
|
135
|
+
po.one_row_per_arena = one_row_per_arena
|
|
136
|
+
po.one_row_per_frame = one_row_per_frame
|
|
140
137
|
cv2.imwrite(f"Analysis efficiency, last image.jpg", po.last_image.bgr)
|
|
141
138
|
cv2.imwrite(f"Analysis efficiency, {np.ceil(po.vars['img_number'] / 10).astype(np.uint64)}th image.jpg",
|
|
142
139
|
po.first_image.bgr)
|
|
140
|
+
return po
|
|
143
141
|
|
|
144
|
-
def detect_network_in_one_image(im_path, save_path):
|
|
142
|
+
def detect_network_in_one_image(im_path, save_path=None):
|
|
145
143
|
im = readim(im_path)
|
|
146
|
-
im = im[100:870, 200:1000]
|
|
144
|
+
# im = im[100:870, 200:1000]
|
|
147
145
|
greyscale_image = im.mean(axis=2)
|
|
148
146
|
net = NetworkDetection(greyscale_image, add_rolling_window=True)
|
|
149
147
|
net.get_best_network_detection_method()
|
|
150
148
|
display_network_methods(net, save_path)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if __name__ == "__main__":
|
|
155
|
-
po = load_data(pathway=os.getcwd() + "/data/experiment", sample_number=1, extension='tif')
|
|
156
|
-
po = run_image_analysis(po)
|
|
157
|
-
po = write_videos(po)
|
|
158
|
-
run_all_arenas(po)
|
cellects/gui/first_window.py
CHANGED
|
@@ -97,6 +97,7 @@ class FirstWindow(MainTabsType):
|
|
|
97
97
|
# self.im_or_vid_label = FixedText('Image list or Videos:', tip="What type of data do(es) contain(s) folder(s)?", night_mode=self.parent().po.all['night_mode'])
|
|
98
98
|
self.im_or_vid = Combobox(["Image list", "Videos"], self.parent().po.all['im_or_vid'], night_mode=self.parent().po.all['night_mode'])
|
|
99
99
|
self.im_or_vid.setFixedWidth(150)
|
|
100
|
+
self.im_or_vid.currentTextChanged.connect(self.im2vid)
|
|
100
101
|
# Set their positions on layout
|
|
101
102
|
self.second_row_layout.addItem(self.horizontal_space)
|
|
102
103
|
self.second_row_layout.addWidget(self.im_or_vid_label)
|
|
@@ -121,7 +122,7 @@ class FirstWindow(MainTabsType):
|
|
|
121
122
|
if self.parent().po.all['extension'] == '.JPG':
|
|
122
123
|
self.parent().po.all['radical'] = ''
|
|
123
124
|
self.parent().po.all['extension'] = '.mp4'
|
|
124
|
-
self.arena_number_label = FixedText('Arena number per
|
|
125
|
+
self.arena_number_label = FixedText('Arena number per folder:',
|
|
125
126
|
tip=FW["Arena_number_per_folder"]["tips"], #"If this number is not always the same (depending on the video), it can be changed later",
|
|
126
127
|
night_mode=self.parent().po.all['night_mode'])
|
|
127
128
|
what = 'Videos'
|
|
@@ -152,7 +153,6 @@ class FirstWindow(MainTabsType):
|
|
|
152
153
|
self.third_row_widget.setLayout(self.third_row_layout)
|
|
153
154
|
self.Vlayout.addWidget(self.third_row_widget)
|
|
154
155
|
# If im_or_vid changes, adjust these 2 widgets
|
|
155
|
-
self.im_or_vid.currentTextChanged.connect(self.im2vid)
|
|
156
156
|
|
|
157
157
|
# 3) Get the path to the right folder:
|
|
158
158
|
# Open the layout:
|
|
@@ -293,7 +293,8 @@ class FirstWindow(MainTabsType):
|
|
|
293
293
|
"""
|
|
294
294
|
Toggle between processing images or videos based on UI selection.
|
|
295
295
|
"""
|
|
296
|
-
|
|
296
|
+
self.parent().po.all['im_or_vid'] = self.im_or_vid.currentIndex()
|
|
297
|
+
if self.im_or_vid.currentIndex() == 0:
|
|
297
298
|
what = 'Images'
|
|
298
299
|
if self.parent().po.all['extension'] == '.mp4':
|
|
299
300
|
self.parent().po.all['radical'] = 'IMG_'
|
|
@@ -376,7 +377,7 @@ class FirstWindow(MainTabsType):
|
|
|
376
377
|
"""
|
|
377
378
|
if len(self.parent().po.all['folder_list']) == 0 and len(self.parent().po.data_list) == 0:
|
|
378
379
|
if self.parent().po.all['im_or_vid'] == 1:
|
|
379
|
-
error_message = f"There is no videos ({self.parent().po.all['extension']})in the selected folder and its sub-folders"
|
|
380
|
+
error_message = f"There is no videos ({self.parent().po.all['extension']}) in the selected folder and its sub-folders"
|
|
380
381
|
else:
|
|
381
382
|
error_message = f"There is no images ({self.parent().po.all['extension']}) in the selected folder and its sub-folders"
|
|
382
383
|
self.message.setText(error_message)
|
|
@@ -510,6 +511,8 @@ class FirstWindow(MainTabsType):
|
|
|
510
511
|
self.required_outputs.setVisible(False)
|
|
511
512
|
self.Run_all_directly.setVisible(False)
|
|
512
513
|
self.next.setVisible(False)
|
|
514
|
+
self.instantiate = True
|
|
515
|
+
self.video_tab.set_not_usable()
|
|
513
516
|
# 2) Load the dict
|
|
514
517
|
self.thread["LoadDataToRunCellectsQuickly"].start()
|
|
515
518
|
self.thread["LoadDataToRunCellectsQuickly"].message_from_thread.connect(self.load_data_quickly_finished)
|