def create_track_from_slices(image, slices, depth2slice, id_project, track_prefix="object", label=None, upload_group_id=False, depth="time"): """Create an annotation track from a list of AnnotationSlice Parameters ---------- image: ImageInstance The image instance in which the track is added slices: iterable (of AnnotationSlice) The polygon slices of the objects to draw depth2slice: dict A dictionary mapping the depths of the image instance with their respective SliceInstance id_project: int Project identifier track_prefix: str (default: "object") A prefix for the track name label: int|str (default: None) A label for the track upload_group_id: bool True to upload the group identifier depth: str Which depth field to read in the AnnotationSlice if both are present. One of {'time', 'depth'}. Returns ------- saved_tracks: TrackCollection The saved track objects annotations: AnnotationCollection The annotations associated with the traped. The collection is NOT saved. """ if label is None and len(slices) > 0: label = slices[0].label track = Track(name="{}-{}".format(track_prefix, label), id_image=image.id, color=None if upload_group_id else DEFAULT_COLOR).save() if upload_group_id: Property(track, key="label", value=label).save() collection = AnnotationCollection() for _slice in slices: collection.append( Annotation( location=change_referential(p=_slice.polygon, height=image.height).wkt, id_image=image.id, id_project=id_project, id_tracks=[track.id], slice=depth2slice[_slice.depth if _slice.time is None or depth == "depth" else _slice.time].id)) return track, collection
# We first add a point in (10,10) where (0,0) is bottom-left corner point = Point(10, 10) annotation_point = Annotation( location=point.wkt, id_image=params.id_image_instance).save() if params.id_term: AnnotationTerm(annotation_point.id, params.id_term).save() # Then, we add a rectangle as annotation rectangle = box(20, 20, 100, 100) annotation_rectangle = Annotation( location=rectangle.wkt, id_image=params.id_image_instance).save() if params.id_term: AnnotationTerm(annotation_rectangle.id, params.id_term).save() # We can also add a property (key-value pair) to an annotation Property(annotation_rectangle, key="my_property", value=10).save() # Print the list of annotations in the given image: annotations = AnnotationCollection() annotations.image = params.id_image_instance annotations.fetch() print(annotations) # We can also add multiple annotation in one request: annotations = AnnotationCollection() annotations.append( Annotation(location=point.wkt, id_image=params.id_image_instance, id_project=params.id_project)) annotations.append( Annotation(location=rectangle.wkt,
def main(): with CytomineJob.from_cli(sys.argv) as conn: conn.job.update(status=Job.RUNNING, progress=0, status_comment="Initialization of the training phase") # 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(conn.job.id)) in_path = os.path.join(working_path, "in/") in_txt = os.path.join(in_path, 'txt/') 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) os.makedirs(in_txt) # 2. Download the images (first input, then ground truth image) conn.job.update( progress=10, statusComment="Downloading images (to {})...".format(in_path)) print(conn.parameters) images = ImageInstanceCollection().fetch_with_filter( "project", conn.parameters.cytomine_id_project) xpos = {} ypos = {} terms = {} for image in images: image.dump(dest_pattern=in_path.rstrip('/') + '/%d.%s' % (image.id, 'jpg')) annotations = AnnotationCollection() annotations.project = conn.parameters.cytomine_id_project annotations.showWKT = True annotations.showMeta = True annotations.showGIS = True annotations.showTerm = True annotations.image = image.id annotations.fetch() for ann in annotations: l = ann.location if l.rfind('POINT') == -1: pol = shapely.wkt.loads(l) poi = pol.centroid else: poi = shapely.wkt.loads(l) (cx, cy) = poi.xy xpos[(ann.term[0], image.id)] = int(cx[0]) ypos[(ann.term[0], image.id)] = image.height - int(cy[0]) terms[ann.term[0]] = 1 for image in images: F = open(in_txt + '%d.txt' % image.id, 'w') for t in terms.keys(): if (t, image.id) in xpos: F.write('%d %d %d %f %f\n' % (t, xpos[(t, image.id)], ypos[(t, image.id)], xpos[(t, image.id)] / float(image.width), ypos[(t, image.id)] / float(image.height))) F.close() depths = 1. / (2.**np.arange(conn.parameters.model_depth)) (xc, yc, xr, yr, ims, t_to_i, i_to_t) = getallcoords(in_txt) if conn.parameters.cytomine_id_terms == 'all': term_list = t_to_i.keys() else: term_list = [ int(term) for term in conn.parameters.cytomine_id_terms.split(',') ] if conn.parameters.cytomine_training_images == 'all': tr_im = ims else: tr_im = [ int(id_im) for id_im in conn.parameters.cytomine_training_images.split(',') ] DATA = None REP = None be = 0 #leprogres = 10 #pr_spacing = 90/len(term_list) #print(term_list) sfinal = "" for id_term in conn.monitor(term_list, start=10, end=90, period=0.05, prefix="Model building for terms..."): sfinal += "%d " % id_term (xc, yc, xr, yr) = getcoordsim(in_txt, id_term, tr_im) nimages = np.max(xc.shape) mx = np.mean(xr) my = np.mean(yr) P = np.zeros((2, nimages)) P[0, :] = xr P[1, :] = yr cm = np.cov(P) passe = False # additional parameters feature_parameters = None if conn.parameters.model_feature_type.lower() == 'gaussian': std_matrix = np.eye(2) * ( conn.parameters.model_feature_gaussian_std**2) feature_parameters = np.round( np.random.multivariate_normal( [0, 0], std_matrix, conn.parameters.model_feature_gaussian_n)).astype(int) elif conn.parameters.model_feature_type.lower() == 'haar': W = conn.parameters.model_wsize n = conn.parameters.model_feature_haar_n / ( 5 * conn.parameters.model_depth) h2 = generate_2_horizontal(W, n) v2 = generate_2_vertical(W, n) h3 = generate_3_horizontal(W, n) v3 = generate_3_vertical(W, n) sq = generate_square(W, n) feature_parameters = (h2, v2, h3, v3, sq) for times in range(conn.parameters.model_ntimes): if times == 0: rangrange = 0 else: rangrange = conn.parameters.model_angle T = build_datasets_rot_mp( in_path, tr_im, xc, yc, conn.parameters.model_R, conn.parameters.model_RMAX, conn.parameters.model_P, conn.parameters.model_step, rangrange, conn.parameters.model_wsize, conn.parameters.model_feature_type, feature_parameters, depths, nimages, 'jpg', conn.parameters.model_njobs) for i in range(len(T)): (data, rep, img) = T[i] (height, width) = data.shape if not passe: passe = True DATA = np.zeros((height * (len(T) + 100) * conn.parameters.model_ntimes, width)) REP = np.zeros(height * (len(T) + 100) * conn.parameters.model_ntimes) b = 0 be = height DATA[b:be, :] = data REP[b:be] = rep b = be be = be + height REP = REP[0:b] DATA = DATA[0:b, :] clf = ExtraTreesClassifier( n_jobs=conn.parameters.model_njobs, n_estimators=conn.parameters.model_ntrees) clf = clf.fit(DATA, REP) parameters_hash = {} parameters_hash[ 'cytomine_id_terms'] = conn.parameters.cytomine_id_terms parameters_hash['model_R'] = conn.parameters.model_R parameters_hash['model_RMAX'] = conn.parameters.model_RMAX parameters_hash['model_P'] = conn.parameters.model_P parameters_hash['model_npred'] = conn.parameters.model_npred parameters_hash['model_ntrees'] = conn.parameters.model_ntrees parameters_hash['model_ntimes'] = conn.parameters.model_ntimes parameters_hash['model_angle'] = conn.parameters.model_angle parameters_hash['model_depth'] = conn.parameters.model_depth parameters_hash['model_step'] = conn.parameters.model_step parameters_hash['window_size'] = conn.parameters.model_wsize parameters_hash[ 'feature_type'] = conn.parameters.model_feature_type parameters_hash[ 'feature_haar_n'] = conn.parameters.model_feature_haar_n parameters_hash[ 'feature_gaussian_n'] = conn.parameters.model_feature_gaussian_n parameters_hash[ 'feature_gaussian_std'] = conn.parameters.model_feature_gaussian_std model_filename = joblib.dump(clf, os.path.join( out_path, '%d_model.joblib' % (id_term)), compress=3)[0] cov_filename = joblib.dump([mx, my, cm], os.path.join( out_path, '%d_cov.joblib' % (id_term)), compress=3)[0] parameter_filename = joblib.dump( parameters_hash, os.path.join(out_path, '%d_parameters.joblib' % id_term), compress=3)[0] AttachedFile( conn.job, domainIdent=conn.job.id, filename=model_filename, domainClassName="be.cytomine.processing.Job").upload() AttachedFile( conn.job, domainIdent=conn.job.id, filename=cov_filename, domainClassName="be.cytomine.processing.Job").upload() AttachedFile( conn.job, domainIndent=conn.job.id, filename=parameter_filename, domainClassName="be.cytomine.processing.Job").upload() if conn.parameters.model_feature_type == 'haar' or conn.parameters.model_feature_type == 'gaussian': add_filename = joblib.dump( feature_parameters, out_path.rstrip('/') + '/' + '%d_fparameters.joblib' % (id_term))[0] AttachedFile( conn.job, domainIdent=conn.job.id, filename=add_filename, domainClassName="be.cytomine.processing.Job").upload() Property(conn.job, key="id_terms", value=sfinal.rstrip(" ")).save() conn.job.update(progress=100, status=Job.TERMINATED, statusComment="Job terminated.")
conn.job, domainIdent=conn.job.id, filename=model_filename, domainClassName="be.cytomine.processing.Job").upload() conn.job.update(status=Job.RUNNING, progress=80, statusComment="Computing the post-processing model...") xt = procrustes(Xc, Yc) (mu, P) = apply_pca(xt, conn.parameters.model_n_reduc) muP_filename = joblib.dump((mu, P), 'muP.joblib', compress=3)[0] features_filename = joblib.dump((h2, v2, h3, v3, sq), 'features.joblib', compress=3)[0] coords_filename = joblib.dump((xc, yc), 'coords.joblib', compress=3)[0] AttachedFile(conn.job, domainIdent=conn.job.id, filename=muP_filename, domainClassName="be.cytomine.processing.Job").upload() AttachedFile(conn.job, domainIdent=conn.job.id, filename=features_filename, domainClassName="be.cytomine.processing.Job").upload() AttachedFile(conn.job, domainIdent=conn.job.id, filename=coords_filename, domainClassName="be.cytomine.processing.Job").upload() Property(conn.job, key="id_terms", value=sfinal.rstrip(" ")).save() conn.job.update(progress=100, status=Job.TERMINATED, statusComment="Job terminated.")
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...") cj.job.update(progress=30, statusComment="Execution: download model...") model_job = Job().fetch(cj.parameters.model_job_id) model_path = load_model(model_job, tmp_path, model_filename="weights.hf5") height, width = load_property(model_job, "image_height"), load_property( model_job, "image_width") n_channels = load_property(model_job, "n_channels") train_mean = load_property(model_job, "train_mean") train_std = load_property(model_job, "train_std") # load data cj.job.update(progress=30, statusComment="Execution: preparing data...") dims = height, width, n_channels # load input images images = load_data( cj, dims, in_path, **{ "start": 35, "end": 45, "period": 0.1, "prefix": "Execution: load training input images" }) images -= train_mean images /= train_std # load model cj.job.update(progress=45, statusComment="Execution: build model...") unet = create_unet(dims) unet.load_weights(model_path) # inference masks = np.zeros([len(images), 1, dims[0], dims[1]], dtype=np.uint8) for i, image_name in cj.monitor(enumerate(images), start=45, end=55, period=0.1, prefix="Execution: inference"): masks[i] = unet.predict([images[i]])[0] cv2.imwrite(os.path.join(out_path, image_name), (masks[i] >= cj.parameters.threshold_probas).astype( np.uint8)) # 4. Upload the annotation and masks to Cytomine (annotations are extracted from the mask using # the AnnotationExporter module) # for image in cj.monitor(input_images, start=60, end=80, period=0.1, prefix="Extracting and uploading polygons from masks"): # file = "{}.tif".format(image.id) # path = os.path.join(out_path, file) # data = io.imread(path) # # # extract objects # slices = mask_to_objects_2d(data) # # print("Found {} polygons in this image {}.".format(len(slices), image.id)) # # # upload # collection = AnnotationCollection() # for obj_slice in slices: # collection.append(Annotation( # location=affine_transform(obj_slice.polygon, [1, 0, 0, -1, 0, image.height]).wkt, # id_image=image.id, id_project=cj.parameters.cytomine_id_project, property=[ # {"key": "index", "value": str(obj_slice.label)} # ] # )) # collection.save() # 5. Compute and upload the metrics cj.job.update(progress=80, statusComment="Computing and uploading metrics...") outfiles, reffiles = zip( *[(os.path.join(out_path, "{}.tif".format(image.id)), os.path.join(gt_path, "{}.tif".format(image.id))) for image in input_images]) results = computemetrics_batch(outfiles, reffiles, "PixCla", tmp_path) for key, value in results.items(): Property(cj.job, key=key, value=str(value)).save() Property(cj.job, key="IMAGE_INSTANCES", value=str([im.id for im in input_images])).save() # 6. End cj.job.update(status=Job.TERMINATED, progress=100, statusComment="Finished.")
def main(): with NeubiasJob.from_cli(sys.argv) as conn: problem_cls = get_discipline(conn, default=CLASS_LNDDET) is_2d = True conn.job.update(status=Job.RUNNING, progress=0, statusComment="Initialization of the training phase") in_images, gt_images, in_path, gt_path, out_path, tmp_path = prepare_data( problem_cls, conn, is_2d=is_2d, **conn.flags) tmax = 1 for f in os.listdir(gt_path): if f.endswith('.tif'): gt_img = imageio.imread(os.path.join(gt_path, f)) tmax = np.max(gt_img) break term_list = range(1, tmax + 1) depths = 1. / (2.**np.arange(conn.parameters.model_depth)) tr_im = [ int(id_im) for id_im in conn.parameters.cytomine_training_images.split(',') ] DATA = None REP = None be = 0 sfinal = "" for id_term in term_list: sfinal += "%d " % id_term sfinal = sfinal.rstrip(' ') for id_term in conn.monitor(term_list, start=10, end=90, period=0.05, prefix="Model building for terms..."): (xc, yc, xr, yr) = getcoordsim_neubias(gt_path, id_term, tr_im) nimages = np.max(xc.shape) mx = np.mean(xr) my = np.mean(yr) P = np.zeros((2, nimages)) P[0, :] = xr P[1, :] = yr cm = np.cov(P) passe = False # additional parameters feature_parameters = None if conn.parameters.model_feature_type.lower() == 'gaussian': std_matrix = np.eye(2) * ( conn.parameters.model_feature_gaussian_std**2) feature_parameters = np.round( np.random.multivariate_normal( [0, 0], std_matrix, conn.parameters.model_feature_gaussian_n)).astype(int) elif conn.parameters.model_feature_type.lower() == 'haar': W = conn.parameters.model_wsize n = conn.parameters.model_feature_haar_n / ( 5 * conn.parameters.model_depth) h2 = generate_2_horizontal(W, n) v2 = generate_2_vertical(W, n) h3 = generate_3_horizontal(W, n) v3 = generate_3_vertical(W, n) sq = generate_square(W, n) feature_parameters = (h2, v2, h3, v3, sq) for times in range(conn.parameters.model_ntimes): if times == 0: rangrange = 0 else: rangrange = conn.parameters.model_angle T = build_datasets_rot_mp( in_path, tr_im, xc, yc, conn.parameters.model_R, conn.parameters.model_RMAX, conn.parameters.model_P, conn.parameters.model_step, rangrange, conn.parameters.model_wsize, conn.parameters.model_feature_type, feature_parameters, depths, nimages, 'tif', conn.parameters.model_njobs) for i in range(len(T)): (data, rep, img) = T[i] (height, width) = data.shape if not passe: passe = True DATA = np.zeros((height * (len(T) + 100) * conn.parameters.model_ntimes, width)) REP = np.zeros(height * (len(T) + 100) * conn.parameters.model_ntimes) b = 0 be = height DATA[b:be, :] = data REP[b:be] = rep b = be be = be + height REP = REP[0:b] DATA = DATA[0:b, :] clf = ExtraTreesClassifier( n_jobs=conn.parameters.model_njobs, n_estimators=conn.parameters.model_ntrees) clf = clf.fit(DATA, REP) parameters_hash = {} parameters_hash['cytomine_id_terms'] = sfinal.replace(' ', ',') parameters_hash['model_R'] = conn.parameters.model_R parameters_hash['model_RMAX'] = conn.parameters.model_RMAX parameters_hash['model_P'] = conn.parameters.model_P parameters_hash['model_npred'] = conn.parameters.model_npred parameters_hash['model_ntrees'] = conn.parameters.model_ntrees parameters_hash['model_ntimes'] = conn.parameters.model_ntimes parameters_hash['model_angle'] = conn.parameters.model_angle parameters_hash['model_depth'] = conn.parameters.model_depth parameters_hash['model_step'] = conn.parameters.model_step parameters_hash['window_size'] = conn.parameters.model_wsize parameters_hash[ 'feature_type'] = conn.parameters.model_feature_type parameters_hash[ 'feature_haar_n'] = conn.parameters.model_feature_haar_n parameters_hash[ 'feature_gaussian_n'] = conn.parameters.model_feature_gaussian_n parameters_hash[ 'feature_gaussian_std'] = conn.parameters.model_feature_gaussian_std model_filename = joblib.dump(clf, os.path.join( out_path, '%d_model.joblib' % (id_term)), compress=3)[0] cov_filename = joblib.dump([mx, my, cm], os.path.join( out_path, '%d_cov.joblib' % (id_term)), compress=3)[0] parameter_filename = joblib.dump( parameters_hash, os.path.join(out_path, '%d_parameters.joblib' % id_term), compress=3)[0] AttachedFile( conn.job, domainIdent=conn.job.id, filename=model_filename, domainClassName="be.cytomine.processing.Job").upload() AttachedFile( conn.job, domainIdent=conn.job.id, filename=cov_filename, domainClassName="be.cytomine.processing.Job").upload() AttachedFile( conn.job, domainIndent=conn.job.id, filename=parameter_filename, domainClassName="be.cytomine.processing.Job").upload() if conn.parameters.model_feature_type == 'haar' or conn.parameters.model_feature_type == 'gaussian': add_filename = joblib.dump( feature_parameters, out_path.rstrip('/') + '/' + '%d_fparameters.joblib' % (id_term))[0] AttachedFile( conn.job, domainIdent=conn.job.id, filename=add_filename, domainClassName="be.cytomine.processing.Job").upload() Property(conn.job, key="id_terms", value=sfinal.rstrip(" ")).save() conn.job.update(progress=100, status=Job.TERMINATED, statusComment="Job terminated.")
def end_successful_import(self, path: Path, image: Image, *args, **kwargs): uf = self.get_uf(path) ai = AbstractImage() ai.uploadedFile = uf.id ai.originalFilename = uf.originalFilename ai.width = image.width ai.height = image.height ai.depth = image.depth ai.duration = image.duration ai.channels = image.n_intrinsic_channels ai.extrinsicChannels = image.n_channels if image.physical_size_x: ai.physicalSizeX = round( convert_quantity(image.physical_size_x, "micrometers"), 6) if image.physical_size_y: ai.physicalSizeY = round( convert_quantity(image.physical_size_y, "micrometers"), 6) if image.physical_size_z: ai.physicalSizeZ = round( convert_quantity(image.physical_size_z, "micrometers"), 6) if image.frame_rate: ai.fps = round(convert_quantity(image.frame_rate, "Hz"), 6) ai.magnification = parse_int(image.objective.nominal_magnification) ai.bitPerSample = dtype_to_bits(image.pixel_type) ai.samplePerPixel = image.n_channels / image.n_intrinsic_channels ai.save() self.abstract_images.append(ai) asc = AbstractSliceCollection() set_channel_names = image.n_intrinsic_channels == image.n_channels for c in range(image.n_intrinsic_channels): name = None color = None if set_channel_names: name = image.channels[c].suggested_name color = image.channels[c].hex_color for z in range(image.depth): for t in range(image.duration): mime = "image/pyrtiff" # TODO: remove asc.append( AbstractSlice(ai.id, uf.id, mime, c, z, t, channelName=name, channelColor=color)) asc.save() properties = PropertyCollection(ai) for metadata in image.raw_metadata.values(): if metadata.value is not None and str(metadata.value) != '': properties.append( Property(ai, metadata.namespaced_key, str(metadata.value))) try: properties.save() except CollectionPartialUploadException: pass # TODO: improve handling of this exception, but prevent to fail the import uf.status = UploadedFile.DEPLOYED uf.update() properties = PropertyCollection(ai) for k, v in self.user_properties: if v is not None and str(v) != '': properties.append(Property(ai, k, v)) try: properties.save() except CollectionPartialUploadException: pass # TODO: improve handling of this exception, but prevent to fail the import instances = [] for p in self.projects: instances.append(ImageInstance(ai.id, p.id).save()) self.images.append((ai, instances)) # TODO: temporary add annotations for backwards compatibility. # BUT it should be done by core when an image instance is created. if image.n_planes == 1 and len(instances) > 0: # TODO: currently only supports metadata annots on 2D images metadata_annots = image.annotations if len(metadata_annots) > 0: metadata_terms = [ ma.terms for ma in metadata_annots if len(ma.terms) > 0 ] metadata_terms = set(flatten(metadata_terms)) for instance in instances: project_id = instance.project project = self.projects.find_by_attribute('id', project_id) ontology_id = project.ontology # noqa ontology_terms = TermCollection().fetch_with_filter( "project", project_id) terms_id_mapping = {t.name: t.id for t in ontology_terms} for metadata_term in metadata_terms: if metadata_term not in terms_id_mapping: # TODO: user must have ontology rights ! term = Term(name=metadata_term, id_ontology=ontology_id, color="#AAAAAA").save() terms_id_mapping[term.name] = term.id annots = AnnotationCollection() for metadata_annot in metadata_annots: term_ids = [ terms_id_mapping[t] for t in metadata_annot.terms ] properties = [ dict(key=k, value=v) for k, v in metadata_annot.properties.items() ] annots.append( Annotation(location=metadata_annot.wkt, id_image=instance.id, id_terms=term_ids if len(term_ids) > 0 else None, properties=properties if len(properties) > 0 else None, user=uf.user)) try: annots.save() except CollectionPartialUploadException: pass
def run(debug=False): """ Gets project image from cytomine Args: debug (bool): If true will save annotations individually and plot any error Example: python main.py --cytomine_host 'localhost-core' --cytomine_public_key 'dadb7d7a-5822-48f7-ab42-59bce27750ae' --cytomine_private_key 'd73f4602-51d2-4d15-91e4-d4cc175d65fd' --cytomine_id_project 187 --cytomine_id_image_instance 375 --cytomine_id_software 228848 python main.py --cytomine_host 'localhost-core' --cytomine_public_key 'b6ebb23c-00ff-427b-be24-87b2a82490df' --cytomine_private_key '6812f09b-3f33-4938-82ca-b23032d377fd' --cytomine_id_project 154 --cytomine_id_image_instance 3643 python main.py --cytomine_host 'localhost-core' --cytomine_public_key 'd2be8bd7-2b0b-40c3-9e81-5ad5765568f3' --cytomine_private_key '6dfe27d7-2ad1-4ca2-8ee9-6321ec3f1318' --cytomine_id_project 197 --cytomine_id_image_instance 2140 --cytomine_id_software 2633 docker run --gpus all -it --rm --mount type=bind,source=/home/giussepi/Public/environments/Cytomine/cyto_CRLM/,target=/CRLM,bind-propagation=private --network=host ttt --cytomine_host 'localhost-core' --cytomine_public_key 'd2be8bd7-2b0b-40c3-9e81-5ad5765568f3' --cytomine_private_key '6dfe27d7-2ad1-4ca2-8ee9-6321ec3f1318' --cytomine_id_project 197 --cytomine_id_image_instance 31296 --cytomine_id_software 79732 """ parser = ArgumentParser(prog="Cytomine Python client example") # Cytomine connection parameters parser.add_argument('--cytomine_host', dest='host', default='demo.cytomine.be', help="The Cytomine host") parser.add_argument('--cytomine_public_key', dest='public_key', help="The Cytomine public key") parser.add_argument('--cytomine_private_key', dest='private_key', help="The Cytomine private key") parser.add_argument('--cytomine_id_project', dest='id_project', help="The project from which we want the images") parser.add_argument('--cytomine_id_software', dest='id_software', help="The software to be used to process the image") parser.add_argument('--cytomine_id_image_instance', dest='id_image_instance', help="The image to which the annotation will be added") params, _ = parser.parse_known_args(sys.argv[1:]) with CytomineJob.from_cli(sys.argv[1:]) as cytomine: # TODO: To be tested on TITANx img = ImageInstance().fetch(params.id_image_instance) download_image(img) process_wsi_and_save(get_container_image_path(img)) new_annotations = generate_polygons(get_container_image_path(img), adapt_to_cytomine=True) annotation_collection = None for label_key in new_annotations: # Sending annotation batches to the server for sub_list in chunks(new_annotations[label_key], ANNOTATION_BATCH): if not debug: annotation_collection = AnnotationCollection() for exterior_points in sub_list: if debug: annotation_collection = AnnotationCollection() annotation_collection.append( Annotation(location=Polygon( exterior_points.astype(int).reshape( exterior_points.shape[0], exterior_points.shape[2]).tolist()).wkt, id_image=params.id_image_instance, id_project=params.id_project, id_terms=[CYTOMINE_LABELS[label_key]])) if debug: try: annotation_collection.save() except Exception as e: print( exterior_points.astype(int).reshape( exterior_points.shape[0], exterior_points.shape[2]).tolist()) plt.plot(*Polygon( exterior_points.astype(int).reshape( exterior_points.shape[0], exterior_points. shape[2])).exterior.coords.xy) plt.show() # raise(e) print(e) finally: time.sleep(1) if not debug: annotation_collection.save() time.sleep(ANNOTATION_SLEEP_TIME) # Adding pie chart labels data as image property # TODO: Change delete_results_file to True for final test on titanX num_pixels_per_label = get_pie_chart_data( get_container_image_path(img), delete_results_file=False) for percentage, label_ in zip(num_pixels_per_label, Label.names): Property(img, key=label_, value='{}%'.format(percentage)).save() remove_image_local_copy(img) cytomine.job.update(statusComment="Finished.")
def create_tracking_from_slice_group(image, slices, slice2point, depth2slice, id_project, upload_object=False, track_prefix="object", label=None, upload_group_id=False): """Create a set of tracks and annotations to represent a tracked element. A trackline is created to reflect the movement of the object in the image. Optionally the object's polygon can also be uploaded. Parameters ---------- image: ImageInstance An ImageInstance slices: list of AnnotationSlice A list of AnnotationSlice of one object slice2point: callable A function that transform a slice into its representative point to be used for generating the tracking line depth2slice: dict Maps time step with corresponding SliceInstance id_project: int Project identifier upload_object: bool True if the object should be uploaded as well (the trackline is uploaded in any case) track_prefix: str A prefix for the track name label: int (default: None) The label of the tracked object upload_group_id: bool True for uploading the object label with the track Returns ------- saved_tracks: TrackCollection The saved track objects annotations: AnnotationCollection The annotations associated with the traped. The collection is NOT saved. """ if label is None and len(slices) > 0: label = slices[0].label # create tracks tracks = TrackCollection() object_track = Track( "{}-{}".format(track_prefix, label), image.id, color=None if upload_group_id else DEFAULT_COLOR).save() trackline_track = Track( "{}-{}-trackline".format(track_prefix, label), image.id, color=None if upload_group_id else DEFAULT_COLOR).save() tracks.extend([object_track, trackline_track]) if upload_group_id: Property(object_track, key="label", value=int(label)).save() Property(trackline_track, key="label", value=int(label)).save() # create actual annotations annotations = AnnotationCollection() sorted_group = sorted(slices, key=lambda s: s.time) prev_line = [] for _slice in sorted_group: point = slice2point(_slice) if point.is_empty: # skip empty points continue if len(prev_line) == 0 or not prev_line[-1].equals(point): prev_line.append(point) if len(prev_line) == 1: polygon = slice2point(_slice) else: polygon = LineString(prev_line) depth = _slice.time if _slice.depth is None else _slice.depth annotations.append( Annotation(location=change_referential(polygon, image.height).wkt, id_image=image.id, slice=depth2slice[depth].id, id_project=id_project, id_tracks=[trackline_track.id])) if upload_object: annotations.append( Annotation(location=change_referential(_slice.polygon, image.height).wkt, id_image=image.id, slice=depth2slice[depth].id, id_project=id_project, id_tracks=[object_track.id])) return tracks, annotations
def extract_annotations_objtrk(out_path, in_image, project_id, track_prefix, **kwargs): """ out_path: str in_image: BiaflowsCytomineInput project_id: int track_prefix: str kwargs: dict """ image = in_image.object path = os.path.join(out_path, in_image.filename) data, dim_order, _ = imread(path, return_order=True) ndim = get_dimensionality(dim_order) if ndim < 3: raise ValueError( "Object tracking should be at least 3D (only {} spatial dimension(s) found)" .format(ndim)) tracks = TrackCollection() annotations = AnnotationCollection() if ndim == 3: slices = mask_to_objects_3d(data, time=True, assume_unique_labels=True) time_to_image = get_depth_to_slice(image) for slice_group in slices: curr_tracks, curr_annots = create_tracking_from_slice_group( image, slice_group, slice2point=lambda _slice: _slice.polygon.centroid, depth2slice=time_to_image, id_project=project_id, upload_object=True, upload_group_id=True, track_prefix=track_prefix + "-object") tracks.extend(curr_tracks) annotations.extend(curr_annots) elif ndim == 4: objects = mask_to_objects_3dt(mask=data) depths_to_image = get_depth_to_slice(image, depth=("time", "depth")) # TODO add tracking lines one way or another for time_steps in objects: label = time_steps[0][0].label track = Track(name="{}-{}".format(track_prefix, label), id_image=image.id, color=DEFAULT_COLOR).save() Property(track, key="label", value=label).save() annotations.extend([ Annotation(location=change_referential( p=slice.polygon, height=image.height).wkt, id_image=image.id, id_project=project_id, id_tracks=[track.id], slice=depths_to_image[(slice.time, slice.depth)].id) for slices in time_steps for slice in slices ]) tracks.append(track) else: raise ValueError( "Annotation extraction for object tracking does not support masks with more than 4 dims..." ) return tracks, annotations
parser.add_argument('--key', help="the property key") parser.add_argument('--value', help="the property value") parser.add_argument('--cytomine_id_project', dest='id_project', required=False, help="The project to which the property will be added (optional)") parser.add_argument('--cytomine_id_image_instance', dest='id_image_instance', required=False, help="The image to which the property will be added (optional)") parser.add_argument('--cytomine_id_annotation', dest='id_annotation', required=False, help="The annotation to which the property will be added (optional)") params, other = parser.parse_known_args(sys.argv[1:]) with Cytomine(host=params.host, public_key=params.public_key, private_key=params.private_key, verbose=logging.INFO) as cytomine: if params.id_project: prop = Property(Project().fetch(params.id_project), key=params.key, value=params.value).save() print(prop) if params.id_image_instance: prop = Property(ImageInstance().fetch(params.id_image_instance), key=params.key, value=params.value).save() print(prop) if params.id_annotation: prop = Property(Annotation().fetch(params.id_annotation), key=params.key, value=params.value).save() print(prop) """ You can add property to any Cytomine domain. You can also attach a file (see AttachedFile) or add a description (see Description) to any Cytomine domain. """
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 main(argv): with CytomineJob.from_cli(argv) as cj: # use only images from the current project cj.job.update( progress=1, statusComment="Preparing execution (creating folders,...).") # hardcode parameter for setup classify to fetch alphamask instead of plain crop. cj.parameters.cytomine_download_alpha = True cj.parameters.cytomine_id_projects = "{}".format(cj.project.id) cj.job.update(progress=2, statusComment="Downloading crops.") base_path, downloaded = setup_classify(args=cj.parameters, logger=cj.job_logger(2, 25), dest_pattern=os.path.join( "{term}", "{image}_{id}.png"), root_path=str("tmp"), set_folder="train", showTerm=True) x = np.array( [f for annotation in downloaded for f in annotation.filenames]) y = np.array([ int(os.path.basename(os.path.dirname(filepath))) for filepath in x ]) # transform classes cj.job.update(progress=25, statusComment="Transform classes...") positive_terms = parse_domain_list( cj.parameters.cytomine_id_positive_terms) selected_terms = parse_domain_list(cj.parameters.cytomine_id_terms) is_binary = len(selected_terms) > 0 and len(positive_terms) > 0 foreground_terms = np.unique(y) if len( selected_terms) == 0 else np.array(selected_terms) if len(positive_terms) == 0: classes = np.hstack((np.zeros((1, ), dtype=int), foreground_terms)) else: # binary foreground_terms = np.array(positive_terms) classes = np.array([0, 1]) # cast to binary fg_idx = np.in1d(y, list(foreground_terms)) bg_idx = np.in1d( y, list(set(selected_terms).difference(foreground_terms))) y[fg_idx] = 1 y[bg_idx] = 0 n_classes = classes.shape[0] # filter unwanted terms cj.logger.info("Size before filtering:") cj.logger.info(" - x: {}".format(x.shape)) cj.logger.info(" - y: {}".format(y.shape)) keep = np.in1d(y, classes) x, y = x[keep], y[keep] cj.logger.info("Size after filtering:") cj.logger.info(" - x: {}".format(x.shape)) cj.logger.info(" - y: {}".format(y.shape)) if x.shape[0] == 0: raise ValueError("No training data") if is_binary: # 0 (background) vs 1 (classes in foreground ) cj.logger.info("Binary segmentation:") cj.logger.info("> class '0': background & terms {}".format( set(selected_terms).difference(positive_terms))) cj.logger.info("> class '1': {}".format(set(foreground_terms))) else: # 0 (background vs 1 vs 2 vs ... n (n classes from cytomine_id_terms) cj.logger.info("Multi-class segmentation:") cj.logger.info("> background class '0'") cj.logger.info("> term classes: {}".format(set(foreground_terms))) # build model cj.job.update(progress=27, statusComment="Build model...") et, pyxit = build_models( n_subwindows=cj.parameters.pyxit_n_subwindows, min_size=cj.parameters.pyxit_min_size, max_size=cj.parameters.pyxit_max_size, target_width=cj.parameters.pyxit_target_width, target_height=cj.parameters.pyxit_target_height, interpolation=cj.parameters.pyxit_interpolation, transpose=cj.parameters.pyxit_transpose, colorspace=cj.parameters.pyxit_colorspace, fixed_size=cj.parameters.pyxit_fixed_size, verbose=int(cj.logger.level == 10), random_state=cj.parameters.seed, n_estimators=cj.parameters.forest_n_estimators, min_samples_split=cj.parameters.forest_min_samples_split, max_features=cj.parameters.forest_max_features, n_jobs=cj.parameters.n_jobs) # to extract the classes form the mask pyxit.get_output = _get_output_from_mask # extract subwindows manually to avoid class problem cj.job.update(progress=30, statusComment="Extract subwindwos...") _x, _y = pyxit.extract_subwindows(x, y) actual_classes = np.unique(_y) if actual_classes.shape[0] != classes.shape[0]: raise ValueError( "Some classes are missing from the dataset: actual='{}', expected='{}'" .format(",".join(map(str, actual_classes)), ",".join(map(str, classes)))) cj.logger.info("Size of actual training data:") cj.logger.info(" - x : {}".format(_x.shape)) cj.logger.info(" - y : {}".format(_y.shape)) cj.logger.info(" - dist: {}".format(", ".join([ "{}: {}".format(v, c) for v, c in zip(*np.unique(_y, return_counts=True)) ]))) cj.job.update(progress=60, statusComment="Train model...") # "re-implement" pyxit.fit to avoid incorrect class handling pyxit.classes_ = classes pyxit.n_classes_ = n_classes pyxit.base_estimator.fit(_x, _y) cj.job.update(progress=90, statusComment="Save model....") model_filename = joblib.dump(pyxit, os.path.join(base_path, "model.joblib"), compress=3)[0] AttachedFile(cj.job, domainIdent=cj.job.id, filename=model_filename, domainClassName="be.cytomine.processing.Job").upload() Property(cj.job, key="classes", value=stringify(classes)).save() Property(cj.job, key="binary", value=is_binary).save() cj.job.update(status=Job.TERMINATED, status_comment="Finish", progress=100)
def load_property(job, property_name): property = Property(job, key=property_name).fetch() return property.value
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()
annotations.showGIS = True annotations.fetch() print(annotations) for annotation in annotations: print( "ID: {} | Image: {} | Project: {} | Term: {} | User: {} | Area: {} | Perimeter: {} | WKT: {}" .format(annotation.id, annotation.image, annotation.project, annotation.term, annotation.user, annotation.area, annotation.perimeter, annotation.location)) annot = Annotation().fetch(annotation.id) # Toutes les proprietes (collection) de l annotation properties = PropertyCollection(annot).fetch() # Une propriété avec une clé spécifique de l'annotation propert = Property(annot).fetch(key="ANNOTATION_GROUP_ID") image_id = str(annotation.image) if image_id in id2info: tissue, dye = id2info[image_id] path_patch = os.path.join(params.download_path, str(params.size), tissue, dye, str(propert.value) + ".jpg") if params.download_path and not os.path.exists(path_patch): # default size is 300x300 annotation.dump(dest_pattern=path_patch, increase_area=params.size / 100)
def main(): with NeubiasJob.from_cli(sys.argv) as conn: problem_cls = get_discipline(conn, default=CLASS_LNDDET) conn.job.update(progress=0, status=Job.RUNNING, statusComment="Initialization of the training phase...") in_images, gt_images, in_path, gt_path, out_path, tmp_path = prepare_data(problem_cls, conn, is_2d=True, **conn.flags) tmax = 1 for f in os.listdir(gt_path): if f.endswith('.tif'): gt_img = imageio.imread(os.path.join(gt_path, f)) tmax = np.max(gt_img) break term_list = range(1, tmax + 1) tr_im = [int(id_im) for id_im in conn.parameters.cytomine_training_images.split(',')] (xc, yc, xr, yr) = get_neubias_coords(gt_path, tr_im) (nims, nldms) = xc.shape Xc = np.zeros((nims, len(term_list))) Yc = np.zeros(Xc.shape) for id_term in term_list: Xc[:, id_term - 1] = xc[:, id_term - 1] Yc[:, id_term - 1] = yc[:, id_term - 1] conn.job.update(progress=10, status=Job.RUNNING, statusComment="Building model for phase 1") (dataset, rep, img, feature_offsets_1) = build_phase_1_model(in_path, image_ids=tr_im, n_jobs=conn.parameters.model_njobs, F=conn.parameters.model_F_P1, R=conn.parameters.model_R_P1, sigma=conn.parameters.model_sigma, delta=conn.parameters.model_delta, P=conn.parameters.model_P, X=Xc, Y=Yc) clf = SeparateTrees(n_estimators=int(conn.parameters.model_NT_P1), n_jobs=int(conn.parameters.model_njobs)) clf = clf.fit(dataset, rep) model_filename = joblib.dump(clf, os.path.join(out_path, 'model_phase1.joblib'), compress=3)[0] AttachedFile( conn.job, domainIdent=conn.job.id, filename=model_filename, domainClassName="be.cytomine.processing.Job" ).upload() model_filename = joblib.dump((Xc, Yc), os.path.join(out_path, 'coords.joblib'), compress=3)[0] AttachedFile( conn.job, domainIdent=conn.job.id, filename=model_filename, domainClassName="be.cytomine.processing.Job" ).upload() model_filename = joblib.dump(feature_offsets_1, os.path.join(out_path, 'offsets_phase1.joblib'), compress=3)[0] AttachedFile( conn.job, domainIdent=conn.job.id, filename=model_filename, domainClassName="be.cytomine.processing.Job" ).upload() for id_term in conn.monitor(term_list, start=20, end=80, period=0.05,prefix="Visual model building for terms..."): (dataset, rep, number, feature_offsets_2) = build_phase_2_model(in_path, image_ids=tr_im, n_jobs=conn.parameters.model_njobs, NT=conn.parameters.model_NT_P2, F=conn.parameters.model_F_P2, R=conn.parameters.model_R_P2, N=conn.parameters.model_ns_P2, sigma=conn.parameters.model_sigma, delta=conn.parameters.model_delta, Xc = Xc[:, id_term-1], Yc = Yc[:, id_term-1]) reg = SeparateTreesRegressor(n_estimators=int(conn.parameters.model_NT_P2), n_jobs=int(conn.parameters.model_njobs)) reg.fit(dataset, rep) model_filename = joblib.dump(reg, os.path.join(out_path, 'reg_%d_phase2.joblib'%id_term), compress=3)[0] AttachedFile( conn.job, domainIdent=conn.job.id, filename=model_filename, domainClassName="be.cytomine.processing.Job" ).upload() model_filename = joblib.dump(feature_offsets_2, os.path.join(out_path, 'offsets_%d_phase2.joblib' % id_term), compress=3)[0] AttachedFile( conn.job, domainIdent=conn.job.id, filename=model_filename, domainClassName="be.cytomine.processing.Job" ).upload() conn.job.update(progress=90, status=Job.RUNNING, statusComment="Building model for phase 3") edges = build_edgematrix_phase_3(Xc, Yc, conn.parameters.model_sde, conn.parameters.model_delta, conn.parameters.model_T) model_filename = joblib.dump(edges, os.path.join(out_path, 'model_edges.joblib'), compress=3)[0] AttachedFile( conn.job, domainIdent=conn.job.id, filename=model_filename, domainClassName="be.cytomine.processing.Job" ).upload() sfinal = "" for id_term in term_list: sfinal += "%d " % id_term sfinal = sfinal.rstrip(' ') Property(conn.job, key="id_terms", value=sfinal.rstrip(" ")).save() conn.job.update(progress=100, status=Job.TERMINATED, statusComment="Job terminated.")