Skip to content

Commit dbc5300

Browse files
author
Casey Schneider-Mizell
committed
Initial commit
0 parents  commit dbc5300

File tree

5 files changed

+439
-0
lines changed

5 files changed

+439
-0
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# catmaid-interface
2+
Python tools for interacting with CATMAID projects
3+
4+
This is a slowly increasing toolbox for extracting CATMAID (http://www.catmaid.org) annotations, typically EM reconstructions of neurons and eventually visualizing and doing various kinds of analysis of neuronal morphology, connectivity, and connections between the two. See Quantitative neuroanatomy for connectomics in Drosophila (http://biorxiv.org/content/early/2016/02/17/026617) for more details of what sort of things will eventually be possible.
5+
6+
Currently it needs the following packages:
7+
json - for parsing and handling the JSON files that CATMAID trades in
8+
requests - for happily interacting with a CATMAID server
9+
10+
A similar tool has been developed by Greg Jefferis for R, which can be found at https://github.com/jefferis/rcatmaid

annotation_map.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Annotation_map takes a list of strings (annotations) in the command line and
2+
# writes a file where each row is for a different annotation.
3+
# The first element is the annotation itself, then a comma-separated list of skeleton ids
4+
# for the neurons having that annotation.
5+
6+
import catmaid_interface as ci
7+
import cardonalab_project_parser as cpp
8+
import sys
9+
10+
def main():
11+
12+
project_flag = sys.argv[1]
13+
input_args = [ sys.argv[i] for i in range(2,len(sys.argv)) ]
14+
15+
proj_opts = cpp.project_map( project_flag )
16+
anno_dict = ci.get_annotation_dict( proj_opts )
17+
arg_ids = [ anno_dict[arg] for arg in input_args ]
18+
19+
f_anno = open('annotation_ids.txt','w')
20+
21+
for arg in input_args:
22+
f_anno.write(arg)
23+
id_list_base = ci.get_ids_from_annotation( [anno_dict[arg]], proj_opts )
24+
for skid in id_list_base:
25+
f_anno.write(',' + str(skid))
26+
f_anno.write('\n')
27+
f_anno.close()
28+
29+
if __name__ == "__main__":
30+
main()

catmaid_interface.py

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# catmaid_interface: A library for interacting with CATMAID projects through python
2+
# Started by Tom Kazimiers (1/2013)
3+
# Adapted by Albert Cardona (1/2013)
4+
# Continued since by Casey Schneider-Mizell with a lot of assistance from Andrew Champion
5+
6+
import json
7+
import requests
8+
9+
# requests.py authentication class for a CATMAID server with both token authentication and HTTP Basic authentication
10+
class catmaid_auth_token( requests.auth.HTTPBasicAuth ):
11+
def __init__(self, token, authname=None, authpass=None):
12+
self.token = token
13+
super(catmaid_auth_token, self).__init__(authname, authpass)
14+
def __call__(self, r):
15+
r.headers['X-Authorization'] = 'Token {}'.format(self.token)
16+
return super(catmaid_auth_token, self).__call__(r)
17+
18+
# Project info
19+
def set_project_opts( baseurl, project_id, token, authname = None, authpass = None):
20+
proj_opts = dict()
21+
proj_opts['baseurl'] = baseurl
22+
proj_opts['project_id'] = project_id
23+
proj_opts['token'] = token
24+
proj_opts['authname'] = authname
25+
proj_opts['authpass'] = authpass
26+
return proj_opts
27+
28+
# get_neuron_name: Given a skeleton ID, fetch the neuron name
29+
def get_neuron_name( skeleton_id, proj_opts ):
30+
url = proj_opts['baseurl'] + '/{}/skeleton/{}/neuronname'.format( proj_opts['project_id'], skeleton_id)
31+
d = requests.get( url, auth = catmaid_auth_token( proj_opts['token'], proj_opts['authname'], proj_opts['authpass'] ) ).json()
32+
return d['neuronname']
33+
34+
# skeleton_object: Fetch full JSON info (plus a few other useful things) for a skeleton
35+
# sk[0] is the list of skeleton nodes
36+
# sk[1] is the list of connectors
37+
# sk[2] is tags
38+
# sk[3] is the skeleton id
39+
# sk[4] is the neuron name
40+
# sk[5] is the number of postsynaptic targets for each presynaptic connector
41+
def get_skeleton_json( skeleton_id, proj_opts, withtags = True):
42+
if withtags:
43+
url = proj_opts['baseurl'] + '/{}/{}/1/1/compact-skeleton'.format( proj_opts['project_id'], skeleton_id)
44+
else:
45+
url = proj_opts['baseurl'] + '/{}/{}/1/0/compact-skeleton'.format( proj_opts['project_id'], skeleton_id)
46+
d = requests.get( url, auth = catmaid_auth_token( proj_opts['token'], proj_opts['authname'], proj_opts['authpass'] ) ).json()
47+
d.append( skeleton_id )
48+
d.append( get_neuron_name( skeleton_id, proj_opts ) )
49+
presyn_weight = post_synaptic_count( [conn[1] for conn in d[1] if conn[2] == 0], proj_opts )
50+
if presyn_weight!=None:
51+
d.append([ [cid, presyn_weight[cid]] for cid in presyn_weight.keys() ])
52+
else:
53+
d.append([])
54+
return d
55+
56+
# add_annotation: Add a single annotation to a list of skeleton IDs.
57+
def add_annotation( annotation_list, id_list, proj_opts ):
58+
if type(annotation_list) is not list:
59+
raise TypeError('annotation_list must be a list even if just one element')
60+
if type(id_list) is not list:
61+
raise TypeError('id_list must be a list even if just one element')
62+
63+
url = proj_opts['baseurl'] + '/{}/annotations/add'.format( proj_opts['project_id'])
64+
postdata = dict()
65+
for i, anno in enumerate(annotation_list):
66+
postdata['annotations[{}]'.format(i)] = anno
67+
for i, id in enumerate(id_list):
68+
postdata['skeleton_ids[{}]'.format(i)] = id
69+
d = requests.post( url, data = postdata, auth = catmaid_auth_token( proj_opts['token'], proj_opts['authname'], proj_opts['authpass'] ) )
70+
return d
71+
72+
# get_annotation_dict: Gets the dict of annotation_id to annotation string for a project
73+
def get_annotation_dict( proj_opts ):
74+
url = proj_opts['baseurl'] + '/{}/annotations/'.format(proj_opts['project_id'])
75+
all_annotations = requests.get( url, auth = catmaid_auth_token( proj_opts['token'], proj_opts['authname'], proj_opts['authpass'] ) ).json()
76+
anno_dict = { item['name']:item['id'] for item in all_annotations['annotations'] }
77+
return anno_dict
78+
79+
# get_ids_from_annotation: Given an annotation id, pull all skeletons with that annotation
80+
def get_ids_from_annotation( annotation_id_list, proj_opts ):
81+
if type(annotation_id_list) is not list:
82+
raise TypeError('annotation_id_list must be a list even if just one element')
83+
url = proj_opts['baseurl'] + '/{}/annotations/query-targets'.format( proj_opts['project_id'] )
84+
skids = []
85+
for anno_id in annotation_id_list:
86+
postdata = { 'annotated_with' : anno_id }
87+
d = requests.post( url, data = postdata, auth = catmaid_auth_token( proj_opts['token'], proj_opts['authname'], proj_opts['authpass'] ) ).json()
88+
ids_returned = [ item['skeleton_ids'][0] for item in d['entities'] ]
89+
skids = skids + ids_returned
90+
return list( set( skids ) )
91+
92+
# get_ids_by_nodecount: Ids of all skeletons with more nodes than min_nodes
93+
def get_ids_by_nodecount( min_nodes, proj_opts ):
94+
url = proj_opts['baseurl'] + '/{}/skeletons/'.format( proj_opts['project_id'] )
95+
d = requests.get( url, data = {'nodecount_gt': min_nodes}, auth = catmaid_auth_token( proj_opts['token'], proj_opts['authname'], proj_opts['authpass'] ) )
96+
return d
97+
98+
# get_connected_skeleton_info: get skeleton ids, nodes, and number of synapses connected upstream/downstream for a list of ids.
99+
def get_connected_skeleton_info( id_list, proj_opts ):
100+
if type(id_list) is not list:
101+
raise TypeError('id_list must be a list even if just one element')
102+
103+
url = proj_opts['baseurl'] + '/{}/skeletons/connectivity'.format( proj_opts['project_id'] )
104+
postdata = {'boolean_op' : 'OR'}
105+
for i, id in enumerate( id_list ):
106+
postdata['source_skeleton_ids[{}]'.format(i)] = id
107+
d = requests.post( url, data = postdata, auth = catmaid_auth_token( proj_opts['token'], proj_opts['authname'], proj_opts['authpass'] ) ).json()
108+
connected_inds = dict()
109+
connected_inds['incoming'] = {}
110+
connected_inds['outgoing'] = {}
111+
for id in d['incoming'].keys():
112+
connected_inds['incoming'][int(id)] = d['incoming'][id]
113+
for id in d['outgoing'].keys():
114+
connected_inds['outgoing'][int(id)] = d['outgoing'][id]
115+
return connected_inds
116+
117+
# Grow a list of skeleton ids along the connectivity network
118+
def increase_id_list( id_list, proj_opts, min_pre=0, min_post=0, hops=1):
119+
if type(id_list) is not list:
120+
raise TypeError('id_list must be a list even if just one element')
121+
122+
postdata = {'n_circles': hops,
123+
'min_pre': min_pre,
124+
'min_post': min_post}
125+
for i, id in enumerate(id_list):
126+
postdata[ 'skeleton_ids[{}]'.format(i) ] = id
127+
128+
url = proj_opts['baseurl'] + '/{}/graph/circlesofhell'.format(proj_opts['project_id'])
129+
d = requests.post( url, data = postdata, auth = catmaid_auth_token( proj_opts['token'], proj_opts['authname'], proj_opts['authpass'] )).json()
130+
131+
for id in id_list_base:
132+
id_list.append( id )
133+
id_list = list(set(id_list))
134+
return id_list
135+
136+
# post_synaptic_count: Count how many postsynaptic targets are associated with each connector in a list of connectors
137+
def post_synaptic_count( connector_list, proj_opts ):
138+
url = proj_opts['baseurl'] + '/{}/connector/skeletons'.format(proj_opts['project_id'])
139+
opts = {}
140+
for ind, id in enumerate(connector_list):
141+
opts[ 'connector_ids[{}]'.format(ind) ] = id
142+
d = requests.post( url, data = opts, auth = catmaid_auth_token( proj_opts['token'], proj_opts['authname'], proj_opts['authpass'] )).json()
143+
nps = dict()
144+
for conn in d:
145+
nps[conn[0]] = len( conn[1]['postsynaptic_to'] )
146+
return nps
147+
148+
# write_skeletons_from_list: pull JSON files (plus key details) for
149+
def write_skeletons_from_list( id_list, proj_opts ):
150+
for id in id_list:
151+
sk = get_skeleton_json( id, proj_opts )
152+
f_nodes = open( 'sk_' + str(id) + '.json' ,'w')
153+
json.dump(sk,f_nodes)
154+
f_nodes.close()
155+
156+
#######
157+
158+
# Parse a file to a list of skeleton ids.
159+
def import_ids(filename):
160+
f = open(filename,'r')
161+
id_list = []
162+
for line in f:
163+
if int(line) == -1:
164+
f.close()
165+
return []
166+
else:
167+
id_list.append(int(line))
168+
f.close()
169+
return id_list

0 commit comments

Comments
 (0)