如何在3D Numpy数组中生成球体

时间:2017-10-08 00:01:20

标签: python numpy

给定一个3D numpy形状阵列(256,256,256),我如何在里面制作实心球形?下面的代码生成一系列增加和减少的圆,但在另外两个维度中查看时呈菱形。

def make_sphere(arr, x_pos, y_pos, z_pos, radius=10, size=256, plot=False):

    val = 255            
    for r in range(radius):
        y, x = np.ogrid[-x_pos:n-x_pos, -y_pos:size-y_pos]
        mask = x*x + y*y <= r*r 
        top_half = arr[z_pos+r]
        top_half[mask] = val #+ np.random.randint(val)
        arr[z_pos+r] = top_half

    for r in range(radius, 0, -1):
        y, x = np.ogrid[-x_pos:size-x_pos, -y_pos:size-y_pos]
        mask = x*x + y*y <= r*r 
        bottom_half = arr[z_pos+r]
        bottom_half[mask] = val#+ np.random.randint(val)
        arr[z_pos+2*radius-r] = bottom_half

    if plot:
        for i in range(2*radius):
            if arr[z_pos+i].max() != 0:
                print(z_pos+i)
                plt.imshow(arr[z_pos+i])
                plt.show()

    return arr

2 个答案:

答案 0 :(得分:8)

免责声明:我是pymrt的作者。

如果您只需要拥有球体,则可以使用pip - 可安装模块pymrt,尤其是pymrt.geometry.sphere(),例如:

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)

在内部,这是作为一个n维superellipsoid生成器实现的,您可以查看其source code以获取详细信息。 简而言之,(简化)代码将如下所示:

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):
        arr += (np.abs(x_i / semisize) ** 2)
    # the inner part of the sphere will have distance below 1
    return arr <= 1.0

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)

我没有弄清楚你的代码是如何工作的,但要检查这实际上是在制作领域(使用你的数字),你可以尝试:

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()

答案 1 :(得分:1)

好问题。 My answer也适用于类似问题。

您可以尝试以下代码。在下面提到的代码AA中是您想要的矩阵。

import numpy as np
from copy import deepcopy

''' size : size of original 3D numpy matrix A.
    radius : radius of circle inside A which will be filled with ones. 
'''
size, radius = 5, 2

''' A : numpy.ndarray of shape size*size*size. '''
A = np.zeros((size,size, size)) 

''' AA : copy of A (you don't want the original copy of A to be overwritten.) '''
AA = deepcopy(A) 

''' (x0, y0, z0) : coordinates of center of circle inside A. '''
x0, y0, z0 = int(np.floor(A.shape[0]/2)), \
        int(np.floor(A.shape[1]/2)), int(np.floor(A.shape[2]/2))


for x in range(x0-radius, x0+radius+1):
    for y in range(y0-radius, y0+radius+1):
        for z in range(z0-radius, z0+radius+1):
            ''' deb: measures how far a coordinate in A is far from the center. 
                    deb>=0: inside the sphere.
                    deb<0: outside the sphere.'''   
            deb = radius - abs(x0-x) - abs(y0-y) - abs(z0-z) 
            if (deb)>=0: AA[x,y,z] = 1

以下是size=5radius=2(形状为2的numpy数组内半径为5*5*5像素的球体)的输出示例:

[[[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. 0. 1. 0. 0.]
  [0. 1. 1. 1. 0.]
  [0. 0. 1. 0. 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. 0. 1. 0. 0.]
  [0. 1. 1. 1. 0.]
  [0. 0. 1. 0. 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.]]]

我没有按照您要求的大小和半径(size=32radius=4)打印输出,因为输出会很长。