def profile_projections( self, axis=None, csv=False, csv_dest_pattern="projections-annotation-{id}.csv"): """ Get profile projections (min, max, average) for the given annotation. Parameters ---------- axis The axis along which the projections (min, max, average) are performed. By default last axis is used. To project along spatial X, Y axes, use special value "xy" or "spatial". csv True to return result in a CSV file. csv_dest_pattern The CSV destination pattern. """ if self.id is None: raise ValueError("Cannot review an annotation with no ID.") uri = "{}/{}/profile/projections.{}".format(self.callback_identifier, self.id, "csv" if csv else "json") if csv: pattern = re.compile("{(.*?)}") destination = re.sub( pattern, lambda m: str(getattr(self, str(m.group(0))[1:-1], "_")), csv_dest_pattern) return Cytomine.get_instance().download_file( uri, destination, payload={"axis": axis}) data = Cytomine.get_instance().get(uri, {"axis": axis}) return data['collection'] if "collection" in data else data
def update(self, id=None, **attributes): Cytomine.get_instance().log( "Job (id:{job_id}) status update: \"{statusComment}\" (status: {status}, progress: {progress}%)" .format(job_id=self.id, statusComment=attributes.get("statusComment", self.statusComment), status=_HUMAN_READABLE_JOB_STATUS[attributes.get( "status", self.status)], progress=attributes.get("progress", self.progress))) return super(Job, self).update(id=id, **attributes)
def merge(self, id_other_annotation_group): if self.id is None: raise ValueError("Cannot merge an annotaiton group with no ID.") return Cytomine.get_instance().post( "annotationgroup/{}/annotationgroup/{}/merge.json".format( self.id, id_other_annotation_group))
def rectangle(self, x, y, width, height): uri = "imagegroupHDF5/{}/{}/{}/{}/{}/rectangle.json".format(self.id, x, y, width, height) collection = Cytomine.get_instance().get(uri)["collection"] spectrum = np.array([data["spectra"] for data in collection]) spectrum = np.expand_dims(spectrum, axis=1) _, _, depth = spectrum.shape return spectrum.reshape((width, height, depth))
def fetch(self, id=None): if self.id is None and id is None: raise ValueError("Cannot fetch a model with no ID.") if id is not None: self.id = id return Cytomine.get_instance().get_model(self, self.query_parameters)
def save(self, chunk=15, n_workers=0): """ chunk: int|None Maximum number of object to send at once in a single HTTP request. None for sending them all at once. n_workers: int Number of threads to use for sending chunked requests (ignored if chunk is None). Value 0 for using as many threads as cpus on the machine. """ if chunk is None: return Cytomine.get_instance().post_collection(self) elif isinstance(chunk, int): def upload_fn(collection): if not isinstance(collection, Collection): _tmp = self.__class__(self._model) _tmp.extend(collection) collection = _tmp return Cytomine.get_instance().post_collection(collection) results = generic_chunk_parallel(self, worker_fn=upload_fn, chunk_size=chunk, n_workers=n_workers) added, failed = list(), list() for (start, end), success in results: (added if success else failed).extend(self[start:end]) if len(added) != len(self): raise CollectionPartialUploadException( "Some annotations could not be uploaded", created=added, failed=failed) return True else: raise ValueError( "Invalid value '{}' for chunk parameter.".format(chunk))
def delete(self, id=None): if self.id is None and id is None: raise ValueError("Cannot delete a model with no ID.") if id is not None: self.id = id return Cytomine.get_instance().delete_model(self)
def download(self, dest_pattern="{originalFilename}", override=True, parent=False): """ Download the original image. Parameters ---------- dest_pattern : str, optional Destination path for the downloaded image. "{X}" patterns are replaced by the value of X attribute if it exists. override : bool, optional True if a file with same name can be overrided by the new file. parent : bool, optional True to download image parent if the abstract image is a part of a multidimensional file. Returns ------- downloaded : bool True if everything happens correctly, False otherwise. """ if self.id is None: raise ValueError("Cannot dump an annotation with no ID.") pattern = re.compile("{(.*?)}") dest_pattern = re.sub(pattern, lambda m: str(getattr(self, str(m.group(0))[1:-1], "_")), dest_pattern) parameters = {"parent": parent} destination = os.path.dirname(dest_pattern) if not os.path.exists(destination): os.makedirs(destination) return Cytomine.get_instance().download_file("{}/{}/download".format(self.callback_identifier, self.id), dest_pattern, override, parameters)
def profile(self): if self.id is None: raise ValueError("Cannot review an annotation with no ID.") data = Cytomine.get_instance().get("{}/{}/profile.json".format( self.callback_identifier, self.id)) return data['collection'] if "collection" in data else data
def image_servers(self): if not self._image_servers: data = Cytomine.get_instance().get( "{}/{}/imageservers.json".format(self.callback_identifier, self.id)) self._image_servers = data["imageServersURLs"] return self._image_servers
def _fetch(self, append_mode=False): if len(self._filters) == 0 and None not in self._allowed_filters: raise ValueError( "This collection cannot be fetched without a filter.") return Cytomine.get_instance().get_collection(self, self.parameters, append_mode)
def upload(self, filename): if self.is_new(): raise ValueError("Cannot upload file if not existing ID.") return Cytomine.get_instance().upload_file( self, filename, uri="{}/{}/upload".format(self.callback_identifier, self.id))
def review(self, id_terms=None): if self.id is None: raise ValueError("Cannot review an annotation with no ID.") if not id_terms: id_terms = [] data = {"id": self.id, "terms": id_terms} return Cytomine.get_instance().post("{}/{}/review.json".format(self.callback_identifier, self.id), data)
def rectangle(self, x, y, width, height): uri = "imagegroupHDF5/{}/{}/{}/{}/{}/rectangle.json".format( self.id, x, y, width, height) collection = Cytomine.get_instance().get(uri)["collection"] spectrum = np.array([data["spectra"] for data in collection]) spectrum = np.expand_dims(spectrum, axis=1) _, _, depth = spectrum.shape return spectrum.reshape((width, height, depth))
def dump_crops(self, dest_pattern, n_workers=0, override=True, **dump_params): """Download the crops of the annotations Parameters ---------- dest_pattern : str, optional Destination path for the downloaded image. "{X}" patterns are replaced by the value of X attribute if it exists. override : bool, optional True if a file with same name can be overrided by the new file. n_workers: int Number of workers to use (default: uses all the available processors) dump_params: dict Parameters for dumping the annotations (see Annotation.dump) Returns ------- annotations: AnnotationCollection Annotations that have been successfully downloaded (containing a `filenames` attribute) """ def dump_crop(an): if is_false( an.dump(dest_pattern=dest_pattern, override=override, **dump_params)): return False else: return an results = generic_download(self, download_instance_fn=dump_crop, n_workers=n_workers) # check errors count_fail = 0 failed = list() for in_annot, out_annot in results: if is_false(out_annot): count_fail += 1 failed.append(in_annot.id) logger = Cytomine.get_instance().logger if count_fail > 0: n_annots = len(self) ratio = 100 * count_fail / float(n_annots) logger.info( "Failed to download crops for {}/{} annotations ({:3.2f} %).". format(count_fail, n_annots, ratio)) logger.debug( "Annotation with crop download failure: {}".format(failed)) collection = AnnotationCollection() collection.extend( [an for _, an in results if not isinstance(an, bool) or an]) return collection
def execute(self): if self.is_new(): raise ValueError("Cannot execute job if no ID was provided.") response = Cytomine.get_instance().post(uri="{}/{}/execute.json" .format(self.callback_identifier, self.id), data=self.to_json(), query_parameters={id: self.id}) self.populate(response) return self
def download(self, destination="{filename}", override=False): if self.is_new(): raise ValueError("Cannot download file if not existing ID.") pattern = re.compile("{(.*?)}") destination = re.sub(pattern, lambda m: str(getattr(self, str(m.group(0))[1:-1], "_")), destination) return Cytomine.get_instance().download_file("{}/{}/download".format(self.callback_identifier, self.id), destination, override)
def update(self, id=None, **attributes): if self.id is None and id is None: raise ValueError("Cannot update a model with no ID.") if id is not None: self.id = id if attributes: self.populate(attributes) return Cytomine.get_instance().put_model(self)
def execute(self): if self.is_new(): raise ValueError("Cannot execute job if no ID was provided.") response = Cytomine.get_instance().post( uri="{}/{}/execute.json".format(self.callback_identifier, self.id), data=self.to_json(), query_parameters={id: self.id}) self.populate(response) return self
def upload(self): return Cytomine.get_instance().upload_file(self, self.filename, query_parameters={ "domainClassName": self.domainClassName, "domainIdent": self.domainIdent })
def review(self, id_terms=None): if self.id is None: raise ValueError("Cannot review an annotation with no ID.") if not id_terms: id_terms = [] data = {"id": self.id, "terms": id_terms} return Cytomine.get_instance().post( "{}/{}/review.json".format(self.callback_identifier, self.id), data)
def fetch(self, id=None, key=None): if self.id is None and id is None and self.key is None and key is None: raise ValueError("Cannot fetch a model with no ID and no key.") if id is not None: self.id = id if key is not None: self.key = key self._by_key = True model = Cytomine.get_instance().get_model(self, self.query_parameters) self._by_key = False return model
def reference_slice(self): if self.id is None: raise ValueError( "Cannot get the reference slice of an image with no ID.") if not self._reference_slice: data = Cytomine.get_instance().get( "imageinstance/{}/sliceinstance/reference.json".format( self.id)) self._reference_slice = SliceInstance().populate( data) if data else False return self._reference_slice
def fetch(self, id_user=None, id_role=None): self.id = -1 if self.user is None and id_user is None: raise ValueError("Cannot fetch a model with no user ID.") elif self.role is None and id_role is None: raise ValueError("Cannot fetch a model with no role ID.") if id_user is not None: self.user = id_user if id_role is not None: self.role = id_role return Cytomine.get_instance().get_model(self, self.query_parameters)
def fetch(self, id_annotation=None, id_term=None): self.id = -1 if self.annotation is None and id_annotation is None: raise ValueError("Cannot fetch a model with no annotation ID.") elif self.term is None and id_term is None: raise ValueError("Cannot fetch a model with no term ID.") if id_annotation is not None: self.annotation = id_annotation if id_term is not None: self.term = id_term return Cytomine.get_instance().get_model(self, self.query_parameters)
def fetch(self, id_term1=None, id_term2=None): self.id = -1 if self.term1 is None and id_term1 is None: raise ValueError("Cannot fetch a model with no term 1 ID.") elif self.term2 is None and id_term2 is None: raise ValueError("Cannot fetch a model with no term 2 ID.") if id_term1 is not None: self.term1 = id_term1 if id_term2 is not None: self.term2 = id_term2 return Cytomine.get_instance().get_model(self, self.query_parameters)
def fetch(self, id_annotation=None, id_track=None): self.id = -1 if self.annotationIdent is None and id_annotation is None: raise ValueError("Cannot fetch a model with no annotation ID.") elif self.track is None and id_track is None: raise ValueError("Cannot fetch a model with no term ID.") if id_annotation is not None: self.annotationIdent = id_annotation if id_track is not None: self.track = id_track return Cytomine.get_instance().get_model(self, self.query_parameters)
def profile(self, x, y, width=None, height=None): import numpy as np from shapely.geometry import Point, box geometry = box(x, y, x + width, y + height) if (width and height) else Point(x, y) uri = "{}/{}/profile.json".format(self.callback_identifier, self.id) data = Cytomine.get_instance().get(uri, {"geometry": geometry}) if width and height: data = data["collection"] depth = len(data[0]["profile"]) profile = np.empty((height, width, depth), dtype=np.uint) for p in data: row = p["point"][1] - y col = p["point"][0] - x profile[row, col, :] = p["profile"] return profile else: return np.asarray([[data["profile"]]])
def dump(self, dest_pattern="{id}.jpg", override=True, max_size=None, bits=8, contrast=None, gamma=None, colormap=None, inverse=None): """ Download the image with optional image modifications. Parameters ---------- dest_pattern : str, optional Destination path for the downloaded image. "{X}" patterns are replaced by the value of X attribute if it exists. override : bool, optional True if a file with same name can be overrided by the new file. max_size : int, tuple, optional Maximum size (width or height) of returned image. None to get original size. bits : int (8,16,32) or str ("max"), optional Bit depth (bit per channel) of returned image. "max" returns the original image bit depth contrast : float, optional Optional contrast applied on returned image. gamma : float, optional Optional gamma applied on returned image. colormap : int, optional Cytomine identifier of a colormap to apply on returned image. inverse : bool, optional True to inverse color mapping, False otherwise. Returns ------- downloaded : bool True if everything happens correctly, False otherwise. As a side effect, object attribute "filename" is filled with downloaded file path. """ if self.id is None: raise ValueError("Cannot dump an annotation with no ID.") pattern = re.compile("{(.*?)}") dest_pattern = re.sub(pattern, lambda m: str(getattr(self, str(m.group(0))[1:-1], "_")), dest_pattern) destination = os.path.dirname(dest_pattern) filename, extension = os.path.splitext(os.path.basename(dest_pattern)) extension = extension[1:] if extension not in ("jpg", "png", "tif", "tiff"): extension = "jpg" if not os.path.exists(destination): os.makedirs(destination) if isinstance(max_size, tuple) or max_size is None: max_size = max(self.width, self.height) parameters = { "maxSize": max_size, "contrast": contrast, "gamma": gamma, "colormap": colormap, "inverse": inverse, "bits": bits } file_path = os.path.join(destination, "{}.{}".format(filename, extension)) url = self.preview[:self.preview.index("?")] url = url.replace(".png", ".{}".format(extension)) result = Cytomine.get_instance().download_file(url, file_path, override, parameters) if result: self.filename = file_path return result
def _fetch(self, append_mode=False): if len(self._filters) == 0 and None not in self._allowed_filters: raise ValueError("This collection cannot be fetched without a filter.") return Cytomine.get_instance().get_collection(self, self.parameters, append_mode)
def characteristics(self): uri = "imagegroup/{}/characteristics.json".format(self.id) return Cytomine.get_instance().get(uri)
def pixel(self, x, y): uri = "imagegroupHDF5/{}/{}/{}/pixel.json".format(self.id, x, y) return np.asarray([[Cytomine.get_instance().get(uri)["spectra"]]])
def upload(self): return Cytomine.get_instance().upload_file(self, self.filename, query_parameters={"domainClassName": self.domainClassName, "domainIdent": self.domainIdent})
def signature(self): return Cytomine.get_instance().get("signature.json")
def dump(self, dest_pattern="{id}.jpg", override=True, mask=False, alpha=False, bits=8, zoom=None, max_size=None, increase_area=None, contrast=None, gamma=None, colormap=None, inverse=None): """ Download the annotation crop, with optional image modifications. Parameters ---------- dest_pattern : str, optional Destination path for the downloaded image. "{X}" patterns are replaced by the value of X attribute if it exists. override : bool, optional True if a file with same name can be overrided by the new file. mask : bool, optional True if a binary mask based on given annotations must be returned, False otherwise. alpha : bool, optional True if image background (outside annotations) must be transparent, False otherwise. zoom : int, optional Optional image zoom number bits : int (8,16,32) or str ("max"), optional Bit depth (bit per channel) of returned image. "max" returns the original image bit depth max_size : int, tuple, optional Maximum size (width or height) of returned image. None to get original size. increase_area : float, optional Increase the crop size. For example, an annotation whose bounding box size is (w,h) will have a crop dimension of (w*increase_area, h*increase_area). contrast : float, optional Optional contrast applied on returned image. gamma : float, optional Optional gamma applied on returned image. colormap : int, optional Cytomine identifier of a colormap to apply on returned image. inverse : bool, optional True to inverse color mapping, False otherwise. Returns ------- downloaded : bool True if everything happens correctly, False otherwise. As a side effect, object attribute "filename" is filled with downloaded file path. """ if self.id is None: raise ValueError("Cannot dump an annotation with no ID.") pattern = re.compile("{(.*?)}") dest_pattern = re.sub(pattern, lambda m: str(getattr(self, str(m.group(0))[1:-1], "_")), dest_pattern) destination = os.path.dirname(dest_pattern) filename, extension = os.path.splitext(os.path.basename(dest_pattern)) extension = extension[1:] if extension not in ("jpg", "png", "tif", "tiff"): extension = "jpg" if not os.path.exists(destination): os.makedirs(destination) parameters = { "zoom": zoom, "maxSize": max_size, "increaseArea": increase_area, "contrast": contrast, "gamma": gamma, "colormap": colormap, "inverse": inverse, "bits": bits } if mask and alpha: image = "alphamask" if extension == "jpg": extension = "png" elif mask: image = "mask" else: image = "crop" file_path = os.path.join(destination, "{}.{}".format(filename, extension)) url = self.cropURL.replace("crop.jpg", "{}.{}".format(image, extension)) result = Cytomine.get_instance().download_file(url, file_path, override, parameters) if result: self.filename = file_path return result
def keys(self): # Only works if you are superadmin. if hasattr(self, "id") and self.id: return Cytomine.get_instance().get("user/{}/keys.json".format(self.id))
def image_servers(self): if not self._image_servers: data = Cytomine.get_instance().get("abstractimage/{}/imageservers.json".format(self.baseImage)) self._image_servers = data["imageServersURLs"] return self._image_servers
def image_servers(self): if not self._image_servers: data = Cytomine.get_instance().get("{}/{}/imageservers.json".format(self.callback_identifier, self.id)) self._image_servers = data["imageServersURLs"] return self._image_servers
def save(self): return Cytomine.get_instance().post_collection(self)
def keys(self): return Cytomine.get_instance().get("userkey/{}/keys.json".format(self.publicKey))
def window(self, x, y, w, h, dest_pattern="{id}-{x}-{y}-{w}-{h}.jpg", override=True, mask=None, alpha=None, bits=8, annotations=None, terms=None, users=None, reviewed=None): """ Extract a window (rectangle) from an image and download it. Parameters ---------- x : int The X position of window top-left corner. 0 is image left. y : int The Y position of window top-left corner. 0 is image top. w : int The window width h : int The window height dest_pattern : str, optional Destination path for the downloaded image. "{X}" patterns are replaced by the value of X attribute if it exists. override : bool, optional True if a file with same name can be overrided by the new file. mask : bool, optional True if a binary mask based on given annotations must be returned, False otherwise. alpha : bool, optional True if image background (outside annotations) must be transparent, False otherwise. bits : int (8/16/32), optional Optional output bit depth of returned images annotations : list of int, optional If mask=True or alpha=True, annotation identifiers that must be taken into account for masking terms : list of int, optional If mask=True or alpha=True, term identifiers that must be taken into account for masking users : list of int, optional If mask=True or alpha=True, user identifiers that must be taken into account for masking reviewed : bool, optional If mask=True or alpha=True, indicate if only reviewed annotations mut be taken into account for masking Returns ------- downloaded : bool True if everything happens correctly, False otherwise. """ self.x = x self.y = y self.w = w self.h = h pattern = re.compile("{(.*?)}") dest_pattern = re.sub(pattern, lambda m: str(getattr(self, str(m.group(0))[1:-1], "_")), dest_pattern) del self.x del self.y del self.w del self.h destination = os.path.dirname(dest_pattern) filename, extension = os.path.splitext(os.path.basename(dest_pattern)) extension = extension[1:] if extension not in ("jpg", "png", "tif", "tiff"): extension = "jpg" if not os.path.exists(destination): os.makedirs(destination) if mask is None and alpha is None: alphamask = None elif mask and alpha: alphamask = True if extension == "jpg": extension = "png" else: alphamask = False # Temporary fix due to Cytomine-core if mask is not None: mask = str(mask).lower() if alphamask is not None: alphamask = str(alphamask).lower() # === parameters = { "annotations": ",".join(str(item) for item in annotations) if annotations else None, "terms": ",".join(str(item) for item in terms) if terms else None, "users": ",".join(str(item) for item in users) if users else None, "reviewed": reviewed, "bits": bits, "mask": mask, "alphaMask": alphamask } file_path = os.path.join(destination, "{}.{}".format(filename, extension)) return Cytomine.get_instance().download_file("{}/{}/window-{}-{}-{}-{}.{}".format( self.callback_identifier, self.id, x, y, w, h, extension), file_path, override, parameters)
def save(self): self.id = None return Cytomine.get_instance().post_model(self)
def fetch(self, id=None): if id is not None: self.id = id return Cytomine.get_instance().get_model(self, self.query_parameters)
def upload(self, filename): if self.is_new(): raise ValueError("Cannot upload file if not existing ID.") return Cytomine.get_instance().upload_file(self, filename, uri="{}/{}/upload".format(self.callback_identifier, self.id))