Ejemplo n.º 1
0
    parser.add_argument(
        '--cytomine_id_tags_for_images',
        help=
        "List of tags (id), separated by comma, that images must have to be used in dataset. "
        "If unset, all images in the project are used.",
        default=None)
    parser.add_argument('--cytomine_id_job')
    params, _ = parser.parse_known_args(sys.argv[1:])

    with Cytomine(params.cytomine_host, params.cytomine_public_key,
                  params.cytomine_private_key) as c:
        id_tags_for_images = params.cytomine_id_tags_for_images
        id_project = params.cytomine_id_project

        image_tags = id_tags_for_images if id_tags_for_images else None
        images = ImageInstanceCollection(tags=image_tags).fetch_with_filter(
            "project", id_project)
        image_ids = [image.id for image in images]

        groundtruths = AnnotationCollection()
        groundtruths.showTerm = True
        groundtruths.showWKT = True
        groundtruths.images = image_ids
        groundtruths.fetch()

        predictions = AnnotationCollection()
        predictions.showTerm = True
        predictions.showWKT = True
        predictions.images = image_ids
        predictions.job = params.cytomine_id_job
        predictions.fetch()
def main(argv):
    with CytomineJob.from_cli(argv) as conn:
        conn.job.update(status=Job.RUNNING,
                        progress=0,
                        statusComment="Initialization...")
        base_path = os.getenv("HOME")  # Mandatory for Singularity
        working_path = os.path.join(base_path, str(conn.job.id))

        # Loading pre-trained Stardist model
        np.random.seed(17)

        # use local model file in ~/models/2D_versatile_HE/
        model = StarDist2D(None, name='2D_versatile_HE', basedir='/models/')

        # Select images to process
        images = ImageInstanceCollection().fetch_with_filter(
            "project", conn.parameters.cytomine_id_project)

        if conn.parameters.cytomine_id_images == 'all':
            list_imgs = [int(image.id) for image in images]
        else:
            list_imgs = [
                int(id_img)
                for id_img in conn.parameters.cytomine_id_images.split(',')
            ]

        # Go over images
        for id_image in conn.monitor(list_imgs,
                                     prefix="Running detection on image",
                                     period=0.1):
            # Dump ROI annotations in img from Cytomine server to local images
            roi_annotations = AnnotationCollection(
                project=conn.parameters.cytomine_id_project,
                term=conn.parameters.cytomine_id_roi_term,
                image=id_image,
                showWKT=True).fetch()

            print(roi_annotations)

            # Go over ROI in this image
            for roi in roi_annotations:
                # Get Cytomine ROI coordinates for remapping to whole-slide
                # Cytomine cartesian coordinate system, (0,0) is bottom left corner
                print(
                    "----------------------------ROI------------------------------"
                )
                roi_geometry = wkt.loads(roi.location)
                print(f"ROI Geometry from Shapely: {roi_geometry}")
                print("ROI Bounds")
                print(roi_geometry.bounds)

                minx, miny = roi_geometry.bounds[0], roi_geometry.bounds[3]

                # Dump ROI image into local PNG file
                roi_path = os.path.join(working_path,
                                        str(roi_annotations.project),
                                        str(roi_annotations.image),
                                        str(roi.id))
                roi_png_filename = os.path.join(roi_path, f'{roi.id}.png')
                print(f"roi_png_filename: {roi_png_filename}")
                roi.dump(dest_pattern=roi_png_filename, mask=True, alpha=True)

                # Stardist works with TIFF images without alpha channel, flattening PNG alpha mask to TIFF RGB
                im = Image.open(roi_png_filename)
                bg = Image.new("RGB", im.size, (255, 255, 255))
                bg.paste(im, mask=im.split()[3])

                roi_tif_filename = os.path.join(roi_path, f'{roi.id}.tif')
                bg.save(roi_tif_filename, quality=100)

                X_files = sorted(glob(os.path.join(roi_path,
                                                   f'{roi.id}*.tif')))
                X = list(map(imread, X_files))
                n_channel = 3 if X[0].ndim == 3 else X[0].shape[-1]
                axis_norm = (
                    0, 1
                )  # normalize channels independently  (0,1,2) normalize channels jointly
                if n_channel > 1:
                    type = 'jointly' if axis_norm is None or 2 in axis_norm else 'independently'
                    print(f"Normalizing image channels {type}.")

                # Going over ROI images in ROI directory (in our case: one ROI per directory)
                for x in range(0, len(X)):
                    print(
                        f"------------------- Processing ROI file {X}: {roi_tif_filename}"
                    )
                    img = normalize(X[x],
                                    conn.parameters.stardist_norm_perc_low,
                                    conn.parameters.stardist_norm_perc_high,
                                    axis=axis_norm)
                    # Stardist model prediction with thresholds
                    labels, details = model.predict_instances(
                        img,
                        prob_thresh=conn.parameters.stardist_prob_t,
                        nms_thresh=conn.parameters.stardist_nms_t)

                    print("Number of detected polygons: %d" %
                          len(details['coord']))

                    cytomine_annotations = AnnotationCollection()
                    # Go over detections in this ROI, convert and upload to Cytomine
                    for polygroup in details['coord']:
                        # Converting to Shapely annotation
                        points = list()
                        for i in range(len(polygroup[0])):
                            # Cytomine cartesian coordinate system, (0,0) is bottom left corner
                            # Mapping Stardist polygon detection coordinates to Cytomine ROI in whole slide image
                            p = Point(minx + polygroup[1][i],
                                      miny - polygroup[0][i])
                            points.append(p)

                        annotation = Polygon(points)
                        # Append to Annotation collection
                        cytomine_annotations.append(
                            Annotation(
                                location=annotation.wkt,
                                id_image=
                                id_image,  # conn.parameters.cytomine_id_image,
                                id_project=conn.parameters.cytomine_id_project,
                                id_terms=[
                                    conn.parameters.cytomine_id_cell_term
                                ]))
                        print(".", end='', flush=True)
                    print()

                    # Send Annotation Collection (for this ROI) to Cytomine server in one http request
                    cytomine_annotations.save()

        conn.job.update(status=Job.TERMINATED,
                        progress=100,
                        statusComment="Finished.")
Ejemplo n.º 3
0
def main(argv):
    with CytomineJob.from_cli(argv) as cj:

        images = ImageInstanceCollection().fetch_with_filter(
            "project", cj.parameters.cytomine_id_project)
        for image in cj.monitor(images,
                                prefix="Running detection on image",
                                period=0.1):
            # Resize image if needed
            resize_ratio = max(image.width,
                               image.height) / cj.parameters.max_image_size
            if resize_ratio < 1:
                resize_ratio = 1

            resized_width = int(image.width / resize_ratio)
            resized_height = int(image.height / resize_ratio)

            image.dump(dest_pattern="/tmp/{id}.jpg",
                       max_size=max(resized_width, resized_height),
                       bits=image.bitDepth)
            img = cv2.imread(image.filename, cv2.IMREAD_GRAYSCALE)

            thresholded_img = cv2.adaptiveThreshold(
                img, 2**image.bitDepth, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                cv2.THRESH_BINARY, cj.parameters.threshold_blocksize,
                cj.parameters.threshold_constant)

            kernel = np.ones((5, 5), np.uint8)
            eroded_img = cv2.erode(thresholded_img,
                                   kernel,
                                   iterations=cj.parameters.erode_iterations)
            dilated_img = cv2.dilate(
                eroded_img, kernel, iterations=cj.parameters.dilate_iterations)

            extension = 10
            extended_img = cv2.copyMakeBorder(dilated_img,
                                              extension,
                                              extension,
                                              extension,
                                              extension,
                                              cv2.BORDER_CONSTANT,
                                              value=2**image.bitDepth)

            components = find_components(extended_img)
            zoom_factor = image.width / float(resized_width)
            for i, component in enumerate(components):
                converted = []
                for point in component[0]:
                    x = int((point[0] - extension) * zoom_factor)
                    y = int(image.height -
                            ((point[1] - extension) * zoom_factor))
                    converted.append((x, y))

                components[i] = Polygon(converted)

            # Find largest component (whole image)
            largest = max(components, key=attrgetter('area'))
            components.remove(largest)

            # Only keep components greater than 5% of whole image
            min_area = int(0.05 * image.width * image.height)

            annotations = AnnotationCollection()
            for component in components:
                if component.area > min_area:
                    annotations.append(
                        Annotation(
                            location=component.wkt,
                            id_image=image.id,
                            id_terms=[
                                cj.parameters.cytomine_id_predicted_term
                            ],
                            id_project=cj.parameters.cytomine_id_project))

                    if len(annotations) % 100 == 0:
                        annotations.save()
                        annotations = AnnotationCollection()

            annotations.save()

        cj.job.update(statusComment="Finished.")
    def run(self):
        self.super_admin = Cytomine.get_instance().current_user
        connect_as(self.super_admin, True)

        users = UserCollection().fetch()
        users_json = [
            f for f in os.listdir(self.working_path)
            if f.endswith(".json") and f.startswith("user-collection")
        ][0]
        remote_users = UserCollection()
        for u in json.load(open(os.path.join(self.working_path, users_json))):
            remote_users.append(User().populate(u))

        roles = ["project_manager", "project_contributor", "ontology_creator"]
        if self.with_images:
            roles += ["image_creator", "image_reviewer"]

        if self.with_userannotations:
            roles += ["userannotation_creator", "userannotationterm_creator"]

        roles = set(roles)
        remote_users = [
            u for u in remote_users
            if len(roles.intersection(set(u.roles))) > 0
        ]

        for remote_user in remote_users:
            user = find_first(
                [u for u in users if u.username == remote_user.username])
            if not user:
                user = copy.copy(remote_user)
                if not user.password:
                    user.password = random_string(8)
                if not self.with_original_date:
                    user.created = None
                    user.updated = None
                user.save()
            self.id_mapping[remote_user.id] = user.id

        # --------------------------------------------------------------------------------------------------------------
        logging.info("1/ Import ontology and terms")
        """
        Import the ontology with terms and relation terms that are stored in pickled files in working_path.
        If the ontology exists (same name and same terms), the existing one is used.
        Otherwise, an ontology with an available name is created with new terms and corresponding relationships.
        """
        ontologies = OntologyCollection().fetch()
        ontology_json = [
            f for f in os.listdir(self.working_path)
            if f.endswith(".json") and f.startswith("ontology")
        ][0]
        remote_ontology = Ontology().populate(
            json.load(open(os.path.join(self.working_path, ontology_json))))
        remote_ontology.name = remote_ontology.name.strip()

        terms = TermCollection().fetch()
        terms_json = [
            f for f in os.listdir(self.working_path)
            if f.endswith(".json") and f.startswith("term-collection")
        ]
        remote_terms = TermCollection()
        if len(terms_json) > 0:
            for t in json.load(
                    open(os.path.join(self.working_path, terms_json[0]))):
                remote_terms.append(Term().populate(t))

        def ontology_exists():
            compatible_ontology = find_first([
                o for o in ontologies
                if o.name == remote_ontology.name.strip()
            ])
            if compatible_ontology:
                set1 = set((t.name, t.color) for t in terms
                           if t.ontology == compatible_ontology.id)
                difference = [
                    term for term in remote_terms
                    if (term.name, term.color) not in set1
                ]
                if len(difference) == 0:
                    return True, compatible_ontology
                return False, None
            else:
                return True, None

        i = 1
        remote_name = remote_ontology.name
        found, existing_ontology = ontology_exists()
        while not found:
            remote_ontology.name = "{} ({})".format(remote_name, i)
            found, existing_ontology = ontology_exists()
            i += 1

        # SWITCH to ontology creator user
        connect_as(User().fetch(self.id_mapping[remote_ontology.user]))
        if not existing_ontology:
            ontology = copy.copy(remote_ontology)
            ontology.user = self.id_mapping[remote_ontology.user]
            if not self.with_original_date:
                ontology.created = None
                ontology.updated = None
            ontology.save()
            self.id_mapping[remote_ontology.id] = ontology.id
            logging.info("Ontology imported: {}".format(ontology))

            for remote_term in remote_terms:
                logging.info("Importing term: {}".format(remote_term))
                term = copy.copy(remote_term)
                term.ontology = self.id_mapping[term.ontology]
                term.parent = None
                if not self.with_original_date:
                    term.created = None
                    term.updated = None
                term.save()
                self.id_mapping[remote_term.id] = term.id
                logging.info("Term imported: {}".format(term))

            remote_relation_terms = [(term.parent, term.id)
                                     for term in remote_terms]
            for relation in remote_relation_terms:
                parent, child = relation
                if parent:
                    rt = RelationTerm(self.id_mapping[parent],
                                      self.id_mapping[child]).save()
                    logging.info("Relation term imported: {}".format(rt))
        else:
            self.id_mapping[remote_ontology.id] = existing_ontology.id

            ontology_terms = [
                t for t in terms if t.ontology == existing_ontology.id
            ]
            for remote_term in remote_terms:
                self.id_mapping[remote_term.id] = find_first([
                    t for t in ontology_terms if t.name == remote_term.name
                ]).id

            logging.info(
                "Ontology already encoded: {}".format(existing_ontology))

        # SWITCH USER
        connect_as(self.super_admin, True)

        # --------------------------------------------------------------------------------------------------------------
        logging.info("2/ Import project")
        """
        Import the project (i.e. the Cytomine Project domain) stored in pickled file in working_path.
        If a project with the same name already exists, append a (x) suffix where x is an increasing number.
        """
        projects = ProjectCollection().fetch()
        project_json = [
            f for f in os.listdir(self.working_path)
            if f.endswith(".json") and f.startswith("project")
        ][0]
        remote_project = Project().populate(
            json.load(open(os.path.join(self.working_path, project_json))))
        remote_project.name = remote_project.name.strip()

        def available_name():
            i = 1
            existing_names = [o.name for o in projects]
            new_name = project.name
            while new_name in existing_names:
                new_name = "{} ({})".format(project.name, i)
                i += 1
            return new_name

        project = copy.copy(remote_project)
        project.name = available_name()
        project.discipline = None
        project.ontology = self.id_mapping[project.ontology]
        project_contributors = [
            u for u in remote_users if "project_contributor" in u.roles
        ]
        project.users = [self.id_mapping[u.id] for u in project_contributors]
        project_managers = [
            u for u in remote_users if "project_manager" in u.roles
        ]
        project.admins = [self.id_mapping[u.id] for u in project_managers]
        if not self.with_original_date:
            project.created = None
            project.updated = None
        project.save()
        self.id_mapping[remote_project.id] = project.id
        logging.info("Project imported: {}".format(project))

        # --------------------------------------------------------------------------------------------------------------
        logging.info("3/ Import images")
        storages = StorageCollection().fetch()
        abstract_images = AbstractImageCollection().fetch()
        images_json = [
            f for f in os.listdir(self.working_path)
            if f.endswith(".json") and f.startswith("imageinstance-collection")
        ]
        remote_images = ImageInstanceCollection()
        if len(images_json) > 0:
            for i in json.load(
                    open(os.path.join(self.working_path, images_json[0]))):
                remote_images.append(ImageInstance().populate(i))

        remote_images_dict = {}

        for remote_image in remote_images:
            image = copy.copy(remote_image)

            # Fix old image name due to urllib3 limitation
            remote_image.originalFilename = bytes(
                remote_image.originalFilename,
                'utf-8').decode('ascii', 'ignore')
            if remote_image.originalFilename not in remote_images_dict.keys():
                remote_images_dict[remote_image.originalFilename] = [
                    remote_image
                ]
            else:
                remote_images_dict[remote_image.originalFilename].append(
                    remote_image)
            logging.info("Importing image: {}".format(remote_image))

            # SWITCH user to image creator user
            connect_as(User().fetch(self.id_mapping[remote_image.user]))
            # Get its storage
            storage = find_first([
                s for s in storages
                if s.user == Cytomine.get_instance().current_user.id
            ])
            if not storage:
                storage = storages[0]

            # Check if image is already in its storage
            abstract_image = find_first([
                ai for ai in abstract_images
                if ai.originalFilename == remote_image.originalFilename and
                ai.width == remote_image.width and ai.height == remote_image.
                height and ai.resolution == remote_image.resolution
            ])
            if abstract_image:
                logging.info(
                    "== Found corresponding abstract image. Linking to project."
                )
                ImageInstance(abstract_image.id,
                              self.id_mapping[remote_project.id]).save()
            else:
                logging.info("== New image starting to upload & deploy")
                filename = os.path.join(
                    self.working_path, "images",
                    image.originalFilename.replace("/", "-"))
                Cytomine.get_instance().upload_image(
                    self.host_upload, filename, storage.id,
                    self.id_mapping[remote_project.id])
                time.sleep(0.8)

            # SWITCH USER
            connect_as(self.super_admin, True)

        # Waiting for all images...
        n_new_images = -1
        new_images = None
        count = 0
        while n_new_images != len(
                remote_images) and count < len(remote_images) * 5:
            new_images = ImageInstanceCollection().fetch_with_filter(
                "project", self.id_mapping[remote_project.id])
            n_new_images = len(new_images)
            if count > 0:
                time.sleep(5)
            count = count + 1
        print("All images have been deployed. Fixing image-instances...")

        # Fix image instances meta-data:
        for new_image in new_images:
            remote_image = remote_images_dict[new_image.originalFilename].pop()
            if self.with_original_date:
                new_image.created = remote_image.created
                new_image.updated = remote_image.updated
            new_image.reviewStart = remote_image.reviewStart if hasattr(
                remote_image, 'reviewStart') else None
            new_image.reviewStop = remote_image.reviewStop if hasattr(
                remote_image, 'reviewStop') else None
            new_image.reviewUser = self.id_mapping[
                remote_image.reviewUser] if hasattr(
                    remote_image,
                    'reviewUser') and remote_image.reviewUser else None
            new_image.instanceFilename = remote_image.instanceFilename
            new_image.update()
            self.id_mapping[remote_image.id] = new_image.id
            self.id_mapping[remote_image.baseImage] = new_image.baseImage

            new_abstract = AbstractImage().fetch(new_image.baseImage)
            if self.with_original_date:
                new_abstract.created = remote_image.created
                new_abstract.updated = remote_image.updated
            if new_abstract.resolution is None:
                new_abstract.resolution = remote_image.resolution
            if new_abstract.magnification is None:
                new_abstract.magnification = remote_image.magnification
            new_abstract.update()

        print("All image-instances have been fixed.")

        # --------------------------------------------------------------------------------------------------------------
        logging.info("4/ Import user annotations")
        annots_json = [
            f for f in os.listdir(self.working_path) if f.endswith(".json")
            and f.startswith("user-annotation-collection")
        ]
        remote_annots = AnnotationCollection()
        if len(annots_json) > 0:
            for a in json.load(
                    open(os.path.join(self.working_path, annots_json[0]))):
                remote_annots.append(Annotation().populate(a))

        def _add_annotation(remote_annotation, id_mapping, with_original_date):
            if remote_annotation.project not in id_mapping.keys() \
                    or remote_annotation.image not in id_mapping.keys():
                return

            annotation = copy.copy(remote_annotation)
            annotation.project = id_mapping[remote_annotation.project]
            annotation.image = id_mapping[remote_annotation.image]
            annotation.user = id_mapping[remote_annotation.user]
            annotation.term = [id_mapping[t] for t in remote_annotation.term]
            if not with_original_date:
                annotation.created = None
                annotation.updated = None
            annotation.save()

        for user in [
                u for u in remote_users if "userannotation_creator" in u.roles
        ]:
            remote_annots_for_user = [
                a for a in remote_annots if a.user == user.id
            ]
            # SWITCH to annotation creator user
            connect_as(User().fetch(self.id_mapping[user.id]))
            Parallel(n_jobs=-1, backend="threading")(
                delayed(_add_annotation)(remote_annotation, self.id_mapping,
                                         self.with_original_date)
                for remote_annotation in remote_annots_for_user)

            # SWITCH back to admin
            connect_as(self.super_admin, True)

        # --------------------------------------------------------------------------------------------------------------
        logging.info(
            "5/ Import metadata (properties, attached files, description)")
        obj = Model()
        obj.id = -1
        obj.class_ = ""

        properties_json = [
            f for f in os.listdir(self.working_path)
            if f.endswith(".json") and f.startswith("properties")
        ]
        for property_json in properties_json:
            for remote_prop in json.load(
                    open(os.path.join(self.working_path, property_json))):
                prop = Property(obj).populate(remote_prop)
                prop.domainIdent = self.id_mapping[prop.domainIdent]
                prop.save()

        attached_files_json = [
            f for f in os.listdir(self.working_path)
            if f.endswith(".json") and f.startswith("attached-files")
        ]
        for attached_file_json in attached_files_json:
            for remote_af in json.load(
                    open(os.path.join(self.working_path, attached_file_json))):
                af = AttachedFile(obj).populate(remote_af)
                af.domainIdent = self.id_mapping[af.domainIdent]
                af.filename = os.path.join(self.working_path, "attached_files",
                                           remote_af.filename)
                af.save()

        descriptions_json = [
            f for f in os.listdir(self.working_path)
            if f.endswith(".json") and f.startswith("description")
        ]
        for description_json in descriptions_json:
            desc = Description(obj).populate(
                json.load(
                    open(os.path.join(self.working_path, description_json))))
            desc.domainIdent = self.id_mapping[desc.domainIdent]
            desc._object.class_ = desc.domainClassName
            desc._object.id = desc.domainIdent
            desc.save()
def main(argv):
    # 0. Initialize Cytomine client and job
    with CytomineJob.from_cli(argv) as cj:
        cj.job.update(status=Job.RUNNING,
                      progress=0,
                      statusComment="Initialisation...")

        # 1. Create working directories on the machine:
        # - WORKING_PATH/in: input images
        # - WORKING_PATH/out: output images
        # - WORKING_PATH/ground_truth: ground truth images
        # - WORKING_PATH/tmp: temporary path
        base_path = "{}".format(os.getenv("HOME"))
        gt_suffix = "_lbl"
        working_path = os.path.join(base_path, str(cj.job.id))
        in_path = os.path.join(working_path, "in")
        out_path = os.path.join(working_path, "out")
        gt_path = os.path.join(working_path, "ground_truth")
        tmp_path = os.path.join(working_path, "tmp")

        if not os.path.exists(working_path):
            os.makedirs(working_path)
            os.makedirs(in_path)
            os.makedirs(out_path)
            os.makedirs(gt_path)
            os.makedirs(tmp_path)

        # 2. Download the images (first input, then ground truth image)
        cj.job.update(
            progress=1,
            statusComment="Downloading images (to {})...".format(in_path))
        image_instances = ImageInstanceCollection().fetch_with_filter(
            "project", cj.parameters.cytomine_id_project)
        input_images = [
            i for i in image_instances if gt_suffix not in i.originalFilename
        ]
        gt_images = [
            i for i in image_instances if gt_suffix in i.originalFilename
        ]

        for input_image in input_images:
            input_image.download(os.path.join(in_path, "{id}.tif"))

        for gt_image in gt_images:
            related_name = gt_image.originalFilename.replace(gt_suffix, '')
            related_image = [
                i for i in input_images if related_name == i.originalFilename
            ]
            if len(related_image) == 1:
                gt_image.download(
                    os.path.join(gt_path,
                                 "{}.tif".format(related_image[0].id)))

        # 3. Call the image analysis workflow using the run script
        cj.job.update(progress=25, statusComment="Launching workflow...")

        # load data
        cj.job.update(progress=30, statusComment="Workflow: preparing data...")
        dims = (cj.parameters.image_height, cj.parameters.image_width,
                cj.parameters.n_channels)
        mask_dims = (dims[0], dims[1], cj.parameters.n_classes)

        # load input images
        imgs = load_data(
            cj, dims, in_path, **{
                "start": 35,
                "end": 45,
                "period": 0.1,
                "prefix": "Workflow: load training input images"
            })
        train_mean = np.mean(imgs)
        train_std = np.std(imgs)
        imgs -= train_mean
        imgs /= train_std

        # load masks
        masks = load_data(cj,
                          mask_dims,
                          gt_path,
                          dtype=np.int,
                          is_masks=True,
                          n_classes=cj.parameters.n_classes,
                          **{
                              "start": 45,
                              "end": 55,
                              "period": 0.1,
                              "prefix": "Workflow: load training masks images"
                          })

        cj.job.update(progress=56, statusComment="Workflow: build model...")
        unet = create_unet(dims, n_classes=cj.parameters.n_classes)
        unet.compile(optimizer=Adam(lr=cj.parameters.learning_rate),
                     loss='binary_crossentropy')

        cj.job.update(progress=60,
                      statusComment="Workflow: prepare training...")
        datagen = ImageDataGenerator(
            rotation_range=cj.parameters.aug_rotation,
            width_shift_range=cj.parameters.aug_width_shift,
            height_shift_range=cj.parameters.aug_height_shift,
            shear_range=cj.parameters.aug_shear_range,
            horizontal_flip=cj.parameters.aug_hflip,
            vertical_flip=cj.parameters.aug_vflip)

        weight_filepath = os.path.join(tmp_path, 'weights.hdf5')
        callbacks = [
            ModelCheckpoint(weight_filepath,
                            monitor='loss',
                            save_best_only=True)
        ]

        cj.job.update(progress=65, statusComment="Workflow: train...")
        unet.fit_generator(datagen.flow(imgs,
                                        masks,
                                        batch_size=cj.parameters.batch_size,
                                        seed=42),
                           steps_per_epoch=math.ceil(imgs.shape[0] /
                                                     cj.parameters.batch_size),
                           epochs=cj.parameters.epochs,
                           callbacks=callbacks)

        # save model and metadata
        cj.job.update(progress=85, statusComment="Save model...")
        AttachedFile(cj.job,
                     domainIdent=cj.job.id,
                     filename=weight_filepath,
                     domainClassName="be.cytomine.processing.Job").upload()

        cj.job.update(progress=90, statusComment="Save metadata...")
        Property(cj.job, key="image_width",
                 value=cj.parameters.image_width).save()
        Property(cj.job, key="image_height",
                 value=cj.parameters.image_height).save()
        Property(cj.job, key="n_channels",
                 value=cj.parameters.n_channels).save()
        Property(cj.job, key="train_mean", value=float(train_mean)).save()
        Property(cj.job, key="image_width", value=float(train_std)).save()

        cj.job.update(status=Job.TERMINATED,
                      progress=100,
                      statusComment="Finished.")
    def run(self):
        logging.info("Export will be done in directory {}".format(self.project_path))
        os.makedirs(self.project_path)

        if self.with_metadata or self.with_annotation_metadata:
            self.attached_file_path = os.path.join(self.project_path, "attached_files")
            os.makedirs(self.attached_file_path)

        # --------------------------------------------------------------------------------------------------------------
        logging.info("1/ Export project {}".format(self.project.id))
        self.save_object(self.project)

        logging.info("1.1/ Export project managers")
        admins = UserCollection(admin=True).fetch_with_filter("project", self.project.id)
        for admin in admins:
            self.save_user(admin, "project_manager")

        logging.info("1.2/ Export project contributors")
        users = UserCollection().fetch_with_filter("project", self.project.id)
        for user in users:
            self.save_user(user, "project_contributor")

        if self.with_metadata:
            logging.info("1.3/ Export project metadata")
            self.export_metadata([self.project])

        # --------------------------------------------------------------------------------------------------------------
        logging.info("2/ Export ontology {}".format(self.project.ontology))
        ontology = Ontology().fetch(self.project.ontology)
        self.save_object(ontology)

        logging.info("2.1/ Export ontology creator")
        user = User().fetch(ontology.user)
        self.save_user(user, "ontology_creator")

        if self.with_metadata:
            logging.info("2.2/ Export ontology metadata")
            self.export_metadata([ontology])

        # --------------------------------------------------------------------------------------------------------------
        logging.info("3/ Export terms")
        terms = TermCollection().fetch_with_filter("project", self.project.id)
        self.save_object(terms)

        if self.with_metadata:
            logging.info("3.1/ Export term metadata")
            self.export_metadata(terms)

        # --------------------------------------------------------------------------------------------------------------
        logging.info("4/ Export images")
        images = ImageInstanceCollection().fetch_with_filter("project", self.project.id)
        self.save_object(images)

        if self.with_image_download:
            image_path = os.path.join(self.project_path, "images")
            os.makedirs(image_path)

            def _download_image(image, path):
                logging.info("Download file for image {}".format(image))
                image.download(os.path.join(path, image.originalFilename), override=False, parent=True)

            # Temporary use threading as backend, as we need to connect to Cytomine in every other processes.
            Parallel(n_jobs=-1, backend="threading")(delayed(_download_image)(image, image_path) for image in images)

        logging.info("4.1/ Export image slices")
        slices = SliceInstanceCollection()
        for image in images:
            slices += SliceInstanceCollection().fetch_with_filter("imageinstance", image.id)
        self.save_object(slices)

        logging.info("4.2/ Export image creator users")
        image_users = set([image.user for image in images])
        for image_user in image_users:
            user = User().fetch(image_user)
            self.save_user(user, "image_creator")

        logging.info("4.3/ Export image reviewer users")
        image_users = set([image.reviewUser for image in images if image.reviewUser])
        for image_user in image_users:
            user = User().fetch(image_user)
            self.save_user(user, "image_reviewer")

        if self.with_metadata:
            logging.info("4.4/ Export image metadata")
            self.export_metadata(images)

        # --------------------------------------------------------------------------------------------------------------
        logging.info("4/ Export user annotations")
        user_annotations = AnnotationCollection(showWKT=True, showTerm=True, project=self.project.id).fetch()
        self.save_object(user_annotations, filename="user-annotation-collection")

        logging.info("4.1/ Export user annotation creator users")
        annotation_users = set([annotation.user for annotation in user_annotations])
        for annotation_user in annotation_users:
            user = User().fetch(annotation_user)
            self.save_user(user, "userannotation_creator")

        logging.info("4.2/ Export user annotation term creator users")
        annotation_users = set([annotation.userTerm for annotation in user_annotations if hasattr(annotation, "userTerm") and annotation.userTerm])
        for annotation_user in annotation_users:
            user = User().fetch(annotation_user)
            self.save_user(user, "userannotationterm_creator")

        if self.with_annotation_metadata:
            logging.info("4.3/ Export user annotation metadata")
            self.export_metadata(user_annotations)

        # --------------------------------------------------------------------------------------------------------------
        logging.info("5/ Export users")
        if self.anonymize:
            for i, user in enumerate(self.users):
                        user.username = "******".format(i + 1)
                        user.firstname = "Anonymized"
                        user.lastname = "User {}".format(i + 1)
                        user.email = "anonymous{}@unknown.com".format(i + 1)

        self.save_object(self.users)

        # Disabled due to core issue.
        # if self.with_metadata:
        #     logging.info("5.1/ Export user metadata")
        #     self.export_metadata(self.users)

        # --------------------------------------------------------------------------------------------------------------
        logging.info("Finished.")
def get_images_mask_per_annotation_per_user(proj_id, image_id, user_id,
                                            scale_factor, dest):
    im = ImageInstanceCollection()
    im.project = proj_id
    im.image = image_id
    im.fetch_with_filter("project", proj_id)
    image_width = int(im[0].width)
    image_height = int(im[0].height)
    print(image_height, image_width)

    annotations = AnnotationCollection()
    annotations.project = proj_id
    annotations.image = image_id

    annotations.user = user_id
    annotations.showWKT = True
    annotations.showMeta = True
    annotations.showTerm = True
    annotations.showGIS = True
    annotations.showImage = True
    annotations.showUser = True
    annotations.fetch()

    dct_anotations = {}
    for a in annotations:
        print(a.user)
        if len(a.term) == 1:
            term = a.term[0]
            if term not in dct_anotations:
                dct_anotations[term] = []
            dct_anotations[term].append(a.location)
        else:
            warnings.warn("Not suited for multiple or no annotation term")
    for t, lanno in dct_anotations.items():
        result_image = Image.new(mode='1',
                                 size=(int(image_width * scale_factor),
                                       int(image_height * scale_factor)),
                                 color=0)
        for pwkt in lanno:
            if pwkt.startswith("POLYGON"):
                label = "POLYGON"
            elif pwkt.startswith("MULTIPOLYGON"):
                label = "MULTIPOLYGON"

            coordinatesStringList = pwkt.replace(label, '')

            if label == "POLYGON":
                coordinates_string_lists = [coordinatesStringList]
            elif label == "MULTIPOLYGON":
                coordinates_string_lists = coordinatesStringList.split(
                    ')), ((')

                coordinates_string_lists = [
                    coordinatesStringList.replace('(', '').replace(')', '')
                    for coordinatesStringList in coordinates_string_lists
                ]

            for coordinatesStringList in coordinates_string_lists:
                #  create lists of x and y coordinates
                x_coords = []
                y_coords = []
                for point in coordinatesStringList.split(','):
                    point = point.strip(
                        string.whitespace)  # remove leading and ending spaces
                    point = point.strip(
                        string.punctuation
                    )  # Have seen some strings have a ')' at the end so remove it
                    x_coords.append(round(float(point.split(' ')[0])))
                    y_coords.append(round(float(point.split(' ')[1])))

                x_coords_correct_lod = [
                    int(x * scale_factor) for x in x_coords
                ]
                y_coords_correct_lod = [
                    image_height * scale_factor - int(x * scale_factor)
                    for x in y_coords
                ]
                coords = [
                    (i, j)
                    for i, j in zip(x_coords_correct_lod, y_coords_correct_lod)
                ]

                #  draw the polygone in an image and fill it
                ImageDraw.Draw(result_image).polygon(coords, outline=1, fill=1)

        result_image.save(params.dest + '/' + str(t) + '.png')
Ejemplo n.º 8
0
def preprocess(cytomine, working_path, id_project, id_terms=None, id_tags_for_images=None):
    """
    Get data from Cytomine in order to train YOLO.
    :param cytomine: The Cytomine client
    :param working_path: The path where files will be stored
    :param id_project: The Cytomine project ID used to get data
    :param id_terms: The Cytomine term IDS used to get data
    :param id_tags_for_images: The Cytomine tags IDS associated to images used to get data
    :return:
        classes_filename: The name of the file with classes
        image_filenames: A list of image filenames
        annotation_filenames: A list of filenames with annotations in YOLO format
    """
    if not os.path.exists(working_path):
        os.makedirs(working_path)

    images_path = os.path.join(working_path, IMG_DIRECTORY)
    if not os.path.exists(images_path):
        os.makedirs(images_path)

    annotations_path = os.path.join(working_path, ANNOTATION_DIRECTORY)
    if not os.path.exists(annotations_path):
        os.makedirs(annotations_path)

    terms = TermCollection().fetch_with_filter("project", id_project)
    if id_terms:
        filtered_term_ids = [int(id_term) for id_term in id_terms.split(',')]
        filtered_terms = [term for term in terms if term.id in filtered_term_ids]
    else:
        filtered_terms = terms
    terms_indexes = {term.id: i for i, term in enumerate(filtered_terms)}

    # https://github.com/eriklindernoren/PyTorch-YOLOv3#train-on-custom-dataset
    # Write obj.names
    classes_filename = os.path.join(working_path, CLASSES_FILENAME)
    with open(classes_filename, 'w') as f:
        for term in filtered_terms:
            f.write(term.name + os.linesep)

    # Download images
    image_filenames = []
    image_tags = id_tags_for_images if id_tags_for_images else None
    images = ImageInstanceCollection(tags=image_tags).fetch_with_filter("project", id_project)
    for image in images:
        image.dump(os.path.join(working_path, IMG_DIRECTORY, "{id}.png"), override=False)
        image_filenames.append(image.filename)

    # Create annotation files
    annotation_filenames = []
    for image in images:
        annotations = AnnotationCollection()
        annotations.image = image.id
        annotations.terms = [t.id for t in filtered_terms] if id_terms else None
        annotations.showWKT = True
        annotations.showTerm = True
        annotations.fetch()

        filename = os.path.join(working_path, ANNOTATION_DIRECTORY, "{}.txt".format(image.id))
        with open(filename, 'w') as f:
            for annotation in annotations:
                geometry = wkt.loads(annotation.location)
                x, y, w, h = geometry_to_yolo(geometry, image.width, image.height)
                for term_id in annotation.term:
                    # <object-class> <x_center> <y_center> <width> <height>
                    f.write("{} {:.12f} {:.12f} {:.12f} {:.12f}".format(terms_indexes[term_id], x, y, w, h) + os.linesep)
        annotation_filenames.append(filename)

    return classes_filename, image_filenames, annotation_filenames
Ejemplo n.º 9
0
def main(argv):
    with CytomineJob.from_cli(argv) as cj:
        cj.job.update(progress=1, statusComment="Initialisation")
        cj.log(str(cj.parameters))

        term_ids = [int(term_id) for term_id in cj.parameters.cytomine_id_terms.split(",")]
        terms = TermCollection().fetch_with_filter("project", cj.parameters.cytomine_id_project)
        terms = [term for term in terms if term.id in term_ids]

        image_ids = [int(image_id) for image_id in cj.parameters.cytomine_id_images.split(",")]
        images = ImageInstanceCollection(light=True).fetch_with_filter("project", cj.parameters.cytomine_id_project)
        images = [image for image in images if image.id in image_ids]

        if hasattr(cj.parameters, "cytomine_id_users") and cj.parameters.cytomine_id_users is not None:
            user_ids = [int(user_id) for user_id in cj.parameters.cytomine_id_users.split(",")]
        else:
            user_ids = []

        if hasattr(cj.parameters, "cytomine_id_jobs") and cj.parameters.cytomine_id_jobs is not None:
            job_ids = [int(job_id) for job_id in cj.parameters.cytomine_id_jobs.split(",")]
            jobs = JobCollection(project=cj.parameters.cytomine_id_project).fetch()
            jobs = [job for job in jobs if job.id in job_ids]
        else:
            jobs = []

        userjobs_ids = [job.userJob for job in jobs]
        all_user_ids = user_ids + userjobs_ids

        cj.job.update(progress=20, statusComment="Collect data")
        ac = AnnotationCollection()
        ac.terms = term_ids
        ac.images = image_ids
        ac.showMeta = True
        ac.showGIS = True
        ac.showTerm = True
        ac.reviewed = True if cj.parameters.cytomine_reviewed_only else None
        ac.users = all_user_ids if len(all_user_ids) > 0 else None
        ac.fetch()

        cj.job.update(progress=55, statusComment="Compute statistics")
        data = dict()
        for image in images:
            d = dict()
            areas = [a.area for a in ac if a.image == image.id]
            total_area = np.sum(areas)
            d['total'] = total_area
            d['count'] = len(areas)
            d['ratio'] = 1.0
            for term in terms:
                annotations = [a for a in ac if a.image == image.id and term.id in a.term]
                areas = [a.area for a in annotations]
                d[term.name] = dict()
                d[term.name]['total'] = np.sum(areas)
                d[term.name]['count'] = len(annotations)
                d[term.name]['ratio'] = d[term.name]['total'] / float(total_area) if total_area > 0 else 0
                d[term.name]['mean'] = np.mean(areas)
                d[term.name]['annotations'] = [{"created": a.created, "area": a.area} for a in annotations]
            data[image.instanceFilename] = d

        cj.job.update(progress=90, statusComment="Write CSV report")
        with open("stat-area.csv", "w") as f:
            for l in write_csv(data, terms):
                f.write("{}\n".format(l))

        job_data = JobData(id_job=cj.job.id, key="Area CSV report", filename="stat-area.csv")
        job_data = job_data.save()
        job_data.upload("stat-area.csv")
        
        cj.job.update(statusComment="Finished.", progress=100)
Ejemplo n.º 10
0
def main(argv):
    with CytomineJob.from_cli(argv) as conn:
        conn.job.update(status=Job.RUNNING, progress=0, statusComment='Intialization...')
        base_path = "{}".format(os.getenv('HOME'))  # Mandatory for Singularity
        working_path = os.path.join(base_path, str(conn.job.id))

        # Loading models from models directory
        with tf.device('/cpu:0'):
            h_model = load_model('/models/head_dice_sm_9976.hdf5', compile=False)  # head model
            h_model.compile(optimizer='adam', loss=dice_coef_loss,
                            metrics=['accuracy'])
            op_model = load_model('/models/op_ce_sm_9991.hdf5', compile=True)  # operculum model
            #op_model.compile(optimizer='adam', loss=dice_coef_loss,
                            #metrics=['accuracy'])

        # Select images to process
        images = ImageInstanceCollection().fetch_with_filter('project', conn.parameters.cytomine_id_project)
        if conn.parameters.cytomine_id_images != 'all':  # select only given image instances = [image for image in image_instances if image.id in id_list]
            images = [_ for _ in images if _.id
                      in map(lambda x: int(x.strip()),
                             conn.parameters.cytomine_id_images.split(','))]
        images_id = [image.id for image in images]

        # Download selected images into 'working_directory'
        img_path = os.path.join(working_path, 'images')
        # if not os.path.exists(img_path):
        os.makedirs(img_path)

        for image in conn.monitor(
                images, start=2, end=50, period=0.1,
                prefix='Downloading images into working directory...'):
            fname, fext = os.path.splitext(image.filename)
            if image.download(dest_pattern=os.path.join(
                    img_path,
                    "{}{}".format(image.id, fext))) is not True:  # images are downloaded with image_ids as names
                print('Failed to download image {}'.format(image.filename))

        # Prepare image file paths from image directory for execution
        conn.job.update(progress=50,
                        statusComment="Preparing data for execution..")
        image_paths = glob.glob(os.path.join(img_path, '*'))
        std_size = (1032,1376)   #maximum size that the model can handle
        model_size = 256
        for i in range(len(image_paths)):

            org_img = Image.open(image_paths[i]) 
            
            filename = os.path.basename(image_paths[i])
            fname, fext = os.path.splitext(filename)
            fname = int(fname)
            org_img = img_to_array(org_img)
            img = org_img.copy()
            org_size = org_img.shape[:2]
            asp_ratio = org_size[0] / org_size[1]  #for cropping and upscaling to original size
            if org_size[1] > std_size[1]:
                img = tf.image.resize(img, (675,900), method='nearest')
                img = tf.image.resize_with_crop_or_pad(img, std_size[0],std_size[1])
                h_mask = predict_mask(img, h_model,model_size)
                h_mask = crop_to_aspect(h_mask, asp_ratio)
                h_mask = tf.image.resize(h_mask, std_size, method='nearest')
                h_up_mask = tf.image.resize_with_crop_or_pad(h_mask, 675,900)
                h_up_mask = tf.image.resize(h_up_mask, org_size, method='nearest')
                h_up_mask = np.asarray(h_up_mask).astype(np.uint8)
                _, h_up_mask = cv.threshold(h_up_mask, 0.001, 255, 0)
                kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (17, 17))
                h_up_mask = cv.morphologyEx(h_up_mask, cv.MORPH_OPEN, kernel, iterations=5)
                h_up_mask = cv.morphologyEx(h_up_mask, cv.MORPH_CLOSE, kernel, iterations=1)
                #h_up_mask = cv.erode(h_up_mask ,kernel,iterations = 3)
                #h_up_mask = cv.dilate(h_up_mask ,kernel,iterations = 3)
                h_up_mask = np.expand_dims(h_up_mask, axis=-1)
                
            else:
                h_mask = predict_mask(img, h_model, model_size)
                h_mask = crop_to_aspect(h_mask, asp_ratio)
                h_up_mask = tf.image.resize(h_mask, org_size, method='nearest')
                h_up_mask = np.asarray(h_up_mask).astype(np.uint8)
                _, h_up_mask = cv.threshold(h_up_mask, 0.001, 255, 0)
                kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (5, 5))
                #kernel = np.ones((9,9),np.uint8)
                h_up_mask = cv.morphologyEx(h_up_mask, cv.MORPH_CLOSE, kernel, iterations=3)
                h_up_mask = np.expand_dims(h_up_mask, axis=-1)
        
            box = bb_pts(h_up_mask)  # bounding box points for operculum (x_min, y_min, x_max, y_max)
            w = box[0]
            h = box[1]
            tr_h = box[3] - box[1]  # target height
            tr_w = box[2] - box[0]  # target width
            crop_op_img = tf.image.crop_to_bounding_box(org_img, h, w, tr_h, tr_w)

            op_asp_ratio = crop_op_img.shape[0] / crop_op_img.shape[1]
            op_mask = predict_mask(crop_op_img, op_model, model_size)
            op_mask = crop_to_aspect(op_mask, op_asp_ratio)
            op_mask = tf.image.resize(op_mask, (crop_op_img.shape[0], crop_op_img.shape[1]), method='nearest')
            op_up_mask = np.zeros((org_img.shape[0],org_img.shape[1],1)).astype(np.uint8) # array of zeros to be filled with op mask
            op_up_mask[box[1]:box[3], box[0]:box[2]] = op_mask # paste op_mask in org_img (reversing the crop operation)
            #op_up_mask = tf.image.resize_with_crop_or_pad(op_mask, org_size[0], org_size[1])
        

            h_polygon = h_make_polygon(h_up_mask)
            op_polygon = o_make_polygon(op_up_mask)

            conn.job.update(
                status=Job.RUNNING, progress=95,
                statusComment="Uploading new annotations to Cytomine server..")

            annotations = AnnotationCollection()
            annotations.append(Annotation(location=h_polygon[0].wkt, id_image=fname, id_terms=143971108,
                                          id_project=conn.parameters.cytomine_id_project))
            annotations.append(Annotation(location=op_polygon[0].wkt, id_image=fname, id_term=143971084,
                                          id_project=conn.parameters.cytomine_id_project))
            annotations.save()

        conn.job.update(status=Job.TERMINATED, status_comment="Finish", progress=100)  # 524787186
def run(cyto_job, parameters):
    logging.info("----- segmentation_prediction v%s -----", __version__)
    logging.info("Entering run(cyto_job=%s, parameters=%s)", cyto_job,
                 parameters)

    job = cyto_job.job
    project = cyto_job.project
    current_tile_annotation = None

    working_path = os.path.join("tmp", str(job.id))
    if not os.path.exists(working_path):
        logging.info("Creating annotation directory: %s", working_path)
        os.makedirs(working_path)

    try:
        # Initialization
        pyxit_target_width = parameters.pyxit_target_width
        pyxit_target_height = parameters.pyxit_target_height
        tile_size = parameters.cytomine_tile_size
        zoom = parameters.cytomine_zoom_level
        predictionstep = int(parameters.cytomine_predict_step)
        mindev = parameters.cytomine_tile_min_stddev
        maxmean = parameters.cytomine_tile_max_mean

        logging.info("Loading prediction model (local)")
        fp = open(parameters.pyxit_load_from, "r")
        logging.debug(fp)
        pickle.load(fp)  # classes => not needed
        pyxit = pickle.load(fp)
        pyxit.n_jobs = parameters.pyxit_nb_jobs  # multithread subwindows extraction in pyxit
        pyxit.base_estimator.n_jobs = parameters.pyxit_nb_jobs  # multithread tree propagation

        # loop for images in the project id TODO let user specify the images to process
        images = ImageInstanceCollection().fetch_with_filter(
            "project", project.id)
        nb_images = len(images)
        logging.info("# images in project: %d", nb_images)
        progress = 0
        progress_delta = 100 / nb_images

        # Go through all images
        for (i, image) in enumerate(images):
            image_str = "{} ({}/{})".format(image.instanceFilename, i + 1,
                                            nb_images)
            job.update(progress=progress,
                       statusComment="Analyzing image {}...".format(image_str))
            logging.debug(
                "Image id: %d width: %d height: %d resolution: %f magnification: %d filename: %s",
                image.id, image.width, image.height, image.resolution,
                image.magnification, image.filename)

            image.colorspace = "RGB"  # required for correct handling in CytomineReader

            # Create local object to access the remote whole slide
            logging.debug(
                "Creating connector to Slide Image from Cytomine server")
            whole_slide = WholeSlide(image)
            logging.debug("Wholeslide: %d x %d pixels", whole_slide.width,
                          whole_slide.height)

            # endx and endy allow to stop image analysis at a given x, y position  (for debugging)
            endx = parameters.cytomine_endx if parameters.cytomine_endx else whole_slide.width
            endy = parameters.cytomine_endy if parameters.cytomine_endy else whole_slide.height

            # initialize variables and tools for ROI
            nx = tile_size
            ny = tile_size

            local_tile_component = ([(0, 0), (0, ny), (nx, ny), (nx, 0),
                                     (0, 0)], [])

            # We can apply the segmentation model either in the whole slide (including background area), or only within
            # multiple ROIs (of a given term)
            # For example ROI could be generated first using a thresholding step to detect the tissue
            # Here we build a polygon union containing all roi_annotations locations (user or reviewed annotations) to
            # later match tile with roi masks
            if parameters.cytomine_roi_term:
                logging.debug("Retrieving ROI annotations")
                roi_annotations = AnnotationCollection(
                    image=image.id,
                    term=parameters.cytomine_roi_term,
                    showWKT=True,
                    showTerm=True,
                    reviewed=parameters.cytomine_reviewed_roi).fetch()

                roi_annotations_locations = []
                for roi_annotation in roi_annotations:
                    roi_annotations_locations.append(
                        shapely.wkt.loads(roi_annotation.location))
                roi_annotations_union = shapely.ops.unary_union(
                    roi_annotations_locations)

            else:  # no ROI used
                # We build a rectangular roi_mask corresponding to the whole image filled with ones
                logging.debug("Processing all tiles")
                roi_mask = np.ones((ny, nx), dtype=np.bool)

            # Initiate the reader object which browse the whole slide image with tiles of size tile_size
            logging.info("Initiating the Slide reader")
            reader = CytomineReader(
                whole_slide,
                window_position=Bounds(parameters.cytomine_startx,
                                       parameters.cytomine_starty, tile_size,
                                       tile_size),
                zoom=zoom,
                # overlap needed because the predictions at the borders of the tile are removed
                overlap=pyxit_target_width + 1)

            wsi = 0  # tile number

            logging.info("Starting browsing the image using tiles")
            while True:
                tile_component = reader.convert_to_real_coordinates(
                    [local_tile_component])[0]
                tile_polygon = shapely.geometry.Polygon(
                    tile_component[0], tile_component[1])

                # Get rasterized roi mask to match with this tile (if no ROI used, the roi_mask was built before and
                # corresponds to the whole image).
                if parameters.cytomine_roi_term:
                    roi_mask = rasterize_tile_roi_union(
                        nx, ny, tile_polygon, roi_annotations_union, reader)

                if np.count_nonzero(roi_mask) == 0:
                    logging.info(
                        "Tile %d is not included in any ROI, skipping processing",
                        wsi)

                else:
                    # Browse the whole slide image with catch exception
                    while True:
                        try:
                            reader.read()
                            break

                        except socket.timeout:
                            logging.error("Socket timeout for tile %d: %s",
                                          wsi, socket.timeout)
                            time.sleep(1)

                        except socket.error:
                            logging.error("Socket error for tile %d: %s", wsi,
                                          socket.error)
                            time.sleep(1)

                    tile = reader.data

                    # Get statistics about the current tile
                    logging.info("Computing tile %d statistics", wsi)
                    pos = reader.window_position
                    logging.debug(
                        "Tile zoom: %d, posx: %d, posy: %d, poswidth: %d, posheight: %d",
                        zoom, pos.x, pos.y, pos.width, pos.height)
                    tilemean = ImageStat.Stat(tile).mean
                    logging.debug("Tile mean pixel values: %d %d %d",
                                  tilemean[0], tilemean[1], tilemean[2])
                    tilestddev = ImageStat.Stat(tile).stddev
                    logging.debug("Tile stddev pixel values: %d %d %d",
                                  tilestddev[0], tilestddev[1], tilestddev[2])

                    # Criteria to determine if tile is empty, specific to this application
                    if ((tilestddev[0] < mindev and tilestddev[1] < mindev
                         and tilestddev[2] < mindev)
                            or (tilemean[0] > maxmean and tilemean[1] > maxmean
                                and tilemean[2] > maxmean)):
                        logging.info(
                            "Tile %d empty (filtered by min stddev or max mean)",
                            wsi)

                    else:
                        # This tile is not empty, we process it

                        # Add current tile annotation on server just for progress visualization purpose
                        current_tile_annotation = Annotation(
                            tile_polygon.wkt, image.id).save()

                        # Save the tile image locally
                        image_filename = "%s/%d-zoom_%d-tile_%d_x%d_y%d_w%d_h%d.png" \
                                         % (working_path, image.id, zoom, wsi, pos.x, pos.y, pos.width, pos.height)
                        tile.save(image_filename, "PNG")

                        logging.debug("Tile file: %s", image_filename)
                        logging.info("Extraction of subwindows in tile %d",
                                     wsi)
                        width, height = tile.size

                        half_subwindow_width = int(pyxit_target_width / 2)
                        half_subwindow_height = int(pyxit_target_height / 2)

                        # Coordinates of centers of extracted subwindows
                        y_roi = range(half_subwindow_height,
                                      height - half_subwindow_height,
                                      predictionstep)
                        x_roi = range(half_subwindow_width,
                                      width - half_subwindow_width,
                                      predictionstep)
                        logging.info("%d subwindows to extract",
                                     len(x_roi) * len(y_roi))

                        n_jobs = parameters.cytomine_nb_jobs
                        n_jobs, _, starts = _partition_images(
                            n_jobs, len(y_roi))

                        # Parallel extraction of subwindows in the current tile
                        all_data = Parallel(n_jobs=n_jobs)(
                            delayed(_parallel_crop_boxes)
                            (y_roi[starts[k]:starts[k +
                                                    1]], x_roi, image_filename,
                             half_subwindow_width, half_subwindow_height,
                             parameters.pyxit_colorspace)
                            for k in xrange(n_jobs))

                        # Reduce
                        boxes = np.vstack(box for box, _ in all_data)
                        _X = np.vstack([X for _, X in all_data])

                        logging.info("Prediction of subwindows for tile %d",
                                     wsi)
                        # Propagate subwindow feature vectors (X) into trees and get probabilities
                        _Y = pyxit.base_estimator.predict_proba(_X)

                        # Warning: we get output vectors for all classes for pixel (0,0) for all subwindows, then pixel
                        # predictions for pixel (0,1) for all subwindows, ... We do not get predictions window after
                        # window, but output after output
                        # => Y is a list of length m, where m = nb of pixels by subwindow ;
                        #    each element of the list is itself a list of size n, where n = nb of subwindows
                        #    for each subwindow, the probabilities for each class are given

                        # <optimized code
                        logging.info(
                            "Parallel construction of confidence map in current tile"
                        )
                        pixels = range(pyxit_target_width *
                                       pyxit_target_height)
                        n_jobs, _, starts = _partition_images(
                            n_jobs, len(pixels))

                        all_votes_class = Parallel(n_jobs=n_jobs)(
                            delayed(_parallel_confidence_map)
                            (pixels[starts[k]:starts[k + 1]],
                             _Y[starts[k]:starts[k + 1]], boxes, width, height,
                             pyxit.base_estimator.n_classes_[0],
                             pyxit_target_width, pyxit_target_height)
                            for k in xrange(n_jobs))

                        votes_class = all_votes_class[0]
                        for v in all_votes_class[1:]:
                            votes_class += v
                        # optimized code>

                        logging.info("Delete borders")
                        # Delete predictions at borders
                        for k in xrange(0, width):
                            for j in xrange(0, half_subwindow_height):
                                votes_class[j, k, :] = [1, 0]
                            for j in xrange(height - half_subwindow_height,
                                            height):
                                votes_class[j, k, :] = [1, 0]

                        for j in xrange(0, height):
                            for k in xrange(0, half_subwindow_width):
                                votes_class[j, k, :] = [1, 0]
                            for k in xrange(width - half_subwindow_width,
                                            width):
                                votes_class[j, k, :] = [1, 0]

                        votes = np.argmax(votes_class, axis=2) * 255

                        # only predict in roi region based on roi mask
                        votes[np.logical_not(roi_mask)] = 0

                        # process mask
                        votes = process_mask(votes)
                        votes = votes.astype(np.uint8)

                        # Save of confidence map locally
                        logging.info("Creating output tile file locally")
                        output = Image.fromarray(votes)
                        outputfilename = "%s/%d-zoom_%d-tile_%d_xxOUTPUT-%dx%d.png" \
                                         % (working_path, image.id, zoom, wsi, pyxit_target_width, pyxit_target_height)
                        output.save(outputfilename, "PNG")
                        logging.debug("Tile OUTPUT file: %s", outputfilename)

                        # Convert and transfer annotations of current tile
                        logging.info("Find components")
                        components = ObjectFinder(votes).find_components()
                        components = reader.convert_to_real_coordinates(
                            components)
                        polygons = [
                            Polygon(component[0], component[1])
                            for component in components
                        ]

                        logging.info("Uploading annotations...")
                        logging.debug("Number of polygons: %d" % len(polygons))
                        start = time.time()

                        for poly in polygons:
                            geometry = poly.wkt

                            if not poly.is_valid:
                                logging.warning(
                                    "Invalid geometry, try to correct it with buffer"
                                )
                                logging.debug(
                                    "Geometry prior to modification: %s",
                                    geometry)
                                new_poly = poly.buffer(0)
                                if not new_poly.is_valid:
                                    logging.error(
                                        "Failed to make valid geometry, skipping this polygon"
                                    )
                                    continue
                                geometry = new_poly.wkt

                            logging.debug("Uploading geometry %s", geometry)

                            startsingle = time.time()
                            while True:
                                try:
                                    # TODO: save collection of annotations
                                    annot = Annotation(
                                        geometry, image.id,
                                        [parameters.cytomine_predict_term
                                         ]).save()
                                    if not annot:
                                        logging.error(
                                            "Annotation could not be saved ; location = %s",
                                            geometry)
                                    break
                                except socket.timeout, socket.error:
                                    logging.error(
                                        "socket timeout/error add_annotation")
                                    time.sleep(1)

                            endsingle = time.time()
                            logging.debug(
                                "Elapsed time for adding single annotation: %d",
                                endsingle - startsingle)

                        # current time
                        end = time.time()
                        logging.debug(
                            "Elapsed time for adding all annotations: %d",
                            end - start)

                        # Delete current tile annotation (progress visualization)
                        current_tile_annotation.delete()

                wsi += 1

                if not reader.next() or (reader.window_position.x > endx
                                         and reader.window_position.y > endy):
                    break  # end of browsing the whole slide

            # Postprocessing to remove small/large annotations according to min/max area
            if parameters.cytomine_postproc:
                logging.info("Post-processing before union...")
                job.update(progress=progress + progress_delta / 4,
                           statusComment="Post-processing image {}...".format(
                               image_str))
                while True:
                    try:
                        annotations = AnnotationCollection(id_user=job.userJob,
                                                           id_image=image.id,
                                                           showGIS=True)
                        break
                    except socket.timeout, socket.error:
                        logging.error(
                            "Socket timeout/error when fetching annotations")
                        time.sleep(1)

                # remove/edit useless annotations
                start = time.time()
                for annotation in annotations:
                    if (annotation.area == 0
                            or annotation.area < parameters.cytomine_min_size
                            or annotation.area > parameters.cytomine_max_size):
                        annotation.delete()
                    else:
                        logging.debug("Keeping annotation %d", annotation.id)

                end = time.time()
                logging.debug(
                    "Elapsed time for post-processing all annotations: %d" %
                    (end - start))

            # Segmentation model was applied on individual tiles. We need to merge geometries generated from each tile.
            # We use a groovy/JTS script that downloads annotation geometries and perform union locally to relieve the
            # Cytomine server
            if parameters.cytomine_union:
                logging.info("Union of polygons for image %s",
                             image.instanceFilename)
                job.update(
                    progress=progress + progress_delta / 3,
                    statusComment="Union of polygons in image {}...".format(
                        image_str))
                start = time.time()
                union_command = (
                    "groovy -cp \"lib/jars/*\" lib/union4.groovy " +
                    "%s %s %s %d %d %d %d %d %d %d %d %d %d" %
                    (cyto_job._base_url(False), parameters.publicKey,
                     parameters.privateKey, image.id, job.userJob,
                     parameters.cytomine_predict_term,
                     parameters.cytomine_union_min_length,
                     parameters.cytomine_union_bufferoverlap,
                     parameters.cytomine_union_min_point_for_simplify,
                     parameters.cytomine_union_min_point,
                     parameters.cytomine_union_max_point,
                     parameters.cytomine_union_nb_zones_width,
                     parameters.cytomine_union_nb_zones_height))
                logging.info("Union command: %s", union_command)
                os.system(union_command)
                end = time.time()
                logging.info("Elapsed time union: %d s", end - start)

            # Perform classification of detected geometries using a classification model (pkl)
            if parameters.pyxit_post_classification:
                logging.info("Post classification of all candidates")
                job.update(
                    progress=progress + progress_delta * 2 / 3,
                    statusComment="Post-classification in image {}...".format(
                        image_str))

                # Retrieve locally annotations from Cytomine core produced by the segmentation job as candidates
                candidate_annotations = AnnotationCollection(
                    user=job.userJob,
                    image=image.id,
                    showWKT=True,
                    showMeta=True).fetch()

                folder_name = "%s/crops-candidates-%d/zoom-%d/" % (
                    working_path, image.id, zoom)
                if not os.path.exists(folder_name):
                    os.makedirs(folder_name)

                dest_pattern = os.path.join(folder_name, "{id}.png")
                for annotation in candidate_annotations:
                    annotation.dump(dest_pattern, mask=True, alpha=True)
                    # np_image = cv2.imread(annotation.filename, -1)
                    # if np_image is not None:
                    #     alpha = np.array(np_image[:, :, 3])
                    #     image = np.array(np_image[:, :, 0:3])
                    # image[alpha == 0] = (255,255,255)  # to replace surrounding by white
                    # cv2.imwrite(annotation.filename, image)

                logging.debug("Building attributes from %s", folder_name)
                # Extract subwindows from all candidates
                x, y = build_from_dir(folder_name)
                post_fp = open(parameters.pyxit_post_classification_save_to,
                               "r")
                classes = pickle.load(post_fp)
                pyxit = pickle.load(post_fp)
                logging.debug(pyxit)

                # pyxit parameters are in the model file
                y_proba = pyxit.predict_proba(x)
                y_predict = classes.take(np.argmax(y_proba, axis=1), axis=0)
                y_rate = np.max(y_proba, axis=1)

                # We classify each candidate annotation and keep only those predicted as cytomine_predict_term
                for annotation in candidate_annotations:
                    j = np.where(x == annotation.filename)[0][0]
                    new_term = int(y_predict[j])
                    accepted = (new_term == parameters.cytomine_predict_term)
                    logging.debug(
                        "Annotation %d %s during post-classification (class: %d proba: %d)",
                        annotation.id, "accepted" if accepted else "rejected",
                        int(y_predict[j]), y_rate[j])

                    if not accepted:
                        AlgoAnnotationTerm(
                            annotation.id,
                            parameters.cytomine_predict_term).delete()
                        AlgoAnnotationTerm(annotation.id, new_term).save()

                logging.info("End of post-classification")
                # ...

            # Perform stats (counting) in roi area
            if parameters.cytomine_count and parameters.cytomine_roi_term:
                logging.info("Compute statistics")
                # Count number of annotations in roi area
                # Get Rois
                roi_annotations = AnnotationCollection(
                    image=image.id,
                    term=parameters.cytomine_roi_term,
                    showGIS=True).fetch()

                # Count included annotations (term = predict_term) in each ROI
                for roi_annotation in roi_annotations:
                    included_annotations = AnnotationCollection(
                        image=image.id,
                        user=job.userJob,
                        bboxAnnotation=roi_annotation.id).fetch()
                    logging.info(
                        "Stats of image %s: %d annotations included in ROI %d (%d %s)",
                        image.instanceFilename, len(included_annotations),
                        roi_annotation.id, roi_annotation.area,
                        roi_annotation.areaUnit)

            logging.info("Finished processing image %s",
                         image.instanceFilename)
            progress += progress_delta
    parser.add_argument(
        '--opencv',
        dest='opencv',
        action='store_true',
        help=
        "Print the annotation geometry using OpenCV coordinate system for points."
    )

    params, other = parser.parse_known_args(sys.argv[1:])

    with Cytomine(host=params.host,
                  public_key=params.public_key,
                  private_key=params.private_key) as cytomine:

        if params.opencv:
            image_instances = ImageInstanceCollection().fetch_with_filter(
                "project", params.id_project)

        # We want all annotations in a given project.
        annotations = AnnotationCollection()
        annotations.project = params.id_project  # Add a filter: only annotations from this project
        # You could add other filters:
        # annotations.image = id_image => Add a filter: only annotations from this image
        # annotations.images = [id1, id2] => Add a filter: only annotations from these images
        # annotations.user = id_user => Add a filter: only annotations from this user
        # ...
        annotations.showWKT = True  # Ask to return WKT location (geometry) in the response
        annotations.showMeta = True  # Ask to return meta information (id, ...) in the response
        annotations.showGIS = True  # Ask to return GIS information (perimeter, area, ...) in the response
        # ...
        # => Fetch annotations from the server with the given filters.
        annotations.fetch()
Ejemplo n.º 13
0
def main(argv):
    with CytomineJob.from_cli(argv) as conn:
        # with Cytomine(argv) as conn:
        print(conn.parameters)

        conn.job.update(status=Job.RUNNING,
                        progress=0,
                        statusComment="Initialization...")
        base_path = "{}".format(os.getenv("HOME"))  # Mandatory for Singularity
        working_path = os.path.join(base_path, str(conn.job.id))

        # with Cytomine(host=params.host, public_key=params.public_key, private_key=params.private_key,
        #           verbose=logging.INFO) as cytomine:

        # ontology = Ontology("classPNcells"+str(conn.parameters.cytomine_id_project)).save()
        # ontology_collection=OntologyCollection().fetch()
        # print(ontology_collection)
        # ontology = Ontology("CLASSPNCELLS").save()
        # terms = TermCollection().fetch_with_filter("ontology", ontology.id)
        terms = TermCollection().fetch_with_filter(
            "project", conn.parameters.cytomine_id_project)
        conn.job.update(status=Job.RUNNING,
                        progress=1,
                        statusComment="Terms collected...")
        print(terms)

        # term_P = Term("PositiveCell", ontology.id, "#FF0000").save()
        # term_N = Term("NegativeCell", ontology.id, "#00FF00").save()
        # term_P = Term("PositiveCell", ontology, "#FF0000").save()
        # term_N = Term("NegativeCell", ontology, "#00FF00").save()

        # Get all the terms of our ontology
        # terms = TermCollection().fetch_with_filter("ontology", ontology.id)
        # terms = TermCollection().fetch_with_filter("ontology", ontology)
        # print(terms)

        # #Loading pre-trained Stardist model
        # np.random.seed(17)
        # lbl_cmap = random_label_cmap()
        # #Stardist H&E model downloaded from https://github.com/mpicbg-csbd/stardist/issues/46
        # #Stardist H&E model downloaded from https://drive.switch.ch/index.php/s/LTYaIud7w6lCyuI
        # model = StarDist2D(None, name='2D_versatile_HE', basedir='/models/')   #use local model file in ~/models/2D_versatile_HE/

        #Select images to process
        images = ImageInstanceCollection().fetch_with_filter(
            "project", conn.parameters.cytomine_id_project)
        conn.job.update(status=Job.RUNNING,
                        progress=2,
                        statusComment="Images gathered...")

        list_imgs = []
        if conn.parameters.cytomine_id_images == 'all':
            for image in images:
                list_imgs.append(int(image.id))
        else:
            list_imgs = [
                int(id_img)
                for id_img in conn.parameters.cytomine_id_images.split(',')
            ]
            print(list_imgs)

        #Go over images
        conn.job.update(status=Job.RUNNING,
                        progress=10,
                        statusComment="Running PN classification on image...")
        #for id_image in conn.monitor(list_imgs, prefix="Running PN classification on image", period=0.1):
        for id_image in list_imgs:

            roi_annotations = AnnotationCollection()
            roi_annotations.project = conn.parameters.cytomine_id_project
            roi_annotations.term = conn.parameters.cytomine_id_cell_term
            roi_annotations.image = id_image  #conn.parameters.cytomine_id_image
            roi_annotations.job = conn.parameters.cytomine_id_annotation_job
            roi_annotations.user = conn.parameters.cytomine_id_user_job
            roi_annotations.showWKT = True
            roi_annotations.fetch()
            print(roi_annotations)

            #Go over ROI in this image
            #for roi in conn.monitor(roi_annotations, prefix="Running detection on ROI", period=0.1):
            for roi in roi_annotations:
                #Get Cytomine ROI coordinates for remapping to whole-slide
                #Cytomine cartesian coordinate system, (0,0) is bottom left corner
                print(
                    "----------------------------Cells------------------------------"
                )
                roi_geometry = wkt.loads(roi.location)
                # print("ROI Geometry from Shapely: {}".format(roi_geometry))
                #                 print("ROI Bounds")
                #                 print(roi_geometry.bounds)
                minx = roi_geometry.bounds[0]
                miny = roi_geometry.bounds[3]
                #Dump ROI image into local PNG file
                # roi_path=os.path.join(working_path,str(roi_annotations.project)+'/'+str(roi_annotations.image)+'/'+str(roi.id))
                roi_path = os.path.join(
                    working_path,
                    str(roi_annotations.project) + '/' +
                    str(roi_annotations.image) + '/')
                #                 print(roi_path)
                roi_png_filename = os.path.join(roi_path + str(roi.id) +
                                                '.png')
                conn.job.update(status=Job.RUNNING,
                                progress=20,
                                statusComment=roi_png_filename)
                #                 print("roi_png_filename: %s" %roi_png_filename)
                roi.dump(dest_pattern=roi_png_filename, alpha=True)
                #roi.dump(dest_pattern=os.path.join(roi_path,"{id}.png"), mask=True, alpha=True)

                # im=Image.open(roi_png_filename)

                J = cv2.imread(roi_png_filename, cv2.IMREAD_UNCHANGED)
                J = cv2.cvtColor(J, cv2.COLOR_BGRA2RGBA)
                [r, c, h] = J.shape
                # print("J: ",J)

                if r < c:
                    blocksize = r
                else:
                    blocksize = c
                # print("blocksize:",blocksize)
                rr = np.zeros((blocksize, blocksize))
                cc = np.zeros((blocksize, blocksize))

                zz = [*range(1, blocksize + 1)]
                # print("zz:", zz)
                for i in zz:
                    rr[i - 1, :] = zz
                # print("rr shape:",rr.shape)

                zz = [*range(1, blocksize + 1)]
                for i in zz:
                    cc[:, i - 1] = zz
                # print("cc shape:",cc.shape)

                cc1 = np.asarray(cc) - 16.5
                rr1 = np.asarray(rr) - 16.5
                cc2 = np.asarray(cc1)**2
                rr2 = np.asarray(rr1)**2
                rrcc = np.asarray(cc2) + np.asarray(rr2)

                weight = np.sqrt(rrcc)
                # print("weight: ",weight)
                weight2 = 1. / weight
                # print("weight2: ",weight2)
                #                 print("weight2 shape:",weight2.shape)
                coord = [c / 2, r / 2]
                halfblocksize = blocksize / 2

                y = round(coord[1])
                x = round(coord[0])

                # Convert the RGB image to HSV
                Jalpha = J[:, :, 3]
                Jalphaloc = Jalpha / 255
                Jrgb = cv2.cvtColor(J, cv2.COLOR_RGBA2RGB)
                Jhsv = cv2.cvtColor(Jrgb, cv2.COLOR_RGB2HSV_FULL)
                Jhsv = Jhsv / 255
                Jhsv[:, :, 0] = Jhsv[:, :, 0] * Jalphaloc
                Jhsv[:, :, 1] = Jhsv[:, :, 1] * Jalphaloc
                Jhsv[:, :, 2] = Jhsv[:, :, 2] * Jalphaloc
                # print("Jhsv: ",Jhsv)

                # print("Jhsv size:",Jhsv.shape)
                # print("Jhsv class:",Jhsv.dtype)

                currentblock = Jhsv[0:blocksize, 0:blocksize, :]
                # print("currentblock: ",currentblock)
                #                 print(currentblock.dtype)
                currentblockH = currentblock[:, :, 0]
                currentblockV = 1 - currentblock[:, :, 2]
                hue = sum(sum(currentblockH * weight2))
                val = sum(sum(currentblockV * weight2))
                #                 print("hue:", hue)
                #                 print("val:", val)

                if hue < 2:
                    cellclass = 1
                elif val < 15:
                    cellclass = 2
                else:
                    if hue < 30 or val > 40:
                        cellclass = 1
                    else:
                        cellclass = 2

                # tags = TagCollection().fetch()
                # tags = TagCollection()
                # print(tags)

                if cellclass == 1:
                    #                     print("Positive (H: ", str(hue), ", V: ", str(val), ")")
                    id_terms = conn.parameters.cytomine_id_positive_term
                    # tag = Tag("Positive (H: ", str(hue), ", V: ", str(val), ")").save()
                    # print(tag)
                    # id_terms=Term("PositiveCell", ontology.id, "#FF0000").save()
                elif cellclass == 2:
                    #                     print("Negative (H: ", str(hue), ", V: ", str(val), ")")
                    id_terms = conn.parameters.cytomine_id_negative_term
                    # for t in tags:
                    # tag = Tag("Negative (H: ", str(hue), ", V: ", str(val), ")").save()
                    # print(tag)
                    # id_terms=Term("NegativeCell", ontology.id, "#00FF00").save()

                    # First we create the required resources

                cytomine_annotations = AnnotationCollection()
                # property_collection = PropertyCollection(uri()).fetch("annotation",id_image)
                # property_collection = PropertyCollection().uri()
                # print(property_collection)
                # print(cytomine_annotations)

                # property_collection.append(Property(Annotation().fetch(id_image), key="Hue", value=str(hue)))
                # property_collection.append(Property(Annotation().fetch(id_image), key="Val", value=str(val)))
                # property_collection.save()

                # prop1 = Property(Annotation().fetch(id_image), key="Hue", value=str(hue)).save()
                # prop2 = Property(Annotation().fetch(id_image), key="Val", value=str(val)).save()

                # prop1.Property(Annotation().fetch(id_image), key="Hue", value=str(hue)).save()
                # prop2.Property(Annotation().fetch(id_image), key="Val", value=str(val)).save()

                # for pos, polygroup in enumerate(roi_geometry,start=1):
                #     points=list()
                #     for i in range(len(polygroup[0])):
                #         p=Point(minx+polygroup[1][i],miny-polygroup[0][i])
                #         points.append(p)

                annotation = roi_geometry

                # tags.append(TagDomainAssociation(Annotation().fetch(id_image, tag.id))).save()

                # association = append(TagDomainAssociation(Annotation().fetch(id_image, tag.id))).save()
                # print(association)

                cytomine_annotations.append(
                    Annotation(
                        location=annotation.wkt,  #location=roi_geometry,
                        id_image=id_image,  #conn.parameters.cytomine_id_image,
                        id_project=conn.parameters.cytomine_id_project,
                        id_terms=[id_terms]))
                print(".", end='', flush=True)

                #Send Annotation Collection (for this ROI) to Cytomine server in one http request
                ca = cytomine_annotations.save()

        conn.job.update(status=Job.TERMINATED,
                        progress=100,
                        statusComment="Finished.")
Ejemplo n.º 14
0
def main(argv):
    with CytomineJob.from_cli(argv) as conn:
        conn.job.update(progress=0, statusComment="Initialization..")
        base_path = "{}".format(os.getenv("HOME"))  # Mandatory for Singularity
        working_path = os.path.join(base_path, str(conn.job.id))

        # Load pretrained model (assume the best of all)
        conn.job.update(progress=0,
                        statusComment="Loading segmentation model..")

        with open("/models/resnet50b_fpn256/config.json") as f:
            config = json.load(f)
        model = FPN.build_resnet_fpn(
            name=config['name'],
            input_size=conn.parameters.dataset_patch_size,  # must be / by 16
            input_channels=1 if config['input']['mode'] == 'grayscale' else 3,
            output_channels=config['fpn']['out_channels'],
            num_classes=2,  # legacy
            in_features=config['fpn']['in_features'],
            out_features=config['fpn']['out_features'])
        model.to(_DEVICE)
        model_dict = torch.load(config['weights'],
                                map_location=torch.device(_DEVICE))
        model.load_state_dict(model_dict['model'])

        # Select images to process
        images = ImageInstanceCollection().fetch_with_filter(
            "project", conn.parameters.cytomine_id_project)

        if conn.parameters.cytomine_id_images != 'all':
            images = [
                _ for _ in images
                if _.id in map(lambda x: int(x.strip()),
                               conn.parameters.cytomine_id_images.split(','))
            ]
        images_id = [image.id for image in images]

        # Download selected images into "working_directory"
        img_path = os.path.join(working_path, "images")
        os.makedirs(img_path)
        for image in conn.monitor(
                images,
                start=2,
                end=50,
                period=0.1,
                prefix="Downloading images into working directory.."):
            fname, fext = os.path.splitext(image.filename)
            if image.download(dest_pattern=os.path.join(
                    img_path, "{}{}".format(image.id, fext))) is not True:

                print("Failed to download image {}".format(image.filename))

        # create a file that lists all images (used by PatchBasedDataset
        conn.job.update(progress=50,
                        statusComment="Preparing data for execution..")
        images = os.listdir(img_path)
        images = list(map(lambda x: x + '\n', images))
        with open(os.path.join(working_path, 'images.txt'), 'w') as f:
            f.writelines(images)

        # Prepare dataset and dataloader objects
        ImgTypeBits = {'.dcm': 16}
        channel_bits = ImgTypeBits.get(fext.lower(), 8)
        mean, std = compute_mean_and_std(img_path, bits=channel_bits)

        dataset = InferencePatchBasedDataset(
            path=working_path,
            subset='images',
            patch_size=conn.parameters.dataset_patch_size,
            mode=config['input']['mode'],
            bits=channel_bits,
            mean=mean,
            std=std)

        dataloader = DataLoader(
            dataset=dataset,
            batch_size=conn.parameters.model_batch_size,
            drop_last=False,
            shuffle=False,
            num_workers=0,
            collate_fn=InferencePatchBasedDataset.collate_fn)

        # Go over images
        conn.job.update(status=Job.RUNNING,
                        progress=55,
                        statusComment="Running inference on images..")
        results = inference_on_segmentation(
            model, dataloader, conn.parameters.postprocess_p_threshold)

        for id_image in conn.monitor(
                images_id,
                start=90,
                end=95,
                prefix="Deleting old annotations on images..",
                period=0.1):
            # Delete old annotations
            del_annotations = AnnotationCollection()
            del_annotations.image = id_image
            del_annotations.user = conn.job.id
            del_annotations.project = conn.parameters.cytomine_id_project
            del_annotations.term = conn.parameters.cytomine_id_predict_term,
            del_annotations.fetch()
            for annotation in del_annotations:
                annotation.delete()

        conn.job.update(
            status=Job.RUNNING,
            progress=95,
            statusComment="Uploading new annotations to Cytomine server..")
        annotations = AnnotationCollection()
        for instance in results:
            idx, _ = os.path.splitext(instance['filename'])
            width, height = instance['size']

            for box in instance['bbox']:
                points = [
                    Point(box[0], height - 1 - box[1]),
                    Point(box[0], height - 1 - box[3]),
                    Point(box[2], height - 1 - box[3]),
                    Point(box[2], height - 1 - box[1])
                ]
                annotation = Polygon(points)

                annotations.append(
                    Annotation(
                        location=annotation.wkt,
                        id_image=int(idx),
                        id_terms=[conn.parameters.cytomine_id_predict_term],
                        id_project=conn.parameters.cytomine_id_project))
        annotations.save()

        conn.job.update(status=Job.TERMINATED,
                        status_comment="Finish",
                        progress=100)
def main():
    with CytomineJob.from_cli(sys.argv) as conn:
        base_path = "{}".format(os.getenv("HOME"))
        working_path = os.path.join(base_path, str(conn.job.id))
        in_path = os.path.join(working_path, "in/")
        out_path = os.path.join(working_path, "out/")

        tr_working_path = os.path.join(base_path,
                                       str(conn.parameters.model_to_use))
        tr_out_path = os.path.join(tr_working_path, "out/")

        if not os.path.exists(working_path):
            os.makedirs(working_path)
            os.makedirs(in_path)

        images = ImageInstanceCollection().fetch_with_filter(
            "project", conn.parameters.cytomine_id_project)
        list_imgs = []
        if conn.parameters.images_to_predict == 'all':
            for image in images:
                list_imgs.append(int(image.id))
                image.dump(os.path.join(in_path, '%d.jpg' % (image.id)))
        else:
            list_imgs = [
                int(id_img)
                for id_img in conn.parameters.images_to_predict.split(',')
            ]
            for image in images:
                if image.id in list_imgs:
                    image.dump(os.path.join(in_path, '%d.jpg' % (image.id)))

        annotation_collection = AnnotationCollection()
        train_job = Job().fetch(conn.parameters.model_to_use)
        properties = PropertyCollection(train_job).fetch()
        str_terms = ""
        for prop in properties:
            if prop.fetch(key='id_terms') != None:
                str_terms = prop.fetch(key='id_terms').value
        term_list = [int(x) for x in str_terms.split(' ')]
        attached_files = AttachedFileCollection(train_job).fetch()

        for id_term in conn.monitor(term_list,
                                    start=10,
                                    end=90,
                                    period=0.05,
                                    prefix="Finding landmarks for terms..."):
            model_file = find_by_attribute(attached_files, "filename",
                                           "%d_model.joblib" % id_term)
            model_filepath = os.path.join(in_path, "%d_model.joblib" % id_term)
            model_file.download(model_filepath, override=True)
            cov_file = find_by_attribute(attached_files, 'filename',
                                         '%d_cov.joblib' % id_term)
            cov_filepath = os.path.join(in_path, "%d_cov.joblib" % id_term)
            cov_file.download(cov_filepath, override=True)
            parameters_file = find_by_attribute(
                attached_files, 'filename', '%d_parameters.joblib' % id_term)
            parameters_filepath = os.path.join(
                in_path, '%d_parameters.joblib' % id_term)
            parameters_file.download(parameters_filepath, override=True)

            model = joblib.load(model_filepath)
            [mx, my, cm] = joblib.load(cov_filepath)
            parameters_hash = joblib.load(parameters_filepath)
            feature_parameters = None
            if parameters_hash['feature_type'] in ['haar', 'gaussian']:
                fparameters_file = find_by_attribute(
                    attached_files, 'filename',
                    "%d_fparameters.joblib" % id_term)
                fparametersl_filepath = os.path.join(
                    in_path, "%d_fparameters.joblib" % id_term)
                fparameters_file.download(fparametersl_filepath, override=True)
                feature_parameters = joblib.load(fparametersl_filepath)
            for id_img in list_imgs:
                (x, y) = searchpoint_cytomine(
                    in_path, id_img, model, mx, my, cm,
                    1. / (2.**np.arange(parameters_hash['model_depth'])),
                    parameters_hash['window_size'],
                    parameters_hash['feature_type'], feature_parameters, 'jpg',
                    parameters_hash['model_npred'])
                circle = Point(x, y)
                annotation_collection.append(
                    Annotation(location=circle.wkt,
                               id_image=id_img,
                               id_terms=[id_term],
                               id_project=conn.parameters.cytomine_id_project))

        annotation_collection.save()