def get_segmentations(experiment_id, mapobject_type_id): """ .. http:get:: /api/experiments/(string:experiment_id)/mapobject_types/(string:mapobject_type_id)/segmentations Get segmentations for each :class:`Mapobject <tmlib.model.mapobject.Mapobject>` contained within the specified :class:`Site <tmlib.models.site.Site>`. Segmentations are provided in form of a 2D labeled array, where pixel values encode object identity with unsigned integer values and background pixels are zero. **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "data": [ [1205, 7042, 4438, 7446, 3213, 8773, 5445, 9884, 8326, 6357], [4663, 2740, 9954, 5187, 309, 8029, 4502, 4927, 5259, 1802], [8117, 8489, 8495, 1194, 9788, 8182, 5431, 9969, 5931, 6490], [7974, 3990, 8892, 1866, 7890, 1147, 9630, 3687, 1565, 3613], [3977, 7318, 5252, 3270, 6746, 822, 7035, 5184, 7808, 4013], [4380, 6719, 5882, 4279, 7996, 2139, 1760, 2548, 3753, 5511], [7829, 8825, 224, 1192, 9296, 1663, 5213, 9040, 463, 9080], [6922, 6781, 9776, 9002, 6992, 8887, 9672, 8500, 1085, 563] ] } :reqheader Authorization: JWT token issued by the server :statuscode 200: no error :statuscode 400: malformed request :query plate_name: name of the plate (required) :query well_name: name of the well (required) :query well_pos_x: x-coordinate of the site within the well (required) :query well_pos_y: y-coordinate of the site within the well (required) :query tpoint: time point (required) :query zplane: z-plane (required) """ plate_name = request.args.get('plate_name') well_name = request.args.get('well_name') well_pos_x = request.args.get('well_pos_x', type=int) well_pos_y = request.args.get('well_pos_y', type=int) zplane = request.args.get('zplane', type=int) tpoint = request.args.get('tpoint', type=int) align = is_true(request.args.get('align')) logger.info( 'get segmentations for mapobject type %d of experiment %d at ' 'plate "%s", well "%s", well position %d/%d, zplane %d, time point %d', mapobject_type_id, experiment_id, plate_name, well_name, well_pos_y, well_pos_x, zplane, tpoint) with tm.utils.MainSession() as session: experiment = session.query(tm.ExperimentReference).get(experiment_id) experiment_name = experiment.name with tm.utils.ExperimentSession(experiment_id) as session: site = session.query(tm.Site).\ join(tm.Well).\ join(tm.Plate).\ filter( tm.Plate.name == plate_name, tm.Well.name == well_name, tm.Site.x == well_pos_x, tm.Site.y == well_pos_y ).\ one() mapobject_type = session.query(tm.MapobjectType).\ get(mapobject_type_id) polygons = mapobject_type.get_segmentations_per_site(site.id, tpoint=tpoint, zplane=zplane) if len(polygons) == 0: raise ResourceNotFoundError(tm.MapobjectSegmentation, request.args) if align: y_offset, x_offset = site.aligned_offset height = site.aligned_height width = site.aligned_width else: y_offset, x_offset = site.offset height = site.height width = site.width img = SegmentationImage.create_from_polygons(polygons, y_offset, x_offset, (height, width)) return jsonify(data=img.array.tolist())
def add_segmentations(experiment_id, mapobject_type_id): """ .. http:post:: /api/experiments/(string:experiment_id)/mapobject_types/(string:mapobject_type_id)/segmentations Provide segmentations in form of a labeled 2D pixels array for a given :class:`Site <tmlib.models.site.Site>`. A :class:`Mapobject <tmlib.models.mapobject.Mapobject>` and :class:`MapobjectSegmentation <tmlib.models.mapobject.MapobjectSegmentation>` will be created for each labeled connected pixel component in *image*. :reqheader Authorization: JWT token issued by the server :statuscode 200: no error :statuscode 400: malformed request :query npz_file: npz file containing the segmentation image "segmentation" (required) :query plate_name: name of the plate (required) :query well_name: name of the well (required) :query well_pos_x: x-coordinate of the site within the well (required) :query well_pos_y: y-coordinate of the site within the well (required) :query tpoint: time point (required) :query zplane: z-plane (required) """ data = request.get_json() plate_name = data.get('plate_name') well_name = data.get('well_name') well_pos_x = int(data.get('well_pos_x')) well_pos_y = int(data.get('well_pos_y')) zplane = int(data.get('zplane')) tpoint = int(data.get('tpoint')) align = is_true(request.args.get('align')) # TODO logger.info( 'add segmentations for mapobject type %d of experiment %d at ' 'plate "%s", well "%s", well position %d/%d, zplane %d, time point %d', mapobject_type_id, experiment_id, plate_name, well_name, well_pos_y, well_pos_x, zplane, tpoint) npz_file = base64.b64decode(data.get('npz_file')) pixels = np.load(BytesIO(npz_file))["segmentation"] array = np.array(pixels, dtype=np.int32) labels = np.unique(array[array > 0]) n_objects = len(labels) with tm.utils.ExperimentSession(experiment_id) as session: segmentation_layer = session.get_or_create( tm.SegmentationLayer, mapobject_type_id=mapobject_type_id, tpoint=tpoint, zplane=zplane) segmentation_layer_id = segmentation_layer.id site = session.query(tm.Site).\ join(tm.Well).\ join(tm.Plate).\ filter( tm.Plate.name == plate_name, tm.Well.name == well_name, tm.Site.y == well_pos_y, tm.Site.x == well_pos_x ).\ one() if align: y_offset, x_offset = site.aligned_offset if array.shape != site.aligned_image_size: raise MalformedRequestError('Image has wrong dimensions') else: y_offset, x_offset = site.offset if array.shape != site.image_size: raise MalformedRequestError('Image has wrong dimensions') site_id = site.id metadata = SegmentationImageMetadata(mapobject_type_id, site_id, tpoint, zplane) image = SegmentationImage(array, metadata) existing_segmentations_map = dict( session.query( tm.MapobjectSegmentation.label, tm.MapobjectSegmentation.mapobject_id ).\ join(tm.Mapobject).\ filter( tm.MapobjectSegmentation.label.in_(labels.tolist()), tm.Mapobject.mapobject_type_id == mapobject_type_id, tm.MapobjectSegmentation.partition_key == site_id ).\ all() ) with tm.utils.ExperimentSession(experiment_id, False) as session: segmentations = list() for label, polygon in image.extract_polygons(y_offset, x_offset): mapobject = tm.Mapobject(site_id, mapobject_type_id) if label in existing_segmentations_map: # A parent mapobject with the same label may already exist, # because it got already created for another zplane/tpoint. # The segmentation for the given zplane/tpoint must not yet # exist, however. This will lead to an error upon insertion. mapobject.id = existing_segmentations_map[label] else: session.add(mapobject) session.flush() s = tm.MapobjectSegmentation( partition_key=site_id, mapobject_id=mapobject.id, geom_polygon=polygon, geom_centroid=polygon.centroid, segmentation_layer_id=segmentation_layer_id, label=label) segmentations.append(s) session.bulk_ingest(segmentations) return jsonify(message='ok')
def get_channel_image_file(experiment_id, channel_id): """ .. http:get:: /api/experiments/(string:experiment_id)/channels/(string:channel_id)/image-files Get a specific image belonging to a channel. **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: image/png :query plate_name: name of the plate (required) :query cycle_index: cycle's index (required) :query well_name: name of the well (required) :query well_pos_x: x-coordinate of the site within the well (optional) :query well_pos_y: y-coordinate of the site within the well (optional) :query tpoint: time point (required) :query zplane: z-plane (required) :query illumcorr: correct image for illumination artifacts (optional) :query align: align image relative to reference cycle (optional) :reqheader Authorization: JWT token issued by the server :statuscode 200: no error :statuscode 404: no matching image found :statuscode 400: not all query parameters provided """ logger.info( 'get image of channel %d from experiment %d', channel_id, experiment_id ) plate_name = request.args.get('plate_name') well_name = request.args.get('well_name') x = request.args.get('well_pos_x', type=int) y = request.args.get('well_pos_y', type=int) cycle_index = request.args.get('cycle_index', type=int) tpoint = request.args.get('tpoint', type=int) zplane = request.args.get('zplane', type=int) illumcorr = is_true(request.args.get('correct')) align = is_true(request.args.get('align')) with tm.utils.MainSession() as session: experiment = session.query(tm.ExperimentReference).get(experiment_id) experiment_name = experiment.name with tm.utils.ExperimentSession(experiment_id) as session: site_id = session.query(tm.Site.id).\ join(tm.Well).\ join(tm.Plate).\ filter( tm.Plate.name == plate_name, tm.Well.name == well_name, tm.Site.x == x, tm.Site.y == y ).\ one()[0] channel = session.query(tm.Channel).get(channel_id) channel_name = channel.name image_file = session.query(tm.ChannelImageFile).\ join(tm.Cycle).\ filter( tm.Cycle.index == cycle_index, tm.ChannelImageFile.site_id == site_id, tm.ChannelImageFile.channel_id == channel_id, tm.ChannelImageFile.tpoint == tpoint, tm.ChannelImageFile.zplane == zplane ).\ one() img = image_file.get() if illumcorr: # TODO: cache in Redis for a limited amount of time to not having to # load the file repeatedly when user downloads multiple files of the # same channel logger.info('correct image for illumination artefacts') illumstats_file = session.query(tm.IllumstatsFile).\ filter_by(channel_id=channel_id).\ one_or_none() if illumstats_file is None: raise ResourceNotFoundError( 'No illumination statistics file found for channel %d' % channel_id ) stats = illumstats_file.get() img = img.correct(stats) if align: img = img.align() pixels = img.png_encode() f = StringIO() f.write(pixels) f.seek(0) filename = '%s_%s_%s_y%.3d_x%.3d_z%.3d_t%.3d_%s.png' % ( experiment_name, plate_name, well_name, y, x, zplane, tpoint, channel_name ) return send_file( f, attachment_filename=secure_filename(filename), mimetype='image/png', as_attachment=True )
def get_channel_image_file(experiment_id, channel_id): """ .. http:get:: /api/experiments/(string:experiment_id)/channels/(string:channel_id)/image-files Get a specific image belonging to a channel. **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: image/png :query plate_name: name of the plate (required) :query cycle_index: cycle's index (required) :query well_name: name of the well (required) :query well_pos_x: x-coordinate of the site within the well (optional) :query well_pos_y: y-coordinate of the site within the well (optional) :query tpoint: time point (required) :query zplane: z-plane (required) :query illumcorr: correct image for illumination artifacts (optional) :query align: align image relative to reference cycle (optional) :reqheader Authorization: JWT token issued by the server :statuscode 200: no error :statuscode 404: no matching image found :statuscode 400: not all query parameters provided """ logger.info('get image of channel %d from experiment %d', channel_id, experiment_id) plate_name = request.args.get('plate_name') well_name = request.args.get('well_name') x = request.args.get('well_pos_x', type=int) y = request.args.get('well_pos_y', type=int) cycle_index = request.args.get('cycle_index', type=int) tpoint = request.args.get('tpoint', type=int) zplane = request.args.get('zplane', type=int) illumcorr = is_true(request.args.get('correct')) align = is_true(request.args.get('align')) with tm.utils.MainSession() as session: experiment = session.query(tm.ExperimentReference).get(experiment_id) experiment_name = experiment.name with tm.utils.ExperimentSession(experiment_id) as session: site_id = session.query(tm.Site.id).\ join(tm.Well).\ join(tm.Plate).\ filter( tm.Plate.name == plate_name, tm.Well.name == well_name, tm.Site.x == x, tm.Site.y == y ).\ one()[0] channel = session.query(tm.Channel).get(channel_id) channel_name = channel.name image_file = session.query(tm.ChannelImageFile).\ join(tm.Cycle).\ filter( tm.Cycle.index == cycle_index, tm.ChannelImageFile.site_id == site_id, tm.ChannelImageFile.channel_id == channel_id, tm.ChannelImageFile.tpoint == tpoint, tm.ChannelImageFile.zplane == zplane ).\ one() img = image_file.get() if illumcorr: # TODO: cache in Redis for a limited amount of time to not having to # load the file repeatedly when user downloads multiple files of the # same channel logger.info('correct image for illumination artefacts') illumstats_file = session.query(tm.IllumstatsFile).\ filter_by(channel_id=channel_id).\ one_or_none() if illumstats_file is None: raise ResourceNotFoundError( 'No illumination statistics file found for channel %d' % channel_id) stats = illumstats_file.get() img = img.correct(stats) if align: img = img.align() pixels = img.png_encode() f = StringIO() f.write(pixels) f.seek(0) filename = '%s_%s_%s_y%.3d_x%.3d_z%.3d_t%.3d_%s.png' % ( experiment_name, plate_name, well_name, y, x, zplane, tpoint, channel_name) return send_file(f, attachment_filename=secure_filename(filename), mimetype='image/png', as_attachment=True)
def get_segmentations(experiment_id, mapobject_type_id): """ .. http:get:: /api/experiments/(string:experiment_id)/mapobject_types/(string:mapobject_type_id)/segmentations Get segmentations for each :class:`Mapobject <tmlib.model.mapobject.Mapobject>` contained within the specified :class:`Site <tmlib.models.site.Site>`. Segmentations are provided in form of a 2D labeled array, where pixel values encode object identity with unsigned integer values and background pixels are zero. **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "data": [ [1205, 7042, 4438, 7446, 3213, 8773, 5445, 9884, 8326, 6357], [4663, 2740, 9954, 5187, 309, 8029, 4502, 4927, 5259, 1802], [8117, 8489, 8495, 1194, 9788, 8182, 5431, 9969, 5931, 6490], [7974, 3990, 8892, 1866, 7890, 1147, 9630, 3687, 1565, 3613], [3977, 7318, 5252, 3270, 6746, 822, 7035, 5184, 7808, 4013], [4380, 6719, 5882, 4279, 7996, 2139, 1760, 2548, 3753, 5511], [7829, 8825, 224, 1192, 9296, 1663, 5213, 9040, 463, 9080], [6922, 6781, 9776, 9002, 6992, 8887, 9672, 8500, 1085, 563] ] } :reqheader Authorization: JWT token issued by the server :statuscode 200: no error :statuscode 400: malformed request :query plate_name: name of the plate (required) :query well_name: name of the well (required) :query well_pos_x: x-coordinate of the site within the well (required) :query well_pos_y: y-coordinate of the site within the well (required) :query tpoint: time point (required) :query zplane: z-plane (required) """ plate_name = request.args.get('plate_name') well_name = request.args.get('well_name') well_pos_x = request.args.get('well_pos_x', type=int) well_pos_y = request.args.get('well_pos_y', type=int) zplane = request.args.get('zplane', type=int) tpoint = request.args.get('tpoint', type=int) align = is_true(request.args.get('align')) logger.info( 'get segmentations for mapobject type %d of experiment %d at ' 'plate "%s", well "%s", well position %d/%d, zplane %d, time point %d', mapobject_type_id, experiment_id, plate_name, well_name, well_pos_y, well_pos_x, zplane, tpoint ) with tm.utils.MainSession() as session: experiment = session.query(tm.ExperimentReference).get(experiment_id) experiment_name = experiment.name with tm.utils.ExperimentSession(experiment_id) as session: site = session.query(tm.Site).\ join(tm.Well).\ join(tm.Plate).\ filter( tm.Plate.name == plate_name, tm.Well.name == well_name, tm.Site.x == well_pos_x, tm.Site.y == well_pos_y ).\ one() mapobject_type = session.query(tm.MapobjectType).\ get(mapobject_type_id) polygons = mapobject_type.get_segmentations_per_site( site.id, tpoint=tpoint, zplane=zplane ) if len(polygons) == 0: raise ResourceNotFoundError(tm.MapobjectSegmentation, request.args) if align: y_offset, x_offset = site.aligned_offset height = site.aligned_height width = site.aligned_width else: y_offset, x_offset = site.offset height = site.height width = site.width img = SegmentationImage.create_from_polygons( polygons, y_offset, x_offset, (height, width) ) return jsonify(data=img.array.tolist())
def add_segmentations(experiment_id, mapobject_type_id): """ .. http:post:: /api/experiments/(string:experiment_id)/mapobject_types/(string:mapobject_type_id)/segmentations Provide segmentations in form of a labeled 2D pixels array for a given :class:`Site <tmlib.models.site.Site>`. A :class:`Mapobject <tmlib.models.mapobject.Mapobject>` and :class:`MapobjectSegmentation <tmlib.models.mapobject.MapobjectSegmentation>` will be created for each labeled connected pixel component in *image*. :reqheader Authorization: JWT token issued by the server :statuscode 200: no error :statuscode 400: malformed request :query npz_file: npz file containing the segmentation image "segmentation" (required) :query plate_name: name of the plate (required) :query well_name: name of the well (required) :query well_pos_x: x-coordinate of the site within the well (required) :query well_pos_y: y-coordinate of the site within the well (required) :query tpoint: time point (required) :query zplane: z-plane (required) """ data = request.get_json() plate_name = data.get('plate_name') well_name = data.get('well_name') well_pos_x = int(data.get('well_pos_x')) well_pos_y = int(data.get('well_pos_y')) zplane = int(data.get('zplane')) tpoint = int(data.get('tpoint')) align = is_true(request.args.get('align')) # TODO logger.info( 'add segmentations for mapobject type %d of experiment %d at ' 'plate "%s", well "%s", well position %d/%d, zplane %d, time point %d', mapobject_type_id, experiment_id, plate_name, well_name, well_pos_y, well_pos_x, zplane, tpoint ) npz_file = base64.b64decode(data.get('npz_file')) pixels = np.load(BytesIO(npz_file))["segmentation"] array = np.array(pixels, dtype=np.int32) labels = np.unique(array[array > 0]) n_objects = len(labels) with tm.utils.ExperimentSession(experiment_id) as session: segmentation_layer = session.get_or_create( tm.SegmentationLayer, mapobject_type_id=mapobject_type_id, tpoint=tpoint, zplane=zplane ) segmentation_layer_id = segmentation_layer.id site = session.query(tm.Site).\ join(tm.Well).\ join(tm.Plate).\ filter( tm.Plate.name == plate_name, tm.Well.name == well_name, tm.Site.y == well_pos_y, tm.Site.x == well_pos_x ).\ one() if align: y_offset, x_offset = site.aligned_offset if array.shape != site.aligned_image_size: raise MalformedRequestError('Image has wrong dimensions') else: y_offset, x_offset = site.offset if array.shape != site.image_size: raise MalformedRequestError('Image has wrong dimensions') site_id = site.id metadata = SegmentationImageMetadata( mapobject_type_id, site_id, tpoint, zplane ) image = SegmentationImage(array, metadata) existing_segmentations_map = dict( session.query( tm.MapobjectSegmentation.label, tm.MapobjectSegmentation.mapobject_id ).\ join(tm.Mapobject).\ filter( tm.MapobjectSegmentation.label.in_(labels.tolist()), tm.Mapobject.mapobject_type_id == mapobject_type_id, tm.MapobjectSegmentation.partition_key == site_id ).\ all() ) with tm.utils.ExperimentSession(experiment_id, False) as session: segmentations = list() for label, polygon in image.extract_polygons(y_offset, x_offset): mapobject = tm.Mapobject(site_id, mapobject_type_id) if label in existing_segmentations_map: # A parent mapobject with the same label may already exist, # because it got already created for another zplane/tpoint. # The segmentation for the given zplane/tpoint must not yet # exist, however. This will lead to an error upon insertion. mapobject.id = existing_segmentations_map[label] else: session.add(mapobject) session.flush() s = tm.MapobjectSegmentation( partition_key=site_id, mapobject_id=mapobject.id, geom_polygon=polygon, geom_centroid=polygon.centroid, segmentation_layer_id=segmentation_layer_id, label=label ) segmentations.append(s) session.bulk_ingest(segmentations) return jsonify(message='ok')