def test_imread_use_dask_false(resources_dir): # Load image as delayed dask array then as numpy array # Check computed task count with dask_utils.cluster_and_client(processes=False) as (cluster, client): # Get filepath f = resources_dir / BIG_OME_FILE # Check that there are no open file pointers after init proc = Process() assert str(f) not in [f.path for f in proc.open_files()] # Check that a client does exist get_client() # Don't use dask for reads use_dask(False) # Read image without dask img = AICSImage(f) assert img.data.shape == (3, 1, 3, 5, 325, 475) # Check that the file was read with base reader then rechunked with dask # Normally the task count for this file is 90 assert len(optimize(img.dask_data)[0].__dask_graph__()) == 3 # Check that there are no open file pointers after basics assert str(f) not in [f.path for f in proc.open_files()]
def _generate_single_cell_features( row_index: int, row: pd.Series, cell_ceiling_adjustment: int, save_dir: Path, overwrite: bool, ) -> Union[SingleCellFeaturesResult, SingleCellFeaturesError]: # Don't use dask for image reading aicsimageio.use_dask(False) # Get the ultimate end save path for this cell save_path = save_dir / f"{row.CellId}.json" # Check skip if not overwrite and save_path.is_file(): log.info(f"Skipping cell feature generation for Cell Id: {row.CellId}") return SingleCellFeaturesResult(row.CellId, save_path) # Overwrite or didn't exist log.info(f"Beginning cell feature generation for CellId: {row.CellId}") # Wrap errors for debugging later try: # Read the standardized FOV image = AICSImage(row.StandardizedFOVPath) # Preload image data image.data # Select and adjust cell shape ceiling for this cell adjusted = image_utils.select_and_adjust_segmentation_ceiling( image=image.get_image_data("CYXZ", S=0, T=0), cell_index=row.CellIndex, cell_ceiling_adjustment=cell_ceiling_adjustment, ) # Crop the FOV to the segmentation portions cropped = image_utils.crop_raw_channels_with_segmentation( image=adjusted, channels=image.get_channel_names(), ) # Generate features features = image_utils.get_features_from_image(cropped) # Save to JSON with open(save_path, "w") as write_out: json.dump(features, write_out) log.info(f"Completed cell feature generation for CellId: {row.CellId}") return SingleCellFeaturesResult(row.CellId, save_path) # Catch and return error except Exception as e: log.info( f"Failed cell feature generation for CellId: {row.CellId}. Error: {e}" ) return SingleCellFeaturesError(row.CellId, str(e))
def _collect_group( row_index: int, row: pd.Series, diagnostic_sheet_dir: Path, overwrite: bool, metadata: str, max_cells: int, ) -> Union[DiagnosticSheetResult, DiagnosticSheetError]: # Don't use dask for image reading aicsimageio.use_dask(False) try: # Get the ultimate end save paths for grouped plot if row[str(metadata)] or row[str(metadata)] == 0: assert DatasetFields.CellImage2DAllProjectionsPath in row.index save_path_index = int( np.ceil((row["SubplotNumber" + str(metadata)] + 1) / max_cells) ) # np ceil for 0 = 0 if save_path_index == 0: save_path_index = 1 # Clean metadata name of spaces cleaned_metadata_name = str(row[str(metadata)]).replace(" ", "-") save_path = ( diagnostic_sheet_dir / f"{metadata}" f"_{cleaned_metadata_name}" f"_{save_path_index}.png" ) log.info( f"Collecting diagnostic sheet path for cell ID: {row.CellId}, " f"{metadata}: {row[str(metadata)]}" ) else: # else no path to save save_path = None # Check skip if not overwrite and save_path.is_file(): log.info( f"Skipping diagnostic sheet path for cell ID: {row.CellId}, " f"{metadata}: {row[str(metadata)]}" ) return DiagnosticSheetResult(row.CellId, None) # Return ready to save image return DiagnosticSheetResult(row.CellId, str(save_path)) # Catch and return error except Exception as e: log.info( f"Failed to retrieve the CellImage2DAllProjectionsPath" f"for cell ID: {row.CellId}," f"{metadata} {row[str(metadata)]}" f"Error: {e}" ) return DiagnosticSheetError(row.CellId, str(e))
def _generate_single_cell_images( row_index: int, row: pd.Series, cell_ceiling_adjustment: int, bounding_box: np.ndarray, projection_method: str, cell_images_3d_dir: Path, cell_images_2d_all_proj_dir: Path, cell_images_2d_yx_proj_dir: Path, overwrite: bool, ) -> Union[CellImagesResult, CellImagesError]: # Don't use dask for image reading aicsimageio.use_dask(False) # Get the ultimate end save paths for this cell cell_image_3d_save_path = cell_images_3d_dir / f"{row.CellId}.ome.tiff" cell_image_2d_all_proj_save_path = (cell_images_2d_all_proj_dir / f"{row.CellId}.png") cell_image_2d_yx_proj_save_path = (cell_images_2d_yx_proj_dir / f"{row.CellId}.png") # Check skip if (not overwrite # Only skip if all images exist for this cell and all(p.is_file() for p in [ cell_image_3d_save_path, cell_image_2d_all_proj_save_path, cell_image_2d_yx_proj_save_path, ])): log.info( f"Skipping single cell image generation for CellId: {row.CellId}" ) return CellImagesResult( row.CellId, cell_image_3d_save_path, cell_image_2d_all_proj_save_path, cell_image_2d_yx_proj_save_path, ) # Overwrite or didn't exist log.info( f"Beginning single cell image generation for CellId: {row.CellId}") # Wrap errors for debugging later try: # Initialize image object with standardized FOV standardized_image = AICSImage(row.StandardizedFOVPath) channels = standardized_image.get_channel_names() # Preload image data standardized_image.data # Select and adjust cell shape ceiling for this cell image = image_utils.select_and_adjust_segmentation_ceiling( # Unlike most other operations, we can read in normal "CZYX" dimension # order here as all future operations are expecting it image=standardized_image.get_image_data("CYXZ", S=0, T=0), cell_index=row.CellIndex, cell_ceiling_adjustment=cell_ceiling_adjustment, ) # Perform a rigid registration on the image image, _, _ = proc.cell_rigid_registration( image, # Reorder bounding box as image is currently CYXZ bbox_size=bounding_box[[0, 2, 3, 1]], ) # Reduce size crop_3d = image * 255 crop_3d = crop_3d.astype(np.uint8) # Transpose to CZYX for saving crop_3d = transforms.transpose_to_dims(crop_3d, "CYXZ", "CZYX") # Save to OME-TIFF with OmeTiffWriter(cell_image_3d_save_path, overwrite_file=True) as writer: writer.save( crop_3d, dimension_order="CZYX", channel_names=standardized_image.get_channel_names(), pixels_physical_size=standardized_image. get_physical_pixel_size(), ) # Generate 2d image projections # Crop raw channels using segmentations image = image_utils.crop_raw_channels_with_segmentation( image, channels) # Transpose to CZYX for projections image = transforms.transpose_to_dims(image, "CYXZ", "CZYX") # Select the DNA, Membrane, and Structure channels image = image[[ channels.index(target) for target in [Channels.DNA, Channels.Membrane, Channels.Structure] ]] # Set RGB colors # This will set: # DNA to Blue # Membrane to Red # Structure to Green colors = [[0, 0, 1], [1, 0, 0], [0, 1, 0]] # Get all axes projection image all_proj = proc.imgtoprojection( image, proj_all=True, proj_method=projection_method, local_adjust=False, global_adjust=True, colors=colors, ) # Convert to YXC for PNG writing all_proj = transforms.transpose_to_dims(all_proj, "CYX", "YXC") # Drop size to uint8 all_proj = all_proj.astype(np.uint8) # Save to PNG imwrite(cell_image_2d_all_proj_save_path, all_proj) # Get YX axes projection image yx_proj = proc.imgtoprojection( image, proj_all=False, proj_method=projection_method, local_adjust=False, global_adjust=True, colors=colors, ) # Convert to YXC for PNG writing yx_proj = transforms.transpose_to_dims(yx_proj, "CYX", "YXC") # Drop size to uint8 yx_proj = yx_proj.astype(np.uint8) # Save to PNG imwrite(cell_image_2d_yx_proj_save_path, yx_proj) log.info( f"Completed single cell image generation for CellId: {row.CellId}" ) # Return ready to save image return CellImagesResult( row.CellId, cell_image_3d_save_path, cell_image_2d_all_proj_save_path, cell_image_2d_yx_proj_save_path, ) # Catch and return error except Exception as e: log.info( f"Failed single cell image generation for CellId: {row.CellId}. " "Error: {e}") return CellImagesError(row.CellId, str(e))
def single_cell_gen_one_fov( row_index: int, row: pd.Series, single_cell_dir: Path, per_fov_dir: Path, overwrite: bool = False, ) -> List: ######################################## # parameters ######################################## # Don't use dask for image reading aicsimageio.use_dask(False) standard_res_qcb = 0.108 print(f"ready to process FOV: {row.FOVId}") ######################################## # check if results already exist ######################################## this_fov_path = per_fov_dir / Path(str(row.FOVId)) tag_file = this_fov_path / "done.txt" bad_tag_file = this_fov_path / "bad.txt" single_fov_csv = this_fov_path / "fov_meta.csv" cells_in_fov_csv = this_fov_path / "cell_meta.csv" if this_fov_path.exists(): if overwrite: rmtree(this_fov_path) os.mkdir(this_fov_path) else: if tag_file.exists(): # this fov has been fully processed, simply return # the path to fov csv and cells csv return [single_fov_csv, cells_in_fov_csv] else: if bad_tag_file.exists(): # this fov is known to be a bad one, no need to re-run return [ row.FOVId, False, "bad FOV, check text file for detail" ] else: # this fov has only been partially processed, wipe out rmtree(this_fov_path) os.mkdir(this_fov_path) else: os.mkdir(this_fov_path) ######################################## # load image and segmentation ######################################## """ if row.AlignedImageReadPath is None: raw_fn = row.SourceReadPath else: raw_fn = row.AlignedImageReadPath """ # SourceReadPath should be always available raw_fn = row.SourceReadPath # verify filepaths if not (os.path.exists(raw_fn) and os.path.exists(row.MembraneSegmentationReadPath) and os.path.exists(row.StructureSegmentationReadPath)): # fail return [row.FOVId, True, "missing segmentation or raw files"] raw_reader = AICSImage(raw_fn) if raw_reader.shape[0] > 1: # multi-scene return [row.FOVId, False, "multi scene image"] try: # get the raw image and split into different channels raw_data = np.squeeze(raw_reader.data) raw_mem0 = raw_data[int(row.ChannelNumber638), :, :, :] raw_nuc0 = raw_data[int(row.ChannelNumber405), :, :, :] raw_struct0 = raw_data[int(row.ChannelNumberStruct), :, :, :] assert row.MembraneSegmentationReadPath == row.NucleusSegmentationReadPath seg_reader = AICSImage(row.MembraneSegmentationReadPath) nuc_seg_whole = seg_reader.get_image_data("ZYX", S=0, T=0, C=0) mem_seg_whole = seg_reader.get_image_data("ZYX", S=0, T=0, C=1) assert (mem_seg_whole.shape[0] == raw_mem0.shape[0] and mem_seg_whole.shape[1] == raw_mem0.shape[1] and mem_seg_whole.shape[2] == raw_mem0.shape[2]), "raw and seg dim mismatch" assert ((not np.any(raw_mem0 < 1)) and (not np.any(raw_nuc0 < 1)) and (not np.any(raw_struct0 < 1)) ), "one z frame is blank, ignore this FOV" # get structure segmentation struct_seg_whole = np.squeeze(imread( row.StructureSegmentationReadPath)) print(f"Segmentation load successfully: {row.FOVId}") except (Exception, AssertionError) as e: return [row.FOVId, True, e] # make a copy to be used for calculating true edge cell labels mem_seg_whole_copy = mem_seg_whole.copy() ######################################### # run single cell qc in this fov ######################################### ###################### min_mem_size = 70000 min_nuc_size = 10000 ###################### # flag for any segmented object in this FOV removed as bad cells full_fov_pass = 1 # double check big failure, quick reject if mem_seg_whole.max() <= 3 or nuc_seg_whole.max() <= 3: # bad images, but not bug, use "False" with open(bad_tag_file, "w") as f: f.write("very few cells segmented") return [row.FOVId, False, "very few cells segmented"] # prune the results (remove cells touching image boundary) boundary_mask = np.zeros_like(mem_seg_whole) boundary_mask[:, :3, :] = 1 boundary_mask[:, -3:, :] = 1 boundary_mask[:, :, :3] = 1 boundary_mask[:, :, -3:] = 1 bd_idx = list(np.unique(mem_seg_whole[boundary_mask > 0])) # maintain a valid cell list, initialize with all cells minus # cells touching the image boundary, and minus cells with # no record in labkey (e.g., manually removed based on user's feedback) all_cell_index_list = list(np.unique(mem_seg_whole[mem_seg_whole > 0])) full_set_with_no_boundary = set(all_cell_index_list) - set(bd_idx) set_not_in_labkey = full_set_with_no_boundary - set( row.index_to_id_dict[0].keys()) valid_cell = list(full_set_with_no_boundary - set_not_in_labkey) # single cell QC valid_cell_0 = valid_cell.copy() for list_idx, this_cell_index in enumerate(valid_cell): single_mem = mem_seg_whole == this_cell_index single_nuc = nuc_seg_whole == this_cell_index # remove too small cells from valid cell list if (np.count_nonzero(single_mem) < min_mem_size or np.count_nonzero(single_nuc) < min_nuc_size): valid_cell_0.remove(this_cell_index) full_fov_pass = 0 # no need to go to next QC criteria continue # make sure the cell is not leaking to the bottom or top z_range_single = np.where(np.any(single_mem, axis=(1, 2))) single_min_z = z_range_single[0][0] single_max_z = z_range_single[0][-1] if single_min_z == 0 or single_max_z >= single_mem.shape[0] - 1: valid_cell_0.remove(this_cell_index) full_fov_pass = 0 valid_cell = valid_cell_0.copy() # if only one cell left or no cell left, just throw it away if len(valid_cell_0) < 2: with open(bad_tag_file, "w") as f: f.write("very few cells left after single cell QC") return [row.FOVId, False, "very few cells left after single cell QC"] print(f"single cell QC done in FOV: {row.FOVId}") ################################################################# # resize the image into isotropic dimension ################################################################# raw_nuc = resize( raw_nuc0, ( row.PixelScaleZ / standard_res_qcb, row.PixelScaleY / standard_res_qcb, row.PixelScaleX / standard_res_qcb, ), method="bilinear", ).astype(np.uint16) raw_mem = resize( raw_mem0, ( row.PixelScaleZ / standard_res_qcb, row.PixelScaleY / standard_res_qcb, row.PixelScaleX / standard_res_qcb, ), method="bilinear", ).astype(np.uint16) raw_str = resize( raw_struct0, ( row.PixelScaleZ / standard_res_qcb, row.PixelScaleY / standard_res_qcb, row.PixelScaleX / standard_res_qcb, ), method="bilinear", ).astype(np.uint16) mem_seg_whole = resize_to(mem_seg_whole, raw_mem.shape, method="nearest") nuc_seg_whole = resize_to(nuc_seg_whole, raw_nuc.shape, method="nearest") struct_seg_whole = resize_to(struct_seg_whole, raw_str.shape, method="nearest") ################################################################# # calculate fov related info ################################################################# index_to_cellid_map = dict() cellid_to_index_map = dict() for list_idx, this_cell_index in enumerate(valid_cell): # this is always valid since indices not in index_to_id_dict.keys() # have been removed index_to_cellid_map[this_cell_index] = row.index_to_id_dict[0][ this_cell_index] for index_dict, cellid_dict in index_to_cellid_map.items(): cellid_to_index_map[cellid_dict] = index_dict # compute center of mass index_to_centroid_map = dict() center_list = center_of_mass(nuc_seg_whole > 0, nuc_seg_whole, valid_cell) for list_idx, this_cell_index in enumerate(valid_cell): index_to_centroid_map[this_cell_index] = center_list[list_idx] # compute whole stack min/max z mem_seg_whole_valid = np.zeros_like(mem_seg_whole) for list_idx, this_cell_index in enumerate(valid_cell): mem_seg_whole_valid[mem_seg_whole == this_cell_index] = this_cell_index z_range_whole = np.where(np.any(mem_seg_whole_valid, axis=(1, 2))) stack_min_z = z_range_whole[0][0] stack_max_z = z_range_whole[0][-1] # find true edge cells, the cells in the outer layer of a colony true_edge_cells = [] edge_fov_flag = False if row.ColonyPosition is None: # parse colony position from file name reg = re.compile("(-|_)((\d)?)(e)((\d)?)(-|_)") # noqa: W605 if reg.search(os.path.basename(raw_fn)): edge_fov_flag = True else: if row.ColonyPosition.lower() == "edge": edge_fov_flag = True if edge_fov_flag: true_edge_cells = find_true_edge_cells(mem_seg_whole_copy) ################################################################# # calculate a dictionary to store FOV info ################################################################# fov_meta = { "FOVId": row.FOVId, "structure_name": row.Gene, "position": row.ColonyPosition, "raw_fn": raw_fn, "str_filename": row.StructureSegmentationReadPath, "mem_seg_fn": row.MembraneSegmentationReadPath, "nuc_seg_fn": row.NucleusSegmentationReadPath, "index_to_id_dict": index_to_cellid_map, "id_to_index_dict": cellid_to_index_map, "xy_res": row.PixelScaleX, "z_res": row.PixelScaleZ, "stack_min_z": stack_min_z, "stack_max_z": stack_max_z, "scope_id": row.InstrumentId, "well_id": row.WellId, "well_name": row.WellName, "plateId": row.PlateId, "passage": row.Passage, "image_size": list(raw_mem.shape), "fov_seg_pass": full_fov_pass, "imaging_mode": row.ImagingMode, } df_fov_meta = pd.DataFrame([fov_meta]) df_fov_meta.to_csv(single_fov_csv, header=True, index=False) print(f"FOV info is done: {row.FOVId}, ready to loop through cells") # loop through all valid cells in this fov cell_meta = [] for list_idx, this_cell_index in enumerate(valid_cell): nuc_seg = nuc_seg_whole == this_cell_index mem_seg = mem_seg_whole == this_cell_index ########################### # implement nbr info ########################### single_mem_dilate = dilation(mem_seg, selem=ball(3)) whole_template = mem_seg_whole.copy() whole_template[mem_seg] = 0 this_cell_nbr_candiate_list = list( np.unique(whole_template[single_mem_dilate > 0])) this_cell_nbr_dist_3d = [] this_cell_nbr_dist_2d = [] this_cell_nbr_overlap_area = [] this_cell_nbr_complete = 1 for nbr_index, nbr_id in enumerate(this_cell_nbr_candiate_list): if nbr_id == 0 or nbr_id == this_cell_index: continue elif not (nbr_id in valid_cell): this_cell_nbr_complete = 0 continue # only do calculation for valid neighbors nuc_dist_3d = euc_dist_3d(index_to_centroid_map[nbr_id], index_to_centroid_map[this_cell_index]) nuc_dist_2d = euc_dist_2d(index_to_centroid_map[nbr_id], index_to_centroid_map[this_cell_index]) overlap = overlap_area(mem_seg, mem_seg_whole == nbr_id) this_cell_nbr_dist_3d.append( (index_to_cellid_map[nbr_id], nuc_dist_3d)) this_cell_nbr_dist_2d.append( (index_to_cellid_map[nbr_id], nuc_dist_2d)) this_cell_nbr_overlap_area.append( (index_to_cellid_map[nbr_id], overlap)) if len(this_cell_nbr_dist_3d) == 0: this_cell_nbr_complete = 0 # get cell id cell_id = index_to_cellid_map[this_cell_index] # make the path for saving single cell crop result thiscell_path = single_cell_dir / Path(str(cell_id)) if os.path.isdir(thiscell_path): rmtree(thiscell_path) os.mkdir(thiscell_path) ############################### # compute and generate crop ############################### # determine crop roi z_range = np.where(np.any(mem_seg, axis=(1, 2))) y_range = np.where(np.any(mem_seg, axis=(0, 2))) x_range = np.where(np.any(mem_seg, axis=(0, 1))) z_range = z_range[0] y_range = y_range[0] x_range = x_range[0] # define a large ROI based on bounding box roi = [ max(z_range[0] - 10, 0), min(z_range[-1] + 12, mem_seg.shape[0]), max(y_range[0] - 40, 0), min(y_range[-1] + 40, mem_seg.shape[1]), max(x_range[0] - 40, 0), min(x_range[-1] + 40, mem_seg.shape[2]), ] # roof augmentation mem_nearly_top_z = int(z_range[0] + round(0.75 * (z_range[-1] - z_range[0] + 1))) mem_top_mask = np.zeros(mem_seg.shape, dtype=np.byte) mem_top_mask[ mem_nearly_top_z:, :, :] = mem_seg[mem_nearly_top_z:, :, :] > 0 mem_top_mask_dilate = dilation(mem_top_mask > 0, selem=np.ones((21, 1, 1), dtype=np.byte)) mem_top_mask_dilate[:mem_nearly_top_z, :, :] = ( mem_seg[:mem_nearly_top_z, :, :] > 0) # crop mem/nuc seg mem_seg = mem_seg.astype(np.uint8) mem_seg = mem_seg[roi[0]:roi[1], roi[2]:roi[3], roi[4]:roi[5]] mem_seg[mem_seg > 0] = 255 nuc_seg = nuc_seg.astype(np.uint8) nuc_seg = nuc_seg[roi[0]:roi[1], roi[2]:roi[3], roi[4]:roi[5]] nuc_seg[nuc_seg > 0] = 255 mem_top_mask_dilate = mem_top_mask_dilate.astype(np.uint8) mem_top_mask_dilate = mem_top_mask_dilate[roi[0]:roi[1], roi[2]:roi[3], roi[4]:roi[5]] mem_top_mask_dilate[mem_top_mask_dilate > 0] = 255 # crop str seg (without roof augmentation) str_seg_crop = struct_seg_whole[roi[0]:roi[1], roi[2]:roi[3], roi[4]:roi[5]].astype(np.uint8) str_seg_crop[mem_seg < 1] = 0 str_seg_crop[str_seg_crop > 0] = 255 # crop str seg (with roof augmentation) str_seg_crop_roof = struct_seg_whole[roi[0]:roi[1], roi[2]:roi[3], roi[4]:roi[5]].astype(np.uint8) str_seg_crop_roof[mem_top_mask_dilate < 1] = 0 str_seg_crop_roof[str_seg_crop_roof > 0] = 255 # merge and save the cropped segmentation all_seg = np.stack( [ nuc_seg, mem_seg, mem_top_mask_dilate, str_seg_crop, str_seg_crop_roof ], axis=0, ) all_seg = np.expand_dims(np.transpose(all_seg, (1, 0, 2, 3)), axis=0) crop_seg_path = thiscell_path / "segmentation.ome.tif" writer = save_tif.OmeTiffWriter(crop_seg_path, overwrite_file=True) writer.save(all_seg) # crop raw image raw_nuc_thiscell = raw_nuc[roi[0]:roi[1], roi[2]:roi[3], roi[4]:roi[5]] raw_mem_thiscell = raw_mem[roi[0]:roi[1], roi[2]:roi[3], roi[4]:roi[5]] raw_str_thiscell = raw_str[roi[0]:roi[1], roi[2]:roi[3], roi[4]:roi[5]] crop_raw_merged = np.expand_dims( np.stack((raw_nuc_thiscell, raw_mem_thiscell, raw_str_thiscell), axis=1), axis=0, ) crop_raw_path = thiscell_path / "raw.ome.tif" writer = save_tif.OmeTiffWriter(crop_raw_path, overwrite_file=True) writer.save(crop_raw_merged) ############################ # check for pair ############################ dist_cutoff = 85 dna_label, dna_num = label(nuc_seg > 0, return_num=True) if dna_num < 2: # certainly not pair if there is only one cc this_cell_is_pair = 0 else: stats = regionprops(dna_label) region_size = [stats[i]["area"] for i in range(dna_num)] large_two = sorted(range(len(region_size)), key=lambda sub: region_size[sub])[-2:] dis = euc_dist_3d(stats[large_two[0]]["centroid"], stats[large_two[1]]["centroid"]) if dis > dist_cutoff: sz1 = stats[large_two[0]]["area"] sz2 = stats[large_two[1]]["area"] if sz1 / sz2 > 1.5625 or sz1 / sz2 < 0.64: # the two parts do not have comparable sizes this_cell_is_pair = 0 else: this_cell_is_pair = 1 else: # not far apart enough this_cell_is_pair = 0 name_dict = { "crop_raw": ["dna", "membrane", "structure"], "crop_seg": [ "dna_segmentation", "membrane_segmentation", "membrane_segmentation_roof", "struct_segmentation", "struct_segmentation_roof", ], } # out for mitotic classifier img_out = build_one_cell_for_classification(crop_raw_merged, mem_seg) out_fn = thiscell_path / "for_mito_prediction.npy" if out_fn.exists(): os.remove(out_fn) np.save(out_fn, img_out) ######################################### if len(true_edge_cells) > 0 and (this_cell_index in true_edge_cells): this_is_edge_cell = 1 else: this_is_edge_cell = 0 # write qcb cell meta cell_meta.append({ "CellId": cell_id, "structure_name": row.Gene, "pair": this_cell_is_pair, "this_cell_nbr_complete": this_cell_nbr_complete, "this_cell_nbr_dist_3d": this_cell_nbr_dist_3d, "this_cell_nbr_dist_2d": this_cell_nbr_dist_2d, "this_cell_nbr_overlap_area": this_cell_nbr_overlap_area, "roi": roi, "crop_raw": crop_raw_path, "crop_seg": crop_seg_path, "name_dict": name_dict, "scale_micron": [0.108333, 0.108333, 0.108333], "edge_flag": this_is_edge_cell, "fov_id": row.FOVId, "fov_path": raw_fn, "fov_seg_path": row.MembraneSegmentationReadPath, "struct_seg_path": row.StructureSegmentationReadPath, "this_cell_index": this_cell_index, "stack_min_z": stack_min_z, "stack_max_z": stack_max_z, "image_size": list(raw_mem.shape), "plateId": row.PlateId, "position": row.ColonyPosition, "scope_id": row.InstrumentId, "well_id": row.WellId, "well_name": row.WellName, "passage": row.Passage, "imaging_mode": row.ImagingMode, }) print(f"Cell {cell_id} is done") df_cell_meta = pd.DataFrame(cell_meta) df_cell_meta.to_csv(cells_in_fov_csv, header=True, index=False) # single cell generation succeeds in this FOV print(f"FOV {row.FOVId} is done") with open(tag_file, "w") as f: f.write("all cells completed") return [single_fov_csv, cells_in_fov_csv]
def _generate_standardized_fov_array( row_index: int, row: pd.Series, current_pixel_sizes: Optional[Tuple[float]], desired_pixel_sizes: Optional[Tuple[float]], save_dir: Path, overwrite: bool, ) -> Union[StandardizeFOVArrayResult, StandardizeFOVArrayError]: # Don't use dask for image reading aicsimageio.use_dask(False) # Get the ultimate end save path for this cell save_path = save_dir / f"{row.FOVId}.ome.tiff" # Check skip if not overwrite and save_path.is_file(): log.info( f"Skipping standardized FOV generation for FOVId: {row.FOVId}") return StandardizeFOVArrayResult(row.FOVId, save_path) # Overwrite or didn't exist log.info( f"Beginning standardized FOV generation for FOVId: {row.FOVId}") # Wrap errors for debugging later try: # Get normalized image array normalized_img, channels, pixel_sizes = image_utils.get_normed_image_array( raw_image=row.SourceReadPath, nucleus_seg_image=row.NucleusSegmentationReadPath, membrane_seg_image=row.MembraneSegmentationReadPath, dna_channel_index=row.ChannelIndexDNA, membrane_channel_index=row.ChannelIndexMembrane, structure_channel_index=row.ChannelIndexStructure, brightfield_channel_index=row.ChannelIndexBrightfield, nucleus_seg_channel_index=row.ChannelIndexNucleusSegmentation, membrane_seg_channel_index=row. ChannelIndexMembraneSegmentation, current_pixel_sizes=current_pixel_sizes, desired_pixel_sizes=desired_pixel_sizes, ) # Reshape data for serialization reshaped = transforms.transpose_to_dims(normalized_img, "CYXZ", "CZYX") # Save array as OME Tiff with OmeTiffWriter(save_path, overwrite_file=True) as writer: writer.save( data=reshaped, dimension_order="CZYX", channel_names=channels, pixels_physical_size=pixel_sizes, ) log.info( f"Completed standardized FOV generation for FOVId: {row.FOVId}" ) return StandardizeFOVArrayResult(row.FOVId, save_path) # Catch and return error except Exception as e: log.info( f"Failed standardized FOV generation for FOVId: {row.FOVId}. Error: {e}" ) return StandardizeFOVArrayError(row.FOVId, str(e))