def _measure_ring_intensity_around_nucleus(self, image, cfg):
        assert self._ix.any(), "no rows in the filtered dataframe"
        for ix, row in self._mdf[self._ix].iterrows():
            nucl_bnd = shapely.wkt.loads(row["nuc_pix"])
            thickness = float(cfg['rng_thickness'])
            thickness *= self.pix_per_um
            rng_bnd = (
                nucl_bnd.buffer(thickness).difference(nucl_bnd).simplify(
                    self.pix_per_um / 2, preserve_topology=True))
            if rng_bnd.area > 0:
                rng_int = m.integral_over_surface(image, rng_bnd)
                if np.isnan(rng_int): continue
                rng_density = rng_int / rng_bnd.area
            else:
                logger.warning(
                    "Ring polygon with no area!\r\nThickness of ring set to %.2f [pix]"
                    % thickness)
                continue

            logger.debug(
                "ring_around_nucleus on tag '%s' for nucleus id %d = %s" %
                (cfg['tag'].iloc[0], row['id'],
                 m.eng_string(rng_int, si=True, format='%.2f')))
            rng_um = affinity.scale(rng_bnd,
                                    xfact=self.um_per_pix,
                                    yfact=self.um_per_pix,
                                    origin=(0, 0, 0))

            # TODO: scale intensity from pixels^2 to um^2
            self._mdf.loc[ix, 'ring'] = dumps(rng_um, rounding_precision=4)
            self._mdf.loc[ix, 'ring_pix'] = dumps(rng_bnd,
                                                  rounding_precision=1)
            self._mdf.loc[ix, '%s_rng_int' % cfg['tag'].iloc[0]] = int(rng_int)
            self._mdf.loc[ix, '%s_rng_dens' %
                          cfg['tag'].iloc[0]] = int(rng_density)
    def _measure_nuclei(self, nuclei_img, cfg, r=10):
        imgseg, nuclei = m.nuclei_segmentation(nuclei_img,
                                               radius=r * self.pix_per_um)
        nuclei = m.exclude_contained(nuclei)

        if len(nuclei) == 0:
            logger.warning("Couldn't find nuclei in image.")
            return

        for nucleus in nuclei:
            nucl_bnd = (nucleus['boundary'].buffer(
                self.pix_per_um,
                join_style=1).buffer(-self.pix_per_um, join_style=1).simplify(
                    self.pix_per_um / 2, preserve_topology=True))
            if nucl_bnd.is_empty: continue
            logger.debug("previous simplify %d, after %d" %
                         (len(nucleus['boundary'].exterior.coords),
                          len(nucl_bnd.exterior.coords)))
            if nucl_bnd.area < np.pi * (3 * self.pix_per_um)**2: continue
            dna_int = m.integral_over_surface(nuclei_img, nucl_bnd)

            # convert everything to um space for dataframe construction
            n_bum = affinity.scale(nucl_bnd,
                                   xfact=self.um_per_pix,
                                   yfact=self.um_per_pix,
                                   origin=(0, 0, 0))

            # TODO: Add units support
            d = pd.DataFrame(
                data={
                    'id': [nucleus['id']],
                    'row': [self._row],
                    'col': [self._col],
                    'fid': [self._fid],
                    'p': [self._zp],
                    '%s_int' % cfg['tag'].iloc[0]: [int(dna_int)],
                    '%s_dens' % cfg['tag'].iloc[0]:
                    [int(dna_int / nucl_bnd.area)],
                    'nucleus': dumps(n_bum, rounding_precision=4),
                    'nuc_pix': dumps(nucl_bnd, rounding_precision=1),
                })
            self._mdf = self._mdf.append(d, ignore_index=True, sort=False)
        logger.debug("%d nuclei found in image" % len(nuclei))

        return self._mdf.index
    def _measure_intensity_in_nucleus(self, image, cfg):
        assert self._ix.any(), "no rows in the filtered dataframe"

        nuclei = list()
        for _id, nuc in self._mdf.loc[self._ix,
                                      "nuc_pix"].set_index("id").iteritems():
            nuclei.append({"id": _id, "boundary": shapely.wkt.loads(nuc)})

        for ix, row in self._mdf[self._ix].iterrows():
            _id = row["id"]
            nucl_bnd = shapely.wkt.loads(row["nuc_pix"])

            logger.debug("intensity_in_nucleus for nucleus id %d" % _id)
            signal_int = m.integral_over_surface(image, nucl_bnd)
            signal_density = signal_int / nucl_bnd.area

            # TODO: scale intensity from pixels^2 to um^2
            self._mdf.loc[ix, '%s_int' % cfg['tag'].iloc[0]] = int(signal_int)
            self._mdf.loc[ix,
                          '%s_dens' % cfg['tag'].iloc[0]] = int(signal_density)
    def _measure_particle_in_cytoplasm(self, image, cfg):
        assert self._ix.any(), "no rows in the filtered dataframe"
        width, height = image.shape
        frame = Polygon([(0, 0), (0, height), (width, height), (width, 0)])

        nuclei = list()
        for _id, nuc in self._mdf.set_index("id").loc[self._ix,
                                                      "nuc_pix"].iteritems():
            nuclei.append({"id": _id, "boundary": shapely.wkt.loads(nuc)})

        for ix, row in self._mdf[self._ix].iterrows():
            _id = row["id"]
            nucl_bnd = shapely.wkt.loads(row["nuc_pix"])
            cell_bnd = shapely.wkt.loads(row["cell_pix"])
            x0, y0, xf, yf = [int(u) for u in nucl_bnd.bounds]

            valid_sample, reason = m.is_valid_sample(frame, cell_bnd, nucl_bnd,
                                                     nuclei)
            if not valid_sample: continue
            logger.debug("particle_in_cytoplasm for cell id %d" % _id)

            centr_crop = image[y0:yf, x0:xf]
            logger.info('applying centrosome algorithm for nuclei %d' % _id)

            # load boundaries of um space for dataframe construction
            n_bum = shapely.wkt.loads(row["nucleus"])
            c_bum = shapely.wkt.loads(row["cell"])

            cntr = m.centrosomes(centr_crop,
                                 min_size=0.2 * self.pix_per_um,
                                 max_size=0.5 * self.pix_per_um,
                                 threshold=0.01)
            cntr[:, 0] += x0
            cntr[:, 1] += y0
            cntrsmes = list()
            for k, c in enumerate(cntr):
                pt = Point(c[0], c[1])
                pti = m.integral_over_surface(image,
                                              pt.buffer(1 * self.pix_per_um))
                cntrsmes.append({
                    'id':
                    k,
                    'pt':
                    Point(c[0] / self.pix_per_um, c[1] / self.pix_per_um),
                    'i':
                    pti
                })
                cntrsmes = sorted(cntrsmes,
                                  key=lambda ki: ki['i'],
                                  reverse=True)

            logger.debug('found {:d} centrosomes'.format(len(cntrsmes)))

            twocntr = len(cntrsmes) >= 2
            c1 = cntrsmes[0] if len(cntrsmes) > 0 else None
            c2 = cntrsmes[1] if twocntr else None

            lc = 2 if c2 is not None else 1 if c1 is not None else np.nan
            # TODO: Add units support
            self._mdf.loc[ix, 'centrosomes'] = lc
            self._mdf.loc[ix, 'c1'] = c1['pt'].wkt if c1 is not None else None
            self._mdf.loc[ix, 'c2'] = c2['pt'].wkt if c2 is not None else None
            self._mdf.loc[ix, 'c1_int'] = c1['i'] if c1 is not None else np.nan
            self._mdf.loc[ix, 'c2_int'] = c2['i'] if c2 is not None else np.nan
            self._mdf.loc[ix, 'c1_d_nuc_centr'] = n_bum.centroid.distance(
                c1['pt']) if c1 is not None else np.nan
            self._mdf.loc[ix, 'c2_d_nuc_centr'] = n_bum.centroid.distance(
                c2['pt']) if twocntr else np.nan
            self._mdf.loc[ix, 'c1_d_nuc_bound'] = n_bum.exterior.distance(
                c1['pt']) if c1 is not None else np.nan
            self._mdf.loc[ix, 'c2_d_nuc_bound'] = n_bum.exterior.distance(
                c2['pt']) if twocntr else np.nan
            self._mdf.loc[ix, 'c1_d_cell_centr'] = c_bum.centroid.distance(
                c1['pt']) if c1 is not None else np.nan
            self._mdf.loc[ix, 'c2_d_cell_centr'] = c_bum.centroid.distance(
                c2['pt']) if twocntr else np.nan
            self._mdf.loc[ix, 'c1_d_cell_bound'] = c_bum.exterior.distance(
                c1['pt']) if c1 is not None else np.nan
            self._mdf.loc[ix, 'c2_d_cell_bound'] = c_bum.exterior.distance(
                c2['pt']) if twocntr else np.nan
            self._mdf.loc[ix,
                          'nuc_centr_d_cell_centr'] = n_bum.centroid.distance(
                              c_bum.centroid)
            self._mdf.loc[ix, 'c1_d_c2'] = c1['pt'].distance(
                c2['pt']) if twocntr else np.nan
    def _measure_cells(self, cell_img, cfg):
        assert self._ix.any(), "no rows in the filtered dataframe"
        # generate an image based on nuclei found previously
        nuclei_img = np.zeros(cell_img.shape, dtype=np.bool)
        nuclei = list()
        for _id, nuc in self._mdf.set_index("id").loc[self._ix,
                                                      "nuc_pix"].iteritems():
            _nimg = m.generate_mask_from(nuc, cell_img.shape)
            nuclei_img = nuclei_img | _nimg
            nuclei.append({"id": _id, "boundary": shapely.wkt.loads(nuc)})
        cells, cells_mask = m.cell_boundary(cell_img, nuclei_img)

        #  filter polygons contained in others
        cells = m.exclude_contained(cells)
        logger.debug("%d cells found in image" % len(cells))

        width, height = cell_img.shape
        frame = Polygon([(0, 0), (0, height), (width, height), (width, 0)])
        touching_fr = too_big = no_nuclei = two_nuclei = 0
        # iterate through all cells
        for cl in cells:
            logger.debug("processing cell id %d" % cl['id'])
            cell_bnd = (cl['boundary'].buffer(self.pix_per_um,
                                              join_style=1).buffer(
                                                  -self.pix_per_um,
                                                  join_style=1).simplify(
                                                      self.pix_per_um / 2,
                                                      preserve_topology=True))
            logger.debug("previous simplify %d, after %d" %
                         (len(cl['boundary'].exterior.coords),
                          len(cell_bnd.exterior.coords)))

            for _id, nucleus in self._mdf.loc[self._ix, "nuc_pix"]:
                valid_sample, reason = m.is_valid_sample(
                    frame, cell_bnd, nucleus, nuclei)
                if reason == m.REJECTION_TOUCHING_FRAME: touching_fr += 1
                if reason == m.REJECTION_NO_NUCLEUS: no_nuclei += 1
                if reason == m.REJECTION_TWO_NUCLEI: two_nuclei += 1
                if reason == m.REJECTION_CELL_TOO_BIG: too_big += 1
                if valid_sample:
                    tubulin_int = m.integral_over_surface(cell_img, cell_bnd)
                    tub_density = tubulin_int / cell_bnd.area
                    if tub_density < 150:
                        logger.warning(
                            "Sample rejected after validation because it had a low tubulin density."
                        )
                        logger.debug(
                            'tubulin density in cell: %0.2f, intensity %0.2f, area %0.2f'
                            % (tub_density, tubulin_int, cell_bnd.area))
                        continue

                    # convert everything to um space for dataframe construction
                    c_bum = affinity.scale(cell_bnd,
                                           xfact=self.um_per_pix,
                                           yfact=self.um_per_pix,
                                           origin=(0, 0, 0))

                    # TODO: Add units support
                    ix = self._ix & (self._mdf['id'] == nucleus['id'])
                    self._mdf.loc[ix, 'tubulin_int'] = int(tubulin_int)
                    self._mdf.loc[ix, 'tubulin_dens'] = int(tubulin_int /
                                                            cell_bnd.area)
                    self._mdf.loc[ix, 'cell'] = dumps(c_bum,
                                                      rounding_precision=4)
                    self._mdf.loc[ix, 'cell_pix'] = dumps(cell_bnd,
                                                          rounding_precision=1)

        logger.info(
            "%d samples rejected because they were touching the frame" %
            touching_fr)
        logger.info("%d samples rejected because cell didn't have a nucleus" %
                    no_nuclei)
        logger.info(
            "%d samples rejected because cell had more than two nuclei" %
            two_nuclei)
        logger.info("%d samples rejected because cell area was too big" %
                    too_big)
        if (~self._mdf.loc[self._ix, "cell"].isna()).any():
            self.cells_measured = True