Хороший алгоритм для вычисления объема или площади поверхности в Python - PullRequest
3 голосов
/ 18 января 2012

Я пытаюсь вычислить объем (или площадь поверхности) трехмерного массива. Вокселы во многих случаях анизотропны, и у меня есть коэффициент преобразования пиксель в см в каждом направлении.

Кто-нибудь знает хорошее место, чтобы найти инструментарий или пакет для вышеуказанного?

Прямо сейчас, у меня есть некоторый внутренний код, но я хочу перейти на что-то более промышленное с точки зрения точности.

Edit1: Вот некоторые (плохие) выборки data . Это намного меньше, чем типичная сфера. Я добавлю лучшие данные, когда смогу их сгенерировать! Он находится в (себе). TumBrain.tumor.

Ответы [ 3 ]

5 голосов
/ 18 января 2012

Один из вариантов - использовать VTK. (Я собираюсь использовать для этого здесь привязки tvtk python ...)

По крайней мере, в некоторых случаях получение области в пределах изоповерхности будет немного более точным.

Кроме того, что касается площади поверхности, tvtk.MassProperties также рассчитывает площадь поверхности. Это mass.surface_area (с объектом mass в коде ниже).

import numpy as np
from tvtk.api import tvtk

def main():
    # Generate some data with anisotropic cells...
    # x,y,and z will range from -2 to 2, but with a 
    # different (20, 15, and 5 for x, y, and z) number of steps
    x,y,z = np.mgrid[-2:2:20j, -2:2:15j, -2:2:5j]
    r = np.sqrt(x**2 + y**2 + z**2)

    dx, dy, dz = [np.diff(it, axis=a)[0,0,0] for it, a in zip((x,y,z),(0,1,2))]

    # Your actual data is a binary (logical) array
    max_radius = 1.5
    data = (r <= max_radius).astype(np.int8)

    ideal_volume = 4.0 / 3 * max_radius**3 * np.pi
    coarse_volume = data.sum() * dx * dy * dz
    est_volume = vtk_volume(data, (dx, dy, dz), (x.min(), y.min(), z.min()))

    coarse_error = 100 * (coarse_volume - ideal_volume) / ideal_volume
    vtk_error = 100 * (est_volume - ideal_volume) / ideal_volume

    print 'Ideal volume', ideal_volume
    print 'Coarse approximation', coarse_volume, 'Error', coarse_error, '%'
    print 'VTK approximation', est_volume, 'Error', vtk_error, '%'

def vtk_volume(data, spacing=(1,1,1), origin=(0,0,0)):
    data[data == 0] = -1
    grid = tvtk.ImageData(spacing=spacing, origin=origin)
    grid.point_data.scalars = data.T.ravel() # It wants fortran order???
    grid.point_data.scalars.name = 'scalars'
    grid.dimensions = data.shape

    iso = tvtk.ImageMarchingCubes(input=grid)
    mass = tvtk.MassProperties(input=iso.output)
    return mass.volume

main()

Это дает:

Ideal volume 14.1371669412
Coarse approximation 14.7969924812 Error 4.66731094565 %
VTK approximation 14.1954890878 Error 0.412544796894 %
1 голос
/ 08 июня 2018

если вы попытаетесь использовать ответ Джо выше, вы получите:

traits.trait_errors.TraitError: The 'input' trait of an ImageMarchingCubes instance is 'read only'.

Вот требуемое изменение и разница, показывающая, как это исправить.

Модифицированный код:
import numpy as np
from tvtk.api import tvtk
from tvtk.common import configure_input


def main():
    # Generate some data with anisotropic cells...
    # x,y,and z will range from -2 to 2, but with a
    # different (20, 15, and 5 for x, y, and z) number of steps
    x, y, z = np.mgrid[-2:2:20j, -2:2:15j, -2:2:5j]
    r = np.sqrt(x**2 + y**2 + z**2)

    dx, dy, dz = [np.diff(it, axis=a)[0, 0, 0] for it, a in zip(
        (x, y, z), (0, 1, 2))]

    # Your actual data is a binary (logical) array
    max_radius = 1.5
    data = (r <= max_radius).astype(np.int8)

    ideal_volume = 4.0 / 3 * max_radius**3 * np.pi
    coarse_volume = data.sum() * dx * dy * dz
    est_volume = vtk_volume(data, (dx, dy, dz), (x.min(), y.min(), z.min()))

    coarse_error = 100 * (coarse_volume - ideal_volume) / ideal_volume
    vtk_error = 100 * (est_volume - ideal_volume) / ideal_volume

    print('Ideal volume', ideal_volume)
    print('Coarse approximation', coarse_volume, 'Error', coarse_error, '%')
    print('VTK approximation', est_volume, 'Error', vtk_error, '%')


def vtk_volume(data, spacing=(1, 1, 1), origin=(0, 0, 0)):
    data[data == 0] = -1
    grid = tvtk.ImageData(spacing=spacing, origin=origin)
    grid.point_data.scalars = data.T.ravel()  # It wants fortran order???
    grid.point_data.scalars.name = 'scalars'
    grid.dimensions = data.shape

    iso = tvtk.ImageMarchingCubes()
    configure_input(iso, grid)  # <== will work
    # iso = tvtk.ImageMarchingCubes(input=grid)
    mass = tvtk.MassProperties()
    configure_input(mass, iso)
    # mass = tvtk.MassProperties(input=iso.output)
    return mass.volume


if __name__ == '__main__':
    main()
Различия изменений, которые я сделал
2a3,4
> from tvtk.common import configure_input
> 
6c8
<     # x,y,and z will range from -2 to 2, but with a 
---
>     # x,y,and z will range from -2 to 2, but with a
8c10
<     x,y,z = np.mgrid[-2:2:20j, -2:2:15j, -2:2:5j]
---
>     x, y, z = np.mgrid[-2:2:20j, -2:2:15j, -2:2:5j]
11c13,14
<     dx, dy, dz = [np.diff(it, axis=a)[0,0,0] for it, a in zip((x,y,z),(0,1,2))]
---
>     dx, dy, dz = [np.diff(it, axis=a)[0, 0, 0] for it, a in zip(
>         (x, y, z), (0, 1, 2))]
24,26c27,30
<     print 'Ideal volume', ideal_volume
<     print 'Coarse approximation', coarse_volume, 'Error', coarse_error, '%'
<     print 'VTK approximation', est_volume, 'Error', vtk_error, '%'
---
>     print('Ideal volume', ideal_volume)
>     print('Coarse approximation', coarse_volume, 'Error', coarse_error, '%')
>     print('VTK approximation', est_volume, 'Error', vtk_error, '%')
> 
28c32
< def vtk_volume(data, spacing=(1,1,1), origin=(0,0,0)):
---
> def vtk_volume(data, spacing=(1, 1, 1), origin=(0, 0, 0)):
31c35
<     grid.point_data.scalars = data.T.ravel() # It wants fortran order???
---
>     grid.point_data.scalars = data.T.ravel()  # It wants fortran order???
35,36c39,44
<     iso = tvtk.ImageMarchingCubes(input=grid)
<     mass = tvtk.MassProperties(input=iso.output)
---
>     iso = tvtk.ImageMarchingCubes()
>     configure_input(iso, grid)  # <== will work
>     # iso = tvtk.ImageMarchingCubes(input=grid)
>     mass = tvtk.MassProperties()
>     configure_input(mass, iso)
>     # mass = tvtk.MassProperties(input=iso.output)
39c47,49
< main()
---
> 
> if __name__ == '__main__':
>     main()
Детализированный список изменений:
  • Запустил его через 2to3, чтобы его можно было запустить в python 3
  • Исправлен код соответствия PEP8 в соответствии с autopep8 (синтаксис, длина, изменения пробелов)
  • Импорт configure_imput из-за Эти изменения в TVTK (изменение кода Github)
  • Измените input= kwarg в конструкторе ImageMarchingCubes
  • Измените input= kwargs в конструкторе MassProperties
  • Обернуть вызов main () прямым вызовом (чтобы предотвратить выполнение при импорте) # BestPractices
1 голос
/ 18 января 2012

Вокселы будут довольно простыми, правильными многогранниками, нет?Подсчитайте объем каждого и сложите их.

...