Exemplo n.º 1
0
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()]
Exemplo n.º 2
0
    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))
Exemplo n.º 3
0
    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))
Exemplo n.º 4
0
    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))
Exemplo n.º 5
0
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]
Exemplo n.º 6
0
    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))