# -----------------------------------------------------------------------
# Copyright: 2010-2021, imec Vision Lab, University of Antwerp
# 2013-2021, CWI, Amsterdam
#
# Contact: astra@astra-toolbox.com
# Website: http://www.astra-toolbox.com/
#
# This file is part of the ASTRA Toolbox.
#
#
# The ASTRA Toolbox is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# The ASTRA Toolbox is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with the ASTRA Toolbox. If not, see .
#
# -----------------------------------------------------------------------
#
# distutils: language = c++
# distutils: libraries = astra
import six
cimport cython
cimport PyData3DManager
from .PyData3DManager cimport CData3DManager
from .PyIncludes cimport *
import numpy as np
cimport numpy as np
np.import_array()
cimport PyXMLDocument
from .PyXMLDocument cimport XMLDocument
cimport utils
from .utils import wrap_from_bytes
from .utils cimport linkVolFromGeometry, linkProjFromGeometry
from .pythonutils import geom_size, GPULink
import operator
from six.moves import reduce
include "config.pxi"
cdef CData3DManager * man3d = PyData3DManager.getSingletonPtr()
cdef extern from *:
CFloat32Data3DMemory * dynamic_cast_mem "dynamic_cast" (CFloat32Data3D * )
cdef CFloat32Data3DMemory * dynamic_cast_mem_safe(CFloat32Data3D *obj) except NULL:
cdef CFloat32Data3DMemory *ret = dynamic_cast_mem(obj)
if not ret:
raise RuntimeError("Not a memory 3D data object")
return ret
def create(datatype,geometry,data=None, link=False):
cdef Config *cfg
cdef CVolumeGeometry3D * pGeometry
cdef CProjectionGeometry3D * ppGeometry
cdef CFloat32Data3D * pDataObject3D
cdef CFloat32CustomMemory * pCustom = NULL
IF HAVE_CUDA==True:
cdef MemHandle3D hnd
if link:
geom_shape = geom_size(geometry)
if isinstance(data, np.ndarray):
data_shape = data.shape
elif isinstance(data, GPULink):
data_shape = ( data.z, data.y, data.x )
else:
raise TypeError("data should be a numpy.ndarray or a GPULink object")
if geom_shape != data_shape:
raise ValueError("The dimensions of the data do not match those specified in the geometry: {} != {}".format(data_shape, geom_shape))
if datatype == '-vol':
cfg = utils.dictToConfig(six.b('VolumeGeometry'), geometry)
pGeometry = new CVolumeGeometry3D()
if not pGeometry.initialize(cfg[0]):
del cfg
del pGeometry
raise RuntimeError('Geometry class not initialized.')
if link:
pDataObject3D = linkVolFromGeometry(pGeometry, data)
else:
pDataObject3D = new CFloat32VolumeData3DMemory(pGeometry)
del cfg
del pGeometry
elif datatype == '-sino' or datatype == '-proj3d' or datatype == '-sinocone':
cfg = utils.dictToConfig(six.b('ProjectionGeometry'), geometry)
tpe = wrap_from_bytes(cfg.self.getAttribute(six.b('type')))
if (tpe == "parallel3d"):
ppGeometry = new CParallelProjectionGeometry3D();
elif (tpe == "parallel3d_vec"):
ppGeometry = new CParallelVecProjectionGeometry3D();
elif (tpe == "cone"):
ppGeometry = new CConeProjectionGeometry3D();
elif (tpe == "cone_vec"):
ppGeometry = new CConeVecProjectionGeometry3D();
else:
raise ValueError("Invalid geometry type.")
if not ppGeometry.initialize(cfg[0]):
del cfg
del ppGeometry
raise RuntimeError('Geometry class not initialized.')
if link:
pDataObject3D = linkProjFromGeometry(ppGeometry, data)
else:
pDataObject3D = new CFloat32ProjectionData3DMemory(ppGeometry)
del ppGeometry
del cfg
else:
raise ValueError("Invalid datatype. Please specify '-vol' or '-proj3d'.")
if not pDataObject3D.isInitialized():
del pDataObject3D
raise RuntimeError("Couldn't initialize data object.")
if not link:
fillDataObject(dynamic_cast_mem_safe(pDataObject3D), data)
return man3d.store(pDataObject3D)
def get_geometry(i):
cdef CFloat32Data3DMemory * pDataObject = dynamic_cast_mem_safe(getObject(i))
cdef CFloat32ProjectionData3DMemory * pDataObject2
cdef CFloat32VolumeData3DMemory * pDataObject3
if pDataObject.getType() == THREEPROJECTION:
pDataObject2 = pDataObject
geom = utils.configToDict(pDataObject2.getGeometry().getConfiguration())
elif pDataObject.getType() == THREEVOLUME:
pDataObject3 = pDataObject
geom = utils.configToDict(pDataObject3.getGeometry().getConfiguration())
else:
raise RuntimeError("Not a known data object")
return geom
def change_geometry(i, geom):
cdef CFloat32Data3DMemory * pDataObject = dynamic_cast_mem_safe(getObject(i))
cdef CFloat32ProjectionData3DMemory * pDataObject2
cdef CFloat32VolumeData3DMemory * pDataObject3
if pDataObject.getType() == THREEPROJECTION:
pDataObject2 = pDataObject
# TODO: Reduce code duplication here
cfg = utils.dictToConfig(six.b('ProjectionGeometry'), geom)
tpe = wrap_from_bytes(cfg.self.getAttribute(six.b('type')))
if (tpe == "parallel3d"):
ppGeometry = new CParallelProjectionGeometry3D();
elif (tpe == "parallel3d_vec"):
ppGeometry = new CParallelVecProjectionGeometry3D();
elif (tpe == "cone"):
ppGeometry = new CConeProjectionGeometry3D();
elif (tpe == "cone_vec"):
ppGeometry = new CConeVecProjectionGeometry3D();
else:
raise ValueError("Invalid geometry type.")
if not ppGeometry.initialize(cfg[0]):
del cfg
del ppGeometry
raise RuntimeError('Geometry class not initialized.')
del cfg
geom_shape = (ppGeometry.getDetectorRowCount(), ppGeometry.getProjectionCount(), ppGeometry.getDetectorColCount())
obj_shape = (pDataObject2.getDetectorRowCount(), pDataObject2.getAngleCount(), pDataObject2.getDetectorColCount())
if geom_shape != obj_shape:
del ppGeometry
raise ValueError(
"The dimensions of the data do not match those specified in the geometry: {} != {}".format(obj_shape, geom_shape))
pDataObject2.changeGeometry(ppGeometry)
del ppGeometry
elif pDataObject.getType() == THREEVOLUME:
pDataObject3 = pDataObject
cfg = utils.dictToConfig(six.b('VolumeGeometry'), geom)
pGeometry = new CVolumeGeometry3D()
if not pGeometry.initialize(cfg[0]):
del cfg
del pGeometry
raise RuntimeError('Geometry class not initialized.')
del cfg
geom_shape = (pGeometry.getGridSliceCount(), pGeometry.getGridRowCount(), pGeometry.getGridColCount())
obj_shape = (pDataObject3.getSliceCount(), pDataObject3.getRowCount(), pDataObject3.getColCount())
if geom_shape != obj_shape:
del pGeometry
raise ValueError(
"The dimensions of the data do not match those specified in the geometry.".format(obj_shape, geom_shape))
pDataObject3.changeGeometry(pGeometry)
del pGeometry
else:
raise RuntimeError("Not a known data object")
cdef fillDataObject(CFloat32Data3DMemory * obj, data):
if data is None:
fillDataObjectScalar(obj, 0)
else:
if isinstance(data, np.ndarray):
obj_shape = (obj.getDepth(), obj.getHeight(), obj.getWidth())
if data.shape != obj_shape:
raise ValueError(
"The dimensions of the data do not match those specified in the geometry: {} != {}".format(data.shape, obj_shape))
fillDataObjectArray(obj, np.ascontiguousarray(data,dtype=np.float32))
else:
fillDataObjectScalar(obj, np.float32(data))
cdef fillDataObjectScalar(CFloat32Data3DMemory * obj, float s):
cdef int i
for i in range(obj.getSize()):
obj.getData()[i] = s
@cython.boundscheck(False)
@cython.wraparound(False)
cdef fillDataObjectArray(CFloat32Data3DMemory * obj, float [:,:,::1] data):
cdef float [:,:,::1] cView = obj.getData()
cView[:] = data
cdef CFloat32Data3D * getObject(i) except NULL:
cdef CFloat32Data3D * pDataObject = man3d.get(i)
if pDataObject == NULL:
raise ValueError("Data object not found")
if not pDataObject.isInitialized():
raise RuntimeError("Data object not initialized properly.")
return pDataObject
@cython.boundscheck(False)
@cython.wraparound(False)
def get(i):
cdef CFloat32Data3DMemory * pDataObject = dynamic_cast_mem_safe(getObject(i))
outArr = np.empty((pDataObject.getDepth(),pDataObject.getHeight(), pDataObject.getWidth()),dtype=np.float32,order='C')
cdef float [:,:,::1] mView = outArr
cdef float [:,:,::1] cView = pDataObject.getData()
mView[:] = cView
return outArr
def get_shared(i):
cdef CFloat32Data3DMemory * pDataObject = dynamic_cast_mem_safe(getObject(i))
cdef np.npy_intp shape[3]
shape[0] = pDataObject.getDepth()
shape[1] = pDataObject.getHeight()
shape[2] = pDataObject.getWidth()
return np.PyArray_SimpleNewFromData(3,shape,np.NPY_FLOAT32,pDataObject.getData())
def get_single(i):
raise NotImplementedError("Not yet implemented")
def store(i,data):
cdef CFloat32Data3D * pDataObject = getObject(i)
fillDataObject(dynamic_cast_mem_safe(pDataObject), data)
def dimensions(i):
cdef CFloat32Data3D * pDataObject = getObject(i)
return (pDataObject.getDepth(),pDataObject.getHeight(),pDataObject.getWidth())
def delete(ids):
try:
for i in ids:
man3d.remove(i)
except TypeError:
man3d.remove(ids)
def clear():
man3d.clear()
def info():
six.print_(wrap_from_bytes(man3d.info()))