Beispiel #1
0
class Bacilli(Cell):
    """The Bacilli class represents a bacilli bacterium."""

    _REQUIRED_CONFIG = [
        'bacilli.maxSpeed', 'bacilli.maxSpin', 'bacilli.minGrowth',
        'bacilli.maxGrowth', 'bacilli.minWidth', 'bacilli.maxWidth',
        'bacilli.minLength', 'bacilli.maxLength'
    ]

    def __init__(self, name, x, y, width, length, rotation):
        super().__init__(name)
        self._position = Vector([x, y, 0])
        self._width = width
        self._length = length
        self._rotation = rotation
        self._needs_refresh = True
        # diffraction constant, controlling how much does the pattern spread
        self._sigma = 3.0
        # diffraction value, controlling how bright the diffraction pattern is
        self._diff_v = 0.5

    def _refresh(self):
        # get the positions of the centers of the head and tail circles
        direction = Vector([cos(self._rotation), sin(self._rotation), 0])
        distance = (self._length - self._width) / 2
        displacement = distance * direction

        self._head_center = self._position + displacement
        self._tail_center = self._position - displacement

        # get the positions of the corners of the bacilli box
        side = Vector([-sin(self._rotation), cos(self._rotation), 0])
        radius = self._width / 2

        self._head_right = self._head_center + radius * side
        self._head_left = self._head_center - radius * side
        self._tail_right = self._tail_center + radius * side
        self._tail_left = self._tail_center - radius * side

        # compute the region of interest
        self._region = Rectangle(
            floor(min(self._head_center.x, self._tail_center.x) - radius),
            floor(min(self._head_center.y, self._tail_center.y) - radius),
            ceil(max(self._head_center.x, self._tail_center.x) + radius) + 1,
            ceil(max(self._head_center.y, self._tail_center.y) + radius) + 1)

        self._needs_refresh = False

    def draw(self, image, is_cell, greySyntheticImage, phaseContractImage):
        """Draws the cell by adding the given value to the image."""
        if self._needs_refresh:
            self._refresh()

        # binary images (not grey scale) don't have diffraction pattern
        # currently also no diffraction pattern for PCImg
        if (not greySyntheticImage) or phaseContractImage:
            self._sigma = 0
            self._diff_v = 0

        # calculate the expansion of the region based on how much does diffraction pattern spread
        # if self._sigma = 0, then expansion = 0
        # if self._diff_v = 0, then expansion = 0
        # if phaseContractImage is True, then expansion = 0
        expansion = int(self._sigma * 2) if self._diff_v != 0 else 0

        # expand the square enclosing the cell
        top = self._region.top - expansion
        bottom = self._region.bottom + expansion
        left = self._region.left - expansion
        right = self._region.right + expansion
        width = right - left
        height = bottom - top
        mask = np.zeros((height, width), dtype=np.bool)

        # use an extra mask for diffraction pattern
        diff_mask = np.zeros((height, width), dtype=np.float)

        # body_mask is expanded
        body_mask = polygon(
            r=(self._head_left.y - top, self._head_right.y - top,
               self._tail_right.y - top, self._tail_left.y - top),
            c=(self._head_left.x - left, self._head_right.x - left,
               self._tail_right.x - left, self._tail_left.x - left),
            shape=mask.shape)

        # body_mask_up is for phaseContractImage so no change for now
        body_mask_up = polygon(
            r=(self._head_left.y - self._region.top,
               ceil((self._head_right.y + self._head_left.y) / 2) -
               self._region.top,
               ceil((self._tail_right.y + self._tail_left.y) / 2) -
               self._region.top, self._tail_left.y - self._region.top),
            c=(self._head_left.x - self._region.left,
               ceil((self._head_right.x + self._head_left.x) / 2) -
               self._region.left,
               ceil((self._tail_right.x + self._tail_left.x) / 2) -
               self._region.left, self._tail_left.x - self._region.left),
            shape=mask.shape)

        # body_mask_middle is for phaseContractImage so no change for now
        body_mask_middle = polygon(
            r=(ceil((self._head_right.y + self._head_left.y * 2) / 3) -
               self._region.top,
               ceil((self._head_right.y * 2 + self._head_left.y) / 3) -
               self._region.top,
               ceil((self._tail_right.y * 2 + self._tail_left.y) / 3) -
               self._region.top,
               ceil((self._tail_right.y + self._tail_left.y * 2) / 3) -
               self._region.top),
            c=(ceil((self._head_right.x + self._head_left.x * 2) / 3) -
               self._region.left,
               ceil((self._head_right.x * 2 + self._head_left.x) / 3) -
               self._region.left,
               ceil((self._tail_right.x * 2 + self._tail_left.x) / 3) -
               self._region.left,
               ceil((self._tail_right.x + self._tail_left.x * 2) / 3) -
               self._region.left),
            shape=mask.shape)

        # head_mask is expanded
        head_mask = circle(x=self._head_center.x - left,
                           y=self._head_center.y - top,
                           radius=self._width / 2,
                           shape=mask.shape)

        # tail_mask is expanded
        tail_mask = circle(x=self._tail_center.x - left,
                           y=self._tail_center.y - top,
                           radius=self._width / 2,
                           shape=mask.shape)

        if greySyntheticImage:

            # phaseContractImage == True part remains unchanged
            if phaseContractImage:
                if not is_cell:
                    mask[body_mask] = True
                    mask[head_mask] = True
                    mask[tail_mask] = True

                    image[self._region.top:self._region.bottom, self._region.
                          left:self._region.right][mask] = 0.39  # 0.39*255=100

                else:
                    mask = np.zeros((height, width), dtype=np.bool)
                    mask[body_mask] = True
                    image[self._region.top:self._region.bottom, self._region.
                          left:self._region.right][mask] = 0.25  # 0.25*255=65

                    mask = np.zeros((height, width), dtype=np.bool)
                    mask[head_mask] = True
                    image[self._region.top:self._region.bottom,
                          self._region.left:self._region.right][mask] = 0.25

                    mask = np.zeros((height, width), dtype=np.bool)
                    mask[tail_mask] = True
                    image[self._region.top:self._region.bottom,
                          self._region.left:self._region.right][mask] = 0.25

                    mask = np.zeros((height, width), dtype=np.bool)
                    mask[body_mask_up] = True
                    image[self._region.top:self._region.bottom, self._region.
                          left:self._region.right][mask] = 0.63  # 0.63*255=160

                    mask = np.zeros((height, width), dtype=np.bool)
                    mask[body_mask_middle] = True
                    image[self._region.top:self._region.bottom, self._region.
                          left:self._region.right][mask] = 0.39  # 0.39*255=100

            if not phaseContractImage:

                mask[body_mask] = True
                mask[head_mask] = True
                mask[tail_mask] = True

                if is_cell:

                    if (self._diff_v != 0) and (self._sigma != 0):
                        diff_mask[body_mask] = self._diff_v
                        diff_mask[head_mask] = self._diff_v
                        diff_mask[tail_mask] = self._diff_v

                        # blur the white cell
                        diff_mask = gaussian(diff_mask, self._sigma)

                        # add diffraction pattern to the image
                        image[top:bottom, left:right] += diff_mask

                    # overlap the cell part with the black cell
                    image[top:bottom,
                          left:right][mask] = 0.15  #0.39 - 0.24 = 0.15

                else:

                    if (self._diff_v != 0) and (self._sigma != 0):
                        diff_mask[body_mask] = self._diff_v
                        diff_mask[head_mask] = self._diff_v
                        diff_mask[tail_mask] = self._diff_v

                        # blur the white cell
                        diff_mask = gaussian(diff_mask, self._sigma)

                        # subtract diffraction pattern to the image
                        image[top:bottom, left:right] -= diff_mask

                    # overlap the cell part with background
                    image[top:bottom, left:right][mask] = 0.39
        else:
            mask[body_mask] = True
            mask[head_mask] = True
            mask[tail_mask] = True
            if is_cell:
                image[self._region.top:self._region.bottom,
                      self._region.left:self._region.right][mask] += 1.0
            else:
                image[self._region.top:self._region.bottom,
                      self._region.left:self._region.right][mask] += -1.0

    def drawoutline(self, image, color):
        """Draws the outline of the cell over a color image."""
        if self._needs_refresh:
            self._refresh()

        draw_line(image, int(self._tail_left.x), int(self._tail_left.y),
                  int(self._head_left.x), int(self._head_left.y), color)
        draw_line(image, int(self._tail_right.x), int(self._tail_right.y),
                  int(self._head_right.x), int(self._head_right.y), color)

        r0 = self._head_right - self._head_center
        r1 = self._head_left - self._head_center
        t1 = atan2(r0.y, r0.x)
        t0 = atan2(r1.y, r1.x)
        draw_arc(image, self._head_center.x, self._head_center.y,
                 self._width / 2, t0, t1, color)

        r0 = self._tail_right - self._tail_center
        r1 = self._tail_left - self._tail_center
        t0 = atan2(r0.y, r0.x)
        t1 = atan2(r1.y, r1.x)
        draw_arc(image, self._tail_center.x, self._tail_center.y,
                 self._width / 2, t0, t1, color)

    def split(self, alpha):
        """Splits a cell into two cells with a ratio determined by alpha."""
        if self._needs_refresh:
            self._refresh()

        direction = Vector([cos(self._rotation), sin(self._rotation), 0])
        unit = self._length * direction

        front = self._position + unit / 2
        back = self._position - unit / 2
        center = self._position + (0.5 - alpha) * unit

        position1 = (front + center) / 2
        position2 = (center + back) / 2

        cell1 = Bacilli(self._name + '0', position1.x, position1.y,
                        self._width, self._length * alpha, self._rotation)

        cell2 = Bacilli(self._name + '1',
                        position2.x, position2.y, self._width,
                        self._length * (1 - alpha), self._rotation)

        return cell1, cell2

    def combine(self, cell):
        """Combines this cell with another cell."""
        if self._needs_refresh:
            self._refresh()

        if cell._needs_refresh:
            cell._refresh()

        separation = self._position - cell._position
        direction = separation / sqrt(separation @ separation)

        # get combined front
        direction1 = Vector([cos(self._rotation), sin(self._rotation), 0])
        distance1 = self._length - self._width
        if direction1 @ direction >= 0:
            head1 = self._position + distance1 * direction1 / 2
        else:
            head1 = self._position - distance1 * direction1 / 2
        extent1 = head1 + self._width * direction / 2
        front = self._position + (
            (extent1 - self._position) @ direction) * direction

        # get combined back
        direction2 = Vector([cos(cell._rotation), sin(cell._rotation), 0])
        distance2 = cell._length - cell._width
        if direction2 @ direction >= 0:
            tail2 = cell._position - distance2 * direction2 / 2
        else:
            tail2 = cell._position + distance2 * direction2 / 2
        extent2 = tail2 - cell._width * direction / 2
        back = cell._position + (
            (extent2 - cell._position) @ direction) * direction

        # create new cell
        position = (front + back) / 2
        rotation = atan2(direction.y, direction.x)
        width = (self._width + cell._width) / 2
        length = sqrt((front - back) @ (front - back))

        return Bacilli(self._name[:-1], position.x, position.y, width, length,
                       rotation)

    def __repr__(self):
        return (f'Bacilli('
                f'name="{self._name}", '
                f'x={self._position.x}, y={self._position.y}, '
                f'width={self._width}, length={self._length}, '
                f'rotation={self._rotation})')

    @property
    def region(self):
        if self._needs_refresh:
            self._refresh()
        return self._region

    @property
    def position(self):
        return self._position.copy()

    @property
    def x(self):
        return self._position.x

    @x.setter
    def x(self, value):
        if value != self._position.x:
            self._position.x = value
            self._needs_refresh = True

    @property
    def y(self):
        return self._position.y

    @y.setter
    def y(self, value):
        if value != self._position.y:
            self._position.y = value
            self._needs_refresh = True

    @property
    def width(self):
        return self._width

    @width.setter
    def width(self, value):
        if value != self._width:
            self._width = value
            self._needs_refresh = True

    @property
    def length(self):
        return self._length

    @length.setter
    def length(self, value):
        if value != self._length:
            self._length = value
            self._needs_refresh = True

    @property
    def rotation(self):
        return self._rotation

    @rotation.setter
    def rotation(self, value):
        if value != self._rotation:
            self._rotation = value
            self._needs_refresh = True
Beispiel #2
0
class Bacilli(Cell):
    """The Bacilli class represents a bacilli bacterium."""

    _REQUIRED_CONFIG = [
        'bacilli.maxSpeed', 'bacilli.maxSpin', 'bacilli.minGrowth',
        'bacilli.maxGrowth', 'bacilli.minWidth', 'bacilli.maxWidth',
        'bacilli.minLength', 'bacilli.maxLength'
    ]

    def __init__(self,
                 name,
                 x,
                 y,
                 width,
                 length,
                 rotation,
                 split_alpha=None,
                 opacity=0):
        #upper left corner is the origin
        #x,y are index of the array
        #x:column index y:row index
        super().__init__(name)
        self._position = Vector([x, y, 0])
        self._width = width
        self._length = length
        self._rotation = rotation
        self._opacity = opacity
        self._split_alpha = split_alpha
        self._needs_refresh = True

    def _refresh(self):
        # get the positions of the centers of the head and tail circles
        direction = Vector([cos(self._rotation), sin(self._rotation), 0])
        distance = (self._length - self._width) / 2
        displacement = distance * direction

        self._head_center = self._position + displacement
        self._tail_center = self._position - displacement

        # get the positions of the corners of the bacilli box
        side = Vector([-sin(self._rotation), cos(self._rotation), 0])
        radius = self._width / 2

        self._head_right = self._head_center + radius * side
        self._head_left = self._head_center - radius * side
        self._tail_right = self._tail_center + radius * side
        self._tail_left = self._tail_center - radius * side

        # compute the region of interest
        self._region = Rectangle(
            floor(min(self._head_center.x, self._tail_center.x) -
                  radius),  #left
            floor(min(self._head_center.y, self._tail_center.y) -
                  radius),  #top
            ceil(max(self._head_center.x, self._tail_center.x) + radius) +
            1,  #right
            ceil(max(self._head_center.y, self._tail_center.y) + radius) +
            1)  #bottom

        self._needs_refresh = False

    def draw(self, image, cellmap, is_cell, simulation_config):
        """Draws the cell by adding the given value to the image."""
        if self._needs_refresh:
            self._refresh()

        #Diffraction pattern currently only applies to graySynthetic image
        image_type = simulation_config["image.type"]
        background_color = simulation_config["background.color"]
        cell_color = simulation_config["cell.color"]

        top = self._region.top
        bottom = self._region.bottom
        left = self._region.left
        right = self._region.right
        width = right - left
        height = bottom - top
        mask = np.zeros((height, width), dtype=np.bool)

        body_mask = polygon(
            r=(self._head_left.y - top, self._head_right.y - top,
               self._tail_right.y - top, self._tail_left.y - top),
            c=(self._head_left.x - left, self._head_right.x - left,
               self._tail_right.x - left, self._tail_left.x - left),
            shape=mask.shape)

        body_mask_up = polygon(
            r=(self._head_left.y - self._region.top,
               ceil((self._head_right.y + self._head_left.y) / 2) -
               self._region.top,
               ceil((self._tail_right.y + self._tail_left.y) / 2) -
               self._region.top, self._tail_left.y - self._region.top),
            c=(self._head_left.x - self._region.left,
               ceil((self._head_right.x + self._head_left.x) / 2) -
               self._region.left,
               ceil((self._tail_right.x + self._tail_left.x) / 2) -
               self._region.left, self._tail_left.x - self._region.left),
            shape=mask.shape)

        body_mask_middle = polygon(
            r=(ceil((self._head_right.y + self._head_left.y * 2) / 3) -
               self._region.top,
               ceil((self._head_right.y * 2 + self._head_left.y) / 3) -
               self._region.top,
               ceil((self._tail_right.y * 2 + self._tail_left.y) / 3) -
               self._region.top,
               ceil((self._tail_right.y + self._tail_left.y * 2) / 3) -
               self._region.top),
            c=(ceil((self._head_right.x + self._head_left.x * 2) / 3) -
               self._region.left,
               ceil((self._head_right.x * 2 + self._head_left.x) / 3) -
               self._region.left,
               ceil((self._tail_right.x * 2 + self._tail_left.x) / 3) -
               self._region.left,
               ceil((self._tail_right.x + self._tail_left.x * 2) / 3) -
               self._region.left),
            shape=mask.shape)

        head_mask = circle(x=self._head_center.x - left,
                           y=self._head_center.y - top,
                           radius=self._width / 2,
                           shape=mask.shape)

        tail_mask = circle(x=self._tail_center.x - left,
                           y=self._tail_center.y - top,
                           radius=self._width / 2,
                           shape=mask.shape)
        #phaseContrast unchanged
        if image_type == "phaseContrast":
            if not is_cell:
                mask[body_mask] = True
                mask[head_mask] = True
                mask[tail_mask] = True

                image[self._region.top:self._region.bottom, self._region.
                      left:self._region.right][mask] = 0.39  # 0.39*255=100

            else:
                mask = np.zeros((height, width), dtype=np.bool)
                mask[body_mask] = True
                image[self._region.top:self._region.bottom, self._region.
                      left:self._region.right][mask] = 0.25  # 0.25*255=65

                mask = np.zeros((height, width), dtype=np.bool)
                mask[head_mask] = True
                image[self._region.top:self._region.bottom,
                      self._region.left:self._region.right][mask] = 0.25

                mask = np.zeros((height, width), dtype=np.bool)
                mask[tail_mask] = True
                image[self._region.top:self._region.bottom,
                      self._region.left:self._region.right][mask] = 0.25

                mask = np.zeros((height, width), dtype=np.bool)
                mask[body_mask_up] = True
                image[self._region.top:self._region.bottom, self._region.
                      left:self._region.right][mask] = 0.63  # 0.63*255=160

                mask = np.zeros((height, width), dtype=np.bool)
                mask[body_mask_middle] = True
                image[self._region.top:self._region.bottom, self._region.
                      left:self._region.right][mask] = 0.39  # 0.39*255=100

        elif image_type == "graySynthetic":
            mask[body_mask] = True
            mask[head_mask] = True
            mask[tail_mask] = True

            gaussian_filter_truncate = simulation_config[
                "light.diffraction.truncate"]
            gaussian_filter_sigma = simulation_config[
                "light.diffraction.sigma"]
            diffraction_strength = simulation_config[
                "light.diffraction.strength"]
            cell_opacity = self._opacity if self._opacity != "auto" and self._opacity != "None" else simulation_config[
                "cell.opacity"]
            #in order to use optimze funtion
            gaussian_filter_sigma = max(gaussian_filter_sigma, 0)
            extension = ceil(gaussian_filter_truncate * gaussian_filter_sigma -
                             0.5)
            diff_top = top - (2 * extension)
            diff_left = left - (2 * extension)
            diff_bottom = bottom + (2 * extension)
            diff_right = right + (2 * extension)

            rendering_top = top - extension
            rendering_left = left - extension
            rendering_bottom = bottom + extension
            rendering_right = right + extension

            re_diff_top, re_diff_left, re_rendering_top, re_rendering_left = [
                max(i, 0)
                for i in [diff_top, diff_left, rendering_top, rendering_left]
            ]
            re_diff_bottom, re_rendering_bottom = [
                min(i, image.shape[0])
                for i in [diff_bottom, rendering_bottom]
            ]
            re_diff_right, re_rendering_right = [
                min(i, image.shape[1]) for i in [diff_right, rendering_right]
            ]

            if is_cell:
                cellmap[top:bottom, left:right][mask] += 1
                cellmap_diff = cellmap[re_diff_top:re_diff_bottom,
                                       re_diff_left:re_diff_right]
                diffraction_mask = np.zeros(cellmap_diff.shape, dtype=float)
                diffraction_mask[cellmap_diff > 0] = diffraction_strength
                diffraction_mask = gaussian_filter(
                    diffraction_mask,
                    gaussian_filter_sigma,
                    truncate=gaussian_filter_truncate)
                diffraction_mask[cellmap_diff == 0] += background_color
                diffraction_mask[
                    cellmap_diff >
                    0] = cell_color + cell_opacity * diffraction_mask[
                        cellmap_diff > 0]
                #print("diffraction mask shape: ", diffraction_mask.shape)
                image[re_rendering_top:re_rendering_bottom,
                      re_rendering_left:re_rendering_right] = diffraction_mask[
                          re_rendering_top - re_diff_top:re_rendering_bottom -
                          re_diff_bottom if re_rendering_bottom -
                          re_diff_bottom != 0 else None,
                          re_rendering_left - re_diff_left:re_rendering_right -
                          re_diff_right if re_rendering_right -
                          re_diff_right != 0 else None]
            else:
                cellmap[top:bottom, left:right][mask] -= 1
                cellmap_diff = cellmap[re_diff_top:re_diff_bottom,
                                       re_diff_left:re_diff_right]
                diffraction_mask = np.zeros(cellmap_diff.shape, dtype=float)
                diffraction_mask[cellmap_diff > 0] = diffraction_strength
                diffraction_mask = gaussian_filter(
                    diffraction_mask,
                    gaussian_filter_sigma,
                    truncate=gaussian_filter_truncate)
                diffraction_mask[cellmap_diff == 0] += background_color
                diffraction_mask[
                    cellmap_diff >
                    0] = cell_color + cell_opacity * diffraction_mask[
                        cellmap_diff > 0]
                #print("diffraction mask shape: ", diffraction_mask.shape)
                image[re_rendering_top:re_rendering_bottom,
                      re_rendering_left:re_rendering_right] = diffraction_mask[
                          re_rendering_top - re_diff_top:re_rendering_bottom -
                          re_diff_bottom if re_rendering_bottom -
                          re_diff_bottom != 0 else None,
                          re_rendering_left - re_diff_left:re_rendering_right -
                          re_diff_right if re_rendering_right -
                          re_diff_right != 0 else None]

        elif image_type == "binary":
            mask[body_mask] = True
            mask[head_mask] = True
            mask[tail_mask] = True
            if is_cell:
                image[self._region.top:self._region.bottom,
                      self._region.left:self._region.right][mask] += 1.0
                cellmap[self._region.top:self._region.bottom,
                        self._region.left:self._region.right][mask] += 1
            else:
                image[self._region.top:self._region.bottom,
                      self._region.left:self._region.right][mask] -= 1.0
                cellmap[self._region.top:self._region.bottom,
                        self._region.left:self._region.right][mask] -= 1

    def drawoutline(self, image, color):
        """Draws the outline of the cell over a color image."""
        if self._needs_refresh:
            self._refresh()

        draw_line(image, int(self._tail_left.x), int(self._tail_left.y),
                  int(self._head_left.x), int(self._head_left.y), color)
        draw_line(image, int(self._tail_right.x), int(self._tail_right.y),
                  int(self._head_right.x), int(self._head_right.y), color)

        r0 = self._head_right - self._head_center
        r1 = self._head_left - self._head_center
        t1 = atan2(r0.y, r0.x)
        t0 = atan2(r1.y, r1.x)
        draw_arc(image, self._head_center.x, self._head_center.y,
                 self._width / 2, t0, t1, color)

        r0 = self._tail_right - self._tail_center
        r1 = self._tail_left - self._tail_center
        t0 = atan2(r0.y, r0.x)
        t1 = atan2(r1.y, r1.x)
        draw_arc(image, self._tail_center.x, self._tail_center.y,
                 self._width / 2, t0, t1, color)

    def split(self, alpha):
        """Splits a cell into two cells with a ratio determined by alpha."""
        if self._needs_refresh:
            self._refresh()

        direction = Vector([cos(self._rotation), sin(self._rotation), 0])
        unit = self._length * direction

        front = self._position + unit / 2
        back = self._position - unit / 2
        center = self._position + (0.5 - float(alpha)) * unit

        position1 = (front + center) / 2
        position2 = (center + back) / 2

        cell1 = Bacilli(self._name + '0',
                        position1.x, position1.y, self._width,
                        self._length * float(alpha), self._rotation, alpha,
                        self._opacity)

        cell2 = Bacilli(self._name + '1', position2.x,
                        position2.y, self._width,
                        self._length * (1 - float(alpha)), self._rotation,
                        alpha, self._opacity)

        return cell1, cell2

    def combine(self, cell):
        """Combines this cell with another cell."""
        if self._needs_refresh:
            self._refresh()

        if cell._needs_refresh:
            cell._refresh()

        separation = self._position - cell._position
        direction = separation / sqrt(separation @ separation)

        # get combined front
        direction1 = Vector([cos(self._rotation), sin(self._rotation), 0])
        distance1 = self._length - self._width
        if direction1 @ direction >= 0:
            head1 = self._position + distance1 * direction1 / 2
        else:
            head1 = self._position - distance1 * direction1 / 2
        extent1 = head1 + self._width * direction / 2
        front = self._position + (
            (extent1 - self._position) @ direction) * direction

        # get combined back
        direction2 = Vector([cos(cell._rotation), sin(cell._rotation), 0])
        distance2 = cell._length - cell._width
        if direction2 @ direction >= 0:
            tail2 = cell._position - distance2 * direction2 / 2
        else:
            tail2 = cell._position + distance2 * direction2 / 2
        extent2 = tail2 - cell._width * direction / 2
        back = cell._position + (
            (extent2 - cell._position) @ direction) * direction

        # create new cell
        position = (front + back) / 2
        rotation = atan2(direction.y, direction.x)
        width = (self._width + cell._width) / 2
        length = sqrt((front - back) @ (front - back))

        return Bacilli(self._name[:-1], position.x, position.y, width, length,
                       rotation, "combined alpha unknown",
                       (self._opacity + cell.opacity) / 2)

    def __repr__(self):
        return (f'Bacilli('
                f'name="{self._name}", '
                f'x={self._position.x}, y={self._position.y}, '
                f'width={self._width}, length={self._length}, '
                f'rotation={self._rotation})')

    def simulated_region(self, simulation_config):
        if self._needs_refresh:
            self._refresh()
        if simulation_config["image.type"] == "binary":
            return self._region
        elif simulation_config["image.type"] == "graySynthetic":
            gaussian_filter_truncate = simulation_config[
                "light.diffraction.truncate"]
            gaussian_filter_sigma = simulation_config[
                "light.diffraction.sigma"]
            diffraction_strength = simulation_config[
                "light.diffraction.strength"]

            extension = max(
                floor(gaussian_filter_truncate * gaussian_filter_sigma - 0.5),
                0)
            diff_top = self._region.top - extension
            diff_left = self._region.left - extension
            diff_bottom = self._region.bottom + extension
            diff_right = self._region.right + extension

            region = Rectangle(diff_left, diff_top, diff_right, diff_bottom)
            return region
        else:
            raise ValueError("Cell:simulated_region: unsupported image type")

    @property
    def region(self):
        if self._needs_refresh:
            self._refresh()
        return self._region

    @property
    def position(self):
        return self._position.copy()

    @property
    def x(self):
        return self._position.x

    @x.setter
    def x(self, value):
        if value != self._position.x:
            self._position.x = value
            self._needs_refresh = True

    @property
    def y(self):
        return self._position.y

    @y.setter
    def y(self, value):
        if value != self._position.y:
            self._position.y = value
            self._needs_refresh = True

    @property
    def width(self):
        return self._width

    @width.setter
    def width(self, value):
        if value != self._width:
            self._width = value
            self._needs_refresh = True

    @property
    def length(self):
        return self._length

    @length.setter
    def length(self, value):
        if value != self._length:
            self._length = value
            self._needs_refresh = True

    @property
    def rotation(self):
        return self._rotation

    @property
    def split_alpha(self):
        return self._split_alpha

    @split_alpha.setter
    def split_alpha(self, value):
        self._split_alpha = value

    @property
    def opacity(self):
        return self._opacity

    @opacity.setter
    def opacity(self, value):
        self._opacity = value

    @rotation.setter
    def rotation(self, value):
        if value != self._rotation:
            self._rotation = value
            self._needs_refresh = True
Beispiel #3
0
class Bacilli(Cell):
    """The Bacilli class represents a bacilli bacterium."""

    _REQUIRED_CONFIG = [
        'bacilli.maxSpeed',
        'bacilli.maxSpin',
        'bacilli.minGrowth',
        'bacilli.maxGrowth',
        'bacilli.minWidth',
        'bacilli.maxWidth',
        'bacilli.minLength',
        'bacilli.maxLength'
    ]

    def __init__(self, name, x, y, width, length, rotation):
        super().__init__(name)
        self._position = Vector([x, y, 0])
        self._width = width
        self._length = length
        self._rotation = rotation
        self._needs_refresh = True

    def _refresh(self):
        # get the positions of the centers of the head and tail circles
        direction = Vector([cos(self._rotation), sin(self._rotation), 0])
        distance = (self._length - self._width)/2
        displacement = distance*direction

        self._head_center = self._position + displacement
        self._tail_center = self._position - displacement

        # get the positions of the corners of the bacilli box
        side = Vector([-sin(self._rotation), cos(self._rotation), 0])
        radius = self._width/2

        self._head_right = self._head_center + radius*side
        self._head_left = self._head_center - radius*side
        self._tail_right = self._tail_center + radius*side
        self._tail_left = self._tail_center - radius*side

        # compute the region of interest
        self._region = Rectangle(
            floor(min(self._head_center.x, self._tail_center.x) - radius),
            floor(min(self._head_center.y, self._tail_center.y) - radius),
            ceil(max(self._head_center.x, self._tail_center.x) + radius) + 1,
            ceil(max(self._head_center.y, self._tail_center.y) + radius) + 1)

        self._needs_refresh = False

    def draw(self, image, is_cell, greySyntheticImage, phaseContractImage):
        """Draws the cell by adding the given value to the image."""
        if self._needs_refresh:
            self._refresh()

        width = self._region.right - self._region.left
        height = self._region.bottom - self._region.top
        mask = np.zeros((height, width), dtype=np.bool)


        body_mask = polygon(
            r=(self._head_left.y - self._region.top,
               self._head_right.y - self._region.top,
               self._tail_right.y - self._region.top,
               self._tail_left.y - self._region.top),
            c=(self._head_left.x - self._region.left,
               self._head_right.x - self._region.left,
               self._tail_right.x - self._region.left,
               self._tail_left.x - self._region.left),
            shape=mask.shape)

        body_mask_up = polygon(
            r=(self._head_left.y - self._region.top,
               ceil((self._head_right.y + self._head_left.y) / 2) - self._region.top,
               ceil((self._tail_right.y + self._tail_left.y) / 2) - self._region.top,
               self._tail_left.y - self._region.top),
            c=(self._head_left.x - self._region.left,
               ceil((self._head_right.x + self._head_left.x) / 2) - self._region.left,
               ceil((self._tail_right.x + self._tail_left.x) / 2) - self._region.left,
               self._tail_left.x - self._region.left),
            shape=mask.shape)

        body_mask_middle = polygon(
            r=(ceil((self._head_right.y + self._head_left.y * 2) / 3) - self._region.top,
               ceil((self._head_right.y * 2 + self._head_left.y) / 3) - self._region.top,
               ceil((self._tail_right.y * 2 + self._tail_left.y) / 3) - self._region.top,
               ceil((self._tail_right.y + self._tail_left.y * 2) / 3) - self._region.top),
            c=(ceil((self._head_right.x + self._head_left.x * 2) / 3) - self._region.left,
               ceil((self._head_right.x * 2 + self._head_left.x) / 3) - self._region.left,
               ceil((self._tail_right.x * 2 + self._tail_left.x) / 3) - self._region.left,
               ceil((self._tail_right.x + self._tail_left.x * 2) / 3) - self._region.left),
            shape=mask.shape)

        head_mask = circle(
            x=self._head_center.x - self._region.left,
            y=self._head_center.y - self._region.top,
            radius=self._width / 2,
            shape=mask.shape)

        tail_mask = circle(
            x=self._tail_center.x - self._region.left,
            y=self._tail_center.y - self._region.top,
            radius=self._width / 2,
            shape=mask.shape)

        if greySyntheticImage:
            if phaseContractImage:
                if not is_cell:
                    mask[body_mask] = True
                    mask[head_mask] = True
                    mask[tail_mask] = True

                    image[self._region.top:self._region.bottom,
                    self._region.left:self._region.right][mask] = 0.39  # 0.39*255=100

                else:
                    mask = np.zeros((height, width), dtype=np.bool)
                    mask[body_mask] = True
                    image[self._region.top:self._region.bottom,
                    self._region.left:self._region.right][mask] = 0.25  # 0.25*255=65

                    mask = np.zeros((height, width), dtype=np.bool)
                    mask[head_mask] = True
                    image[self._region.top:self._region.bottom,
                    self._region.left:self._region.right][mask] = 0.25

                    mask = np.zeros((height, width), dtype=np.bool)
                    mask[tail_mask] = True
                    image[self._region.top:self._region.bottom,
                    self._region.left:self._region.right][mask] = 0.25

                    mask = np.zeros((height, width), dtype=np.bool)
                    mask[body_mask_up] = True
                    image[self._region.top:self._region.bottom,
                    self._region.left:self._region.right][mask] = 0.63  # 0.63*255=160

                    mask = np.zeros((height, width), dtype=np.bool)
                    mask[body_mask_middle] = True
                    image[self._region.top:self._region.bottom,
                    self._region.left:self._region.right][mask] = 0.39  # 0.39*255=100

            if not phaseContractImage:
                mask[body_mask] = True
                mask[head_mask] = True
                mask[tail_mask] = True
                if is_cell:
                    image[self._region.top:self._region.bottom,
                          self._region.left:self._region.right][mask] += -0.24
                else:
                    image[self._region.top:self._region.bottom,
                          self._region.left:self._region.right][mask] += 0.24
        else:
            mask[body_mask] = True
            mask[head_mask] = True
            mask[tail_mask] = True
            if is_cell:
                image[self._region.top:self._region.bottom,
                      self._region.left:self._region.right][mask] += 1.0
            else:
                image[self._region.top:self._region.bottom,
                      self._region.left:self._region.right][mask] += -1.0





    def drawoutline(self, image, color):
        """Draws the outline of the cell over a color image."""
        if self._needs_refresh:
            self._refresh()

        draw_line(image, int(self._tail_left.x), int(self._tail_left.y),
                         int(self._head_left.x), int(self._head_left.y), color)
        draw_line(image, int(self._tail_right.x), int(self._tail_right.y),
                         int(self._head_right.x), int(self._head_right.y), color)

        r0 = self._head_right - self._head_center
        r1 = self._head_left - self._head_center
        t1 = atan2(r0.y, r0.x)
        t0 = atan2(r1.y, r1.x)
        draw_arc(image, self._head_center.x, self._head_center.y,
                        self._width/2, t0, t1, color)

        r0 = self._tail_right - self._tail_center
        r1 = self._tail_left - self._tail_center
        t0 = atan2(r0.y, r0.x)
        t1 = atan2(r1.y, r1.x)
        draw_arc(image, self._tail_center.x, self._tail_center.y,
                        self._width/2, t0, t1, color)

    def split(self, alpha):
        """Splits a cell into two cells with a ratio determined by alpha."""
        if self._needs_refresh:
            self._refresh()

        direction = Vector([cos(self._rotation), sin(self._rotation), 0])
        unit = self._length*direction

        front = self._position + unit/2
        back = self._position - unit/2
        center = self._position + (0.5 - alpha)*unit

        position1 = (front + center)/2
        position2 = (center + back)/2

        cell1 = Bacilli(
            self._name + '0',
            position1.x, position1.y,
            self._width, self._length*alpha,
            self._rotation)

        cell2 = Bacilli(
            self._name + '1',
            position2.x, position2.y,
            self._width, self._length*(1 - alpha),
            self._rotation)

        return cell1, cell2

    def combine(self, cell):
        """Combines this cell with another cell."""
        if self._needs_refresh:
            self._refresh()

        if cell._needs_refresh:
            cell._refresh()

        separation = self._position - cell._position
        direction = separation/sqrt(separation@separation)

        # get combined front
        direction1 = Vector([cos(self._rotation), sin(self._rotation), 0])
        distance1 = self._length - self._width
        if direction1@direction >= 0:
            head1 = self._position + distance1*direction1/2
        else:
            head1 = self._position - distance1*direction1/2
        extent1 = head1 + self._width*direction/2
        front = self._position + ((extent1 - self._position)@direction)*direction

        # get combined back
        direction2 = Vector([cos(cell._rotation), sin(cell._rotation), 0])
        distance2 = cell._length - cell._width
        if direction2@direction >= 0:
            tail2 = cell._position - distance2*direction2/2
        else:
            tail2 = cell._position + distance2*direction2/2
        extent2 = tail2 - cell._width*direction/2
        back = cell._position + ((extent2 - cell._position)@direction)*direction

        # create new cell
        position = (front + back)/2
        rotation = atan2(direction.y, direction.x)
        width = (self._width + cell._width)/2
        length = sqrt((front - back)@(front - back))

        return Bacilli(
            self._name[:-1],
            position.x, position.y,
            width, length,
            rotation)

    def __repr__(self):
        return (f'Bacilli('
                f'name="{self._name}", '
                f'x={self._position.x}, y={self._position.y}, '
                f'width={self._width}, length={self._length}, '
                f'rotation={self._rotation})')

    @property
    def region(self):
        if self._needs_refresh:
            self._refresh()
        return self._region

    @property
    def position(self):
        return self._position.copy()

    @property
    def x(self):
        return self._position.x

    @x.setter
    def x(self, value):
        if value != self._position.x:
            self._position.x = value
            self._needs_refresh = True

    @property
    def y(self):
        return self._position.y

    @y.setter
    def y(self, value):
        if value != self._position.y:
            self._position.y = value
            self._needs_refresh = True

    @property
    def width(self):
        return self._width

    @width.setter
    def width(self, value):
        if value != self._width:
            self._width = value
            self._needs_refresh = True

    @property
    def length(self):
        return self._length

    @length.setter
    def length(self, value):
        if value != self._length:
            self._length = value
            self._needs_refresh = True

    @property
    def rotation(self):
        return self._rotation

    @rotation.setter
    def rotation(self, value):
        if value != self._rotation:
            self._rotation = value
            self._needs_refresh = True