def grow_facet(fit, plane, label, support, max_distance=0.90, debugging=True): """ Find voxels of the object which belong to a facet using the facet plane equation and the distance to the plane. :param fit: coefficients of the plane (tuple of 3 numbers) :param plane: 3D binary array of the shape of the data :param label: the label of the plane processed :param support: binary support of the object :param max_distance: in pixels, maximum allowed distance to the facet plane of a voxel :param debugging: set to True to see plots :return: the updated plane, a stop flag """ from scipy.signal import convolve nbz, nby, nbx = plane.shape indices = np.nonzero(plane) if len(indices[0]) == 0: no_points = 1 return plane, no_points kernel = np.ones((3, 3, 3)) start_z = max(indices[0].min() - 20, 0) stop_z = min(indices[0].max() + 21, nbz) start_y = max(indices[1].min() - 20, 0) stop_y = min(indices[1].max() + 21, nby) start_x = max(indices[2].min() - 20, 0) stop_x = min(indices[2].max() + 21, nbx) # find nearby voxels using the coordination number obj = np.copy(plane[start_z:stop_z, start_y:stop_y, start_x:stop_x]) coord = np.rint(convolve(obj, kernel, mode='same')) coord = coord.astype(int) coord[np.nonzero(coord)] = 1 # update plane with new voxels temp_plane = np.copy(plane) temp_plane[start_z:stop_z, start_y:stop_y, start_x:stop_x] = coord # remove voxels not belonging to the support temp_plane[support == 0] = 0 # check distance of new voxels to the plane new_indices = np.nonzero(temp_plane) plane, no_points = distance_threshold(fit=fit, indices=new_indices, shape=temp_plane.shape, max_distance=max_distance) if debugging and len(new_indices[0]) != 0: indices = np.nonzero(plane) gu.scatter_plot(array=np.asarray(indices).T, labels=('x', 'y', 'z'), title='Plane' + str(label) + ' after 1 cycle of facet growing') print(str(len(indices[0])) + ' after 1 cycle of facet growing') return plane, no_points
def plane_fit(indices, label='', threshold=1, debugging=False): """ Fit a plane to the voxels defined by indices. :param indices: a (3xN) numpy array, x values being the 1st row, y values the 2nd row and z values the 3rd row :param label: int, label of the plane used for the title in the debugging plot :param threshold: the fit will be considered as good if the mean distance of the voxels to the plane is smaller than this value :param debugging: True to see plots :return: a tuple of coefficient (a, b, c, d) such that ax+by+cz+d=0, the matrix of covariant values """ valid_plane = True indices = np.asarray(indices) params3d, pcov3d = curve_fit(plane, indices[0:2, :], indices[2, :]) std_param3d = np.sqrt(np.diag(pcov3d)) params = (-params3d[0], -params3d[1], 1, -params3d[2]) std_param = (std_param3d[0], std_param3d[1], 0, std_param3d[2]) if debugging: _, ax = gu.scatter_plot(np.transpose(indices), labels=('axis 0', 'axis 1', 'axis 2'), title='Points and fitted plane ' + str(label)) xlim = ax.get_xlim() ylim = ax.get_ylim() meshx, meshy = np.meshgrid(np.arange(xlim[0], xlim[1] + 1, 1), np.arange(ylim[0], ylim[1] + 1, 1)) # meshx varies horizontally, meshy vertically meshz = plane(np.vstack( (meshx.flatten(), meshy.flatten())), params3d[0], params3d[1], params3d[2]).reshape(meshx.shape) ax.plot_wireframe(meshx, meshy, meshz, color='k') plt.pause(0.1) # calculate the mean distance to the fitted plane distance = plane_dist(indices=indices, params=params) print( f'plane fit using z=a*x+b*y+c: dist.mean()={distance.mean():.2f}, dist.std()={distance.std():.2f}' ) if distance.mean() > threshold and distance.mean() / distance.std() > 1: # probably z does not depend on x and y, try to fit y = a*x + b print('z=a*x+b*y+c: z may not depend on x and y') params2d, pcov2d = curve_fit(line, indices[0, :], indices[1, :]) std_param2d = np.sqrt(np.diag(pcov2d)) params = (-params2d[0], 1, 0, -params2d[1]) std_param = (std_param2d[0], 0, 0, std_param2d[1]) if debugging: _, ax = gu.scatter_plot(np.transpose(indices), labels=('axis 0', 'axis 1', 'axis 2'), title='Points and fitted plane ' + str(label)) xlim = ax.get_xlim() zlim = ax.get_zlim() meshx, meshz = np.meshgrid(np.arange(xlim[0], xlim[1] + 1, 1), np.arange(zlim[0], zlim[1] + 1, 1)) meshy = line(x_array=meshx.flatten(), a=params2d[0], b=params2d[1]).reshape(meshx.shape) ax.plot_wireframe(meshx, meshy, meshz, color='k') plt.pause(0.1) # calculate the mean distance to the fitted plane distance = plane_dist(indices=indices, params=params) print( f'plane fit using y=a*x+b: dist.mean()={distance.mean():.2f}, dist.std()={distance.std():.2f}' ) if distance.mean( ) > threshold and distance.mean() / distance.std() > 1: # probably y does not depend on x, that means x = constant print('y=a*x+b: y may not depend on x') constant = indices[0, :].mean() params = (1, 0, 0, -constant) std_param = (0, 0, 0, indices[0, :].std()) if debugging: _, ax = gu.scatter_plot(np.transpose(indices), labels=('axis 0', 'axis 1', 'axis 2'), title='Points and fitted plane ' + str(label)) ylim = ax.get_ylim() zlim = ax.get_zlim() meshy, meshz = np.meshgrid(np.arange(ylim[0], ylim[1] + 1, 1), np.arange(zlim[0], zlim[1] + 1, 1)) meshx = np.ones(meshy.shape) * constant ax.plot_wireframe(meshx, meshy, meshz, color='k') plt.pause(0.1) # calculate the mean distance to the fitted plane distance = plane_dist(indices=indices, params=params) print( f'plane fit using x=constant: dist.mean()={distance.mean():.2f}, dist.std()={distance.std():.2f}' ) if distance.mean() > threshold and distance.mean() / distance.std() > 1: # probably the distribution of points is not flat print( 'distance.mean() > 1, probably the distribution of points is not flat' ) valid_plane = False return params, std_param, valid_plane
surface = np.copy(support) surface[coordination_matrix > 22] = 0 # remove the bulk 22 bulk = support - surface del coordination_matrix gc.collect() ######################################################## # define edges using the coordination number of voxels # ######################################################## edges = pu.calc_coordination(support, kernel=np.ones((9, 9, 9)), debugging=False) edges[support == 0] = 0 if debug: gu.multislices_plot(edges, invert_yaxis=True, vmin=0, title='Coordination matrix') edges[edges > edges_coord] = 0 # remove facets and bulk edges[np.nonzero(edges)] = 1 # edge support gu.scatter_plot(array=np.asarray(np.nonzero(edges)).T, markersize=2, markercolor='b', labels=('x', 'y', 'z'), title='edges') ######################################################## # define corners using the coordination number of voxels # ######################################################## corners = pu.calc_coordination(support, kernel=np.ones((9, 9, 9)), debugging=False) corners[support == 0] = 0 if debug: gu.multislices_plot(corners, invert_yaxis=True, vmin=0, title='Coordination matrix') corners[corners > corners_coord] = 0 # remove edges, facets and bulk corners[np.nonzero(corners)] = 1 # corner support gu.scatter_plot(array=np.asarray(np.nonzero(corners)).T, markersize=2, markercolor='b', labels=('x', 'y', 'z'), title='corners') ###################################### # Initialize log files and .vti file #
gc.collect() ######################################################## # define edges using the coordination number of voxels # ######################################################## edges = pu.calc_coordination(support, kernel=np.ones((9, 9, 9)), debugging=False) edges[support == 0] = 0 if debug: gu.multislices_plot(edges, vmin=0, title='Coordination matrix') edges[edges > edges_coord] = 0 # remove facets and bulk edges[np.nonzero(edges)] = 1 # edge support gu.scatter_plot(array=np.asarray(np.nonzero(edges)).T, markersize=2, markercolor='b', labels=('axis 0', 'axis 1', 'axis 2'), title='edges') ######################################################## # define corners using the coordination number of voxels # ######################################################## corners = pu.calc_coordination(support, kernel=np.ones((9, 9, 9)), debugging=False) corners[support == 0] = 0 if debug: gu.multislices_plot(corners, vmin=0, title='Coordination matrix') corners[corners > corners_coord] = 0 # remove edges, facets and bulk corners[np.nonzero(corners)] = 1 # corner support gu.scatter_plot(array=np.asarray(np.nonzero(corners)).T,