def single_dipole_search(electrode_potential, leadfield, mesh_file, elements_to_consider=[1002]): from forward.mesh import read_mesh, get_closest_element_to_point from nipype import logging iflogger = logging.getLogger('interface') geo_file = op.abspath("search_points.geo") data_name = "leadfield" lf_data_file = h5py.File(leadfield, "r") lf_data = lf_data_file.get(data_name) leadfield_matrix = lf_data.value _, lf_name, _ = split_filename(leadfield) bounds = (0,leadfield_matrix.shape[0]/3) mesh_data, _, _, _ = read_mesh(mesh_file, elements_to_consider) centroids = [] element_ids = [] for poly in mesh_data: element_ids.append(poly["element_id"]) centroids.append(poly["centroid"]) p0, bounds = random_initial_guess(centroids) #p0 = np.array([0,0,60]) mw = {} args = (leadfield_matrix, electrode_potential, centroids, element_ids) mw['args'] = args #mw['bounds'] = bounds #res = so.basinhopping(calculate_element_cost, p0, T=0.01, stepsize = 3, minimizer_kwargs=mw, disp=True, niter_success=20) # Perform downhill search, minimizing cost function xopt, fopt, n_iter, funcalls, _, allvecs = so.fmin(calculate_element_cost, p0, ftol=0.00000001, args=(leadfield_matrix, electrode_potential, centroids, element_ids), xtol = 1, disp=True, full_output=True, retall=True) write_search_points(allvecs, geo_file) # Need to minimize cost by changing values of orientation and magnitude: _, element_idx, centroid, element_data, lf_idx = get_closest_element_to_point(mesh_file, elements_to_consider, np.array([xopt])) L = np.transpose(leadfield_matrix[lf_idx * 3:lf_idx * 3 + 3]) V = electrode_potential qopt, _, _, _ = np.linalg.lstsq(L, V) return xopt, qopt, element_data, geo_file
def rewrite_mesh_from_binary_mask(mask_file, mesh_file, mesh_id, new_phys_id=1015): # Takes about 8 minutes import numpy as np import nibabel as nb import os.path as op from forward.mesh import read_mesh from nipype import logging iflogger = logging.getLogger('interface') mask_img = nb.load(mask_file) mask_data = mask_img.get_data() mask_affine = mask_img.get_affine() mask_header = mask_img.get_header() #tensor_data = np.flipud(tensor_image.get_data()) # Define various constants elements_to_consider = [1002] #Use only white matter vx, vy, vz = mask_header.get_zooms()[0:3] max_x, max_y, max_z = np.shape(mask_data)[0:3] halfx, halfy, halfz = np.array((vx * max_x, vy * max_y, vz * max_z)) / 2.0 mesh_data, _, _, _ = read_mesh(mesh_file, elements_to_consider) elem_list = [] for idx, poly in enumerate(mesh_data): i = np.round((poly['centroid'][0] + halfx) / vx).astype(int) j = np.round((poly['centroid'][1] + halfy) / vy).astype(int) k = np.round((poly['centroid'][2] + halfz) / vz).astype(int) if mask_data[i, j, k]: elem_list.append(poly['element_id']) #iflogger.info("%3.3f%%" % (float(idx)/num_polygons*100.0)) new_elem_id = new_phys_id + 2000 out_file = swap_element_ids(mesh_file, elem_list, new_elem_id, new_phys_id) print('Writing binary masked mesh file to %s' % out_file) return out_file
def rewrite_mesh_from_binary_mask(mask_file, mesh_file, mesh_id, new_phys_id=1015): # Takes about 8 minutes import numpy as np import nibabel as nb import os.path as op from forward.mesh import read_mesh from nipype import logging iflogger = logging.getLogger('interface') mask_img = nb.load(mask_file) mask_data = mask_img.get_data() mask_affine = mask_img.get_affine() mask_header = mask_img.get_header() #tensor_data = np.flipud(tensor_image.get_data()) # Define various constants elements_to_consider = [1002] #Use only white matter vx, vy, vz = mask_header.get_zooms()[0:3] max_x, max_y, max_z = np.shape(mask_data)[0:3] halfx, halfy, halfz = np.array((vx*max_x, vy*max_y, vz*max_z))/2.0 mesh_data, _, _, _ = read_mesh(mesh_file, elements_to_consider) elem_list = [] for idx, poly in enumerate(mesh_data): i = np.round((poly['centroid'][0]+halfx)/vx).astype(int) j = np.round((poly['centroid'][1]+halfy)/vy).astype(int) k = np.round((poly['centroid'][2]+halfz)/vz).astype(int) if mask_data[i,j,k]: elem_list.append(poly['element_id']) #iflogger.info("%3.3f%%" % (float(idx)/num_polygons*100.0)) new_elem_id = new_phys_id+2000 out_file = swap_element_ids(mesh_file, elem_list, new_elem_id, new_phys_id) print('Writing binary masked mesh file to %s' % out_file) return out_file
def include_gmsh_tensor_elements(mesh_file, tensor_file, mask_file, mask_threshold=0.5, lower_triangular=True): import numpy as np import nibabel as nb from nipype.utils.filemanip import split_filename import os.path as op from forward.mesh import read_mesh from nipype import logging import shutil iflogger = logging.getLogger("interface") # Load 4D (6 volume upper or lower triangular) conductivity tensor image tensor_image = nb.load(tensor_file) tensor_data = np.flipud(tensor_image.get_data()) # Correct the tensors after flipping in the X direction tensor_data[:, :, :, 1] *= -1 if lower_triangular: tensor_data[:, :, :, 3] *= -1 else: tensor_data[:, :, :, 2] *= -1 # Load mask (usually fractional anisotropy) image mask_image = nb.load(mask_file) mask_data = np.flipud(mask_image.get_data()) header = tensor_image.get_header() # Make sure the files share the same (3D) dimensions before continuing assert np.shape(tensor_data)[0:3] == np.shape(mask_data)[0:3] # Define various constants elements_to_consider = [1001] # Use only white matter vx, vy, vz = header.get_zooms()[0:3] max_x, max_y, max_z = np.shape(tensor_data)[0:3] halfx, halfy, halfz = np.array((vx * max_x, vy * max_y, vz * max_z)) / 2.0 mesh_data, _, _, _ = read_mesh(mesh_file, elements_to_consider) # Create the output mesh file path, name, ext = split_filename(mesh_file) out_file = op.abspath(name + "_cond.msh") iflogger.info("Copying current mesh file to %s" % out_file) shutil.copyfile(mesh_file, out_file) f = open(out_file, "a") # Append to the end of the file iflogger.info("Appending Conductivity tensors to %s" % out_file) # Write the tag information to the file: num_polygons = len(mesh_data) f.write("$ElementData\n") str_tag = '"Conductivity"' timestep = 0.0001 f.write("1\n") # Num String tags f.write(str_tag + "\n") f.write("1\n") # Num Real tags f.write("%f\n" % timestep) # Three integer tags: timestep, num field components, num elements f.write("3\n") # Three int tags f.write("0\n") # Time step index f.write("9\n") # Num field components # Get the centroid of all white matter elements # Find out which voxel they lie inside iflogger.info("Getting tensor for each element") # ipdb.set_trace() nonzero = 0 elem_list = [] if lower_triangular: for idx, poly in enumerate(mesh_data): i = np.round((poly["centroid"][0] + halfx) / vx).astype(int) j = np.round((poly["centroid"][1] + halfy) / vy).astype(int) k = np.round((poly["centroid"][2] + halfz) / vz).astype(int) T = tensor_data[i, j, k] if not (all(T == 0) and mask_data[i, j, k] >= mask_threshold): elementdata_str = "%d %e %e %e %e %e %e %e %e %e\n" % ( poly["element_id"], T[0], T[1], T[3], T[1], T[2], T[4], T[3], T[4], T[5], ) elem_list.append(elementdata_str) nonzero += 1 # iflogger.info("%3.3f%%" % (float(idx)/num_polygons*100.0)) else: for idx, poly in enumerate(mesh_data): i = np.round((poly["centroid"][0] + halfx) / vx).astype(int) j = np.round((poly["centroid"][1] + halfy) / vy).astype(int) k = np.round((poly["centroid"][2] + halfz) / vz).astype(int) T = tensor_data[i, j, k] if not (all(T == 0) and mask_data[i, j, k] >= mask_threshold): elementdata_str = "%d %e %e %e %e %e %e %e %e %e\n" % ( poly["element_id"], T[0], T[1], T[2], T[1], T[3], T[4], T[2], T[4], T[5], ) elem_list.append(elementdata_str) nonzero += 1 # iflogger.info("%3.3f%%" % (float(idx)/num_polygons*100.0)) f.write("%d\n" % nonzero) # Num nonzero field components for elementdata_str in elem_list: f.write(elementdata_str) f.write("$EndElementData\n") f.write("$ElementData\n") str_tag = '"FA"' timestep = 0.0002 f.write("1\n") # Num String tags f.write(str_tag + "\n") f.write("1\n") # Num Real tags f.write("%f\n" % timestep) # Three integer tags: timestep, num field components, num elements f.write("3\n") # Three int tags f.write("1\n") # Time step index f.write("1\n") # Num field components # Get the centroid of all white matter elements # Find out which voxel they lie inside iflogger.info("Writing FA for each element") # ipdb.set_trace() nonzero = 0 elem_list = [] for idx, poly in enumerate(mesh_data): i = np.round((poly["centroid"][0] + halfx) / vx).astype(int) j = np.round((poly["centroid"][1] + halfy) / vy).astype(int) k = np.round((poly["centroid"][2] + halfz) / vz).astype(int) if mask_data[i, j, k] > 0: elementdata_str = "%d %e\n" % (poly["element_id"], mask_data[i, j, k]) elem_list.append(elementdata_str) nonzero += 1 # iflogger.info("%3.3f%%" % (float(idx)/num_polygons*100.0)) f.write("%d\n" % nonzero) # Num nonzero field components for elementdata_str in elem_list: f.write(elementdata_str) f.write("$EndElementData\n") f.close() iflogger.info("Finished writing to %s" % out_file) return out_file
import numpy as np from forward.mesh import read_mesh n_electrodes = 42 elem_to_consider = [1000, 1002, 1003, 1004] elem_to_consider.extend(range(5000, 5000+n_electrodes)) mesh_data, nodes, elem, labels = read_mesh('4shell_elec.msh', elem_to_consider) np.savetxt('nodes.txt', nodes) np.savetxt('elem.txt', elem) np.savetxt('labels.txt', labels)
def cost_function_mapping(potential, leadfield, mesh_file, mesh_id): import os.path as op import numpy as np import h5py import shutil from forward.mesh import read_mesh from nipype.utils.filemanip import split_filename from nipype import logging iflogger = logging.getLogger('interface') data_name = "leadfield" lf_data_file = h5py.File(leadfield, "r") lf_data = lf_data_file.get(data_name) leadfield_matrix = lf_data.value _, lf_name, _ = split_filename(leadfield) V = np.load(potential) # ---- Resave the mesh with the cost function as element data ---- # mesh_data, _, _, _ = read_mesh(mesh_file, [mesh_id]) # Create the output mesh file path, name, ext = split_filename(mesh_file) cost_mesh_file = op.abspath(name + "_cost.msh") iflogger.info('Copying current mesh file to %s' % cost_mesh_file) shutil.copyfile(mesh_file, cost_mesh_file) f = open(cost_mesh_file,'a') #Append to the end of the file iflogger.info('Appending cost function scalars to %s' % cost_mesh_file) # Write the tag information to the file: f.write('$ElementData\n') str_tag = '"Cost function %s"' % (leadfield) timestep = 0.0001 f.write('1\n') #Num String tags f.write(str_tag + '\n') f.write('1\n') #Num Real tags f.write('%f\n' % timestep) #Three integer tags: timestep, num field components, num elements f.write('3\n') #Three int tags f.write('0\n') #Time step index f.write('1\n') #Num field components elem_list = [] elem_idx_list = [] cost_list = [] assert(len(mesh_data)*3 == leadfield_matrix.shape[0]) for lf_idx, poly in enumerate(mesh_data): L = np.transpose(leadfield_matrix[lf_idx * 3:lf_idx * 3 + 3]) I = np.eye(len(V)) Linv = np.linalg.pinv(L) val = np.dot(V.T, I - np.dot(L,Linv)) R = np.dot(val,V) cost = np.linalg.norm(R) #cost = np.sqrt(R / np.linalg.norm(V)) cost_list.append(cost) elem_idx_list.append(poly["element_id"]) elementdata_str = ('%d %e\n' % (poly["element_id"], cost)) elem_list.append(elementdata_str) #iflogger.info("%3.3f%%" % (float(lf_idx)/num_polygons*100.0)) import ipdb ipdb.set_trace() minidx = cost_list.index(np.min(cost_list)) maxidx = cost_list.index(np.max(cost_list)) print(elem_idx_list[minidx], elem_idx_list[maxidx]) f.write('%d\n' % len(mesh_data)) #Num nonzero field components for elementdata_str in elem_list: f.write(elementdata_str) f.write('$EndElementData\n') f.close() iflogger.info("Finished writing to %s" % cost_mesh_file) cost_geo_file = "" return cost_mesh_file, cost_geo_file
def cost_function_mapping(potential, leadfield, mesh_file, mesh_id): import os.path as op import numpy as np import h5py import shutil from forward.mesh import read_mesh from nipype.utils.filemanip import split_filename from nipype import logging iflogger = logging.getLogger('interface') data_name = "leadfield" lf_data_file = h5py.File(leadfield, "r") lf_data = lf_data_file.get(data_name) leadfield_matrix = lf_data.value _, lf_name, _ = split_filename(leadfield) V = np.load(potential) # ---- Resave the mesh with the cost function as element data ---- # mesh_data, _, _, _ = read_mesh(mesh_file, [mesh_id]) # Create the output mesh file path, name, ext = split_filename(mesh_file) cost_mesh_file = op.abspath(name + "_cost.msh") iflogger.info('Copying current mesh file to %s' % cost_mesh_file) shutil.copyfile(mesh_file, cost_mesh_file) f = open(cost_mesh_file, 'a') #Append to the end of the file iflogger.info('Appending cost function scalars to %s' % cost_mesh_file) # Write the tag information to the file: f.write('$ElementData\n') str_tag = '"Cost function %s"' % (leadfield) timestep = 0.0001 f.write('1\n') #Num String tags f.write(str_tag + '\n') f.write('1\n') #Num Real tags f.write('%f\n' % timestep) #Three integer tags: timestep, num field components, num elements f.write('3\n') #Three int tags f.write('0\n') #Time step index f.write('1\n') #Num field components elem_list = [] elem_idx_list = [] cost_list = [] assert (len(mesh_data) * 3 == leadfield_matrix.shape[0]) for lf_idx, poly in enumerate(mesh_data): L = np.transpose(leadfield_matrix[lf_idx * 3:lf_idx * 3 + 3]) I = np.eye(len(V)) Linv = np.linalg.pinv(L) val = np.dot(V.T, I - np.dot(L, Linv)) R = np.dot(val, V) cost = np.linalg.norm(R) #cost = np.sqrt(R / np.linalg.norm(V)) cost_list.append(cost) elem_idx_list.append(poly["element_id"]) elementdata_str = ('%d %e\n' % (poly["element_id"], cost)) elem_list.append(elementdata_str) #iflogger.info("%3.3f%%" % (float(lf_idx)/num_polygons*100.0)) import ipdb ipdb.set_trace() minidx = cost_list.index(np.min(cost_list)) maxidx = cost_list.index(np.max(cost_list)) print(elem_idx_list[minidx], elem_idx_list[maxidx]) f.write('%d\n' % len(mesh_data)) #Num nonzero field components for elementdata_str in elem_list: f.write(elementdata_str) f.write('$EndElementData\n') f.close() iflogger.info("Finished writing to %s" % cost_mesh_file) cost_geo_file = "" return cost_mesh_file, cost_geo_file
import numpy as np from forward.mesh import read_mesh n_electrodes = 42 elem_to_consider = [1000, 1002, 1003, 1004] elem_to_consider.extend(range(5000, 5000 + n_electrodes)) mesh_data, nodes, elem, labels = read_mesh('4shell_elec.msh', elem_to_consider) np.savetxt('nodes.txt', nodes) np.savetxt('elem.txt', elem) np.savetxt('labels.txt', labels)
def compare_leadfields(leadfield1, leadfield2, mesh_file, write_mesh=True): ''' Compares two leadfield matrices and outputs the difference as scalars attached to a mesh file. The mesh file must have the same number of elements as the leadfield's second dimension FEM reciprocity leadfield is M x N where M is the number of sensors and N is the number of elements. Mesh file must have N elements. e.g. isotropic white matter conductivity vs. anisotropic conductivity from estimated diffusion tensors ''' import os.path as op import h5py import numpy as np import shutil from forward.mesh import read_mesh from nipype.utils.filemanip import split_filename from nipype import logging iflogger = logging.getLogger('interface') data_name = "leadfield" print("Reading leadfield 1: %s" % leadfield1) lf1_data_file = h5py.File(leadfield1, "r") lf1_data = lf1_data_file.get(data_name) leadfield_matrix1 = lf1_data.value print("Reading leadfield 2: %s" % leadfield2) lf2_data_file = h5py.File(leadfield2, "r") lf2_data = lf2_data_file.get(data_name) leadfield_matrix2 = lf2_data.value path, name, ext = split_filename(mesh_file) if write_mesh: # Electric field elements are only saved in the gray matter #elements_to_consider = [1001] #For Sphere elements_to_consider = [1002] #For head models mesh_data, _, _, _ = read_mesh(mesh_file, elements_to_consider) # Create the output mesh file rms_mesh_file = op.abspath(name + "_rmse.msh") iflogger.info('Copying current mesh file to %s' % rms_mesh_file) shutil.copyfile(mesh_file, rms_mesh_file) f = open(rms_mesh_file,'a') #Append to the end of the file iflogger.info('Appending root mean squared error scalars to %s' % rms_mesh_file) else: rms_mesh_file = None out_rms_hdf5_file_x = op.abspath(name + "_rmse_x.hdf5") out_rms_hdf5_file_y = op.abspath(name + "_rmse_y.hdf5") out_rms_hdf5_file_z = op.abspath(name + "_rmse_z.hdf5") out_rms_hdf5_file_avg = op.abspath(name + "_rmse_avg.hdf5") rms_hdf5_file_x = h5py.File(out_rms_hdf5_file_x, "w") rms_hdf5_file_y = h5py.File(out_rms_hdf5_file_y, "w") rms_hdf5_file_z = h5py.File(out_rms_hdf5_file_z, "w") rms_hdf5_file_avg = h5py.File(out_rms_hdf5_file_avg, "w") # Write the tag information to the file: if write_mesh: num_polygons = len(mesh_data) _, name1, _ = split_filename(leadfield1) _, name2, _ = split_filename(leadfield2) #For testing #noise = np.random.normal(0,1,leadfield_matrix2.shape) #leadfield_matrix2 = leadfield_matrix2 + noise # Reshape the leadfields so they are M x 3 x N vectors (x, y, z direction of electric field) leadfield_mesh1 = np.reshape(leadfield_matrix1, (leadfield_matrix1.shape[0]/3,3,leadfield_matrix1.shape[1])) leadfield_mesh2 = np.reshape(leadfield_matrix2, (leadfield_matrix2.shape[0]/3,3,leadfield_matrix2.shape[1])) #Check that the dimensions are appropriate try: if write_mesh: assert(len(mesh_data) == leadfield_mesh1.shape[0] == leadfield_mesh2.shape[0]) else: assert(leadfield_mesh1.shape[0] == leadfield_mesh2.shape[0]) except AssertionError: iflogger.error("Lead fields could not be compared because the " \ "number of elements in the files do not match") if write_mesh: iflogger.error("Elements in %s: %d" % (mesh_file, len(mesh_data))) iflogger.error("Elements in leadfield 1 %s: %d" % (leadfield1, leadfield_mesh1.shape[0])) iflogger.error("Elements in leadfield 2 %s: %d" % (leadfield2, leadfield_mesh2.shape[0])) raise Exception # Get the mean squared difference between electric field vectors by sensor and element diff = leadfield_mesh2 - leadfield_mesh1 # Gets the norm of the difference for X, Y, and Z directions norm_x_diff = np.linalg.norm(diff[:,0,:],axis=1) norm_y_diff = np.linalg.norm(diff[:,1,:],axis=1) norm_z_diff = np.linalg.norm(diff[:,2,:],axis=1) norm_lf1_x = np.linalg.norm(leadfield_mesh1[:,0,:], axis=1) norm_lf1_y = np.linalg.norm(leadfield_mesh1[:,1,:], axis=1) norm_lf1_z = np.linalg.norm(leadfield_mesh1[:,2,:], axis=1) rmse_x = norm_x_diff / norm_lf1_x rmse_y = norm_y_diff / norm_lf1_y rmse_z = norm_z_diff / norm_lf1_z rmse = (rmse_x + rmse_y + rmse_z) / 3. assert(leadfield_mesh2.shape[0] == rmse.shape[0]) if write_mesh: rmse_avg_list = [] rmse_x_list = [] rmse_y_list = [] rmse_z_list = [] for idx, poly in enumerate(mesh_data): rmse_avg_str = ('%d %e \n' % (poly["element_id"], rmse[idx])) rmse_avg_list.append(rmse_avg_str) rmse_x_str = ('%d %e \n' % (poly["element_id"], rmse_x[idx])) rmse_x_list.append(rmse_x_str) rmse_y_str = ('%d %e \n' % (poly["element_id"], rmse_y[idx])) rmse_y_list.append(rmse_y_str) rmse_z_str = ('%d %e \n' % (poly["element_id"], rmse_z[idx])) rmse_z_list.append(rmse_z_str) iflogger.info("%3.3f%%" % (float(idx)/num_polygons*100.0)) # ----- Average RMSE ----- # f.write('$ElementData\n') str_tag = '"Average Root Mean Squared Error"' timestep = 0.0001 f.write('1\n') #Num String tags f.write(str_tag + '\n') f.write('1\n') #Num Real tags f.write('%f\n' % timestep) #Three integer tags: timestep, num field components, num elements f.write('3\n') #Three int tags f.write('0\n') #Time step index f.write('1\n') #Num field components f.write('%d\n' % len(mesh_data)) #Num nonzero field components for elementdata_str in rmse_avg_list: f.write(elementdata_str) f.write('$EndElementData\n') # ----- RMSE X ----- # f.write('$ElementData\n') str_tag = '"Root Mean Squared Error X-direction"' timestep = 0.0002 f.write('1\n') #Num String tags f.write(str_tag + '\n') f.write('1\n') #Num Real tags f.write('%f\n' % timestep) #Three integer tags: timestep, num field components, num elements f.write('3\n') #Three int tags f.write('0\n') #Time step index f.write('1\n') #Num field components f.write('%d\n' % len(mesh_data)) #Num nonzero field components for elementdata_str in rmse_x_list: f.write(elementdata_str) f.write('$EndElementData\n') # ----- RMSE X ----- # f.write('$ElementData\n') str_tag = '"Root Mean Squared Error Y-direction"' timestep = 0.0003 f.write('1\n') #Num String tags f.write(str_tag + '\n') f.write('1\n') #Num Real tags f.write('%f\n' % timestep) #Three integer tags: timestep, num field components, num elements f.write('3\n') #Three int tags f.write('0\n') #Time step index f.write('1\n') #Num field components f.write('%d\n' % len(mesh_data)) #Num nonzero field components for elementdata_str in rmse_y_list: f.write(elementdata_str) f.write('$EndElementData\n') # ----- RMSE X ----- # f.write('$ElementData\n') str_tag = '"Root Mean Squared Error Z-direction"' timestep = 0.0004 f.write('1\n') #Num String tags f.write(str_tag + '\n') f.write('1\n') #Num Real tags f.write('%f\n' % timestep) #Three integer tags: timestep, num field components, num elements f.write('3\n') #Three int tags f.write('0\n') #Time step index f.write('1\n') #Num field components f.write('%d\n' % len(mesh_data)) #Num nonzero field components for elementdata_str in rmse_z_list: f.write(elementdata_str) f.write('$EndElementData\n') f.close() iflogger.info("Finished writing to %s" % rms_mesh_file) ## Save RMSE data to an HDF5 file dset_x = rms_hdf5_file_x.create_dataset("rmse_x", data=rmse_x) dset_x[...] = rmse_x dset_y = rms_hdf5_file_y.create_dataset("rmse_y", data=rmse_y) dset_y[...] = rmse_y dset_z = rms_hdf5_file_z.create_dataset("rmse_z", data=rmse_z) dset_z[...] = rmse_z dset = rms_hdf5_file_avg.create_dataset("rmse_avg", data=rmse) dset[...] = rmse rms_hdf5_file_x.close() rms_hdf5_file_y.close() rms_hdf5_file_z.close() rms_hdf5_file_avg.close() print("Saved RMSE-X matrix as %s" % out_rms_hdf5_file_x) print("Saved RMSE-Y matrix as %s" % out_rms_hdf5_file_y) print("Saved RMSE-Z matrix as %s" % out_rms_hdf5_file_z) print("Saved RMSE-Avg matrix as %s" % out_rms_hdf5_file_avg) ### return rms_mesh_file, out_rms_hdf5_file_avg, out_rms_hdf5_file_x, out_rms_hdf5_file_y, out_rms_hdf5_file_z
def compare_leadfields(leadfield1, leadfield2, mesh_file, write_mesh=True): """ Compares two leadfield matrices and outputs the difference as scalars attached to a mesh file. The mesh file must have the same number of elements as the leadfield's second dimension FEM reciprocity leadfield is M x N where M is the number of sensors and N is the number of elements. Mesh file must have N elements. e.g. isotropic white matter conductivity vs. anisotropic conductivity from estimated diffusion tensors """ import os.path as op import h5py import numpy as np import shutil from forward.mesh import read_mesh from nipype.utils.filemanip import split_filename from nipype import logging iflogger = logging.getLogger("interface") data_name = "leadfield" print("Reading leadfield 1: %s" % leadfield1) lf1_data_file = h5py.File(leadfield1, "r") lf1_data = lf1_data_file.get(data_name) leadfield_matrix1 = lf1_data.value print("Reading leadfield 2: %s" % leadfield2) lf2_data_file = h5py.File(leadfield2, "r") lf2_data = lf2_data_file.get(data_name) leadfield_matrix2 = lf2_data.value path, name, ext = split_filename(mesh_file) if write_mesh: # Electric field elements are only saved in the gray matter # elements_to_consider = [1001] #For Sphere elements_to_consider = [1002] # For head models mesh_data, _, _, _ = read_mesh(mesh_file, elements_to_consider) # Create the output mesh file rms_mesh_file = op.abspath(name + "_rmse.msh") iflogger.info("Copying current mesh file to %s" % rms_mesh_file) shutil.copyfile(mesh_file, rms_mesh_file) f = open(rms_mesh_file, "a") # Append to the end of the file iflogger.info("Appending root mean squared error scalars to %s" % rms_mesh_file) else: rms_mesh_file = None out_rms_hdf5_file_x = op.abspath(name + "_rmse_x.hdf5") out_rms_hdf5_file_y = op.abspath(name + "_rmse_y.hdf5") out_rms_hdf5_file_z = op.abspath(name + "_rmse_z.hdf5") out_rms_hdf5_file_avg = op.abspath(name + "_rmse_avg.hdf5") rms_hdf5_file_x = h5py.File(out_rms_hdf5_file_x, "w") rms_hdf5_file_y = h5py.File(out_rms_hdf5_file_y, "w") rms_hdf5_file_z = h5py.File(out_rms_hdf5_file_z, "w") rms_hdf5_file_avg = h5py.File(out_rms_hdf5_file_avg, "w") # Write the tag information to the file: if write_mesh: num_polygons = len(mesh_data) _, name1, _ = split_filename(leadfield1) _, name2, _ = split_filename(leadfield2) # For testing # noise = np.random.normal(0,1,leadfield_matrix2.shape) # leadfield_matrix2 = leadfield_matrix2 + noise # Reshape the leadfields so they are M x 3 x N vectors (x, y, z direction of electric field) leadfield_mesh1 = np.reshape(leadfield_matrix1, (leadfield_matrix1.shape[0] / 3, 3, leadfield_matrix1.shape[1])) leadfield_mesh2 = np.reshape(leadfield_matrix2, (leadfield_matrix2.shape[0] / 3, 3, leadfield_matrix2.shape[1])) # Check that the dimensions are appropriate try: if write_mesh: assert len(mesh_data) == leadfield_mesh1.shape[0] == leadfield_mesh2.shape[0] else: assert leadfield_mesh1.shape[0] == leadfield_mesh2.shape[0] except AssertionError: iflogger.error("Lead fields could not be compared because the " "number of elements in the files do not match") if write_mesh: iflogger.error("Elements in %s: %d" % (mesh_file, len(mesh_data))) iflogger.error("Elements in leadfield 1 %s: %d" % (leadfield1, leadfield_mesh1.shape[0])) iflogger.error("Elements in leadfield 2 %s: %d" % (leadfield2, leadfield_mesh2.shape[0])) raise Exception # Get the mean squared difference between electric field vectors by sensor and element diff = leadfield_mesh2 - leadfield_mesh1 # Gets the norm of the difference for X, Y, and Z directions norm_x_diff = np.linalg.norm(diff[:, 0, :], axis=1) norm_y_diff = np.linalg.norm(diff[:, 1, :], axis=1) norm_z_diff = np.linalg.norm(diff[:, 2, :], axis=1) norm_lf1_x = np.linalg.norm(leadfield_mesh1[:, 0, :], axis=1) norm_lf1_y = np.linalg.norm(leadfield_mesh1[:, 1, :], axis=1) norm_lf1_z = np.linalg.norm(leadfield_mesh1[:, 2, :], axis=1) rmse_x = norm_x_diff / norm_lf1_x rmse_y = norm_y_diff / norm_lf1_y rmse_z = norm_z_diff / norm_lf1_z rmse = (rmse_x + rmse_y + rmse_z) / 3.0 assert leadfield_mesh2.shape[0] == rmse.shape[0] if write_mesh: rmse_avg_list = [] rmse_x_list = [] rmse_y_list = [] rmse_z_list = [] for idx, poly in enumerate(mesh_data): rmse_avg_str = "%d %e \n" % (poly["element_id"], rmse[idx]) rmse_avg_list.append(rmse_avg_str) rmse_x_str = "%d %e \n" % (poly["element_id"], rmse_x[idx]) rmse_x_list.append(rmse_x_str) rmse_y_str = "%d %e \n" % (poly["element_id"], rmse_y[idx]) rmse_y_list.append(rmse_y_str) rmse_z_str = "%d %e \n" % (poly["element_id"], rmse_z[idx]) rmse_z_list.append(rmse_z_str) iflogger.info("%3.3f%%" % (float(idx) / num_polygons * 100.0)) # ----- Average RMSE ----- # f.write("$ElementData\n") str_tag = '"Average Root Mean Squared Error"' timestep = 0.0001 f.write("1\n") # Num String tags f.write(str_tag + "\n") f.write("1\n") # Num Real tags f.write("%f\n" % timestep) # Three integer tags: timestep, num field components, num elements f.write("3\n") # Three int tags f.write("0\n") # Time step index f.write("1\n") # Num field components f.write("%d\n" % len(mesh_data)) # Num nonzero field components for elementdata_str in rmse_avg_list: f.write(elementdata_str) f.write("$EndElementData\n") # ----- RMSE X ----- # f.write("$ElementData\n") str_tag = '"Root Mean Squared Error X-direction"' timestep = 0.0002 f.write("1\n") # Num String tags f.write(str_tag + "\n") f.write("1\n") # Num Real tags f.write("%f\n" % timestep) # Three integer tags: timestep, num field components, num elements f.write("3\n") # Three int tags f.write("0\n") # Time step index f.write("1\n") # Num field components f.write("%d\n" % len(mesh_data)) # Num nonzero field components for elementdata_str in rmse_x_list: f.write(elementdata_str) f.write("$EndElementData\n") # ----- RMSE X ----- # f.write("$ElementData\n") str_tag = '"Root Mean Squared Error Y-direction"' timestep = 0.0003 f.write("1\n") # Num String tags f.write(str_tag + "\n") f.write("1\n") # Num Real tags f.write("%f\n" % timestep) # Three integer tags: timestep, num field components, num elements f.write("3\n") # Three int tags f.write("0\n") # Time step index f.write("1\n") # Num field components f.write("%d\n" % len(mesh_data)) # Num nonzero field components for elementdata_str in rmse_y_list: f.write(elementdata_str) f.write("$EndElementData\n") # ----- RMSE X ----- # f.write("$ElementData\n") str_tag = '"Root Mean Squared Error Z-direction"' timestep = 0.0004 f.write("1\n") # Num String tags f.write(str_tag + "\n") f.write("1\n") # Num Real tags f.write("%f\n" % timestep) # Three integer tags: timestep, num field components, num elements f.write("3\n") # Three int tags f.write("0\n") # Time step index f.write("1\n") # Num field components f.write("%d\n" % len(mesh_data)) # Num nonzero field components for elementdata_str in rmse_z_list: f.write(elementdata_str) f.write("$EndElementData\n") f.close() iflogger.info("Finished writing to %s" % rms_mesh_file) ## Save RMSE data to an HDF5 file dset_x = rms_hdf5_file_x.create_dataset("rmse_x", data=rmse_x) dset_x[...] = rmse_x dset_y = rms_hdf5_file_y.create_dataset("rmse_y", data=rmse_y) dset_y[...] = rmse_y dset_z = rms_hdf5_file_z.create_dataset("rmse_z", data=rmse_z) dset_z[...] = rmse_z dset = rms_hdf5_file_avg.create_dataset("rmse_avg", data=rmse) dset[...] = rmse rms_hdf5_file_x.close() rms_hdf5_file_y.close() rms_hdf5_file_z.close() rms_hdf5_file_avg.close() print("Saved RMSE-X matrix as %s" % out_rms_hdf5_file_x) print("Saved RMSE-Y matrix as %s" % out_rms_hdf5_file_y) print("Saved RMSE-Z matrix as %s" % out_rms_hdf5_file_z) print("Saved RMSE-Avg matrix as %s" % out_rms_hdf5_file_avg) ### return rms_mesh_file, out_rms_hdf5_file_avg, out_rms_hdf5_file_x, out_rms_hdf5_file_y, out_rms_hdf5_file_z
def include_gmsh_tensor_elements(mesh_file, tensor_file, mask_file, mask_threshold=0.5, lower_triangular=True): import numpy as np import nibabel as nb from nipype.utils.filemanip import split_filename import os.path as op from forward.mesh import read_mesh from nipype import logging import shutil iflogger = logging.getLogger('interface') # Load 4D (6 volume upper or lower triangular) conductivity tensor image tensor_image = nb.load(tensor_file) tensor_data = np.flipud(tensor_image.get_data()) # Correct the tensors after flipping in the X direction tensor_data[:, :, :, 1] *= -1 if lower_triangular: tensor_data[:, :, :, 3] *= -1 else: tensor_data[:, :, :, 2] *= -1 # Load mask (usually fractional anisotropy) image mask_image = nb.load(mask_file) mask_data = np.flipud(mask_image.get_data()) header = tensor_image.get_header() # Make sure the files share the same (3D) dimensions before continuing assert (np.shape(tensor_data)[0:3] == np.shape(mask_data)[0:3]) # Define various constants elements_to_consider = [1001] #Use only white matter vx, vy, vz = header.get_zooms()[0:3] max_x, max_y, max_z = np.shape(tensor_data)[0:3] halfx, halfy, halfz = np.array((vx * max_x, vy * max_y, vz * max_z)) / 2.0 mesh_data, _, _, _ = read_mesh(mesh_file, elements_to_consider) # Create the output mesh file path, name, ext = split_filename(mesh_file) out_file = op.abspath(name + "_cond.msh") iflogger.info('Copying current mesh file to %s' % out_file) shutil.copyfile(mesh_file, out_file) f = open(out_file, 'a') #Append to the end of the file iflogger.info('Appending Conductivity tensors to %s' % out_file) # Write the tag information to the file: num_polygons = len(mesh_data) f.write('$ElementData\n') str_tag = '"Conductivity"' timestep = 0.0001 f.write('1\n') #Num String tags f.write(str_tag + '\n') f.write('1\n') #Num Real tags f.write('%f\n' % timestep) #Three integer tags: timestep, num field components, num elements f.write('3\n') #Three int tags f.write('0\n') #Time step index f.write('9\n') #Num field components # Get the centroid of all white matter elements # Find out which voxel they lie inside iflogger.info("Getting tensor for each element") #ipdb.set_trace() nonzero = 0 elem_list = [] if lower_triangular: for idx, poly in enumerate(mesh_data): i = np.round((poly['centroid'][0] + halfx) / vx).astype(int) j = np.round((poly['centroid'][1] + halfy) / vy).astype(int) k = np.round((poly['centroid'][2] + halfz) / vz).astype(int) T = tensor_data[i, j, k] if not (all(T == 0) and mask_data[i, j, k] >= mask_threshold): elementdata_str = ('%d %e %e %e %e %e %e %e %e %e\n' % (poly["element_id"], T[0], T[1], T[3], T[1], T[2], T[4], T[3], T[4], T[5])) elem_list.append(elementdata_str) nonzero += 1 #iflogger.info("%3.3f%%" % (float(idx)/num_polygons*100.0)) else: for idx, poly in enumerate(mesh_data): i = np.round((poly['centroid'][0] + halfx) / vx).astype(int) j = np.round((poly['centroid'][1] + halfy) / vy).astype(int) k = np.round((poly['centroid'][2] + halfz) / vz).astype(int) T = tensor_data[i, j, k] if not (all(T == 0) and mask_data[i, j, k] >= mask_threshold): elementdata_str = ('%d %e %e %e %e %e %e %e %e %e\n' % (poly["element_id"], T[0], T[1], T[2], T[1], T[3], T[4], T[2], T[4], T[5])) elem_list.append(elementdata_str) nonzero += 1 #iflogger.info("%3.3f%%" % (float(idx)/num_polygons*100.0)) f.write('%d\n' % nonzero) #Num nonzero field components for elementdata_str in elem_list: f.write(elementdata_str) f.write('$EndElementData\n') f.write('$ElementData\n') str_tag = '"FA"' timestep = 0.0002 f.write('1\n') #Num String tags f.write(str_tag + '\n') f.write('1\n') #Num Real tags f.write('%f\n' % timestep) #Three integer tags: timestep, num field components, num elements f.write('3\n') #Three int tags f.write('1\n') #Time step index f.write('1\n') #Num field components # Get the centroid of all white matter elements # Find out which voxel they lie inside iflogger.info("Writing FA for each element") #ipdb.set_trace() nonzero = 0 elem_list = [] for idx, poly in enumerate(mesh_data): i = np.round((poly['centroid'][0] + halfx) / vx).astype(int) j = np.round((poly['centroid'][1] + halfy) / vy).astype(int) k = np.round((poly['centroid'][2] + halfz) / vz).astype(int) if mask_data[i, j, k] > 0: elementdata_str = ('%d %e\n' % (poly["element_id"], mask_data[i, j, k])) elem_list.append(elementdata_str) nonzero += 1 #iflogger.info("%3.3f%%" % (float(idx)/num_polygons*100.0)) f.write('%d\n' % nonzero) #Num nonzero field components for elementdata_str in elem_list: f.write(elementdata_str) f.write('$EndElementData\n') f.close() iflogger.info("Finished writing to %s" % out_file) return out_file