#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Nov 2017
@author: kostenko
Genereation of CT phantoms from geometrical primitives. Reads geometry data
to compute dimensions correctly.
"""
import numpy
import numpy.random
from scipy.ndimage import interpolation
[docs]
def abstract_nudes(shape, geometry, complexity = 10):
"""
Creates works of abstract art.
"""
vol = random_spheroids(shape, geometry, overlap = 'xor', number = complexity)
vol *= random_spheroids(shape, geometry, overlap = 'and', number = complexity)
vol *= random_spheroids(shape, geometry, overlap = 'or', number = complexity)
vol /= vol.max()
return vol
[docs]
def random_spheroids(shape, geometry, number = 3, overlap = 'xor', rotation = True):
"""
Make a bunch of spheroids...
"""
# Initialize volume:
vol = numpy.zeros(shape, dtype = 'int')
for ii in range(number):
# Generate randomly:
offset = _random_offset_(shape, geometry, 0.6)
radii = numpy.abs(_random_size_(shape, geometry, 0.6))
# Baby of a spheroid:
sp = spheroid(shape, geometry, radii[0], radii[1], radii[2], offset = offset)
# Rotate if needed:
if rotation:
sp = interpolation.rotate(sp, numpy.random.ranf() * 360, axes = (1,2), reshape=False)
# Make bool:
sp = sp > 0.5
# Add to the hive:
if overlap == 'or':
vol = vol | sp
elif overlap == 'and':
vol = vol + sp
elif overlap == 'xor':
vol = vol + sp
vol[vol > 1] = 0
else:
raise Exception('You Fool!')
return vol.astype('float32')
[docs]
def sphere(shape, geometry, r, offset = [0., 0., 0.]):
"""
Make sphere. Radius is in units (geometry.parameters['unit'])
"""
return spheroid(shape, geometry, r, r, r, offset)
[docs]
def spheroid(shape, geometry, r1, r2, r3, offset = [0., 0., 0.]):
"""
Make a spheroid.
"""
# Get the coordinates in mm:
xx,yy,zz = geometry.volume_xyz(shape, offset)
# Volume init:
return numpy.array((((xx[:, None, None]*r2*r3)**2 + (yy[None, :, None]*r1*r3)**2 + (zz[None, None, :]*r1*r2)**2) < (r1*r2*r3)**2), dtype = 'float32')
[docs]
def cuboid(shape, geometry, a, b, c, offset = [0., 0., 0.]):
"""
Make a cuboid. Dimensions are in units (geometry.parameters['unit'])
"""
# Get the coordinates in mm:
xx,yy,zz = geometry.volume_xyz(shape, offset)
return numpy.array((abs(xx[:, None, None]) < a / 2) * (abs(yy[None, :, None]) < b / 2) * (abs(zz[None, None, :]) < c / 2), dtype = 'float32')
[docs]
def cylinder(shape, geometry, r, h, offset = [0., 0., 0.]):
"""
Make a cylinder with a specified radius and height.
"""
volume = numpy.zeros(shape, dtype = 'float32')
# Get the coordinates in mm:
xx,yy,zz = geometry.volume_xyz(shape, offset)
volume = numpy.array(((zz[None, None, :])**2 + (yy[None, :, None])**2) < r ** 2, dtype = 'float32')
return (numpy.abs(xx) < h / 2)[:, None, None] * volume
[docs]
def checkers(shape, geometry, frequency, offset = [0., 0., 0.]):
"""
Make a 3D checkers board.
"""
volume = numpy.zeros(shape, dtype = 'float32')
# Get the coordinates in mm:
xx,yy,zz = geometry.volume_xyz(shape, offset)
volume_ = numpy.zeros(shape, dtype='bool')
step = shape[1] // frequency
for ii in range(0, frequency):
sl = slice(ii*step, int((ii + 0.5) * step))
volume_[sl, :, :] = ~volume_[sl, :, :]
for ii in range(0, frequency):
sl = slice(ii*step, int((ii + 0.5) * step))
volume_[:, sl, :] = ~volume_[:, sl, :]
for ii in range(0, frequency):
sl = slice(ii*step, int((ii + 0.5) * step))
volume_[:, :, sl] = ~volume_[:, :, sl]
volume *= volume_
return volume
def _random_offset_(shape, geometry, area_shrink = 1):
"""
Generate random coordinates. Use area_shrink to shrink the area.
"""
ranges = (numpy.array(shape) - 1) * geometry.voxel * area_shrink
return ranges * (numpy.random.rand(3) - 0.5)
def _random_size_(shape, geometry, area_shrink = 1):
"""
Generate random sizes. It never produces zeros.
"""
ranges = (numpy.array(shape) - 1) * geometry.voxel * area_shrink / 2
res = ranges * (numpy.random.rand(3))
res = (res * 4 / 5 + res.mean() / 5)
return res