diff --git a/conda_env.yml b/conda_env.yml index f39fd99..a3e13b9 100644 --- a/conda_env.yml +++ b/conda_env.yml @@ -1,4 +1,4 @@ -name: vt +name: voxToolv2 channels: !!python/tuple - !!python/unicode 'defaults' diff --git a/model/scan.py b/model/scan.py index 356580b..a221586 100644 --- a/model/scan.py +++ b/model/scan.py @@ -7,6 +7,11 @@ import interpolator import re +# added by Joel +import scipy.spatial.distance +import scipy.ndimage +# + log = logging.getLogger() @@ -157,8 +162,10 @@ def get_center(self): class Contact(object): + scale = (1.0, 1.0, 1.0) + def __init__(self, point_mask, contact_label, - lead_location, lead_group): + lead_location, lead_group,): self.point_mask = point_mask.copy() self.label = contact_label self.lead_location = lead_location @@ -175,8 +182,10 @@ def center(self): return np.round(self.point_mask.get_center(), 1) @property - def center_str(self): - return '({:.1f}, {:.1f}, {:.1f})'.format(*self.point_mask.get_center()) + def center_str(self,ct=None): + tpl = '({:.1f}, {:.1f}, {:.1f})' + pos = np.divide(self.center,self.scale) + return tpl.format(*pos.tolist()) @property def lead_location_str(self): @@ -196,9 +205,6 @@ def __contains__(self, coordinate): def center(self): return np.round(self._center,1) - @property - def center_str(self): - return '({:.1f}, {:.1f}, {:.1f})'.format(*self._center) class Lead(object): @@ -546,10 +552,24 @@ def _load_scan(self, img_file): self.filename = img_file log.debug("Loading {}".format(img_file)) img = nib.load(self.filename) - self.data = img.get_data().squeeze() + + #Changes CT scan resolution to account for slice thickness, effectively sets z dimension to equal x and y. + #On saving and loading, then uses self.img_zoom to put coordinates in native resolution. + #Coordinates in the contact list are still given in the resized resolution. + + img_shape = np.array(img.shape) + Contact.scale = self.img_zoom = np.reciprocal(1.0*img_shape)*np.max(img_shape) + self.data = scipy.ndimage.zoom(img.get_data().squeeze(), self.img_zoom) + self.brainmask = np.zeros(img.get_data().shape, bool) self.affine = img.affine[:3,:] + def upsample(self,coordinate): + return np.multiply(coordinate,self.img_zoom) + + def downsample(self,coordinate): + return np.divide(coordinate,self.img_zoom) + def add_mask(self, filename): mask = nib.load(filename).get_data() self.brainmask = mask @@ -581,14 +601,15 @@ def to_dict(self,include_bipolar=False): lead_loc=contact.lead_location, coordinate_spaces=dict( ct_voxel=dict( - raw=list(contact.center.astype(int)) + raw=(np.rint(self.downsample(contact.center).astype(int)).tolist() ) ) - )) + ))) if include_bipolar: pairs = [{'atlases':{}, 'info':{}, - 'coordinate_spaces':{'ct_voxel':{'raw':list((0.5*(c1.center+c2.center)).astype(int))}}, + 'coordinate_spaces':{'ct_voxel':{ + 'raw':list(np.rint(self.downsample(0.5*(c1.center+c2.center))).astype(int))}}, 'names':(lead.label+c1.label,lead.label+c2.label) } for (c1, c2) in self.calculate_pairs(lead)] else: @@ -617,7 +638,7 @@ def to_vox_mom(self,fname,include_bipolar=False): ltype = lead.type_ dims = lead.dimensions for contact in sorted(lead.contacts.keys(),cmp=lambda x,y: cmp(int(x),int(y))): - voxel = np.rint(lead.contacts[contact].center) + voxel = np.rint(self.downsample(lead.contacts[contact].center)).astype(int) contact_name = lead.label+contact csv_out += "%s\t%s\t%s\t%s\t%s\t%s %s\n"%( contact_name,voxel[0],voxel[1],voxel[2],ltype,dims[0],dims[1] @@ -625,7 +646,7 @@ def to_vox_mom(self,fname,include_bipolar=False): if include_bipolar: pairs = self.calculate_pairs(lead) for pair in pairs: - voxel = np.rint((pair[0].center+pair[1].center)/2) + voxel = np.rint(np.divide((pair[0].center+pair[1].center)/2,self.img_zoom)).astype(int) pair_name = '{lead.label}{pair[0].label}-{lead.label}{pair[1].label}'.format(lead=lead,pair=pair) csv_out += "%s\t%s\t%s\t%s\t%s\t%s %s\n"%( pair_name,voxel[0],voxel[1],voxel[2],ltype,dims[0],dims[1]) @@ -668,7 +689,7 @@ def get_dimensions(lead): self.set_leads(labels, types, dimensions, radii, spacings) for i, lead_label in enumerate(labels): for contact in leads[lead_label]['contacts']: - coordinates = contact['coordinate_spaces']['ct_voxel']['raw'] + coordinates = self.upsample(contact['coordinate_spaces']['ct_voxel']['raw']) point_mask = PointMask.proximity_mask(self._points, coordinates, radii[i]) group = contact['lead_group'] loc = contact['lead_loc'] diff --git a/view/pyloc.py b/view/pyloc.py index 51ca629..c04cb8a 100644 --- a/view/pyloc.py +++ b/view/pyloc.py @@ -243,8 +243,8 @@ def select_coordinate(self, coordinate, do_center=True, allow_seed=True): self.view.contact_panel.set_chosen_leads(self.ct.get_leads()) self.display_seed_contact() else: - self.view.update_ras(self.selected_coordinate) - log.info("Selected coordinate {}".format(self.selected_coordinate)) + self.view.update_ras(self.ct.downsample(self.selected_coordinate)) + log.info("Selected coordinate {}".format(self.ct.downsample(self.selected_coordinate))) else: log.info("No coordinate selected") self.view.update_cloud('_selected') @@ -287,7 +287,7 @@ def add_selection(self): if not self.confirm("Dimensions {} are outside of lead dimensions {}. " "Are you sure you want to continue?".format(lead_location, lead.dimensions)): return - + self.ct.add_selection_to_lead(lead_label, contact_label, lead_location, self.lead_group) self.view.contact_panel.set_chosen_leads(self.ct.get_leads()) self.ct.clear_selection()