Source code for pycellid.images

# !/usr/bin/env python

# -*- coding: utf-8 -*-


# This file is part of the
#   PyCellID Project (
#     https://github.com/pyCellID,
#     https://github.com/darksideoftheshmoo
# ).
# Copyright (c) 2021. Clemente, Jose
# License: MIT
#   Full Text: https://github.com/pyCellID/pyCellID/blob/main/LICENSE

# =============================================================================
# DOCS
# =============================================================================

"""
Images for PyCellID.

Attention! This module will not provide images.
This module provides matrix representations for your analysis or to work with
your favorite framework.
"""

# =============================================================================
# IMPORTS
# =============================================================================


import re
import warnings
from pathlib import Path

import matplotlib.pyplot as plt

import numpy as np


[docs]def img_name(path, ucid, channel, t_frame=None, fmt=".tif.out.tif"): """ Construct the name of an image according to output format of CellID. The returned string contains the path and name of the image. Parameters ---------- path : str Path to the directory containing images asociated to 'data'. ucid : int Unique traking number. channel : str Fluorescence channel of the image. The allowed values are ``'BF'``, ``'CFP'``, ``'RFP'`` or ``'YFP'``. t_frame : int Time tag of the image. fmt : str Format of the image to be read. Returns ------- name : str Name and path of an image according to the output format of ``CellID``. """ base_dir = Path(path) # Extract the position from 'ucid' pos = re.search(r"\d+(?=\d{11})", str(ucid)).group(0) pos = pos.zfill(2) # Check if 't_frame' is provided and onstruct the name of the image if t_frame is None: name = f"{channel.upper()}_Position{pos}{fmt}" else: s = str(int(t_frame) + 1).zfill(2) name = f"{channel.upper()}_Position{pos}_time{s}{fmt}" # Join base directory to name name = base_dir.joinpath(name) return name
def _check_y_pos(im, y_pos, radius): """ Check if the cell of interest is in the upper edge of the image. If the cell is closer to the edge than a 'radius', then it corrects the outcome adding zero-valued rows at the begining of the image. Parameters ---------- im : numpy.array Fluorescence microscopy image. y_pos : int :math:`y`-coordinate of the center of the cell of interest. radius : int lenght (in pixels) between the center of the image and each edge. Returns ------- im : numpy.array An array (image) containing an individualised cell, corrected in case that the cell is close to the upper edge of the original image. """ if y_pos - radius < 0: im = np.concatenate( [np.zeros((np.abs(y_pos - radius), im.shape[1])), im], 0 ) return im def _check_x_pos(im, x_pos, radius): """ Check if the cell of interest is in the left edge of the image. If the cell is closer to the edge than a ``radius``, then it corrects the outcome adding zero-valued columns at the begining of the image. Parameters ---------- im : numpy.array Fluorescence microscopy image. x_pos : int :math:`x`-coordinate of the center of the cell of interest. radius : int lenght (in pixels) between the center of the image and each edge. Returns ------- im : numpy.array An array (image) containing an individualised cell, corrected in case that the cell is close to the left edge of the original image. """ if x_pos - radius < 0: im = np.concatenate( [np.zeros((im.shape[0], np.abs(x_pos - radius))), im], 1 ) return im def _mark_center(im, x_pos, y_pos): """ Pin the center of the cell of interest in the original image. Adds a mark to the center of the individualised cell. Parameters ---------- im : numpy.array Fluorescence microscopy image. x_pos : int :math:`x`-coordinate of the center of the cell of interest. y_pos : int :math:`y`-coordinate of the center of the cell of interest. Returns ------- im : numpy.array An array (image) of the same size as the original image with a mark in the center of the individualised cell. """ center = np.zeros((2, 2)) im[y_pos - 1 : y_pos + 1, x_pos - 1 : x_pos + 1] = center # noqa return im def _img_crop(im, x_pos, y_pos, radius, im_shape): """ Perform crop of the image. Crop the region of the image containing the cell of interest. Parameters ---------- im : numpy.array Fluorescence microscopy image. x_pos : int :math:`x`-coordinate of the center of the cell of interest. y_pos : int :math:`y`-coordinate of the center of the cell of interest. radius : int lenght (in pixels) between the center of the image and each edge. Returns ------- im : numpy.array An array (image) containing an individualised cell. """ y_min = max([y_pos - radius, 0]) y_max = min([y_pos + radius, im_shape[0]]) x_min = max([x_pos - radius, 0]) x_max = min([x_pos + radius, im_shape[1]]) im = im[y_min:y_max, x_min:x_max] return im def _img_shape(n): """ Compute the shape of the array of images. Based on the number of required cells, it computes the shape (rows x colummns) of the array to be displayed. Parameters ---------- n : int Number of cells required to be displayed. Returns ------- shape : ``tuple`` Number of rows and columns needed to display ``n`` cell correctly. """ sqrt_floor = int(np.floor(np.sqrt(n))) sqrt_ceil = int(np.ceil(np.sqrt(n))) if sqrt_floor * sqrt_ceil >= n: shape = (sqrt_floor, sqrt_ceil) else: shape = (sqrt_ceil, sqrt_ceil) return shape
[docs]def box_img(im, x_pos, y_pos, radius=90, mark_center=False): """ Create a single image contatinig an individualised cell. The resulting image posses a mark in the center of the individualised cell and a pair of delimiters in the right and bottom edges. Parameters ---------- im : numpy.array A full fluorescence microscopy image. x_pos : int :math:`x`-coordinate of the center of the cell of interest. y_pos : int :math:`y`-coordinate of the center of the cell of interest. radius : int lenght (in pixels) between the center of the image and each edge. mark_center : ``bool`` mark a black point, defoult = ``False``. Return ------ iarray : numpy.array An array (image) containing an individualised, center-pinned, cell. """ height = width = radius * 2 im_shape = im.shape # Mark the center of the cell if mark_center: im = _mark_center(im, x_pos, y_pos) # crop the region of the image containing the cell of interest im = _img_crop(im, x_pos, y_pos, radius, im_shape) iarray = np.zeros((height, width)) im = _check_y_pos(im, y_pos, radius) im = _check_x_pos(im, x_pos, radius) iarray[0 : im.shape[0], 0 : im.shape[1]] = im # noqa: E203 # Adding delimiters rule_height = np.zeros((height, 3)) rule_width = np.zeros((3, (width + 3))) iarray = np.concatenate([iarray, rule_height], 1) iarray = np.concatenate([iarray, rule_width], 0) return iarray
[docs]def array_img(data, path, channel="BF", n=16, criteria=None): """ Create a grid of images containing cells which satisfy given criteria. Resulting image has 'n' instances ordered in a grid of shape 'shape'. Each instance corresponds to a image centered in a cell satisfying provided criteria. Parameters ---------- data : ``pandas.DataFrame`` Dataframe (output of ``CellID``) containing all the measured parameters of each cell. path : str Path to the directory containing the images asociated to ``data``. channel : str Fluorescence channel of the image. The allowed values are ``'BF'``, ``'CFP'``, ``'RFP'`` or ``'YFP'``. n : int Number of instances composing the grid. criteria : dict Dictionay containing the criteria of selection of cells. Return ------ iarray : numpy.array A grid of ``n`` images of cells satisfying given criteria. Raises ------ ValueError If the number of cells satisfying the selection criteria is less than the number of cells to be shown. """ # Estimate the maximum of the diameters of the cells in data based on # their area and assuming round-like cells diameter = int(2 * np.round(np.sqrt(data["a_tot"].max() / np.pi))) shape = _img_shape(n) s = (2 * diameter + 3, 2 * diameter + 3) # Shape of unitary image # iarray np.ones, with size for contining all individual images iarray = np.ones((s[0] * shape[0], s[1] * shape[1]), dtype=float) data_copy = data.copy() # Checking for extra selection criteria criteria = {} if criteria is None else criteria if len(criteria) != 0: for c in criteria.keys(): data_copy = data_copy[ (criteria[c][0] < data_copy[c]) & (data_copy[c] < criteria[c][1]) ] # Checking if the number of cells satisfying the criteria matches the # number of cells to be shown if data_copy.shape[0] < n: if data_copy.shape[0] == 0: message = "The specified criteria is not satisfied by any cell" warnings.warn(message) return iarray message = f"The specified criteria are not satisfied by {n} cells" warnings.warn(message) n = data_copy.shape[0] shape = _img_shape(n) iarray = np.ones((s[0] * shape[0], s[1] * shape[1]), dtype=float) select = data_copy[["ucid", "t_frame", "xpos", "ypos"]].sample(n) # Registers the name of each image in the series 'name' select["name"] = select.apply( lambda row: img_name(path, row["ucid"], channel, row["t_frame"]), axis=1, ) # Registers the individual image corresponding to each cell in the # series 'box_img' select["box_img"] = select.apply( lambda row: box_img( plt.imread(row["name"], format="tif"), row["xpos"], row["ypos"], diameter, ), axis=1, ) iloc = 0 # img index for i in range(0, shape[0]): for j in range(0, shape[1]): xi = s[0] * i xf = s[0] * (i + 1) yi = s[1] * j yf = s[1] * (j + 1) iarray[xi:xf, yi:yf] = select["box_img"].iloc[iloc] iloc += 1 if iloc == n: break if iloc == n: break return iarray