How to generate a sphere in 3D Numpy array

后端 未结 3 1214
醉梦人生
醉梦人生 2020-12-10 15:11

Given a 3D numpy array of shape (256, 256, 256), how would I make a solid sphere shape inside? The code below generates a series of increasing and decreasing circles but is

3条回答
  •  臣服心动
    2020-12-10 16:08

    EDIT: pymrt.geometry has been removed in favor of raster_geometry.


    DISCLAIMER: I am the author of both pymrt and raster_geometry.

    If you just need to have the sphere, you can use the pip-installable module pymrt, and particularly pymrt.geometry.sphere(), e.g:

    import pymrt as mrt
    import pymrt.geometry
    
    arr = mrt.geometry.sphere(3, 1)
    
    array([[[False, False, False],
            [False,  True, False],
            [False, False, False]],
    
            [[False,  True, False],
            [ True,  True,  True],
            [False,  True, False]],
    
            [[False, False, False],
            [False,  True, False],
            [False, False, False]]], dtype=bool)
    

    internally, this is implemented as an n-dimensional superellipsoid generator, you can check its source code for details. Briefly, the (simplified) code would reads like this:

    import numpy as np
    
    
    def sphere(shape, radius, position):
        # assume shape and position are both a 3-tuple of int or float
        # the units are pixels / voxels (px for short)
        # radius is a int or float in px
        semisizes = (radius,) * 3
    
        # genereate the grid for the support points
        # centered at the position indicated by position
        grid = [slice(-x0, dim - x0) for x0, dim in zip(position, shape)]
        position = np.ogrid[grid]
        # calculate the distance of all points from `position` center
        # scaled by the radius
        arr = np.zeros(shape, dtype=float)
        for x_i, semisize in zip(position, semisizes):
            # this can be generalized for exponent != 2
            # in which case `(x_i / semisize)`
            # would become `np.abs(x_i / semisize)`
            arr += (x_i / semisize) ** 2
    
        # the inner part of the sphere will have distance below 1
        return arr <= 1.0
    

    and testing it:

    arr = sphere((256, 256, 256), 10, (127, 127, 127))
    # this will save a sphere in a boolean array
    # the shape of the containing array is: (256, 256, 256)
    # the position of the center is: (127, 127, 127)
    # if you want is 0 and 1 just use .astype(int)
    # for plotting it is likely that you want that
    
    # just for fun you can check that the volume is matching what expected
    np.sum(arr)
    # gives: 4169
    
    4 / 3 * np.pi * 10 ** 3
    # gives: 4188.790204786391
    # (the two numbers do not match exactly because of the discretization error)
    

    I am failing to get how your code exactly works, but to check that this is actually producing spheres (using your numbers) you could try:

    import pymrt as mrt
    import pymrt.geometry
    
    arr = mrt.geometry.sphere(256, 10, 0.5)
    
    
    # plot in 3D
    import matplotlib.pyplot as plt
    from skimage import measure
    
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1, projection='3d')
    
    verts, faces, normals, values = measure.marching_cubes(arr, 0.5, (2,) * 3)
    ax.plot_trisurf(
        verts[:, 0], verts[:, 1], faces, verts[:, 2], cmap='Spectral',
        antialiased=False, linewidth=0.0)
    plt.show()
    

提交回复
热议问题