Esempio n. 1
0
def mod_zedge(composite, mod_id, algorithm, **kwargs):

    zedge_channel, zedge_channel_created = composite.channels.get_or_create(name="-zedge")

    for t in range(composite.series.ts):
        print("step02 | processing mod_zedge t{}/{}...".format(t + 1, composite.series.ts), end="\r")

        zdiff_mask = composite.masks.get(channel__name__contains=kwargs["channel_unique_override"], t=t).load()
        zbf = exposure.rescale_intensity(composite.gons.get(channel__name="-zbf", t=t).load() * 1.0)
        zedge = zbf.copy()

        binary_mask = zdiff_mask > 0
        outside_edge = distance_transform_edt(dilate(edge_image(binary_mask), iterations=4))
        outside_edge = 1.0 - exposure.rescale_intensity(outside_edge * 1.0)
        zedge *= outside_edge * outside_edge

        zedge_gon, zedge_gon_created = composite.gons.get_or_create(
            experiment=composite.experiment, series=composite.series, channel=zedge_channel, t=t
        )
        zedge_gon.set_origin(0, 0, 0, t)
        zedge_gon.set_extent(composite.series.rs, composite.series.cs, 1)

        zedge_gon.array = zedge.copy()
        zedge_gon.save_array(composite.series.experiment.composite_path, composite.templates.get(name="source"))
        zedge_gon.save()
Esempio n. 2
0
	def create_region_tile(self, channel_unique_override, top_channel='-zbf', side_channel='-zunique', main_channel='-zunique'):
		tile_path = join(self.experiment.video_path, 'regions', self.series.name, channel_unique_override)
		if not exists(tile_path):
			os.makedirs(tile_path)

		for t in range(self.series.ts):
			zbf_gon = self.gons.get(t=t, channel__name=top_channel)
			zcomp_gon = self.gons.get(t=t, channel__name=side_channel)
			zmean_gon = self.gons.get(t=t, channel__name=main_channel)

			zbf = zbf_gon.load()
			zbf = zbf if len(zbf.shape)==2 or (len(zbf.shape)==2 and zbf.shape[2]==2) else np.squeeze(zbf[:,:,0])
			zcomp = zcomp_gon.load()
			zcomp = zcomp if len(zcomp.shape)==2 or (len(zcomp.shape)==2 and zcomp.shape[2]==2) else np.squeeze(zcomp[:,:,0])
			zmean = zmean_gon.load()
			zmean = zmean if len(zmean.shape)==2 or (len(zmean.shape)==2 and zmean.shape[2]==2) else np.squeeze(zmean[:,:,0])

			zbf_mask_r = zbf.copy()
			zbf_mask_g = zbf.copy()
			zbf_mask_b = zbf.copy()

			zcomp_mask_r = zcomp.copy()
			zcomp_mask_g = zcomp.copy()
			zcomp_mask_b = zcomp.copy()

			# draw regions
			for region_instance in self.series.region_instances.filter(region_track_instance__t=t):
				# load mask
				mask = region_instance.masks.all()[0].load()
				# get mask outline
				mask_edge = edge_image(mask)

				# draw outlines in blue channel
				zbf_mask_r[mask_edge>0] = 0
				zbf_mask_g[mask_edge>0] = 0
				zbf_mask_b[mask_edge>0] = 255
				zcomp_mask_r[mask_edge>0] = 0
				zcomp_mask_g[mask_edge>0] = 0
				zcomp_mask_b[mask_edge>0] = 255

			# tile zbf, zbf_mask, zcomp, zcomp_mask
			top_half = np.concatenate((np.dstack([zbf, zbf, zbf]), np.dstack([zbf_mask_r, zbf_mask_g, zbf_mask_b])), axis=0)
			bottom_half = np.concatenate((np.dstack([zmean, zmean, zmean]), np.dstack([zcomp_mask_r, zcomp_mask_g, zcomp_mask_b])), axis=0)
			whole = np.concatenate((top_half, bottom_half), axis=1)

			imsave(join(tile_path, 'tile_{}_s{}_region-{}_t{}.tiff'.format(self.experiment.name, self.series.name, channel_unique_override, str_value(t, self.series.ts))), whole)
Esempio n. 3
0
	def create_zedge(self, channel_unique_override):
		zedge_channel, zedge_channel_created = self.channels.get_or_create(name='-zedge')

		for t in range(self.series.ts):
			print('step02 | processing mod_zedge t{}/{}...'.format(t+1, self.series.ts), end='\r')

			zunique_mask = self.masks.get(channel__name__contains=channel_unique_override, t=t).load()
			zbf = exposure.rescale_intensity(self.gons.get(channel__name='-zbf', t=t).load() * 1.0)
			zedge = zbf.copy()

			binary_mask = zunique_mask>0
			outside_edge = distance_transform_edt(dilate(edge_image(binary_mask), iterations=4))
			outside_edge = 1.0 - exposure.rescale_intensity(outside_edge * 1.0)
			zedge *= outside_edge * outside_edge

			zedge_gon, zedge_gon_created = self.gons.get_or_create(experiment=self.experiment, series=self.series, channel=zedge_channel, t=t)
			zedge_gon.set_origin(0,0,0,t)
			zedge_gon.set_extent(self.series.rs, self.series.cs, 1)

			zedge_gon.array = zedge.copy()
			zedge_gon.save_array(self.experiment.composite_path, self.templates.get(name='source'))
			zedge_gon.save()

		return zedge_channel
Esempio n. 4
0
def mask_edge_image(mask_img):
    full_edge_img = np.zeros(mask_img.shape)
    for unique in [u for u in np.unique(mask_img) if u > 0]:
        full_edge_img += edge_image(mask_img == unique)

    return full_edge_img > 0
Esempio n. 5
0
	def handle(self, *args, **options):
		# vars
		experiment_name = options['expt']
		series_name = options['series']

		if experiment_name!='' and series_name!='':
			experiment = Experiment.objects.get(name=experiment_name)
			series = experiment.series.get(name=series_name)

			# cell_instance = series.cell_instances.get(t=48, cell__pk=4)
			# cell_instance = series.cell_instances.get(t=49, cell__pk=9)
			# cell_instance = series.cell_instances.get(pk=198)

			# load mask image
			# 1. for each cell mask, load mask image
			outlines = {}
			colours = ['red','green','blue','purple']
			for i, cell_mask in enumerate(cell_instance.masks.filter(channel__name__contains='zunique')):
				mask_img = cell_mask.load()

				# get edge
				edge_img = edge_image(mask_img)

				# get list of points
				points_r, points_c = np.where(edge_img)
				points = [list(lm) for lm in list(zip(points_r, points_c))]

				sorted_points = roll_edge_v1(points)

				# plot distances in order
				cell_centre = np.array([cell_mask.r, cell_mask.c])
				distances = np.array([np.linalg.norm(cell_centre - np.array(p)) for p in sorted_points])
				argmin = np.argmin(distances)
				distances = np.roll(distances, -argmin)
				distances = gf(distances, sigma=2)
				# plt.plot(distances)
				# plt.scatter(cell_mask.c, cell_mask.r)

				# find peaks in distance array
				peaks = find_peaks(distances, np.array([9]))
				# plt.scatter(peaks, [distances[peak] for peak in peaks])

				# roll back to find true peak positions
				true_peaks = np.array(peaks) + argmin
				true_peaks[true_peaks>=len(sorted_points)] -= len(sorted_points)

				# find end point of protrusions
				protrusion_end_points = [sorted_points[peak] for peak in true_peaks]

				for protrusion_end_point in protrusion_end_points:
					print('new protrusion for cell mask {} for cell instance {}'.format(cell_mask.pk, cell_instance.pk))
					relative_end_point = cell_centre - np.array(protrusion_end_point)
					print(cell_centre, protrusion_end_point)
					print('length from centre: {} microns'.format(np.linalg.norm(relative_end_point * series.scaling())))
					print('orientation: {} degrees'.format(180 / math.pi * math.atan2(relative_end_point[0], relative_end_point[1])))

				# plt.scatter([sorted_points[peak][1] for peak in true_peaks], [sorted_points[peak][0] for peak in true_peaks])

				# plot outlines to check
				plt.plot([point[1] for point in sorted_points], [point[0] for point in sorted_points], label='radius: 2')
				# plt.scatter(points_c, points_r, label=cell_mask.channel.name, color=colours[i])

			# plt.legend()
			# plt.axis('equal')
			plt.show()

		else:
			print('Please enter an experiment')
Esempio n. 6
0
    def handle(self, *args, **options):

        composite = Composite.objects.get(experiment__name="260714", series__name="15")

        # frames
        previous_frame_index = 0
        target_frame_index = 1
        next_frame_index = 2

        # stacks
        previous_gfp_stack = composite.gons.get(t=previous_frame_index, channel__name="0")
        previous_bf_stack = composite.gons.get(t=previous_frame_index, channel__name="1")

        target_gfp_stack = composite.gons.get(t=target_frame_index, channel__name="0")
        target_bf_stack = composite.gons.get(t=target_frame_index, channel__name="1")
        target_zmean_single = composite.gons.get(t=target_frame_index, channel__name="-zmean")
        target_zbf_single = composite.gons.get(t=target_frame_index, channel__name="-zbf")
        target_zmod_single = composite.gons.get(t=target_frame_index, channel__name="-zmod")

        next_gfp_stack = composite.gons.get(t=next_frame_index, channel__name="0")
        next_bf_stack = composite.gons.get(t=next_frame_index, channel__name="1")

        # images
        # previous_gfp = previous_gfp_stack.load()
        # previous_bf = previous_bf_stack.load()

        # target_gfp = target_gfp_stack.load()
        # target_bf = target_bf_stack.load()
        target_zmean = target_zmean_single.load() / 255.0
        target_zbf = target_zbf_single.load() / 255.0
        target_zbf_canny_s3 = canny(target_zbf, sigma=1).astype(float)
        target_zmod = target_zmod_single.load() / 255.0 * (composite.series.zs - 1)

        # next_gfp = next_gfp_stack.load()
        # next_bf = next_bf_stack.load()

        # 1. find maximum from marker
        # marker = composite.markers.get(pk=124)

        track_image = np.zeros(target_zmod.shape)

        class Traveller:
            movement_cost = 1
            directions = [(0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1), (-1, 0), (-1, 1)]
            spawned = False

            def __init__(self, r, c, direction_index, money, cost_function):
                self.r = r
                self.c = c
                self.direction_index = direction_index
                self.drdc()
                self.money = money
                self.cost_function = cost_function

            def drdc(self):
                self.dr, self.dc = self.directions[self.direction_index]

            def move(self):
                # pick one of five directions in the direction of travel, find the one that costs the least money
                # get array of direction indices relative to current
                new_directions = [
                    (abs(i), (i if i < 8 else i - 8) if i >= 0 else i + 7)
                    for i in range(self.direction_index - 2, self.direction_index + 3)
                ]
                costs = []

                for cost_index, direction in new_directions:
                    dr, dc = self.directions[direction]
                    new_r, new_c = self.r + dr, self.c + dc

                    if (
                        0 <= new_r < composite.series.rs
                        and 0 <= new_c < composite.series.cs
                        and 0 <= self.r < composite.series.rs
                        and 0 <= self.c < composite.series.cs
                        and track_image[new_r, new_c] == 0
                    ):  # not out of bounds
                        # print(new_r, new_c, self.r, self.c, dr, dc)
                        # differences
                        delta_z = int(target_zmod[new_r, new_c]) - int(target_zmod[self.r, self.c])
                        delta_zbf = (target_zbf[new_r, new_c] - target_zbf[self.r, self.c]) / target_zbf[self.r, self.c]
                        delta_zmean = (target_zmean[new_r, new_c] - target_zmean[self.r, self.c]) / target_zmean[
                            self.r, self.c
                        ]

                        abs_zbf = target_zbf[new_r, new_c]
                        abs_zmean = target_zmean[new_r, new_c]

                        cost = self.cost_function(delta_z, delta_zbf, delta_zmean, abs_zbf, abs_zmean)
                        costs.append({"cost": cost, "direction": direction})

                if costs:
                    min_direction = min(costs, key=lambda c: c["cost"])
                    track_image[self.r, self.c] = 1
                    self.r, self.c = tuple(
                        np.array(self.directions[min_direction["direction"]]) + np.array((self.r, self.c))
                    )
                    self.money -= min_direction["cost"]
                else:
                    self.money = 0

        # cost functions
        def test_cost_function(delta_z, delta_zbf, delta_zmean, abs_zbf, abs_zmean):
            cost = 0

            cost += np.abs(delta_z) * 8
            cost += -delta_zbf * 4 if delta_zbf < 0 else 0
            # cost += -delta_zmean * 10.0

            return cost

        def dont_go_through_dark_edges(delta_z, delta_zbf, delta_zmean, abs_zbf, abs_zmean):
            cost = 0

            cost += np.abs(delta_z) * 4
            cost += -delta_zbf * 7 if delta_zbf < 0 else 0

            return cost

        def dont_go_to_another_z(delta_z, delta_zbf, delta_zmean, abs_zbf, abs_zmean):
            cost = 0

            cost += np.abs(delta_z) * 8

            return cost

        # set up segmentation - round 1
        travellers = []
        for marker in composite.markers.filter(track_instance__t=target_frame_index):
            for i in range(10):
                travellers.append(Traveller(marker.r, marker.c, np.random.randint(0, 7), 10, dont_go_to_another_z))

        iterations = 0
        while sum([t.money for t in travellers]) > 0:
            iterations += 1
            for traveller in travellers:
                if traveller.money > 0:
                    traveller.move()
                if traveller.money <= 0:
                    travellers.remove(traveller)
                    del traveller

        # spawn travellers from edge of track_image or round 2
        travellers = []
        track_edge = edge_image(track_image)
        for r, c in zip(*np.where(track_edge > 0)):
            travellers.append(Traveller(r, c, np.random.randint(0, 7), 10, dont_go_through_dark_edges))

        iterations = 0
        while sum([t.money for t in travellers]) > 0:
            iterations += 1
            for traveller in travellers:
                if traveller.money > 0:
                    traveller.move()
                if traveller.money <= 0:
                    travellers.remove(traveller)
                    del traveller

            # print(iterations, len(travellers), sum([t.money for t in travellers]))

        # cut = target_zmean[marker.r-10: marker.r+10, marker.c-10: marker.c+10]
        # plt.imshow(cut)
        # plt.show()

        track_image = dilate(erode(erode(dilate(track_image))))

        display_image = target_zbf.copy()
        display_image[track_image == 1] += 0.3
        plt.imshow(display_image)
        plt.show()
Esempio n. 7
0
def mod_step13_cell_masks(composite, mod_id, algorithm):
  # paths
  template = composite.templates.get(name='mask') # MASK TEMPLATE

  # create batches
  batch = 0
  max_batch_size = 100

  # channel
  cell_mask_channel, cell_mask_channel_created = composite.channels.get_or_create(name='cellmask')

  # mean
  # mask_mean_max = np.max([mask.mean for mask in composite.masks.all()])

  # iterate over frames
  for t in range(composite.series.ts):
    print('step13 | processing mod_step13_cell_masks t{}...                                         '.format(t), end='\r')

    # one mask for each marker
    markers = composite.series.markers.filter(t=t)

    # 1. get masks
    mask_gon_set = composite.gons.filter(channel__name__in=['pmodreduced','bfreduced'], t=t)
    bulk = create_bulk_from_image_set(mask_gon_set)

    for m, marker in enumerate(markers):
      print('step13 | processing mod_step13_cell_masks t{}, marker {}/{}...                                         '.format(t, m, len(markers)), end='\r')
      if composite.gons.filter(marker=marker).count()==0:
        # marker parameters
        r, c, z = marker.r, marker.c, marker.z
        other_marker_positions = [(m.r,m.c) for m in markers.exclude(pk=marker.pk)]

        # get primary mask
        primary_mask = np.zeros(composite.series.shape(), dtype=float) # blank image

        mask_uids = [(i, uid) for i,uid in enumerate(bulk.gon_stack[r,c,:]) if uid>0]
        for i,uid in mask_uids:
          gon_pk = bulk.rv[i]
          mask = composite.masks.get(gon__pk=gon_pk, mask_id=uid)
          mask_array = (bulk.slice(pk=mask.gon.pk)==mask.mask_id).astype(float)

          # modify mask array based on parameters
          mask_z, mask_max_z, mask_mean, mask_std = mask.z, mask.max_z, mask.mean, mask.std

          z_term = 1.0 / (1.0 + 0.1*np.abs(z - mask_z)) # suppress z levels at increasing distances from marker
          max_z_term = 1.0 / (1.0 + 0.1*np.abs(z - mask_max_z)) # suppress z levels at increasing distances from marker
          # mean_term = mask_mean / mask_mean_max # raise mask according to mean
          std_term = 1.0

          mask_array = mask_array * z_term * max_z_term * std_term

          # add to primary mask
          primary_mask += mask_array

        # get secondary mask - get unique masks that touch the edge of the primary mask
        secondary_mask = np.zeros(composite.series.shape(), dtype=float) # blank image

        secondary_mask_uids = []
        edges = np.where(edge_image(primary_mask>0))
        for r, c in zip(*edges):
          for i,uid in enumerate(bulk.gon_stack[r,c,:]):
            if (i,uid) not in secondary_mask_uids and (i,uid) not in mask_uids and uid>0:
              secondary_mask_uids.append((i,uid))

        for i,uid in secondary_mask_uids:
          print('step13 | processing mod_step13_cell_masks t{}, marker {}/{}, secondary {}/{}...                                         '.format(t, m, len(markers), i, len(secondary_mask_uids)), end='\r')
          gon_pk = bulk.rv[i]
          mask = composite.masks.get(gon__pk=gon_pk, mask_id=uid)
          mask_array = (bulk.slice(pk=mask.gon.pk)==mask.mask_id).astype(float)

          # modify mask array based on parameters
          mask_z, mask_max_z, mask_mean, mask_std = mask.z, mask.max_z, mask.mean, mask.std

          z_term = 1.0 / (1.0 + 0.1*np.abs(z - mask_z)) # suppress z levels at increasing distances from marker
          max_z_term = 1.0 / (1.0 + 0.1*np.abs(z - mask_max_z)) # suppress z levels at increasing distances from marker
          # mean_term = mask_mean / mask_mean_max # raise mask according to mean
          std_term = 1.0

          foreign_marker_condition = 1.0 # if the mask contains a different marker
          foreign_marker_match = False
          foreign_marker_counter = 0
          while not foreign_marker_match and foreign_marker_counter!=len(other_marker_positions)-1:
            r, c = other_marker_positions[foreign_marker_counter]
            foreign_marker_match = (mask_array>0)[r,c]
            if foreign_marker_match:
              foreign_marker_condition = 0.0
            foreign_marker_counter += 1

          mask_array = mask_array * z_term * max_z_term * std_term * foreign_marker_condition

          # add to primary mask
          secondary_mask += mask_array

        print('step13 | processing mod_step13_cell_masks t{}, marker {}/{}, saving square mask...                                         '.format(t, m, len(markers)), end='\n' if t==composite.series.ts-1 else '\r')
        cell_mask = primary_mask + secondary_mask

        # finally, mean threshold mask
        cell_mask[cell_mask<nonzero_mean(cell_mask)] = 0
        cell_mask[cell_mask<nonzero_mean(cell_mask)] = 0

        # cut to size
        # I want every mask to be exactly the same size -> 128 pixels wide
        # I want the centre of the mask to be in the centre of image
        # Add black space around even past the borders of larger image
        # 1. determine centre of mass
        com_r, com_c = com(cell_mask>0)
        mask_square = np.zeros((256,256), dtype=float)

        if not np.isnan(com_r):
          # 2. cut to black and preserve boundaries
          cut, (cr, cc, crs, ccs) = cut_to_black(cell_mask)

          # 3. place cut inside square image using the centre of mass and the cut boundaries to hit the centre
          dr, dc = int(128 + cr - com_r), int(128 + cc - com_c)

          # 4. preserve coordinates of square to position gon
          small_r, small_c = mask_square[dr:dr+crs,dc:dc+ccs].shape
          mask_square[dr:dr+crs,dc:dc+ccs] = cut[:small_r,:small_c]

        # check batch and make folders, set url
        if not os.path.exists(os.path.join(composite.experiment.cp2_path, composite.series.name, str(batch))):
          os.makedirs(os.path.join(composite.experiment.cp2_path, composite.series.name, str(batch)))

        if len(os.listdir(os.path.join(composite.experiment.cp2_path, composite.series.name, str(batch))))>=max_batch_size:
          batch += 1
          if not os.path.exists(os.path.join(composite.experiment.cp2_path, composite.series.name, str(batch))):
            os.makedirs(os.path.join(composite.experiment.cp2_path, composite.series.name, str(batch)))

        root = os.path.join(composite.experiment.cp2_path, composite.series.name, str(batch)) # CP PATH

        # cell mask gon
        cell_mask_gon = composite.gons.create(experiment=composite.experiment, series=composite.series, channel=cell_mask_channel, template=template)
        cell_mask_gon.set_origin(cr-dr, cc-dc, z, t)
        cell_mask_gon.set_extent(crs, ccs, 1)

        id_token = generate_id_token('img','Gon')
        cell_mask_gon.id_token = id_token

        file_name = template.rv.format(id_token)
        url = os.path.join(root, file_name)

        imsave(url, mask_square.copy())
        cell_mask_gon.paths.create(composite=composite, channel=cell_mask_channel, template=template, url=url, file_name=file_name, t=t, z=z)

        # associate with marker
        marker.gon = cell_mask_gon
        cell_mask_gon.marker = marker
        marker.save()

        cell_mask_gon.save()
Esempio n. 8
0
	def create_tile(self, channel_unique_override, top_channel='-zbf', side_channel='-zunique', main_channel='-zedge', region_list=[]):
		tile_path = join(self.experiment.video_path, 'tile', self.series.name, '{}-{}'.format(dt.datetime.now().strftime('%Y-%m-%d-%H-%M'), channel_unique_override))
		if not exists(tile_path):
			os.makedirs(tile_path)

		for t in range(self.series.ts):
			zbf_gon = self.gons.get(t=t, channel__name=top_channel)
			zcomp_gon = self.gons.get(t=t, channel__name=side_channel)
			zmean_gon = self.gons.get(t=t, channel__name=main_channel)
			mask_mask = self.masks.get(t=t, channel__name__contains=channel_unique_override)

			zbf = zbf_gon.load()
			zbf = zbf if len(zbf.shape)==2 or (len(zbf.shape)==2 and zbf.shape[2]==2) else np.squeeze(zbf[:,:,0])
			zcomp = zcomp_gon.load()
			zcomp = zcomp if len(zcomp.shape)==2 or (len(zcomp.shape)==2 and zcomp.shape[2]==2) else np.squeeze(zcomp[:,:,0])
			zmean = zmean_gon.load()
			zmean = zmean if len(zmean.shape)==2 or (len(zmean.shape)==2 and zmean.shape[2]==2) else np.squeeze(zmean[:,:,0])
			mask = mask_mask.load()

			# remove cells in regions
			if region_list:
				for cell_mask in mask_mask.cell_masks.exclude(region__name__in=region_list):
					mask[mask==cell_mask.gray_value_id] = 0 # delete masks from image if not in regions

			mask_outline = mask_edge_image(mask)

			zbf_mask_r = zbf.copy()
			zbf_mask_g = zbf.copy()
			zbf_mask_b = zbf.copy()

			zcomp_mask_r = zcomp.copy()
			zcomp_mask_g = zcomp.copy()
			zcomp_mask_b = zcomp.copy()

			# drawing
			# 1. draw outlines in red channel
			zbf_mask_r[mask_outline>0] = 255
			zbf_mask_g[mask_outline>0] = 0
			zbf_mask_b[mask_outline>0] = 0
			zcomp_mask_r[mask_outline>0] = 255
			zcomp_mask_g[mask_outline>0] = 0
			zcomp_mask_b[mask_outline>0] = 0

			# draw markers
			markers = self.markers.filter(track_instance__t=t, track__cell__isnull=False)
			for marker in markers:
				if hasattr(marker.track_instance, 'cell_instance'):
					if marker.track_instance.cell_instance.masks.filter(channel__name__contains=channel_unique_override):
						if region_list==[] or (marker.track_instance.cell_instance.masks.get(channel__name__contains=channel_unique_override).region is not None and marker.track_instance.cell_instance.masks.get(channel__name__contains=channel_unique_override).region.name in region_list):
							# 2. draw markers in blue channel
							zbf_mask_r[marker.r-2:marker.r+3,marker.c-2:marker.c+3] = 0
							zbf_mask_g[marker.r-2:marker.r+3,marker.c-2:marker.c+3] = 0
							zbf_mask_b[marker.r-2:marker.r+3,marker.c-2:marker.c+3] = 255
							zcomp_mask_r[marker.r-2:marker.r+3,marker.c-2:marker.c+3] = 0
							zcomp_mask_g[marker.r-2:marker.r+3,marker.c-2:marker.c+3] = 0
							zcomp_mask_b[marker.r-2:marker.r+3,marker.c-2:marker.c+3] = 255

							# 3. draw text in green channel
							blank_slate = np.zeros(zbf.shape)
							blank_slate_img = Image.fromarray(blank_slate)
							draw = ImageDraw.Draw(blank_slate_img)
							draw.text((marker.c+5, marker.r+5), '{}'.format(marker.track.cell.pk), font=ImageFont.load_default(), fill='rgb(0,0,255)')
							blank_slate = np.array(blank_slate_img)

							zbf_mask_r[blank_slate>0] = 0
							zbf_mask_g[blank_slate>0] = 255
							zbf_mask_b[blank_slate>0] = 0
							zcomp_mask_r[blank_slate>0] = 0
							zcomp_mask_g[blank_slate>0] = 255
							zcomp_mask_b[blank_slate>0] = 0

			# draw regions
			for region_instance in self.series.region_instances.filter(region_track_instance__t=t):
				# load mask
				mask = region_instance.masks.all()[0].load()
				# get mask outline
				mask_edge = edge_image(mask)

				# draw outlines in blue channel
				zbf_mask_r[mask_edge>0] = 0
				zbf_mask_g[mask_edge>0] = 0
				zbf_mask_b[mask_edge>0] = 255
				zcomp_mask_r[mask_edge>0] = 0
				zcomp_mask_g[mask_edge>0] = 0
				zcomp_mask_b[mask_edge>0] = 255

			# tile zbf, zbf_mask, zcomp, zcomp_mask
			top_half = np.concatenate((np.dstack([zbf, zbf, zbf]), np.dstack([zbf_mask_r, zbf_mask_g, zbf_mask_b])), axis=0)
			bottom_half = np.concatenate((np.dstack([zmean, zmean, zmean]), np.dstack([zcomp_mask_r, zcomp_mask_g, zcomp_mask_b])), axis=0)
			whole = np.concatenate((top_half, bottom_half), axis=1)

			imsave(join(tile_path, 'tile_{}_s{}_marker-{}_t{}.tiff'.format(self.experiment.name, self.series.name, channel_unique_override, str_value(t, self.series.ts))), whole)