Giter Site home page Giter Site logo

dkazanc / tomophantom Goto Github PK

View Code? Open in Web Editor NEW
113.0 113.0 52.0 17.32 MB

Software to generate 2D/3D/4D analytical phantoms and their Radon transforms (parallel beam) for image processing

Home Page: https://dkazanc.github.io/TomoPhantom/

License: Apache License 2.0

C 49.17% Python 48.45% Batchfile 0.18% Shell 0.42% CMake 1.79%
3d-phantoms astra-toolbox computed-tomography deblurring denoising-images image image-classification image-reconstruction machine-learning matlab phantom radon reconstruction tomography

tomophantom's Issues

libraryToDict with Python 2.7

Script DemoModel2.py fails with error:
TypeError: str() takes at most 1 argument (2 given)
I think it has to do with extracting parameters from *dat file using libraryToDict function. Check 'Obj' in 'objlist', there should be names of the objects?

Print instead of error

Here the code prints a "warning" to the user. However, this may go unnoticed.

The effect is an empty phantom. IMO the code should raise an exception, or possibly return None

location of Phantom library files

For Python they can be located as

import tomophantom
import os
path = os.path.dirname(tomophantom.__file__)

Demos should be updated

Numerical calculation of projections

A capability to generate numerical projection data as an alternative to analytical projections. Data can be generated as:

  • sino_an = TomoP2D.ModelSino(model, N_size, P, angles, pathTP) analytical sinogram
  • sino_num = TomoP2D.SinoNum(Phantom, P, angles) numerical sinogram

Change of the interface

I've changed the interface to something more comprehensive: def Model - consists of multiple objects and def Object - just one geometrical being. Similarly def ModelSino or def ObjectSino for sinograms.

So now the model can be called as TomoP2D.Model or TomoP3D.Model for 3D Version.
@paskino Regarding a new capability of creating separate objects I think @srikanthnagella already did quite a lot on that here:

def Object(int phantom_size, object_2d[:] obj_params):

So I'm just want to test it and prepare an example.

Error with "compile_mex_windows.m"

Hello,

I have been running into issues with building the MATLAB wrapper on windows. When running the file "compile_mex_windows" I'm met with the following error:

"Error using mex
C:\Users\rvs6146\AppData\Local\Temp\mex_1744130824622514_3484\TomoP2DModel.obj:TomoP2DModel.c:(.text+0x7ea): undefined reference to
__imp_TomoP2DObject_core'
C:\Users\rvs6146\AppData\Local\Temp\mex_1744130824622514_3484\TomoP2DModel.obj:TomoP2DModel.c:(.text+0xc5a): undefined reference to
`__imp_TomoP2DObject_core'
collect2.exe: error: ld returned 1 exit status

Error in compile_mex_windows (line 26)
mex TomoP2DModel.c TomoP2DModel_core.c utils.c COMPFLAGS="$COMPFLAGS -fopenmp -Wall -std=c99"

I have tried to debug this myself and used the second approach using TDM-GCC with no luck unfortunately. Do you by any chance have an idea as to why this may be happening? As well as a possible work around? I had seen a similar issue brought up by a user "bort" here: [https://www.mathworks.com/matlabcentral/fileexchange/63603-tomophantom] but was unable to see a solution.

Many thanks in advance :)

import of ArtifactClass is cumbersome

While this is not wrong, it makes syntaxis a bit cumbersome

import tomophantom
import tomophantom.supp.artifacts
from tomophantom.supp.artifacts import ArtifactClass

artifact = ArtifactClass(sino)

while it'd be easier to do

from tomophantom.supp.artifacts import ArtifactClass

artifact = ArtifactClass(sino)

Cmaking of TomoPhantom

Would be a great advantage for debugging stage to be able to compile C-code independently of conda-build.

Negative amplitude?

Thanks for this amazing work! I have a question of how to set the linear attenuation numbers.

I guess the amplitude in the paper indicates the attenuation values, right? If then, I see some negative values in some data (e.g., in 3d Shepp-Logan -0.8 below) Can I know if it's fine to set negative values?

#----------------------------------------------------
#  3D Shepp-Logan
Model : 13;
Components : 10;
TimeSteps : 1;
Object : ellipsoid 1.0 0.0 0.0 0.0 0.69 0.92 0.81 0.0 0.0 0.0
Object : ellipsoid -0.8 0.0184 0.0 0.0 0.6624 0.874 0.78 0.0 0.0 0.0

An option to create non-cubic 3D phantoms

moving from:
a) TomoP3DModel(ModelNo,N,pathTP) where N is a scalar leading to N x N x N phantom
to:
b) TomoP3DModel(ModelNo, DIM ,pathTP) where DIM is a tuple [N1, N1, N2] leading to N1 x N1 x N2 phantom. Version a) needs to be supported as well with DIM[1] = N

Flat-field simulator

  • add a capability to generate flat fields
  • add a capability to model slightly offset flat fields which can create some realistic mismatch and errors in normalisation

Adaptation of TomoP3DModel to MPI

If one needs to build a subset:

TomoP3DModel_sub(ModelNo,DIM,SUB,pathTP) where DIM[3] = [N1,N1,N2] and SUB = 10:20 leading to the N1 x N1 x 10 subset out of N1 x N1 x N2 phantom

Correct geometry for sinogram input/output

I've changed the geometry handling in core functions slightly [obj_append branch]. Matlab demos work well, however Python demo (DemoTomoPhantom.py) produces sinogram of a wrong content. Changing sinogram dimensions in Cython code does not change the output dimensions.

cdef np.ndarray[np.float32_t, ndim=2, mode="c"] sinogram = np.zeros([detector_size,angles.shape[0]], dtype='float32')

Also when incorrect sinogram obtained you can the right dimensions back after reshape/transpose:
sinoR = sinogram.reshape(P,angles.shape[0]).transpose()

can't create cylinder objects

Hi!
I'm trying to make a phantom with several cylinders, but somehow they just don't show up, even though I don't get an error message. I've also noticed that the name in the Objects3D class is apparently not elliptical_cylinder (as suggested in the model examples) but rather ELLIPCYLINDER?

Here's a snippet of my code:

`import sys
import os
sys.path.append(os.path.dirname(os.path.realpath(file))+"\Functions")
import helpers_data
import numpy as np
import matplotlib.pyplot as plt
from tomophantom import TomoP3D
from tomophantom.TomoP3D import Objects3D
import tomophantom

N3D_size = 128*5
obj3D_1 = {'Obj': Objects3D.ELLIPCYLINDER,
'C0' : 1,
'x0' : 0,
'y0' : 0,
'z0' : 0,
'a' : 0.5,
'b' : 0.5,
'c' : 1,
'phi1' : 0}

Object3D = TomoP3D.Object(N3D_size, obj3D_1)

print ("Building 3D object using TomoPhantom software")

sliceSel = int(0.5*N3D_size)

plt.figure()
plt.subplot(131)
plt.imshow(Object3D[sliceSel,:,:],vmin=0, vmax=1)
plt.title('3D Object, axial view')

plt.subplot(132)
plt.imshow(Object3D[:,sliceSel,:],vmin=0, vmax=1)
plt.title('3D Object, coronal view')

plt.subplot(133)
plt.imshow(Object3D[:,:,sliceSel],vmin=0, vmax=1)
plt.title('3D Object, sagittal view')
plt.show()`

Enable partial objects cutoffs

Much more complicated phantoms and projection data can be generated by enabling cutoffs to the objects, e.g. half of the ellipse, gaussian etc.

cythonizing phantom2D

Trying to cythonize phantom2 (following phantom3 pattern) in newSino3D branch... Although I'm able to compile and run, the result is gibberish. Possibly some issues with phantom2D.pyx? help is appreciated!

utils.c added, python version requires reassembling

I've done some restructuring and also added utils.c to avoid repetition of code (MATLAB version works). Please help to modify phantom3d.pyx accordingly to compile code for python. I guess it should be added somewhere along those lines?
cdef extern float buildSino3D_core_single(float *A, int N, int P, float *Th, int AngTot, int CenTypeIn, int Object, float C0, float x0, float y0, float z0, float a, float b, float c, float phi_rot)

Create a new release?

As the code has received new functionality, I think we should tag it with a new release version 1.2.0.

Then I can try to distribute it on the ccpi channel.

Problem with installation for Windows via Anaconda (No module named 'tomophantom')

Hello,

I am trying to install Tomophantom to our windows PC, but, the module can not be found, and there are not any files in the Lib\site-packages . Nevertheless, the steps I went through are as follows, create new anaconda environment with python 3.6 then install CMake and visual studios followed by TomoPhantom. I do receive a few files namely "tomophantom-1.4-np114py36_0.json" and its two specified files.

Best regards,
Philip

User defined modelling of the temporal behaviour

Long term plans - to ensure ability to model dynamic behaviour of phantoms by providing descriptors (vectors for describing the motion). This insures a capability of creating complex motion patterns. Convenient syntax needs to be established. Tentative - a text file with vectors for each object in the model.

3D Projection of phantoms with shapes of elliptical cylinder and cuboid don't have tilting rectangular shapes whatever Euler angles set

Code that randomly generates 3D phantoms including elliptical cylinders only

# Import the necessary libraries.
# ~~~python
import os
import numpy as np
import matplotlib.pyplot as plt
import tifffile
import random
from copy import deepcopy
import numpy as np

from tomophantom import TomoP3D

# Define the path and name of the `.dat` file that will contain the phantoms model description. If the file exists, the new models will be appended.python

path_1 = '/home/mli/tomograms/pycharm_demos/single_density/without_Gaussian_shapes/normal_test/without_noise/1024*1024_with_oversampling=2/Generate_Training_Data/Phantom3DLibrary_For_att_without_Gaussian_3.dat'
path_2 = '/home/mli/tomograms/pycharm_demos/single_density/without_Gaussian_shapes/normal_test/without_noise/1024*1024_with_oversampling=2/Generate_Training_Data/Phantom3DLibrary_For_phase_without_Gaussian_3.dat'

# Generate the models:

number_of_phantoms = 1000

# components = ['gaussian','paraboloid', 'ellipsoid', 'cone', 'cuboid', 'elliptical_cylinder']
# the unused components are not uniform in density.

components = ['elliptical_cylinder']
mu = [0.328]
delta = [481]

for n in range(1, number_of_phantoms + 1):
    num_of_components = random.randint(1, 5)

    objects_1 = []
    objects_2 = []
    component_subset = random.choices(components, k=random.randint(1, 3))
    for i in range(num_of_components):
        obj_1 = random.choice(component_subset)
        obj_2 = deepcopy(obj_1)
        upper_lim_half_width = random.choice([0.5])

        c0 = 0
        m0 = mu[c0]
        d0 = delta[c0]

        if obj_1 == 'elliptical_cylinder' or 'cuboid':

            p_or_n_x = random.choices([-1, 1], k=1)
            p_or_n_y = random.choices([-1, 1], k=1)
            p_or_n_z = random.choices([-1, 1], k=1)

            x0 = round(random.uniform(-1.0*p_or_n_x[0], -0.75*p_or_n_x[0]), 2)
            y0 = round(random.uniform(-1.0*p_or_n_y[0], -0.75*p_or_n_y[0]), 2)
            z0 = round(random.uniform(-1.0*p_or_n_z[0], -0.75*p_or_n_z[0]), 2)
            a = round(random.uniform(0.3, upper_lim_half_width), 3)
            b = round(random.uniform(0.3, upper_lim_half_width), 3)
            c = round(random.uniform(0.3, upper_lim_half_width), 3)
            # alpha = round(random.uniform(-180, 180), 2)
            alpha = 125
            # beta = round(random.uniform(-180, 180), 2)
            beta = 70
            # gamma = round(random.uniform(-180, 180), 2)
            gamma = 50
            print('ellip')

        else:
            x0 = round(random.uniform(-0.5, 0.5), 2)
            y0 = round(random.uniform(-0.5, 0.5), 2)
            z0 = round(random.uniform(-0.5, 0.5), 2)
            a = round(random.uniform(0.01, upper_lim_half_width), 3)
            b = round(random.uniform(0.01, upper_lim_half_width), 3)
            c = round(random.uniform(0.01, upper_lim_half_width), 3)
            alpha = round(random.uniform(-180, 180), 2)
            beta = round(random.uniform(-180, 180), 2)
            gamma = round(random.uniform(-180, 180), 2)

        obj_1 += ' {c0} {x0} {y0} {x0} {a} {b} {c} {alpha} {beta} {gamma}'.format(c0=m0,
                                                                                  # round(random.uniform(0.1,1),2),
                                                                                  x0=x0,
                                                                                  y0=y0,
                                                                                  z0=z0,
                                                                                  a=a,
                                                                                  b=b,
                                                                                  c=c,
                                                                                  alpha=alpha,
                                                                                  beta=beta,
                                                                                  gamma=gamma)

        obj_2 += ' {c0} {x0} {y0} {x0} {a} {b} {c} {alpha} {beta} {gamma}'.format(c0=d0,
                                                                                  # round(random.uniform(0.1,1),2),
                                                                                  x0=x0,
                                                                                  y0=y0,
                                                                                  z0=z0,
                                                                                  a=a,
                                                                                  b=b,
                                                                                  c=c,
                                                                                  alpha=alpha,
                                                                                  beta=beta,
                                                                                  gamma=gamma)

        objects_1.append(obj_1)
        objects_2.append(obj_2)

    phantom_string_1 = '''#----------------------------------------------------
# random phantom number {num}
Model : {num};
Components : {comp};
TimeSteps : 1;
'''.format(num=n, comp=num_of_components)

    phantom_string_2 = '''#----------------------------------------------------
# random phantom number {num}
Model : {num};
Components : {comp};
TimeSteps : 1;
'''.format(num=n, comp=num_of_components)

    for obj in objects_1:
        phantom_string_1 += 'Object : ' + obj + '\n'

    with open(path_1, 'a') as file:
        file.write(phantom_string_1)

    for obj in objects_2:
        phantom_string_2 += 'Object : ' + obj + '\n'

    with open(path_2, 'a') as file:
        file.write(phantom_string_2)

Code that make projections of 3D randomly generated phantoms

import os
import numpy as np
import matplotlib.pyplot as plt
import tifffile
from sklearn import preprocessing
from tomophantom import TomoP3D

# Define the path and name of the `.dat` file that will contain the phantoms model description. If the file exists, the new models will be appended.python

path_1 = '/home/mli/tomograms/pycharm_demos/single_density/without_Gaussian_shapes/normal_test/without_noise/1024*1024_with_oversampling=2/Generate_Training_Data/Phantom3DLibrary_For_att_without_Gaussian_3.dat'
path_2 = '/home/mli/tomograms/pycharm_demos/single_density/without_Gaussian_shapes/normal_test/without_noise/1024*1024_with_oversampling=2/Generate_Training_Data/Phantom3DLibrary_For_phase_without_Gaussian_3.dat'


# Define the image size.

N_size = 1024

#Horiz_det = int(np.sqrt(2)*N_size) # detector column count (horizontal)
Horiz_det = N_size # detector column count (horizontal)
print("Horiz_det: ", Horiz_det)

Vert_det = N_size # detector row count (vertical) (no reason for it to be > N)
print("Vert_det: ", Vert_det)

# angles_num = int(0.5*np.pi*N_size); # angles number
angles_num = 5
print("angles_num: ", angles_num)

angles = np.linspace(0.0, 179.9, angles_num, dtype='float32') # in degrees
print("angles: ", angles)


# Created files
os.makedirs('../projection_2', exist_ok=True)
os.makedirs('../projection_2/tif_format', exist_ok=True)
os.makedirs('../projection_2/npy_format', exist_ok=True)

# Create the directories to where the files will be saved in tif format. For attenuation
os.makedirs('../projection_2/tif_format/attenuation_projection_in_tif_format', exist_ok=True)

# Create the directories to where the files will be saved in tif format. For phase
os.makedirs('../projection_2/tif_format/phase_projection_in_tif_format', exist_ok=True)

# Create the directories to where the files will be saved in npy format. For attenuation
os.makedirs('../projection_2/npy_format/attenuation_projection_in_npy_format', exist_ok=True)

# Create the directories to where the files will be saved in npy format. For phase
os.makedirs('../projection_2/npy_format/phase_projection_in_npy_format', exist_ok=True)

# Create the directories to where the files contain the stacks of attenuation and phase together and save in .npy format
os.makedirs('../projection_2/npy_format/projection_stack_in_npy_format', exist_ok=True)


# Generate the phantoms from models *a* to *b* from the *Phantom3DLibrary3.dat* library using TomoP3D.ModelSino.
pixel_size = 0.7e-6
scaling_factor = pixel_size * 100

# Projection
N = 1024
# For attenuation
projData3D_analyt_list_For_attenuation = []
for model in range(901, 1001):  # For phantom No.1 ~ 1000
    projData3D_analyt_attenuation = TomoP3D.ModelSino(model, N_size, Horiz_det, Vert_det, angles,
                                                      path_1) * scaling_factor
    print('projection shape: ', projData3D_analyt_attenuation.shape)
    projData3D_analyt_list_For_attenuation.append(projData3D_analyt_attenuation)

# For phase
projData3D_analyt_list_For_phase = []
for model in range(901, 1001):  # For phantom No.1 ~ 99
    projData3D_analyt_phase = TomoP3D.ModelSino(model, N_size, Horiz_det, Vert_det, angles, path_2) * scaling_factor
    projData3D_analyt_list_For_phase.append(projData3D_analyt_phase)



# At this step you may: extract one slice for each projection sinogram, extract one angle
# Save in tiff picure format

# For attenuation
for model in range(1, 101):      # For phantom No.1 ~ 1000
    projected_3D = projData3D_analyt_list_For_attenuation[model-1]
    projected_slice = projected_3D[::, 0, ::]
    tifffile.imsave('../projection_2/tif_format/attenuation_projection_in_tif_format/{:05d}.tif'.format(model+900), projected_slice.astype(np.float32))
    np.save('../projection_2/npy_format/attenuation_projection_in_npy_format/{:05d}.npy'.format(model+900), projected_slice.astype(np.float32))

# For phase
for model in range(1, 101):      # For phantom No.1 ~ 1000
    projected_3D = projData3D_analyt_list_For_phase[model-1]
    projected_slice = projected_3D[::, 0, ::]
    tifffile.imsave('../projection_2/tif_format/phase_projection_in_tif_format/{:05d}.tif'.format(model+900), projected_slice.astype(np.float32))
    np.save('../projection_2/npy_format/phase_projection_in_npy_format/{:05d}.npy'.format(model+900), projected_slice.astype(np.float32))


# At this step you may: extract one slice for each projection sinogram (both attenuation and phase), extract one angle
# Stack them together and save in .npy format

for model in range(1, 101):      # For phantom No.1 ~ 1000
    projected_slice = [0, 0]
    projected_3D_attenuation = projData3D_analyt_list_For_attenuation[model-1]
    projected_3D_phase = projData3D_analyt_list_For_phase[model-1]
    projected_slice[0] = projected_3D_attenuation[::, 0, ::]
    projected_slice[1] = projected_3D_phase[::, 0, ::]
    projected_slice = np.array(projected_slice)
    np.save('../projection_2/npy_format/projection_stack_in_npy_format/{:05d}.npy'.format(model+900), projected_slice.astype(np.float32))

Adding random phantom generator

There is a need for random phantom generator. Specific aim is machine learning applications: artifacts removal, classification etc. Possibly creating phantoms according to some rule, e.g. avoiding significant overlaps.

Refactoring

Why should we have a directory supp? Since it is part of the package I don't think it should be called supplemental.

The ArtifactsClass's instance methods are in facts static methods: it should be changed to that.

DemoObject3D.py crashes for Python 2.7

DemoObject3D.py runs from Python 3.5 but crashes with "kernel died" from 2.7. It seems

   if type(objlist) is dict:
       objlist = [objlist]
       
   for obj in objlist:
       if testParamsPY(obj):
           if sys.version_info.major == 3:
               objectName = bytes(obj['Obj'].value, 'ascii')
           elif sys.version_info.major == 2:
               objectName = bytes(obj['Obj'].value)

somewhat responsible in TomoP3D.pyx?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.