Source code for ClearMap.ImageProcessing.Tracing.Connect
# -*- coding: utf-8 -*-"""Module to Postprocess skeletonized vasculature data"""__author__='Christoph Kirst <ckirst@rockefeller.edu>'__license__='MIT License <http://www.opensource.org/licenses/mit-license.php>'__copyright__='Copyright 2017 by Christoph Kirst, The Rockefeller University, New York City'importnumpyasnpimportscipy.ndimageasndi#TODO:importClearMap.ParallelProcessing.DataProcessing.ArrayProcessingasapimportClearMap.ParallelProcessing.DataProcessing.ConvolvePointListascplimportClearMap.ImageProcessing.Tracing.TraceastrcimportClearMap.ImageProcessing.Differentiation.HessianascurimportClearMap.ImageProcessing.Topology.Topology3dast3dimportClearMap.Visualization.Plot3dasp3d
[docs]defextractNeighbourhood(data,center,radius):"""Extract local neighborhood with specific radius, if to close to border pad with zeros"""order='C';ifnp.isfortran(data):order='F';ifisinstance(radius,int):radius=(radius,);radius=radius*3;radius=np.array(radius[:3]);dlo=[max(0,c-r)forc,rinzip(center,radius)];dhi=[min(s,c+r+1)forc,r,sinzip(center,radius,data.shape)];nlo=[-min(0,c-r)forc,rinzip(center,radius)];nhi=[2*r+1+min(0,s-c-r-1)forc,r,sinzip(center,radius,data.shape)];#print center#print radius#print dlo, dhi#print nlo, nhi nbh=np.zeros(2*radius+1,dtype=data.dtype,order=order);nbh[nlo[0]:nhi[0],nlo[1]:nhi[1],nlo[2]:nhi[2]]=data[dlo[0]:dhi[0],dlo[1]:dhi[1],dlo[2]:dhi[2]];returnnbh;
[docs]deffindEndpoints(skel,points,border=None):"""Find endpoints in skeleton to try to reconnect"""order='C';ifnp.isfortran(skel):order='F';#find node degreesdeg=cpl.convolve_3d_indices(skel,t3d.n26,points,out_dtype='uint8')#isolated and end pointsisolated=points[deg==0];ends=points[deg==1];# remove border pointsifborderisnotNone:ifnotisinstance(border,tuple):border=(border,);border=border*3;border=border[:3];x,y,z=np.unravel_index(isolated,skel.shape,order=order)ids=np.ones(isolated.shape,dtype=bool);forxx,ss,bbinzip((x,y,z),skel.shape,border):ids=np.logical_and(ids,np.logical_and(bb<xx,xx<ss-bb));isolated=isolated[ids];x,y,z=np.unravel_index(ends,skel.shape,order=order)ids=np.ones(ends.shape,dtype=bool);forxx,ss,bbinzip((x,y,z),skel.shape,border):ids=np.logical_and(ids,np.logical_and(bb<xx,xx<ss-bb));ends=ends[ids];returnends,isolated
[docs]deftracePointToMask(data,mask,center,radius,points=None,plot=False,skeleton=None,tubeness=None,removeLocalMask=True,maxSteps=500,verbose=False,**trace_parameter):"""Trace an endpoint to a mask"""# cut out neighbourhood from datacenter_nbh=np.array(center)-center+radius;data_nbh=extractNeighbourhood(data,center,radius);mask_nbh=extractNeighbourhood(mask,center,radius);iftubenessisNone:tubeness_nbh=cur.tubeness(ndi.gaussian_filter(np.asarray(data_nbh,dtype=float),sigma=1.0));else:tubeness_nbh=extractNeighbourhood(tubeness,center,radius);ifremoveLocalMaskisnotNone:mask_nbh_label,mask_label=ndi.label(mask_nbh,structure=np.ones((3,3,3),dtype=bool));ids=mask_nbh_label[tuple(center_nbh)]==mask_nbh_label;mask_nbh[ids]=False;distance_nbh=ndi.distance_transform_edt(np.logical_not(mask_nbh))path,quality=trc.traceToMask(np.asarray(data_nbh,dtype=float),tubeness_nbh,center_nbh,distance_nbh,maxSteps=maxSteps,verbose=verbose,returnQuality=True,**trace_parameter);ifverbose:iflen(path)>0:print('Path of length = %d with quality = %f (per length = %f)'%(len(path),quality,quality/len(path)));else:print('No path found!');ifplot:plotTracingResult(path,data_nbh,mask_nbh,center,radius,tubeness_nbh,skeleton=skeleton,distance_nbh=distance_nbh);returnpath+center-center_nbh,quality;
[docs]deftracePointToNeighbor(data,mask,center,neighbor,radius,points=None,plot=False,skeleton=None,tubeness=None,removeLocalMask=True,maxSteps=500,verbose=False,**trace_parameter):"""Trace an endpoint to a neighbour"""# cut out neighbourhood from datacenter_nbh=np.array(center)-center+radius;neighbor_nbh=np.array(neighbor)-center+radius;data_nbh=extractNeighbourhood(data,center,radius);mask_nbh=extractNeighbourhood(mask,center,radius);iftubenessisNone:tubeness_nbh=cur.tubeness(ndi.gaussian_filter(np.asarray(data_nbh,dtype=float),sigma=1.0));else:tubeness_nbh=extractNeighbourhood(tubeness,center,radius);ifremoveLocalMaskisnotNone:mask_nbh_label,mask_label=ndi.label(mask_nbh,structure=np.ones((3,3,3),dtype=bool));ids=mask_nbh_label[tuple(center_nbh)]==mask_nbh_label;mask_nbh[ids]=False;#distance_nbh = ndi.distance_transform_edt(np.logical_not(mask_nbh))path,quality=trc.trace(np.asarray(data_nbh,dtype=float),tubeness_nbh,center_nbh,neighbor_nbh,maxSteps=maxSteps,verbose=verbose,returnQuality=True,**trace_parameter);ifverbose:iflen(path)>0:print('Path of length = %d with quality = %f (per length = %f)'%(len(path),quality,quality/len(path)));else:print('No path found!');ifplot:plotTracingResult(path,data_nbh,mask_nbh,center,radius,tubeness_nbh,skeleton=skeleton);returnpath+center-center_nbh,quality;
[docs]defconnectPoint(data,mask,endpoints,start_index,radius,tubeness=None,min_quality=None,remove_local_mask=True,skeleton=None,verbose=False,**trace_parameter):"""Tries to connect an end point"""#outine:# find neighbour end points and try to connect to nearest one# if path score good enough add path and remove two endpoints# else try to connect to binarized image# if path score good enugh connect to closest skeleton point# else not connectable#assumes everything is in fotran orderstrides=np.array(data.strides)/data.itemsize;shape=data.shape;#print strides, shapecenter_flat=endpoints[start_index];center_xyz=np.array(np.unravel_index(center_flat,data.shape,order='F'));mask_nbh=extractNeighbourhood(mask,center_xyz,radius);data_nbh=np.asarray(extractNeighbourhood(data,center_xyz,radius),dtype=float,order='F');shape_nbh=mask_nbh.shape;center_nbh_xyz=np.zeros(3,dtype=int)+radius;#center_nbh_flat = np.ravel_multi_index(center_nbh_xyz, shape_nbh, order = 'F');iftubenessisNone:tubeness_nbh=cur.tubeness(ndi.gaussian_filter(np.asarray(data_nbh,dtype=float),sigma=1.0));tubeness_nbh=np.asarray(tubeness_nbh,order='F');else:tubeness_nbh=extractNeighbourhood(tubeness,center_xyz,radius);mask_nbh_label=np.empty(shape_nbh,dtype='int32',order='F');_=ndi.label(mask_nbh,structure=np.ones((3,3,3),dtype=bool),output=mask_nbh_label);local_nbh=mask_nbh_label[tuple(center_nbh_xyz)]==mask_nbh_label;# end point neighboursnbs_flat=ap.findNeighbours(endpoints,start_index,shape,strides,radius);iflen(nbs_flat)>0:nbs_nbh_xyz=np.vstack(np.unravel_index(nbs_flat,shape,order='F')).T-center_xyz+center_nbh_xyz;nbs_nbh_flat=np.ravel_multi_index(nbs_nbh_xyz.T,shape_nbh,order='F');# remove connected neighboursnon_local_nbh_flat=np.reshape(np.logical_not(local_nbh),-1,order='F');nbs_nbh_non_local_flat=nbs_nbh_flat[non_local_nbh_flat[nbs_nbh_flat]];iflen(nbs_nbh_non_local_flat)>0:#find nearest neighbournbs_nbh_non_local_xyz=np.vstack(np.unravel_index(nbs_nbh_non_local_flat,shape,order='F')).T;nbs_nbh_non_local_dist=nbs_nbh_non_local_xyz-center_nbh_xyz;nbs_nbh_non_local_dist=np.sum(nbs_nbh_non_local_dist*nbs_nbh_non_local_dist,axis=1);neighbor_nbh_xyz=nbs_nbh_non_local_xyz[np.argmin(nbs_nbh_non_local_dist)];path,quality=trc.trace(data_nbh,tubeness_nbh,center_nbh_xyz,neighbor_nbh_xyz,verbose=False,returnQuality=True,**trace_parameter);iflen(path)>0:ifquality/len(path)<min_quality:ifverbose:print('Found good path to neighbour of length = %d with quality = %f (per length = %f) [%d / %d nonlocal neighbours]'%(len(path),quality,quality/len(path),len(nbs_nbh_non_local_flat),len(nbs_flat)));#print pathreturnpath+center_xyz-center_nbh_xyz,quality;else:ifverbose:print('Found bad path to neighbour of length = %d with quality = %f (per length = %f) [%d / %d nonlocal neighbours]'%(len(path),quality,quality/len(path),len(nbs_nbh_non_local_flat),len(nbs_flat)));#print pathelse:ifverbose:print('Found no path to neighbour [%d / %d nonlocal neighbours]'%(len(nbs_nbh_non_local_flat),len(nbs_flat)));#print path#tracing to neares neighbour failed ifverbose:print('Found no valid path to neighbour, now tracing to binary!');#print path# Tracing to next binaryifremove_local_mask:mask_nbh[local_nbh]=False;distance_nbh=ndi.distance_transform_edt(np.logical_not(mask_nbh))distance_nbh=np.asarray(distance_nbh,order='F');path,quality=trc.traceToMask(data_nbh,tubeness_nbh,center_nbh_xyz,distance_nbh,verbose=False,returnQuality=True,**trace_parameter);iflen(path)>0:ifquality/len(path)<min_quality:ifverbose:print('Found good path to binary of length = %d with quality = %f (per length = %f)'%(len(path),quality,quality/len(path)));#print path# trace to skeletonifskeletonisnotNone:#find closest point on skeletonfinal_xyz=path[0];skeleton_nbh=extractNeighbourhood(skeleton,center_xyz,radius);local_end_path_nbh=mask_nbh_label[tuple(final_xyz)]==mask_nbh_label;skeleton_nbh_dxyz=np.vstack(np.where(np.logical_and(skeleton_nbh,local_end_path_nbh))).T-final_xyz;iflen(skeleton_nbh_dxyz)==0:# could not find skeleton nearby -> give up for nowreturnpath+center_xyz-center_nbh_xyz,quality;skeleton_nbh_dist=np.sum(skeleton_nbh_dxyz*skeleton_nbh_dxyz,axis=1);closest_dxyz=skeleton_nbh_dxyz[np.argmin(skeleton_nbh_dist)];closest_xyz=closest_dxyz+final_xyz;#print path[0], path[-1]#print center_nbh_xyz, closest_dxyz#generate pixel pathmax_l=np.max(np.abs(closest_dxyz))+1;path_add_xyz=np.vstack([np.asarray(np.linspace(f,c,max_l),dtype=int)forf,cinzip(final_xyz,closest_xyz)]).T;path_add_flat=np.ravel_multi_index(path_add_xyz.T,shape_nbh);_,ids=np.unique(path_add_flat,return_index=True);path_add_xyz=path_add_xyz[ids];#print path_add_xyz;path=np.vstack([path,path_add_xyz]);# note: this is not an ordered path anymore!returnpath+center_xyz-center_nbh_xyz,quality;else:ifverbose:print('Found bad path to binary of length = %d with quality = %f (per length = %f)'%(len(path),quality,quality/len(path)));#print pathifverbose:print('Found no valid path to binary!');returnnp.zeros((0,3)),0;
importClearMap.ParallelProcessing.SharedMemoryManagerassmm;#import ClearMap.ParallelProcessing.SharedMemoryProcessing as smp;importmultiprocessingasmpimporttempfileastmpf
[docs]defaddConnections(data,mask,skeleton,points,radius=20,start_points=None,remove_local_mask=True,min_quality=15.0,add_to_skeleton=True,add_to_mask=False,verbose=True,processes=mp.cpu_count(),block_size=5000,debug=False):globaltemporary_folder;timer=tmr.Timer();timer_total=tmr.Timer();ifstart_pointsisNone:ends,isolated=findEndpoints(skeleton,points,border=20);start_points=np.hstack([ends,isolated]);start_points=smm.asShared(start_points);npts=len(start_points);ifverbose:timer.printElapsedTime('Found %d endpoints'%(npts,));timer.reset();assertsmm.isShared(data);assertsmm.isShared(mask);assertsmm.isShared(skeleton);#steps_total = npts / processes;#steps_done = 0;smm.clean();data_hdl=smm.insert(data);mask_hdl=smm.insert(mask);skel_hdl=smm.insert(skeleton);spts_hdl=smm.insert(start_points);ifnot(data_hdl==0andmask_hdl==1andskel_hdl==2andspts_hdl==3):raiseRuntimeError('The shared memory handles are invalid: %d, %d, %d, %d'%(data_hdl,mask_hdl,skel_hdl,spts_hdl));#generate temporary folder to write path tootemporary_folder=tmpf.mkdtemp();ifverbose:timer.printElapsedTime('Preparation of %d connections'%(npts,));timer.reset();# process in parallel / block processing is to clean up memory leaks for nownblocks=max(1,int(np.ceil(1.0*npts/processes/block_size)));ranges=np.asarray(np.linspace(0,npts,nblocks+1),dtype=int);#nblocks = 1;#ranges = [0, 100];forbinrange(nblocks):argdata=np.arange(ranges[b],ranges[b+1]);ifdebug:result=[processSingleConnection(a)forainargdata];else:#mp.process.current_process()._counter = mp.process.itertools.count(1); #reset worker counterpool=mp.Pool(processes=processes);#for i,_ in enumerate(pool.imap_unordered(processSingleConnection, argdata)):# if i % 100 == 0:# timer.printElapsedTime('Iteration %d / %d' % (i + ranges[b], npts));pool.map(processSingleConnection,argdata)pool.close();pool.join();gc.collect();smm.free(data_hdl);smm.free(mask_hdl);smm.free(skel_hdl);smm.free(spts_hdl);ifverbose:timer.printElapsedTime('Processing of %d connections'%(npts,));timer.reset();#return result;#get paths from temporay filesresult=[];forfinos.listdir(temporary_folder):result.append(np.fromfile(os.path.join(temporary_folder,f),dtype=int));result=np.hstack(result);#clean upshutil.rmtree(temporary_folder);temporary_folder=None;ifverbose:timer.printElapsedTime('Loading paths with %d points'%(len(result),));timer.reset();#add dilated version to skeletonifadd_to_skeleton:skeleton_f=np.reshape(skeleton,-1,order='A');strides=skeleton.strides;skeleton_f_len=len(skeleton_f);fordxin[-1,0,1]:fordyin[-1,0,1]:fordzin[-1,0,1]:offset=dx*strides[0]+dy*strides[1]+dz*strides[2];pos=result+offset;pos=pos[np.logical_and(pos>=0,pos<skeleton_f_len)];skeleton_f[pos]=True;ifverbose:timer.printElapsedTime('Added paths with %d points to skeleton'%(len(result),));#add dilated version to skeletonifadd_to_mask:mask_f=np.reshape(mask,-1,order='A');strides=mask.strides;fordxin[-1,0,1]:fordyin[-1,0,1]:fordzin[-1,0,1]:offset=dx*strides[0]+dy*strides[1]+dz*strides[2];pos=result+offset;pos=pos[np.logical_and(pos>=0,pos<skeleton_f_len)];mask_f[pos]=True;ifverbose:timer.printElapsedTime('Added paths with %d points to mask'%(len(result),));timer_total.printElapsedTime('Connection post-processing added %d points'%(len(result),));returnresult;
def_test():#%%importnumpyasnpimportscipy.ndimageasndiimportClearMap.DataProcessing.LargeDataasldimportClearMap.Visualization.Plot3dasp3dimportClearMap.DataProcessing.ConvolvePointListascplimportClearMap.ImageProcessing.Skeletonization.Topology3dast3dimportClearMap.ImageProcessing.Skeletonization.SkeletonCleanUpasscuimportClearMap.ImageProcessing.Tracing.Connectascon;reload(con);data=np.load('/home/ckirst/Desktop/data.npy');binary=np.load('/home/ckirst/Desktop/binarized.npy');skel=np.load('/home/ckirst/Desktop/skel.npy');#points = np.load('/home/ckirst/Desktop/pts.npy');data=np.copy(data,order='F');binary=np.copy(binary,order='F');skel=np.copy(skel,order='F');skel_copy=np.copy(skel,order='F');points=np.ravel_multi_index(np.where(skel),skel.shape,order='F');skel,points=scu.cleanOpenBranches(skel,skel_copy,points,length=3,clean=True);deg=cpl.convolve3DIndex(skel,t3d.n26,points);ends,isolated=con.findEndpoints(skel,points,border=25);special=np.sort(np.hstack([ends,isolated]))ends_xyz=np.array(np.unravel_index(ends,data.shape,order='F')).T;isolated_xyz=np.array(np.unravel_index(isolated,data.shape,order='F')).T;special_xyz=np.vstack([ends_xyz,isolated_xyz]);#%%importClearMap.ParallelProcessing.SharedMemoryManagerassmmdata_s=smm.asShared(data,order='F');binary_s=smm.asShared(binary.view('uint8'),order='F');skel_s=smm.asShared(skel.view('uint8'),order='F');smm.clean();res=con.addConnections(data_s,binary_s,skel_s,points,radius=20,start_points=None,add_to_skeleton=True,add_to_mask=True,verbose=True,processes=4,debug=False,block_size=10)skel_s=skel_s.view(bool);binary_s=binary_s.view(bool);#%%mask_img=np.asarray(binary,dtype=int,order='A');mask_img[:]=mask_img+binary_s;mask_img[:]=mask_img+skel;data_img=np.copy(data,order='A');data_img[skel]=120;mask_img_f=np.reshape(mask_img,-1,order='A');data_img_f=np.reshape(data_img,-1,order='A');mask_img_f[res]=7;data_img_f[res]=512;mask_img_f[special]=8;data_img_f[special]=150;fordin[3,4,5]:mask_img_f[points[deg==d]]=d+1;try:con.viewer[0].setSource(mask_img);con.viewer[1].setSource(data_img);except:con.viewer=p3d.plot([mask_img,data_img]);con.viewer[0].setMinMax([0,8]);con.viewer[1].setMinMax([24,160]);#%%mask=binary;data_new=np.copy(data,order='A');data_new[skel]=120;skel_new=np.asarray(skel,dtype=int,order='A');skel_new[:]=skel_new+binary;binary_new=np.copy(binary,order='A');qs=[];fori,einenumerate(special):print('------');print('%d / %d'%(i,len(special)));path,quality=con.connectPoint(data,mask,special,i,radius=25,skeleton=skel,tubeness=None,remove_local_mask=True,min_quality=15.0,verbose=True,maxSteps=15000,costPerDistance=1.0);#print path, qualityiflen(path)>0:qs.append(quality*1.0/len(path));q=con.addPathToMask(skel_new,path,value=7);q=con.addPathToMask(data_new,path,value=512);binary_new=con.addDilatedPathToMask(binary_new,path,iterations=1);skel_new[:]=skel_new+binary_new;q=con.addPathToMask(skel_new,special_xyz,value=6);fordin[3,4,5]:xyz=np.array(np.unravel_index(points[deg==d],data.shape,order='F')).T;q=con.addPathToMask(skel_new,xyz,value=d);q=con.addPathToMask(data_new,special_xyz,value=150);try:con.viewer[0].setSource(skel_new);con.viewer[1].setSource(data_new);except:con.viewer=p3d.plot([skel_new,data_new]);con.viewer[0].setMinMax([0,8]);con.viewer[1].setMinMax([24,160]);#%%importmatplotlib.pyplotasplt;plt.figure(1);plt.clf();#plt.plot(qs);plt.hist(qs)#%%i=20;i=21;i=30;i=40;r=25;center=np.unravel_index(ends[i],data.shape)print(center,data.shape)mask=binary;path=con.tracePointToMask(data,mask,center,radius=r,points=special_xyz,plot=True,skel=skel,binary=binary,tubeness=None,removeLocalMask=True,maxSteps=None,verbose=False,costPerDistance=0.0);#%%nbs=ap.findNeighbours(ends,i,skel.shape,skel.strides,r);center=np.unravel_index(ends[i],skel.shape)nbs_xyz=np.array(np.unravel_index(nbs,skel.shape)).Tdists=nbs_xyz-center;dists=np.sum(dists*dists,axis=1);nb=np.argmin(dists)center=np.unravel_index(ends[i],data.shape)print(center,data.shape)mask=binary;path=con.tracePointToNeighbor(data,mask,center,nbs_xyz[nb],radius=r,points=special_xyz,plot=True,skel=skel,binary=binary,tubeness=None,removeLocalMask=True,maxSteps=None,verbose=False,costPerDistance=0.0);#%%importClearMap.ImageProcessing.Filter.FilterKernelasfkr;dog=fkr.filterKernel('DoG',size=(13,13,13));dv.plot(dog)data_filter=ndi.correlate(np.asarray(data,dtype=float),dog);data_filter-=data_filter.min();data_filter=data_filter/3.0;#dv.dualPlot(data, data_filter);#%%add all pathsreload(con)r=25;mask=binary;data_new=data.copy();data_new[skel]=120;skel_new=np.asarray(skel,dtype=int);skel_new=skel_new+binary;binary_new=binary.copy();fori,einenumerate(special):center=np.unravel_index(e,data.shape);print(i,e,center)path=con.tracePointToMask(data,mask,center,radius=r,points=special_xyz,plot=False,skel=skel,binary=binary,tubeness=None,removeLocalMask=True,maxSteps=15000,costPerDistance=1.0);q=con.addPathToMask(skel_new,path,value=7);q=con.addPathToMask(data_new,path,value=512);binary_new=con.addDilatedPathToMask(binary_new,path,iterations=1);q=con.addPathToMask(skel_new,special_xyz,value=6);fordin[3,4,5]:xyz=np.array(np.unravel_index(points[deg==d],data.shape)).T;q=con.addPathToMask(skel_new,xyz,value=d);q=con.addPathToMask(data_new,special_xyz,value=150);skel_new=skel_new+binary_new;try:con.viewer[0].setSource(skel_new);con.viewer[1].setSource(data_new);except:con.viewer=dv.dualPlot(skel_new,data_new);con.viewer[0].setMinMax([0,8]);con.viewer[1].setMinMax([24,160]);#%%importClearMap.ImageProcessing.Skeletonization.Skeletonizeassklskel_2=skl.skeletonize3D(binary_new.copy());#%%np.save('/home/ckirst/Desktop/binarized_con.npy',binary_new)#%%# write imageimportClearMap.IO.IOasio#r = np.asarray(128 * binary_new, dtype = 'uint8');#g = r.copy(); b = r.copy();#r[:] = r + 127 * skel_2[0];#g[:] = g - 128 * skel_2[0];#b[:] = b - 128 * skel_2[0];#img = np.stack((r,g,b), axis = 3)img=np.asarray(128*binary_new,dtype='uint8');img[:]=img+127*skel_2[0];io.writeData('/home/ckirst/Desktop/3d.tif',img)#%%#%%# if removeLocalMask is not False:# if not removeLocalMask is True:# if not isinstance(removeLocalMask, tuple):# removeLocalMask = (removeLocalMask,);# removeLocalMask = removeLocalMask*3;# removeLocalMask = removeLocalMask[:3];# local_mask = extractNeighbourhood(mask_nbh, center_nbh, removeLocalMask);# sl = tuple([slice(c - r, c + r) for c,r in zip(center_nbh, removeLocalMask)]);# else:# local_mask = mask_nbh;# sl = (slice(None),) * 3;# # mask_nbh_label, mask_label = ndi.label(local_mask, structure = np.ones((3,3,3), dtype = bool));# ids = local_mask_label[tuple(center_nbh)] == local_mask_label;# # mask_nbh[ids] = False;# # if removeLocalTubness is not None:## # ker = sel.# ids = np.logical_and(ids, )# tubeness_nbh[sls] = ;