ashapsoncoe/CREST

base segment IDs linked to annotation points are lost

Closed this issue · 5 comments

Hi,

This question is relevant for running CREST on either Mac or PC...

We want to be able to link segment IDs to annotation points.

What Does Work
I am able to link segment IDs with each annotation point in CREST's active instance of neuroglancer viewer. And when I save the file ("save file locally and continue"), the segment IDs ARE in the json file along with the point location for each annotation ('end_points').

What is not working
But then when I re-open that same json file in CREST again, the segment IDs are no longer listed in the annotations tab along with the point location. That segment ID information is now lost from the original json file as well.

Do you know where in the code this is controlled? (I am looking through the CREST.py script and can't figure it out... Obviously I can't read through the .exe).
Can this behavior be changed?

Thanks so much,
Krista

Update; I looked again at the neuroglancer documentation itself and the active json state in neuroglancer while CREST is running it.... and I think I do see where (in the CREST.py) code it is getting lost (and can be added).
Let me know what you think... (and if it is possible to add this feature to the .exe for PC)

The function definition that it seems to need to be a part of def set_endpoint_annotation_layers(self):
    self.point_types = list(set(self.point_types + list(self.cell_data['end_points'].keys())))
    self.point_types = [x for x in self.point_types if not ('base' in x.lower() and 'merge' in x.lower())]

    with self.viewer.txn(overwrite=True) as s:

        for point_type in self.point_types:

            s.layers[point_type] = neuroglancer.AnnotationLayer()
            s.layers[point_type].annotationColor = '#ffffff'
            s.layers[point_type].tool = "annotatePoint"
            s.layers[point_type].tab = 'Annotations'

            # If data already exists for this point type:
            if point_type in self.cell_data['end_points'].keys():

                for pos, point in enumerate(self.cell_data['end_points'][point_type]):
                    
                    point_array = array([int(point[x]/self.vx_sizes['em'][x]) for x in range(3)])
                    point_id = f'{point_type}_{pos}'
                    pa = neuroglancer.PointAnnotation(id=point_id, point = point_array)
                    s.layers[point_type].annotations.append(pa)

It seems like the line pa = neuroglancer.PointAnnotation(id=point_id, point = point_array) just needs the following argument added: segments = segment_id where segment_id is obtained from index position [3] of each row (enumeration) of self.cell_data['end_points'][point_type] (while pos would come from [0:3]). This would need a "length check" on each row because if a segment ID was not recorded/linked with the annotation point, then the segment id would not exist and the row would just be [x,y,z] (length of 3 not 4).

Does that make sense?

The following edit to that function (though not as efficiently written as it could be) does work...

    def set_endpoint_annotation_layers(self): 

        self.point_types = list(set(self.point_types + list(self.cell_data['end_points'].keys())))
        self.point_types = [x for x in self.point_types if not ('base' in x.lower() and 'merge' in x.lower())]

        with self.viewer.txn(overwrite=True) as s:

            for point_type in self.point_types:

                s.layers[point_type] = neuroglancer.AnnotationLayer()
                s.layers[point_type].annotationColor = '#ffffff'
                s.layers[point_type].tool = "annotatePoint"
                s.layers[point_type].tab = 'Annotations'

                # If data already exists for this point type:
                if point_type in self.cell_data['end_points'].keys():

                    for pos, point in enumerate(self.cell_data['end_points'][point_type]):

                        if len(point)==3: # then there is no segment ID associated with the annotation point
                        
                            point_array = array([int(point[x]/self.vx_sizes['em'][x]) for x in range(3)])
                            point_id = f'{point_type}_{pos}'
                            pa = neuroglancer.PointAnnotation(id=point_id, point = point_array)
                            s.layers[point_type].annotations.append(pa)

                        if len(point)==4: # then include the segment ID with the annotation point
                            point_array = array([int(point[x]/self.vx_sizes['em'][x]) for x in range(3)])
                            point_id = f'{point_type}_{pos}'
                            segment_id = point[3]
                            pa = neuroglancer.PointAnnotation(id=point_id, point = point_array, segments = [[segment_id]])
                            s.layers[point_type].annotations.append(pa)

Hi - I did previously save the associated base segments in the json file, but then thought that it wasn't necessary given that the points would generally be used to mark ends of branches. Therefore I removed as some points would be with or without associated base segments The category 'base segment merger' does have this however - the user is forced to select them in such a way that they must be associated with an underlying base segment.

Since it seems that it would be useful to at least one user to have associated base segments, I will modify to bring-back this feature and post a new version

Have now updated to v0.20 which incorporates a slightly more condesed version of your solution. I see that the code to actually save the associated segments in the json was still there, but not reloading, so only your change was needed. Thanks for the input.

I am working on a v0.21 which should hopefully solve the problem with running on mac and introduce a few other features into the GUI

Awesome!