def test_ellipsoid_levelset(): test = ellipsoid(2, 2, 2, levelset=True)[1:-1, 1:-1, 1:-1] test_anisotropic = ellipsoid(2, 2, 4, spacing=(1., 1., 2.), levelset=True) test_anisotropic = test_anisotropic[1:-1, 1:-1, 1:-1] expected = np.array([[[2., 1.25, 1., 1.25, 2.], [1.25, 0.5, 0.25, 0.5, 1.25], [1., 0.25, 0., 0.25, 1.], [1.25, 0.5, 0.25, 0.5, 1.25], [2., 1.25, 1., 1.25, 2.]], [[1.25, 0.5, 0.25, 0.5, 1.25], [0.5, -0.25, -0.5, -0.25, 0.5], [0.25, -0.5, -0.75, -0.5, 0.25], [0.5, -0.25, -0.5, -0.25, 0.5], [1.25, 0.5, 0.25, 0.5, 1.25]], [[1., 0.25, 0., 0.25, 1.], [0.25, -0.5, -0.75, -0.5, 0.25], [0., -0.75, -1., -0.75, 0.], [0.25, -0.5, -0.75, -0.5, 0.25], [1., 0.25, 0., 0.25, 1.]], [[1.25, 0.5, 0.25, 0.5, 1.25], [0.5, -0.25, -0.5, -0.25, 0.5], [0.25, -0.5, -0.75, -0.5, 0.25], [0.5, -0.25, -0.5, -0.25, 0.5], [1.25, 0.5, 0.25, 0.5, 1.25]], [[2., 1.25, 1., 1.25, 2.], [1.25, 0.5, 0.25, 0.5, 1.25], [1., 0.25, 0., 0.25, 1.], [1.25, 0.5, 0.25, 0.5, 1.25], [2., 1.25, 1., 1.25, 2.]]]) assert_allclose(test, expected) assert_allclose(test_anisotropic, expected)
def test_masked_marching_cubes_all_true(): ellipsoid_scalar = ellipsoid(6, 10, 16, levelset=True) mask = np.ones_like(ellipsoid_scalar, dtype=bool) ver_m, faces_m, _, _ = marching_cubes(ellipsoid_scalar, 0, mask=mask) ver, faces, _, _ = marching_cubes(ellipsoid_scalar, 0, mask=mask) assert_allclose(ver_m, ver, rtol=.00001) assert_allclose(faces_m, faces, rtol=.00001)
def test_inertia_tensor_3d(): image = cp.asarray(draw.ellipsoid(10, 5, 3)) T0 = inertia_tensor(image) eig0, V0 = np.linalg.eig(cp.asnumpy(T0)) # principal axis of ellipse = eigenvector of smallest eigenvalue v0 = cp.asarray(V0[:, np.argmin(eig0)]) assert cp.allclose(v0, [1, 0, 0]) or cp.allclose(-v0, [1, 0, 0]) imrot = ndi.rotate(image.astype(float), 30, axes=(0, 1), order=1) Tr = inertia_tensor(imrot) eigr, Vr = np.linalg.eig(cp.asnumpy(Tr)) vr = cp.asarray(Vr[:, np.argmin(eigr)]) # Check that axis has rotated by expected amount pi, cos, sin = np.pi, np.cos, np.sin # fmt: off R = cp.asarray([[cos(pi/6), -sin(pi/6), 0], # noqa [sin(pi/6), cos(pi/6), 0], # noqa [ 0, 0, 1]]) # noqa # fmt: on expected_vr = R @ v0 assert cp.allclose(vr, expected_vr, atol=1e-3, rtol=0.01) or cp.allclose( -vr, expected_vr, atol=1e-3, rtol=0.01 )
def test_marching_cubes_anisotropic(): # test spacing as numpy array (and not just tuple) spacing = np.array([1., 10 / 6., 16 / 6.]) ellipsoid_anisotropic = ellipsoid(6, 10, 16, spacing=spacing, levelset=True) _, surf = ellipsoid_stats(6, 10, 16) # Classic verts, faces = marching_cubes_classic(ellipsoid_anisotropic, 0., spacing=spacing) surf_calc = mesh_surface_area(verts, faces) # Test within 1.5% tolerance for anisotropic. Will always underestimate. assert surf > surf_calc and surf_calc > surf * 0.985 # Lewiner verts, faces = marching_cubes_lewiner(ellipsoid_anisotropic, 0., spacing=spacing)[:2] surf_calc = mesh_surface_area(verts, faces) # Test within 1.5% tolerance for anisotropic. Will always underestimate. assert surf > surf_calc and surf_calc > surf * 0.985 # Test spacing together with allow_degenerate=False marching_cubes_lewiner(ellipsoid_anisotropic, 0, spacing=spacing, allow_degenerate=False)
def test_invalid_input(): # Classic with pytest.raises(ValueError): marching_cubes(np.zeros((2, 2, 1)), 0, method='lorensen') with pytest.raises(ValueError): marching_cubes(np.zeros((2, 2, 1)), 1, method='lorensen') with pytest.raises(ValueError): marching_cubes(np.ones((3, 3, 3)), 1, spacing=(1, 2), method='lorensen') with pytest.raises(ValueError): marching_cubes(np.zeros((20, 20)), 0, method='lorensen') # Lewiner with pytest.raises(ValueError): marching_cubes(np.zeros((2, 2, 1)), 0) with pytest.raises(ValueError): marching_cubes(np.zeros((2, 2, 1)), 1) with pytest.raises(ValueError): marching_cubes(np.ones((3, 3, 3)), 1, spacing=(1, 2)) with pytest.raises(ValueError): marching_cubes(np.zeros((20, 20)), 0) # invalid method name ellipsoid_isotropic = ellipsoid(6, 10, 16, levelset=True) with pytest.raises(ValueError): marching_cubes(ellipsoid_isotropic, 0., method='abcd')
def test_correct_mesh_orientation(): sphere_small = ellipsoid(1, 1, 1, levelset=True) # Mesh with incorrectly oriented faces which was previously returned from # `marching_cubes`, before it guaranteed correct mesh orientation verts = np.array([[1., 2., 2.], [2., 2., 1.], [2., 1., 2.], [2., 2., 3.], [2., 3., 2.], [3., 2., 2.]]) faces = np.array([[0, 1, 2], [2, 0, 3], [1, 0, 4], [4, 0, 3], [1, 2, 5], [2, 3, 5], [1, 4, 5], [5, 4, 3]]) # Correct mesh orientation - descent corrected_faces1 = correct_mesh_orientation(sphere_small, verts, faces, gradient_direction='descent') corrected_faces2 = correct_mesh_orientation(sphere_small, verts, faces, gradient_direction='ascent') # Ensure ascent is opposite of descent for all faces assert_array_equal(corrected_faces1, corrected_faces2[:, ::-1]) # Ensure correct faces have been reversed: 1, 4, and 5 idx = [1, 4, 5] expected = faces.copy() expected[idx] = expected[idx, ::-1] assert_array_equal(expected, corrected_faces1)
def __getitem__(self, _): if self.debug: return self.__getitem_split__() max_elips_size = np.maximum(self.res // 4, 4) TSDF = np.ones((self.res, self.res, self.res)) * max_elips_size max_val = 0 for i in range(self.n_elips): x_, y_, z_ = np.random.randint( 3, max_elips_size), np.random.randint( 3, max_elips_size), np.random.randint(3, max_elips_size) el = ellipsoid(x_, y_, z_, levelset=True) el *= np.min(np.array(el.shape) // 2) #scikit returns values vaguely [-1:1]. assert np.abs(el).max() < self.res x, y, z = el.shape x0, y0, z0 = (np.random.rand(3) * (self.res - np.array(el.shape) - 1)).astype(int) tmp = np.minimum(TSDF[x0:x0 + x, y0:y0 + y, z0:z0 + z], el) assert tmp.shape == TSDF[x0:x0 + x, y0:y0 + y, z0:z0 + z].shape, (tmp.shape, TSDF[x0:x0 + x, y0:y0 + y, z0:z0 + z].shape) TSDF[x0:x0 + x, y0:y0 + y, z0:z0 + z] = tmp max_val = np.maximum(np.max(tmp), max_val) TSDF[TSDF == max_elips_size] = max_val return TSDF, (TSDF + np.random.randn(self.res, self.res, self.res) * self.sigma)[None, :]
def test_3d_ellipsoid_axis_lengths(): """Verify that estimated axis lengths are correct. Uses an ellipsoid at an arbitrary position and orientation. """ # generate a centered ellipsoid with non-uniform half-lengths (radii) half_lengths = (20, 10, 50) e = draw.ellipsoid(*half_lengths).astype(int) # Pad by asymmetric amounts so the ellipse isn't centered. Also, pad enough # that the rotated ellipse will still be within the original volume. e = np.pad(e, pad_width=[(30, 18), (30, 12), (40, 20)], mode='constant') # apply rotations to the ellipsoid R = transform.EuclideanTransform(rotation=[0.2, 0.3, 0.4], dimensionality=3) e = ndi.affine_transform(e, R.params) # Compute regionprops rp = regionprops(e)[0] # estimate principal axis lengths via the inertia tensor eigenvalues evs = rp.inertia_tensor_eigvals axis_lengths = _inertia_eigvals_to_axes_lengths_3D(evs) expected_lengths = sorted([2 * h for h in half_lengths], reverse=True) for ax_len_expected, ax_len in zip(expected_lengths, axis_lengths): # verify accuracy to within 1% assert abs(ax_len - ax_len_expected) < 0.01 * ax_len_expected # verify that the axis length regionprops also agree assert abs(rp.axis_major_length - axis_lengths[0]) < 1e-7 assert abs(rp.axis_minor_length - axis_lengths[-1]) < 1e-7
def silly_gen(denoise=False): # creating base label space spacial_data = np.zeros((50, 50)) # creating base image data = np.zeros((240, 50, 50)) data = random_noise(data) # creating label space rr, cc = ellipse(14, 19, 12, 18, (27, 39)) rr1, cc1 = ellipse(15, 20, 5, 8, (27, 39)) rr2, cc2 = ellipse(12, 13, 3, 3, (27, 39)) rr3, cc3 = ellipse(18, 23, 3, 3, (27, 39)) #prr, pcc = ellipse_perimeter(14, 19, 12, 18, shape=(27, 39)) # prr2, pcc2 = ellipse_perimeter(14, 19, 11, 17, shape=(27, 39)) # assigning labels to label space spacial_data[rr, cc] += 1 spacial_data[rr1, cc1] *= 2 spacial_data[rr2, cc2] *= 3 spacial_data[rr3, cc3] *= 5 # spacial_data[rr3, cc3] *= 7 #spacial_data[prr, pcc] = 11 # spacial_data[prr2,pcc2] = 11 # create ellipse for spectral values at depth ell = ellipsoid(120, 12, 18, levelset=True) # making more similar to real data ell *= -500 ell += 2500 # ell1 *= -250 # ell1 += 1000 # adding ellipse to base image data[0:240, rr, cc] += ell[0:240, rr, cc] data[0:240, rr1, cc1] += ell[0:240, rr1, cc1] ell1 = ell * 0.25 data[25:37, rr2, cc2] -= ell1[25:37, rr2, cc2] data[50:63, rr3, cc3] += ell[50:63, rr3, cc3] if denoise: data = denoise_wavelet(data, mode='soft', multichannel=True) # Reshaping image for label classification data_n = data.swapaxes(0, 2) # data_n = data_n.swapaxes(0, 1) # Reshape to 2D in form samples*lines,bands # data_pix = data_n.transpose(2, 0, 1).reshape(40000, -1) d1, d2, d3 = data_n.shape data_pix = data_n.transpose(2, 0, 1).reshape(d1 * d2, -1) spacial_pix = spacial_data.reshape(d1 * d2, -1).ravel() # Reshaping label space for classification # print(spacial_data.shape) # sd1, sd2, sd3 = spacial_data.shape # spacial_pix = spacial_data.swapaxes(0, 2).transpose(2, 0, 1).reshape(sd2*sd3, -1) # print(spacial_pix.shape) # split train and test data return data_pix, spacial_pix, data, spacial_data
def binary_select_foci(box, image, blobs_new_im): ellip_base = ellipsoid(40, 40, 40, spacing=(1.05, 1.05, 2.1), levelset=False).astype("int") pts = np.transpose(np.nonzero(ellip_base)) z, x, y = image.shape # Need to add some extra padding around the z axis liste = [] box_mask = [] for i in range(len(box)): binary = np.zeros((x + 15, y + 15, z + 30)) mask = np.copy(blobs_new_im) #create elipsoid where Chromosome where found binary[pts[:, 0] + (box[i, 1].astype(int) - 5), pts[:, 1] + (box[i, 0].astype(int) - 5), pts[:, 2] + (box[i, 4].astype(int) - 20)] = 1 binary = binary[:x, :y, :z].transpose(2, 0, 1) # remove every dots that are not on a chromosome mask[~binary.astype(bool)] = 0 if len(np.where(mask > 0)[0]) == 0: liste.append(np.array([[np.nan, np.nan, np.nan, i]])) box_mask.append(False) else: box_mask.append(True) liste.append( np.squeeze( np.dstack((np.where(mask > 0)[0], np.where(mask > 0)[1], np.where(mask > 0)[2], np.full(len(np.where(mask > 0)[0]), i))))) #Remove extra padding return (np.vstack(liste), box_mask)
def test_marching_cubes_isotropic(): ellipsoid_isotropic = ellipsoid(6, 10, 16, levelset=True) _, surf = ellipsoid_stats(6, 10, 16) verts, faces = marching_cubes(ellipsoid_isotropic, 0.) surf_calc = mesh_surface_area(verts, faces) # Test within 1% tolerance for isotropic. Will always underestimate. assert surf > surf_calc and surf_calc > surf * 0.99
def test_marching_cubes_anisotropic(): spacing = (1.0, 10 / 6.0, 16 / 6.0) ellipsoid_anisotropic = ellipsoid(6, 10, 16, spacing=spacing, levelset=True) _, surf = ellipsoid_stats(6, 10, 16) verts, faces = marching_cubes(ellipsoid_anisotropic, 0.0, spacing=spacing) surf_calc = mesh_surface_area(verts, faces) # Test within 1.5% tolerance for anisotropic. Will always underestimate. assert surf > surf_calc and surf_calc > surf * 0.985
def test_ellipsoid_bool(): test = ellipsoid(2, 2, 2)[1:-1, 1:-1, 1:-1] test_anisotropic = ellipsoid(2, 2, 4, spacing=(1., 1., 2.)) test_anisotropic = test_anisotropic[1:-1, 1:-1, 1:-1] expected = np.array([[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0], [0, 1, 1, 1, 0], [0, 1, 1, 1, 0], [0, 1, 1, 1, 0], [0, 0, 0, 0, 0]], [[0, 0, 1, 0, 0], [0, 1, 1, 1, 0], [1, 1, 1, 1, 1], [0, 1, 1, 1, 0], [0, 0, 1, 0, 0]], [[0, 0, 0, 0, 0], [0, 1, 1, 1, 0], [0, 1, 1, 1, 0], [0, 1, 1, 1, 0], [0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]]) assert_array_equal(test, expected.astype(bool)) assert_array_equal(test_anisotropic, expected.astype(bool))
def test_moments_normalized_3d(): image = draw.ellipsoid(1, 1, 10) mu_image = moments_central(image) nu = moments_normalized(mu_image) assert nu[0, 0, 2] > nu[0, 2, 0] assert_almost_equal(nu[0, 2, 0], nu[2, 0, 0]) coords = np.where(image) mu_coords = moments_coords_central(coords) assert_almost_equal(mu_coords, mu_image)
def test_masked_marching_cubes(): ellipsoid_scalar = ellipsoid(6, 10, 16, levelset=True) mask = np.ones_like(ellipsoid_scalar, dtype=bool) mask[:10, :, :] = False mask[:, :, 20:] = False ver, faces, _, _ = marching_cubes(ellipsoid_scalar, 0, mask=mask) area = mesh_surface_area(ver, faces) assert_allclose(area, 299.56878662109375, rtol=.01)
def main(select=3, **kwargs): """Script main function. select: int 1: Medical data 2: Blocky data, different every time 3: Two donuts 4: Ellipsoid """ import visvis as vv # noqa: delay import visvis and GUI libraries # Create test volume if select == 1: vol = vv.volread('stent') isovalue = kwargs.pop('level', 800) elif select == 2: vol = vv.aVolume(20, 128) isovalue = kwargs.pop('level', 0.2) elif select == 3: with timer('computing donuts'): vol = donuts() isovalue = kwargs.pop('level', 0.0) # Uncommenting the line below will yield different results for # classic MC # vol *= -1 elif select == 4: vol = ellipsoid(4, 3, 2, levelset=True) isovalue = kwargs.pop('level', 0.0) else: raise ValueError('invalid selection') # Get surface meshes with timer('finding surface lewiner'): vertices1, faces1 = marching_cubes_lewiner(vol, isovalue, **kwargs)[:2] with timer('finding surface classic'): vertices2, faces2 = marching_cubes_classic(vol, isovalue, **kwargs) # Show vv.figure(1) vv.clf() a1 = vv.subplot(121) vv.title('Lewiner') m1 = vv.mesh(np.fliplr(vertices1), faces1) a2 = vv.subplot(122) vv.title('Classic') m2 = vv.mesh(np.fliplr(vertices2), faces2) a1.camera = a2.camera # visvis uses right-hand rule, gradient_direction param uses left-hand rule m1.cullFaces = m2.cullFaces = 'front' # None, front or back vv.use().Run()
def test_marching_cubes_anisotropic(): sampling = (1., 10 / 6., 16 / 6.) ellipsoid_anisotropic = ellipsoid(6, 10, 16, sampling=sampling, levelset=True) _, surf = ellipsoid_stats(6, 10, 16, sampling=sampling) verts, faces = marching_cubes(ellipsoid_anisotropic, 0., sampling=sampling) surf_calc = mesh_surface_area(verts, faces) # Test within 1.5% tolerance for anisotropic. Will always underestimate. assert surf > surf_calc and surf_calc > surf * 0.985
def mainPutFaceBack(): # imgPath = './tmp.jpg' imgPath = '../../github/vrn-07231340/examples/scaled/trump-12.jpg' objPath = '../../github/vrn-07231340/obj/trump-12.obj' img = cv2.imread(imgPath) '''1. detect landmarks for origin image''' originLandmark2D = getLandmark2D(img) # for (x, y) in originLandmark2D: # cv2.circle(img, (x, y), 1, (0, 255, 0), -1) '''2. read origin 3d model''' objLines, vLines, _ = readObj(objPath) '''3. (OPENCV) fit 3d ellipsoid ''' '''想用XY, YZ, XZ三个面分别拟合三个椭圆,取最大的长短轴拟合椭球体,但是效果不好。''' '''画出来的椭圆不能包围所有的点''' pointsXY, pointsXZ, pointsYZ, pointsXYZ = getAllPoints(vLines) ''' 3.1 fit XY front face ellipse''' # getEllipse(pointsXY, img) ''' 3.2 fit XZ front face ellipse''' # getEllipse(pointsXZ, img) ''' 3.3 fit YZ front face ellipse''' # for i in range(len(pointsYZ)): # pointsYZ[i] = [pointsYZ[i][0]+20, pointsYZ[i][1]+20] # getEllipse(pointsYZ, img) '''3. (SKIMAGE) fit 3d ellipsoid ''' '''由三个轴的半长轴求一个椭圆体''' '''首先求3d model 分别在三个轴上的最大最小值''' maxminDict = maxminXYZ(objLines) maxX = maxminDict['maxXCoord'][0] minX = maxminDict['minXCoord'][0] maxY = maxminDict['maxYCoord'][1] minY = maxminDict['minYCoord'][1] maxZ = maxminDict['maxZCoord'][2] minZ = maxminDict['minZCoord'][2] semiMajorX = (maxX-minX)/2 semiMajorY = (maxY-minY)/2 semiMajorZ = (maxZ-minZ) print semiMajorX, semiMajorY, semiMajorZ ellipsoid_base = ellipsoid(semiMajorX, semiMajorY, semiMajorZ) '''ellipsoid_base就是求得的椭球体,print出来都是False''' print ellipsoid_base '''下面这段代码想把椭球画出来看看长什么样子''' data = ellipsoid_base print data.shape x, y, z = data[0], data[1], data[2] print x.shape, y.shape, z.shape ax = plt.subplot(111, projection='3d') # 创建一个三维的绘图工程 ax.scatter(x, y, z, c='y') # 绘制数据点 ax.set_zlabel('Z') # 坐标轴 ax.set_ylabel('Y') ax.set_xlabel('X') plt.show()
def test_both_algs_same_result_ellipse(): # Performing this test on data that does not have ambiguities sphere_small = ellipsoid(1, 1, 1, levelset=True) vertices1, faces1 = marching_cubes_classic(sphere_small, 0)[:2] vertices2, faces2 = marching_cubes_lewiner(sphere_small, 0, allow_degenerate=False)[:2] vertices3, faces3 = marching_cubes_lewiner(sphere_small, 0, allow_degenerate=False, use_classic=True)[:2] # Order is different, best we can do is test equal shape and same vertices present assert _same_mesh(vertices1, faces1, vertices2, faces2) assert _same_mesh(vertices1, faces1, vertices3, faces3)
def generate_tsdf(vox_size=512, n_elips=10, sigma=0.): TSDF = np.ones((vox_size, vox_size, vox_size)) * 20 for i in range(n_elips): el = ellipsoid(np.random.randint(1, 40), np.random.randint(1, 40), np.random.randint(1, 40), levelset=True) x, y, z = el.shape x0, y0, z0 = (np.random.rand(3) * (vox_size - np.array(el.shape))).astype(int) TSDF[x0:x0 + x, y0:y0 + y, z0:z0 + z] = np.minimum(TSDF[x0:x0 + x, y0:y0 + y, z0:z0 + z], el) return TSDF, TSDF + np.random.randn(vox_size, vox_size, vox_size) * sigma
def test_ellipsoid_levelset(): test = ellipsoid(2, 2, 2, levelset=True)[1:-1, 1:-1, 1:-1] test_anisotropic = ellipsoid(2, 2, 4, spacing=(1., 1., 2.), levelset=True) test_anisotropic = test_anisotropic[1:-1, 1:-1, 1:-1] expected = np.array([[[ 2. , 1.25, 1. , 1.25, 2. ], [ 1.25, 0.5 , 0.25, 0.5 , 1.25], [ 1. , 0.25, 0. , 0.25, 1. ], [ 1.25, 0.5 , 0.25, 0.5 , 1.25], [ 2. , 1.25, 1. , 1.25, 2. ]], [[ 1.25, 0.5 , 0.25, 0.5 , 1.25], [ 0.5 , -0.25, -0.5 , -0.25, 0.5 ], [ 0.25, -0.5 , -0.75, -0.5 , 0.25], [ 0.5 , -0.25, -0.5 , -0.25, 0.5 ], [ 1.25, 0.5 , 0.25, 0.5 , 1.25]], [[ 1. , 0.25, 0. , 0.25, 1. ], [ 0.25, -0.5 , -0.75, -0.5 , 0.25], [ 0. , -0.75, -1. , -0.75, 0. ], [ 0.25, -0.5 , -0.75, -0.5 , 0.25], [ 1. , 0.25, 0. , 0.25, 1. ]], [[ 1.25, 0.5 , 0.25, 0.5 , 1.25], [ 0.5 , -0.25, -0.5 , -0.25, 0.5 ], [ 0.25, -0.5 , -0.75, -0.5 , 0.25], [ 0.5 , -0.25, -0.5 , -0.25, 0.5 ], [ 1.25, 0.5 , 0.25, 0.5 , 1.25]], [[ 2. , 1.25, 1. , 1.25, 2. ], [ 1.25, 0.5 , 0.25, 0.5 , 1.25], [ 1. , 0.25, 0. , 0.25, 1. ], [ 1.25, 0.5 , 0.25, 0.5 , 1.25], [ 2. , 1.25, 1. , 1.25, 2. ]]]) assert_allclose(test, expected) assert_allclose(test_anisotropic, expected)
def test_both_algs_same_result_ellipse(): # Performing this test on data that does not have ambiguities sphere_small = ellipsoid(1, 1, 1, levelset=True) vertices1, faces1 = marching_cubes_classic(sphere_small, 0)[:2] vertices2, faces2 = marching_cubes_lewiner( sphere_small, 0, allow_degenerate=False)[:2] vertices3, faces3 = marching_cubes_lewiner( sphere_small, 0, allow_degenerate=False, use_classic=True)[:2] # Order is different, best we can do is test equal shape and same vertices present assert _same_mesh(vertices1, faces1, vertices2, faces2) assert _same_mesh(vertices1, faces1, vertices3, faces3)
def ellipsoidHead(semiX, semiY, semiZ, centerX, centerY, centerZ): ellip = ellipsoid(semiX, semiY, semiZ, spacing=(0.1, 0.1, 0.1), levelset=True) verts, faces, normals, values = measure.marching_cubes_lewiner(ellip, 0) verts[:, 0] = verts[:, 0] + centerX verts[:, 1] = verts[:, 1] + centerY verts[:, 2] = verts[:, 2] + centerZ ellipsoidHeadVLines = [] for vert in verts: ellipsoidHeadVLines.append('{} {} {} {} {} {} {}'.format( 'v', vert[0], vert[1], vert[2], 0, 0, 0)) return ellipsoidHeadVLines
def ellipsoid_mask(self, a=40, b=60, c=40, apply=False, plot=True): """Method to generate an ellipsoid to mask voxels in the brain. :param a: {int} Length of semimajor axis aligned with x-axis. :param b: {int} Length of semimajor axis aligned with y-axis. :param c: {int} Length of semimajor axis aligned with z-axis. :param apply: {bool} if ``True``, the mask is directly applied to cut down the brains in :py:attr:`data` and reshape them into vectors :param plot: {bool} whether the generated mask should be visualized in a plot. A random image is shown. :return: Mask in the attribute :py:attr:`mask` """ print("Computing ellipsoid mask...") ellps = ellipsoid(a, b, c) # shapes q, r, s = ellps.shape x, y, z = self.img.shape[:3] # get spacing to walls for centering the ellipsoid in the middle of the brain a = (x - q) / 2 c = (y - r) / 2 e = (z - s) / 2 if (x - q) % 2: b = a + 1 else: b = a if (y - r) % 2: d = c + 1 else: d = c if (z - s) % 2: f = e + 1 else: f = e ellps = np.pad(ellps, ((a, b), (c, d), (e, f)), mode='constant', constant_values=False).astype('int') self.mask = Nifti1Image(ellps, self.img.affine) if apply: self.apply_mask() if plot: plot_roi( self.mask, index_img(self.img, np.random.randint(0, self.img.shape[-1]))) print("\tMask computed!")
def build_ground_truth(img3d, blobs, ellipsoid=False, labels=None, spacing=None): """Build ground truth volumetric image from blobs. Attributes: img3d: Image as 3D Numpy array in which to store results blobs: Numpy array of segments to display, given as an (n, 4) dimension array, where each segment is in (z, y, x, radius). ellipsoid: True to draw blobs as ellipsoids; defaults to False. labels: Array of labels the same length as ``blobs`` to assign as the values for each ground truth; defaults to None to assign a default value of 1 instead. spacing: Spacing by which to multiply blobs` radii; defaults to None, in which case each blob's radius will be used for all dimensions. Returns: ``img3d`` with ground drawn as circles or ellipsoids. """ if ellipsoid: # draw blobs as ellipses for i, blob in enumerate(blobs): if spacing is None: centroid = np.repeat(blob[3], 3) else: # multiply spacing directly rather than using in ellipsoid # function since the fn does not appear to place the # ellipsoide in the center of the array centroid = np.multiply(blob[3], spacing) ellip = draw.ellipsoid(*centroid) label = True if labels is None else labels[i] replace_vol(img3d, ellip, blob[:3], vol_as_mask=label) else: # draw blobs as circles only in given z-planes if labels is None: labels = np.ones(len(blobs), dtype=int) for i in range(img3d.shape[0]): mask = blobs[:, 0] == i blobs_in = blobs[mask] labels_in = labels[mask] for blob, label in zip(blobs_in, labels_in): rr, cc = draw.circle(*blob[1:4], img3d[i].shape) #print("drawing circle of {} x {}".format(rr, cc)) img3d[i, rr, cc] = label return img3d
def test_double_ellipsoid_surface_area(self): if USE_SCIKIT: import numpy as np from skimage.draw import ellipsoid from skimage.measure import marching_cubes_classic, mesh_surface_area ellip_base = ellipsoid(6, 10, 16, levelset=True) ellip_double = np.concatenate( (ellip_base[:-1, ...], ellip_base[2:, ...]), axis=0) volume = torch.Tensor(ellip_double).unsqueeze(0) volume = volume.permute(0, 3, 2, 1) # (B, D, H, W) verts, faces = marching_cubes_naive(volume, isolevel=0) verts_sci, faces_sci = marching_cubes_classic(volume[0], level=0) surf = mesh_surface_area(verts[0], faces[0]) surf_sci = mesh_surface_area(verts_sci, faces_sci) self.assertClose(surf, surf_sci)
def _binary(box, image): ellip_base = ellipsoid(30, 30, 30, spacing=(1.05, 1.05, 2.1), levelset=False).astype("int") pts = np.transpose(np.nonzero(ellip_base)) z, x, y = image.shape # Need to add some extra padding around the z axis binary = np.zeros((x, y, z + 30)) for i in range(len(box)): binary[pts[:, 0] + box[i, 1].astype(int), pts[:, 1] + box[i, 0].astype(int), pts[:, 2] + box[i, 4].astype(int)] = 1 #Remove extra padding return (binary[:, :, 15:z + 15].transpose(2, 0, 1))
def test_correct_mesh_orientation(): sphere_small = ellipsoid(1, 1, 1, levelset=True) verts, faces = marching_cubes(sphere_small, 0.) # Correct mesh orientation - descent corrected_faces1 = correct_mesh_orientation(sphere_small, verts, faces, gradient_direction='descent') corrected_faces2 = correct_mesh_orientation(sphere_small, verts, faces, gradient_direction='ascent') # Ensure ascent is opposite of descent for all faces np.testing.assert_array_equal(corrected_faces1, corrected_faces2[:, ::-1]) # Ensure correct faces have been reversed: 1, 4, and 5 idx = [1, 4, 5] expected = faces.copy() expected[idx] = expected[idx, ::-1] np.testing.assert_array_equal(expected, corrected_faces1)
def volume_ellipsoid_spacing(a, b, c, spacing): """ :param a: major semi-axis of an ellipsoid :param b: least semi-axis of an ellipsoid :param c: minor semi-axis of an ellipsoid :param spacing: spacing of a grid, tuple like e.g. (1, 1, 1) :return: volume of an ellipsoid in ml, taking spacing into account """ ellipsoid_array = ellipsoid(a, b, c, spacing) ellipsoid_non_zero = ellipsoid_array.nonzero() num_voxels = len( list( zip(ellipsoid_non_zero[0], ellipsoid_non_zero[1], ellipsoid_non_zero[2]))) volume_object_ml = (num_voxels * spacing[0] * spacing[1] * spacing[2]) / 1000 return volume_object_ml
def overlap(itk_image, anotations, patient, diff): nodules = anotations[anotations.seriesuid == patient][['coordX', 'coordY', 'coordZ', 'diameter_mm']] origin = itk_image.GetOrigin() nodules.coordX = nodules.coordX.apply(lambda x: ((x - origin[0]) / SPACING[1]).astype(int16)) nodules.coordY = nodules.coordY.apply(lambda y: ((y - origin[1]) / SPACING[2]).astype(int16)) nodules.coordZ = nodules.coordZ.apply(lambda z: ((z - origin[2]) / SPACING[0]).astype(int16)) nodules.diameter_mm = nodules.diameter_mm.apply(lambda d: (d / (2 * SPACING) + 4).astype(int8)) if not nodules.shape[0]: return diff, [] vale = list() for i, row in nodules.iterrows(): el = ellipsoid(row.diameter_mm[2], row.diameter_mm[1], row.diameter_mm[0]) relative = list() dim = asarray(el.shape) // 2 relative.append(array([clip(row.coordZ - dim[0], 0, diff.shape[1]), clip(row.coordZ + dim[0], 0, diff.shape[1])])) relative.append(array([clip(row.coordY - dim[1], 0, diff.shape[1]), clip(row.coordY + dim[1], 0, diff.shape[1])])) relative.append(array([clip(row.coordX - dim[2], 0, diff.shape[2]), clip(row.coordX + dim[2], 0, diff.shape[2])])) vale.append((diff[relative[0][0]:relative[0][1], relative[1][0]:relative[1][1], relative[2][0]:relative[2][1]] * el[:relative[0][1] - row.coordZ + row.coordZ - relative[0][0], :relative[1][1] - row.coordY + row.coordY - relative[1][0], :relative[2][1] - row.coordX + row.coordX - relative[2][0]]).sum() / el.shape[0]) diff[relative[0][0]:relative[0][1], relative[1][0]:relative[1][1], relative[2][0]:relative[2][1]] += \ (array([2]) * el[:relative[0][1] - row.coordZ + row.coordZ - relative[0][0], :relative[1][1] - row.coordY + row.coordY - relative[1][0], :relative[2][1] - row.coordX + row.coordX - relative[2][0]]) return diff, vale
def __getitem_single_circle__(self): max_elips_size = self.res // 3 TSDF = np.ones((self.res, self.res, self.res)) * max_elips_size el = ellipsoid(max_elips_size, max_elips_size, max_elips_size, levelset=True) el *= max_elips_size # assert np.abs(el).max() < self.res x, y, z = el.shape x0, y0, z0 = (0.5 * (self.res - np.array(el.shape) - 1)).astype(int) tmp = np.minimum(TSDF[x0:x0 + x, y0:y0 + y, z0:z0 + z], el) assert tmp.shape == TSDF[x0:x0 + x, y0:y0 + y, z0:z0 + z].shape, (tmp.shape, TSDF[x0:x0 + x, y0:y0 + y, z0:z0 + z].shape) TSDF[x0:x0 + x, y0:y0 + y, z0:z0 + z] = tmp return TSDF, TSDF[None, :]
def _binary(box, image): # ellipsoid need to be 35 (it start at upper left corner) to be center. If # bigger, I need to adjust when create the binary. ellip_base = ellipsoid(40, 40, 40, spacing=(1.05, 1.05, 2.1), levelset=False).astype("int") pts = np.transpose(np.nonzero(ellip_base)) z, x, y = image.shape # Need to add some extra padding around the z axis binary = np.zeros((x + 15, y + 15, z + 30)) for i in range(len(box)): binary[pts[:, 0] + (box[i, 1].astype(int) - 5), pts[:, 1] + (box[i, 0].astype(int) - 5), pts[:, 2] + (box[i, 4].astype(int) - 20)] = 1 #Remove extra padding return (binary[:x, :y, :z].transpose(2, 0, 1))
def test_double_ellipsoid(self): if USE_SCIKIT: import numpy as np from skimage.draw import ellipsoid ellip_base = ellipsoid(6, 10, 16, levelset=True) ellip_double = np.concatenate( (ellip_base[:-1, ...], ellip_base[2:, ...]), axis=0) volume = torch.Tensor(ellip_double).unsqueeze(0) volume = volume.permute(0, 3, 2, 1) # (B, D, H, W) verts, faces = marching_cubes_naive(volume, isolevel=0.001) data_filename = "test_marching_cubes_data/double_ellipsoid.pickle" filename = os.path.join(DATA_DIR, data_filename) with open(filename, "rb") as file: verts_and_faces = pickle.load(file) expected_verts = verts_and_faces["verts"] expected_faces = verts_and_faces["faces"] self.assertClose(verts[0], expected_verts[0]) self.assertClose(faces[0], expected_faces[0])
def test_inertia_tensor_3d(): image = draw.ellipsoid(10, 5, 3) T0 = inertia_tensor(image) eig0, V0 = np.linalg.eig(T0) # principal axis of ellipse = eigenvector of smallest eigenvalue v0 = V0[:, np.argmin(eig0)] assert np.allclose(v0, [1, 0, 0]) or np.allclose(-v0, [1, 0, 0]) imrot = ndi.rotate(image.astype(float), 30, axes=(0, 1), order=1) Tr = inertia_tensor(imrot) eigr, Vr = np.linalg.eig(Tr) vr = Vr[:, np.argmin(eigr)] # Check that axis has rotated by expected amount pi, cos, sin = np.pi, np.cos, np.sin R = np.array([[cos(pi / 6), -sin(pi / 6), 0], [sin(pi / 6), cos(pi / 6), 0], [0, 0, 1]]) expected_vr = R @ v0 assert (np.allclose(vr, expected_vr, atol=1e-3, rtol=0.01) or np.allclose(-vr, expected_vr, atol=1e-3, rtol=0.01))
def test_marching_cubes_anisotropic(): spacing = (1., 10 / 6., 16 / 6.) ellipsoid_anisotropic = ellipsoid(6, 10, 16, spacing=spacing, levelset=True) _, surf = ellipsoid_stats(6, 10, 16) # Classic verts, faces = marching_cubes_classic(ellipsoid_anisotropic, 0., spacing=spacing) surf_calc = mesh_surface_area(verts, faces) # Test within 1.5% tolerance for anisotropic. Will always underestimate. assert surf > surf_calc and surf_calc > surf * 0.985 # Lewiner verts, faces = marching_cubes_lewiner(ellipsoid_anisotropic, 0., spacing=spacing)[:2] surf_calc = mesh_surface_area(verts, faces) # Test within 1.5% tolerance for anisotropic. Will always underestimate. assert surf > surf_calc and surf_calc > surf * 0.985 # Test spacing together with allow_degenerate=False marching_cubes_lewiner(ellipsoid_anisotropic, 0, spacing=spacing, allow_degenerate=False)
def test_mc_skimage_orig_example(self): import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d.art3d import Poly3DCollection from skimage import measure from skimage.draw import ellipsoid # Generate a level set about zero of two identical ellipsoids in 3D ellip_base = ellipsoid(6, 10, 16, levelset=True) ellip_double = np.concatenate((ellip_base[:-1, ...], ellip_base[2:, ...]), axis=0) # Use marching cubes to obtain the surface mesh of these ellipsoids # outs = measure.marching_cubes(ellip_double, 0) # verts, faces, normals, values = measure.marching_cubes(ellip_double, 0) verts, faces = measure.marching_cubes(ellip_double, 0) # Display resulting triangular mesh using Matplotlib. This can also be done # with mayavi (see skimage.measure.marching_cubes docstring). fig = plt.figure(figsize=(10, 10)) ax = fig.add_subplot(111, projection='3d') # Fancy indexing: `verts[faces]` to generate a collection of triangles mesh = Poly3DCollection(verts[faces]) mesh.set_edgecolor('k') ax.add_collection3d(mesh) ax.set_xlabel("x-axis: a = 6 per ellipsoid") ax.set_ylabel("y-axis: b = 10") ax.set_zlabel("z-axis: c = 16") ax.set_xlim(0, 24) # a = 6 (times two for 2nd ellipsoid) ax.set_ylim(0, 20) # b = 10 ax.set_zlim(0, 32) # c = 16 plt.tight_layout() plt.show()
def test_inertia_tensor_3d(): image = draw.ellipsoid(10, 5, 3) T0 = inertia_tensor(image) eig0, V0 = np.linalg.eig(T0) # principal axis of ellipse = eigenvector of smallest eigenvalue v0 = V0[:, np.argmin(eig0)] assert np.allclose(v0, [1, 0, 0]) or np.allclose(-v0, [1, 0, 0]) imrot = ndi.rotate(image.astype(float), 30, axes=(0, 1), order=1) Tr = inertia_tensor(imrot) eigr, Vr = np.linalg.eig(Tr) vr = Vr[:, np.argmin(eigr)] # Check that axis has rotated by expected amount pi, cos, sin = np.pi, np.cos, np.sin R = np.array([[ cos(pi/6), -sin(pi/6), 0], [ sin(pi/6), cos(pi/6), 0], [ 0, 0, 1]]) expected_vr = R @ v0 assert (np.allclose(vr, expected_vr, atol=1e-3, rtol=0.01) or np.allclose(-vr, expected_vr, atol=1e-3, rtol=0.01))
def test_ellipsoid_sign_parameters2(): ellipsoid(0, 2, 2)
def im3d(): r = 10 pad = 10 im3 = draw.ellipsoid(r, r, r) im3 = np.pad(im3, pad, mode='constant').astype(np.uint8) return im3
for iz in range(vol.shape[0]): for iy in range(vol.shape[1]): for ix in range(vol.shape[2]): z, y, x = float(iz)*a+b, float(iy)*a+b, float(ix)*a+b vol[iz, iy, ix] = ( ( (8*x)**2 + (8*y-2)**2 + (8*z)**2 + 16 - 1.85*1.85 ) * ( (8*x)**2 + (8*y-2)**2 + (8*z)**2 + 16 - 1.85*1.85 ) - 64 * ( (8*x)**2 + (8*y-2)**2 ) ) * ( ( (8*x)**2 + ((8*y-2)+4)*((8*y-2)+4) + (8*z)**2 + 16 - 1.85*1.85 ) * ( (8*x)**2 + ((8*y-2)+4)*((8*y-2)+4) + (8*z)**2 + 16 - 1.85*1.85 ) - 64 * ( ((8*y-2)+4)*((8*y-2)+4) + (8*z)**2 ) ) + 1025 # Uncommenting the line below will yield different results for classic MC #vol = -vol elif SELECT == 4: vol = ellipsoid(4, 3, 2, levelset=True) isovalue = 0 # Get surface meshes t0 = time.time() vertices1, faces1, _ = marching_cubes_lewiner(vol, isovalue, gradient_direction=gradient_dir, use_classic=False) print('finding surface lewiner took %1.0f ms' % (1000*(time.time()-t0)) ) t0 = time.time() vertices2, faces2, _ = marching_cubes_classic(vol, isovalue, gradient_direction=gradient_dir) print('finding surface classic took %1.0f ms' % (1000*(time.time()-t0)) ) # Show vv.figure(1); vv.clf() a1 = vv.subplot(121); m1 = vv.mesh(np.fliplr(vertices1), faces1) a2 = vv.subplot(122); m2 = vv.mesh(np.fliplr(vertices2), faces2)
def test_ellipsoid_sign_parameters1(): with pytest.raises(ValueError): ellipsoid(-1, 2, 2)
def test_ellipsoid_sign_parameters3(): ellipsoid(-3, -2, 2)
def test_ellipsoid_sign_parameters3(): with testing.raises(ValueError): ellipsoid(-3, -2, 2)
a mesh for regions of bone or bone-like density. This implementation also works correctly on anisotropic datasets, where the voxel spacing is not equal for every spatial dimension, through use of the `spacing` kwarg. """ import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d.art3d import Poly3DCollection from skimage import measure from skimage.draw import ellipsoid # Generate a level set about zero of two identical ellipsoids in 3D ellip_base = ellipsoid(6, 10, 16, levelset=True) ellip_double = np.concatenate((ellip_base[:-1, ...], ellip_base[2:, ...]), axis=0) # Use marching cubes to obtain the surface mesh of these ellipsoids verts, faces, normals, values = measure.marching_cubes(ellip_double, 0) # Display resulting triangular mesh using Matplotlib. This can also be done # with mayavi or visvis (see skimage.measure.marching_cubes docstring). fig = plt.figure(figsize=(10, 12)) ax = fig.add_subplot(111, projection='3d') # Fancy indexing: `verts[faces]` to generate a collection of triangles mesh = Poly3DCollection(verts[faces]) ax.add_collection3d(mesh)
def test_ellipsoid_sign_parameters1(): ellipsoid(-1, 2, 2)