├── .github
└── workflows
│ ├── check.yml
│ └── deploy_pypi.yml
├── .gitignore
├── .readthedocs.yml
├── CHANGELOG
├── LICENSE
├── MANIFEST.in
├── README.rst
├── docs
├── README.md
├── apple.rst
├── conf.py
├── dev_notes
│ └── _Back_3D_tilted.sphere_points_from_angles_and_tilt.pdf
├── ex2d.rst
├── ex3d.rst
├── extensions
│ ├── fancy_include.py
│ └── github_changelog.py
├── index.rst
├── odtbrain.bib
├── processing.rst
├── recon_2d.rst
├── recon_3d.rst
├── requirements.txt
├── sec_changelog.rst
├── sec_code_reference.rst
├── sec_examples.rst
├── sec_introduction.rst
└── zsec_bib.rst
├── examples
├── backprop_from_fdtd_2d.jpg
├── backprop_from_fdtd_2d.py
├── backprop_from_fdtd_3d.jpg
├── backprop_from_fdtd_3d.py
├── backprop_from_fdtd_3d_tilted.jpg
├── backprop_from_fdtd_3d_tilted.py
├── backprop_from_fdtd_3d_tilted2.jpg
├── backprop_from_fdtd_3d_tilted2.py
├── backprop_from_mie_2d_cylinder_offcenter.jpg
├── backprop_from_mie_2d_cylinder_offcenter.py
├── backprop_from_mie_2d_incomplete_coverage.jpg
├── backprop_from_mie_2d_incomplete_coverage.py
├── backprop_from_mie_2d_weights_angles.jpg
├── backprop_from_mie_2d_weights_angles.py
├── backprop_from_mie_3d_sphere.jpg
├── backprop_from_mie_3d_sphere.py
├── backprop_from_qlsi_3d_hl60.jpg
├── backprop_from_qlsi_3d_hl60.py
├── backprop_from_rytov_3d_phantom_apple.jpg
├── backprop_from_rytov_3d_phantom_apple.py
├── data
│ ├── fdtd_2d_sino_A100_R13.zip
│ ├── fdtd_3d_sino_A180_R6.500.tar.lzma
│ ├── fdtd_3d_sino_A220_R6.500_tiltyz0.2.tar.lzma
│ ├── mie_2d_noncentered_cylinder_A250_R2.zip
│ ├── mie_3d_sphere_field.zip
│ └── qlsi_3d_hl60-cell_A140.tar.lzma
├── example_helper.py
├── generate_example_images.py
└── requirements.txt
├── misc
├── Readme.md
├── meep_phantom_2d.cpp
└── meep_phantom_3d.cpp
├── odtbrain
├── __init__.py
├── _alg2d_bpp.py
├── _alg2d_fmp.py
├── _alg2d_int.py
├── _alg3d_bpp.py
├── _alg3d_bppt.py
├── _prepare_sino.py
├── _translate_ri.py
├── apple.py
├── util.py
└── warn.py
├── pyproject.toml
└── tests
├── README.md
├── common_methods.py
├── data
├── alg2d_bpp__test_2d_backprop_full.zip
├── alg2d_bpp__test_2d_backprop_phase.zip
├── alg2d_fmp__test_2d_fmap.zip
├── alg2d_int__test_2d_integrate.zip
├── alg3d_bpp__test_3d_backprop_nopadreal.zip
├── alg3d_bpp__test_3d_backprop_phase.zip
├── alg3d_bpp__test_3d_mprotate.zip
├── apple__test_correct_reproduce.zip
├── processing__test_odt_to_ri.zip
├── processing__test_opt_to_ri.zip
├── processing__test_sino_radon.zip
└── processing__test_sino_rytov.zip
├── requirements.txt
├── test_alg2d_bpp.py
├── test_alg2d_fmp.py
├── test_alg2d_int.py
├── test_alg3d_bpp.py
├── test_alg3d_bppt.py
├── test_angle_weights.py
├── test_apple.py
├── test_copy.py
├── test_counters.py
├── test_processing.py
├── test_rotation_matrices.py
├── test_save_memory.py
├── test_spherecoords_from_angles_and_axis.py
├── test_util.py
└── test_weighting.py
/.github/workflows/check.yml:
--------------------------------------------------------------------------------
1 | name: Checks
2 |
3 | on:
4 | push:
5 | pull_request:
6 |
7 | jobs:
8 | build:
9 |
10 | runs-on: ${{ matrix.os }}
11 | strategy:
12 | fail-fast: false
13 | matrix:
14 | python-version: ['3.9', '3.10']
15 | os: [macos-latest, ubuntu-latest, windows-latest]
16 |
17 | steps:
18 | - uses: actions/checkout@v3
19 | - name: Set up Python ${{ matrix.python-version }}
20 | uses: actions/setup-python@v4
21 | with:
22 | python-version: ${{ matrix.python-version }}
23 | cache: 'pip'
24 | check-latest: true
25 | - name: Install fftw3 libs (Linux)
26 | if: runner.os == 'Linux'
27 | run: |
28 | sudo apt-get install -y libfftw3-dev libfftw3-double3
29 | - name: Install fftw3 libs (macOS)
30 | if: runner.os == 'macOS'
31 | run: |
32 | brew install --overwrite fftw
33 | - name: Install dependencies
34 | run: |
35 | # prerequisites
36 | python -m pip install --upgrade pip wheel
37 | python -m pip install coverage flake8
38 | # install dependencies
39 | pip install -e .
40 | pip install -r tests/requirements.txt
41 | # show installed packages
42 | pip freeze
43 | - name: Test with pytest
44 | run: |
45 | coverage run --source=odtbrain -m pytest tests
46 | - name: Upload test artifacts
47 | if: (runner.os == 'Linux' && matrix.python-version == '3.10')
48 | uses: actions/upload-artifact@v4
49 | with:
50 | name: Test_artifacts
51 | path: |
52 | ./*.zip
53 |
54 | - name: Lint with flake8
55 | run: |
56 | flake8 --exclude _version.py .
57 | - name: Upload coverage to Codecov
58 | uses: codecov/codecov-action@v3
59 |
--------------------------------------------------------------------------------
/.github/workflows/deploy_pypi.yml:
--------------------------------------------------------------------------------
1 | name: Release to PyPI
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 |
8 | jobs:
9 | build_sdist_wheel:
10 | name: Build source distribution
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@main
14 | with:
15 | fetch-depth: 0
16 |
17 | - name: Build sdist
18 | run: pipx run build --sdist --wheel
19 |
20 | - name: publish
21 | env:
22 | TWINE_USERNAME: __token__
23 | TWINE_PASSWORD: ${{ secrets.PYPI_PWD }}
24 | run: |
25 | pipx install twine
26 | twine upload --skip-existing dist/*
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 |
5 | # C extensions
6 | *.so
7 |
8 | # Distribution / packaging
9 | .Python
10 | env/
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | *.egg-info/
23 | .installed.cfg
24 | *.egg
25 |
26 | # PyInstaller
27 | # Usually these files are written by a python script from a template
28 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
29 | *.manifest
30 | *.spec
31 |
32 | # Installer logs
33 | pip-log.txt
34 | pip-delete-this-directory.txt
35 |
36 | # Unit test / coverage reports
37 | htmlcov/
38 | .tox/
39 | .coverage
40 | .coverage.*
41 | .cache
42 | nosetests.xml
43 | coverage.xml
44 | *,cover
45 |
46 | # Translations
47 | *.mo
48 | *.pot
49 |
50 | # Django stuff:
51 | *.log
52 |
53 | # Sphinx documentation
54 | docs/_build/
55 |
56 | # PyBuilder
57 | target/
58 |
59 |
60 | *.md~
61 | *.txt~
62 | *.py~
63 | *.in~
64 | *.yml~
65 | *.rst~
66 |
67 | _version_save.py
68 | _version.py
69 |
70 | # extracted test files
71 | tests/data/*__*.txt
72 | *.tar
73 | .env
74 | .pytest_cache
75 | *bib.bak
76 | *bib.sav
77 |
78 | .idea
79 |
80 | # used by pyenv-virtualenv:
81 | .python-version
--------------------------------------------------------------------------------
/.readthedocs.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | formats:
3 | - pdf
4 | build:
5 | os: ubuntu-22.04
6 | tools:
7 | python: "3.11"
8 | jobs:
9 | post_checkout:
10 | - git fetch --unshallow || true
11 | sphinx:
12 | configuration: docs/conf.py
13 | python:
14 | install:
15 | - requirements: docs/requirements.txt
16 | - method: pip
17 | path: .
18 |
--------------------------------------------------------------------------------
/CHANGELOG:
--------------------------------------------------------------------------------
1 | 0.4.12
2 | - docs: update outdated 2D FDTD example image
3 | 0.4.11
4 | - docs: clarify reconstruction distance in 2D example
5 | 0.4.10
6 | - maintenance release
7 | 0.4.9
8 | - maintenance release
9 | 0.4.8
10 | - fix: support numpy 2 (#23)
11 | - setup: bumpy scikit-image to 0.21.0 (API)
12 | - setup: migrate to pyproject.toml
13 | - fix: cut-off radius too small in fmap_2d algorithm (#19)
14 | 0.4.7
15 | - maintenance release
16 | 0.4.6
17 | - fix: handle more special cases when computing weights for
18 | projections
19 | - docs: update example scripts and images
20 | 0.4.5
21 | - ci: fix build pipeline
22 | 0.4.4
23 | - ref: address scipy deprecation warnings
24 | - ref: replace print statements with warnings (#17)
25 | - ci: switch to Python 3.10
26 | - ci: minor cleanup
27 | 0.4.3
28 | - docs: added original meep C++ simulation files in "misc" (#14)
29 | - docs: add ``if __name__ == "__main__"`` guard to 3D backpropagation
30 | examples (#15)
31 | 0.4.2
32 | - build: migrate to GitHub Actions
33 | - build: setup.py test is deprecated
34 | - docs: refurbish docs
35 | - ref: change instances of np.int to int due to
36 | numpy deprecation warnings
37 | 0.4.1
38 | - setup: bump scipy to 1.4.0 (updated QHull in griddata)
39 | 0.4.0
40 | - BREAKING CHANGES:
41 | - renamed submodule `_preproc` to `_prepare_sino`
42 | - renamed submodule `_postproc` to `_translate_ri`
43 | - missing apple core correction is now applied to the
44 | object function (`f`) instead of the refractive index (`n`),
45 | which is the physically correct approach
46 | - default keyword for `padval` is now "edge"
47 | instead of `None`; the meaning is retained
48 | - enh: added symmetric histogram apple core correction method "sh"
49 | - fix: using "float32" dtype in 3D backpropagation lead to
50 | casting error in numexpr
51 | - enh: improve performance when padding is disabled
52 | - docs: minor update
53 | 0.3.0
54 | - feat: basic missing apple core correction (#6)
55 | - docs: reordered 3D examples (decreasing importance)
56 | 0.2.6
57 | - fix: make phase unwrapping deterministic
58 | - tests: remove one test of the 2D Fourier mapping algorithm due to
59 | instabilities in using scipy.interpolate.griddata
60 | - ref: use `empty_aligned` instead of deprecated `n_byte_align_empty`
61 | - docs: add hint for windows users how to run the 3D examples
62 | 0.2.5
63 | - fix: reconstruction volume rotated by 180° due to floating point
64 | inaccuracies (affects `backpropagate_3d_tilted`).
65 | 0.2.4
66 | - maintenance release
67 | 0.2.3
68 | - enh: employ slice-wise padding to reduce the memory usage (and
69 | possibly the computation time) of
70 | - basic 3D backpropagation algorithm (#7)
71 | - 3D backpropagation with a tilted axis of rotation (#9)
72 | - ref: replace asserts with raises (#8)
73 | - ref: multiprocessing-based rotation does not anymore require
74 | a variable (_shared_array) at the top level of the module; As a
75 | result, multiprocessing-rotation should now also work on Windows.
76 | 0.2.2
77 | - docs: minor update and add changelog
78 | 0.2.1
79 | - fix: Allow sinogram data type other than complex128
80 | - docs: Add example with experimental data (#3)
81 | - ci: automated deployment with travis-ci
82 | 0.2.0
83 | - BREAKING CHANGES:
84 | - Dropped support for Python 2
85 | - Renamed `sum_2d` to `integrate_2d`
86 | - Refactoring (#4, #5):
87 | - Moved each reconstruction algorithm to a separate file
88 | - Modified code to comply with PEP8
89 | - Moved long doc strings from source to docs directory
90 | - Migrate from unwrap to scikit-image
91 | - Cleaned up example scripts
92 | - Bugfixes:
93 | - Mistake in "negative-modulo" method for determination of
94 | 2PI sinogram phase offsets
95 | 0.1.8
96 | - Updated documentation
97 | - Cleaned up examples
98 | 0.1.7
99 | - Move documentation from GitHub to readthedocs.io
100 | - Add universal wheel on PyPI
101 | - Update tests on travis with new versions of NumPy
102 | 0.1.6
103 | - Bugfixes:
104 | - size of reconstruction volume in z too large for cases where
105 | the y-size is larger than the x-size of the sinogram images
106 | - `backpropagate_3d_tilted` used wrong shape of projections
107 | - 3D backpropagation methods did not use power-of-two padding size
108 | 0.1.5
109 | - Code optimization (speed, memory) with numexpr
110 | - New keyword argument `save_memory` for 3D reconstruction
111 | on machines with limited memory
112 | - New keyword argument `copy` for 3D reconstruction to protect
113 | input sinogram data.
114 | 0.1.4
115 | - The exponential term containing the distance between center
116 | of rotation and detector `lD` is now multiplied with
117 | the factor `M-1` instead of `M`. This is necessary,
118 | because usually the scattered wave is normalized in both
119 | amplitude and phase (`u_0`) and not only amplitude `a_0`
120 | - Allow angles of shape (A,1) in `backpropagate_3d_tilted`
121 | - Set default value lD=0 for all reconstruction algorithms
122 | - Improvement of documentation
123 | 0.1.3
124 | - Fixes for `backpropagate_3d_tilted` when `angles` are
125 | points on the unit sphere:
126 | - Make sure each point is normalized
127 | - Correctly rotate each point w.r.t. `tilted_axis`
128 | 0.1.2
129 | - Added reconstruction algorithm for tilted axis of rotation
130 | 0.1.1
131 | - Support NumPy 1.10.
132 | - Allow to weight backpropagation using keyword `weight_angles`
133 | - Bugfix: backpropagate_3d with keyword `onlyreal=True` did not work
134 | - Bugfix: sum_2d did not return correctly shaped array
135 | - Code coverage is now 90%
136 | - Added more examples to the documentation
137 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, Paul Müller
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 |
14 | * Neither the name of ODTbrain nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
29 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include CHANGELOG
2 | include LICENSE
3 | include README.rst
4 | recursive-include examples *.py *.jpg
5 | recursive-include docs *.py *.md *.rst *.txt *.bib
6 | recursive-include tests *.py *.md *.zip
7 | prune docs/_build
8 | exclude docs/_version_save.py
9 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | ODTbrain
2 | ========
3 |
4 | |PyPI Version| |Tests Status| |Coverage Status| |Docs Status|
5 |
6 |
7 | **ODTbrain** provides image reconstruction algorithms for **O**\ ptical **D**\ iffraction **T**\ omography with a **B**\ orn and **R**\ ytov
8 | **A**\ pproximation-based **I**\ nversion to compute the refractive index (**n**\ ) in 2D and in 3D.
9 |
10 |
11 | Documentation
12 | -------------
13 |
14 | The documentation, including the reference and examples, is available at `odtbrain.readthedocs.io `__.
15 |
16 |
17 | Installation
18 | ------------
19 | ::
20 |
21 | pip install odtbrain
22 |
23 |
24 |
25 | Testing
26 | -------
27 |
28 | After cloning into odtbrain, create a virtual environment::
29 |
30 | virtualenv --system-site-packages env
31 | source env/bin/activate
32 |
33 | Install ODTbrain in editable mode::
34 |
35 | pip install -e .
36 |
37 | Running an example::
38 |
39 | python examples/backprop_from_fdtd_2d.py
40 |
41 | Running tests::
42 |
43 | pip install pytest
44 | pytest tests
45 |
46 |
47 | .. |PyPI Version| image:: https://img.shields.io/pypi/v/odtbrain.svg
48 | :target: https://pypi.python.org/pypi/odtbrain
49 | .. |Tests Status| image:: https://img.shields.io/github/actions/workflow/status/RI-Imaging/ODTbrain/check.yml
50 | :target: https://github.com/RI-Imaging/ODTbrain/actions?query=workflow%3AChecks
51 | .. |Coverage Status| image:: https://img.shields.io/codecov/c/github/RI-imaging/ODTbrain/master.svg
52 | :target: https://codecov.io/gh/RI-imaging/ODTbrain
53 | .. |Docs Status| image:: https://readthedocs.org/projects/odtbrain/badge/?version=latest
54 | :target: https://readthedocs.org/projects/odtbrain/builds/
55 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | ODTbrain documentation
2 | ======================
3 | To install the requirements for building the documentation, run
4 |
5 | pip install -r requirements.txt
6 |
7 | To compile the documentation, run
8 |
9 | sphinx-build . _build
10 |
11 |
--------------------------------------------------------------------------------
/docs/apple.rst:
--------------------------------------------------------------------------------
1 | 3D Apple core correction
2 | ------------------------
3 | The missing apple core (in Fourier space) leads to ringing and blurring
4 | artifacts in optical diffraction tomography :cite:`Vertu2009`.
5 | This module contains basic functions that can be used to attenuate
6 | these artifacts.
7 |
8 | .. versionadded:: 0.3.0
9 |
10 | .. versionchanged:: 0.4.0
11 |
12 | .. automodule:: odtbrain.apple
13 | :members:
14 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # project documentation build configuration file, created by
5 | # sphinx-quickstart on Sat Feb 22 09:35:49 2014.
6 | #
7 | # This file is execfile()d with the current directory set to its
8 | # containing dir.
9 | #
10 | # Note that not all possible configuration values are present in this
11 | # autogenerated file.
12 | #
13 | # All configuration values have a default; values that are commented out
14 | # serve to show the default.
15 |
16 | import os.path as op
17 | import sys
18 |
19 | import odtbrain
20 |
21 | # If extensions (or modules to document with autodoc) are in another directory,
22 | # add these directories to sys.path here. If the directory is relative to the
23 | # documentation root, use os.path.abspath to make it absolute, like shown here.
24 |
25 | # include parent directory
26 | pdir = op.dirname(op.dirname(op.abspath(__file__)))
27 | sys.path.insert(0, pdir)
28 |
29 | sys.path.append(op.abspath('extensions'))
30 |
31 | # http://www.sphinx-doc.org/en/stable/ext/autodoc.html#confval-autodoc_member_order
32 | # Order class attributes and functions in separate blocks
33 | autodoc_member_order = 'bysource'
34 |
35 | # Display link to GitHub repo instead of doc on rtfd
36 | rst_prolog = """
37 | :github_url: https://github.com/RI-imaging/ODTbrain
38 | """
39 |
40 | # -- General configuration ------------------------------------------------
41 |
42 | # Add any Sphinx extension module names here, as strings. They can be
43 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
44 | # ones.
45 | extensions = ['sphinx.ext.intersphinx',
46 | 'sphinx.ext.autodoc',
47 | 'sphinx.ext.mathjax',
48 | 'sphinx.ext.autosummary',
49 | 'sphinx.ext.napoleon',
50 | 'sphinxcontrib.bibtex',
51 | 'fancy_include',
52 | 'github_changelog',
53 | ]
54 |
55 | # specify bibtex files (required for sphinxcontrib.bibtex>=2.0)
56 | bibtex_bibfiles = ['odtbrain.bib']
57 |
58 | # Add any paths that contain templates here, relative to this directory.
59 | templates_path = ['_templates']
60 |
61 | # The suffix of source filenames.
62 | source_suffix = '.rst'
63 |
64 | # The master toctree document.
65 | master_doc = 'index'
66 |
67 | # General information about the project.
68 | year = "2015"
69 | name = "odtbrain"
70 | author = "Paul Müller"
71 | authors = [author]
72 | projectname = name
73 |
74 | # The version info for the project you're documenting, acts as replacement for
75 | # |version| and |release|, also used in various other places throughout the
76 | # built documents.
77 | #
78 | # The short X.Y version.
79 | #
80 | # The full version, including alpha/beta/rc tags.
81 | # This gets 'version'
82 | version = odtbrain.__version__
83 | release = version
84 |
85 | project = projectname
86 | copyright = year + ", " + author # @ReservedAssignment
87 | github_project = 'RI-imaging/' + project
88 |
89 | # The language for content autogenerated by Sphinx. Refer to documentation
90 | # for a list of supported languages.
91 | # language = None
92 |
93 | # There are two options for replacing |today|: either, you set today to some
94 | # non-false value, then it is used:
95 | # today = ''
96 | # Else, today_fmt is used as the format for a strftime call.
97 | # today_fmt = '%B %d, %Y'
98 |
99 | # List of patterns, relative to source directory, that match files and
100 | # directories to ignore when looking for source files.
101 | exclude_patterns = ['_build']
102 |
103 | # The reST default role (used for this markup: `text`) to use for all
104 | # documents.
105 | # default_role = None
106 |
107 | # If true, '()' will be appended to :func: etc. cross-reference text.
108 | # add_function_parentheses = True
109 |
110 | # If true, the current module name will be prepended to all description
111 | # unit titles (such as .. function::).
112 | # add_module_names = True
113 |
114 | # If true, sectionauthor and moduleauthor directives will be shown in the
115 | # output. They are ignored by default.
116 | # show_authors = False
117 |
118 | # The name of the Pygments (syntax highlighting) style to use.
119 | # pygments_style = 'default'
120 |
121 | # A list of ignored prefixes for module index sorting.
122 | # modindex_common_prefix = []
123 |
124 | # If true, keep warnings as "system message" paragraphs in the built documents.
125 | # keep_warnings = False
126 |
127 |
128 | # -- Options for HTML output ----------------------------------------------
129 |
130 | # The theme to use for HTML and HTML Help pages. See the documentation for
131 | # a list of builtin themes.
132 | html_theme = 'sphinx_rtd_theme'
133 |
134 | # Output file base name for HTML help builder.
135 | htmlhelp_basename = projectname+'doc'
136 |
137 |
138 | # -- Options for LaTeX output ---------------------------------------------
139 |
140 | latex_elements = {
141 | # The paper size ('letterpaper' or 'a4paper').
142 | # 'papersize': 'letterpaper',
143 |
144 | # The font size ('10pt', '11pt' or '12pt').
145 | # 'pointsize': '10pt',
146 |
147 | # Additional stuff for the LaTeX preamble.
148 | # 'preamble': '',
149 | }
150 |
151 | # Grouping the document tree into LaTeX files. List of tuples
152 | # (source start file, target name, title,
153 | # author, documentclass [howto, manual, or own class]).
154 | latex_documents = [
155 | ('index', projectname+'.tex', projectname+' Documentation',
156 | author, 'manual'),
157 | ]
158 |
159 |
160 | # -- Options for manual page output ---------------------------------------
161 |
162 | # One entry per manual page. List of tuples
163 | # (source start file, name, description, authors, manual section).
164 | man_pages = [
165 | ('index', projectname, projectname+' Documentation',
166 | authors, 1)
167 | ]
168 |
169 |
170 | # -- Options for Texinfo output -------------------------------------------
171 |
172 | # Grouping the document tree into Texinfo files. List of tuples
173 | # (source start file, target name, title, author,
174 | # dir menu entry, description, category)
175 | texinfo_documents = [
176 | ('index', projectname, 'ODTbrain Documentation',
177 | author, projectname,
178 | "Algorithms for optical diffraction tomography",
179 | 'Numeric'),
180 | ]
181 |
182 |
183 | # -----------------------------------------------------------------------------
184 | # intersphinx
185 | # -----------------------------------------------------------------------------
186 | intersphinx_mapping = {
187 | "python": ('https://docs.python.org/', None),
188 | "nrefocus": ('http://nrefocus.readthedocs.io/en/stable', None),
189 | "numpy": ('http://docs.scipy.org/doc/numpy', None),
190 | "cellsino": ('http://cellsino.readthedocs.io/en/stable', None),
191 | "qpimage": ('http://qpimage.readthedocs.io/en/stable', None),
192 | "radontea": ('http://radontea.readthedocs.io/en/stable', None),
193 | "scipy": ('https://docs.scipy.org/doc/scipy/reference/', None),
194 | "skimage": ('http://scikit-image.org/docs/stable/', None),
195 | }
196 |
--------------------------------------------------------------------------------
/docs/dev_notes/_Back_3D_tilted.sphere_points_from_angles_and_tilt.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RI-imaging/ODTbrain/f7bb8b792bad8ae78ea885758a801c52dfea44ad/docs/dev_notes/_Back_3D_tilted.sphere_points_from_angles_and_tilt.pdf
--------------------------------------------------------------------------------
/docs/ex2d.rst:
--------------------------------------------------------------------------------
1 | 2D examples
2 | ===========
3 | These examples require raw data which are automatically
4 | downloaded from the source repository by the script
5 | :download:`example_helper.py <../examples/example_helper.py>`.
6 | Please make sure that this script is present in the example
7 | script folder.
8 |
9 | .. fancy_include:: backprop_from_mie_2d_cylinder_offcenter.py
10 |
11 | .. fancy_include:: backprop_from_mie_2d_weights_angles.py
12 |
13 | .. fancy_include:: backprop_from_mie_2d_incomplete_coverage.py
14 |
15 | .. fancy_include:: backprop_from_fdtd_2d.py
16 |
17 |
--------------------------------------------------------------------------------
/docs/ex3d.rst:
--------------------------------------------------------------------------------
1 | 3D examples
2 | ===========
3 | These examples require raw data which are automatically
4 | downloaded from the source repository by the script
5 | :download:`example_helper.py <../examples/example_helper.py>`.
6 | Please make sure that this script is present in the example
7 | script folder.
8 |
9 | .. note::
10 | The ``if __name__ == "__main__"`` guard is necessary on Windows and macOS
11 | which *spawn* new processes instead of *forking* the current process.
12 | The 3D backpropagation algorithm makes use of ``multiprocessing.Pool``.
13 |
14 |
15 | .. fancy_include:: backprop_from_rytov_3d_phantom_apple.py
16 |
17 | .. fancy_include:: backprop_from_qlsi_3d_hl60.py
18 |
19 | .. fancy_include:: backprop_from_fdtd_3d.py
20 |
21 | .. fancy_include:: backprop_from_fdtd_3d_tilted.py
22 |
23 | .. fancy_include:: backprop_from_fdtd_3d_tilted2.py
24 |
25 | .. fancy_include:: backprop_from_mie_3d_sphere.py
26 |
--------------------------------------------------------------------------------
/docs/extensions/fancy_include.py:
--------------------------------------------------------------------------------
1 | """Include single scripts with doc string, code, and image
2 |
3 | Use case
4 | --------
5 | There is an "examples" directory in the root of a repository,
6 | e.g. 'include_doc_code_img_path = "../examples"' in conf.py
7 | (default). An example is a file ("an_example.py") that consists
8 | of a doc string at the beginning of the file, the example code,
9 | and, optionally, an image file (png, jpg) ("an_example.png").
10 |
11 |
12 | Configuration
13 | -------------
14 | In conf.py, set the parameter
15 |
16 | fancy_include_path = "../examples"
17 |
18 | to wherever the included files reside.
19 |
20 |
21 | Usage
22 | -----
23 | The directive
24 |
25 | .. fancy_include:: an_example.py
26 |
27 | will display the doc string formatted with the first line as a
28 | heading, a code block with line numbers, and the image file.
29 | """
30 | import pathlib
31 |
32 | from docutils.statemachine import ViewList
33 | from docutils.parsers.rst import Directive
34 | from sphinx.util.nodes import nested_parse_with_titles
35 | from docutils import nodes
36 |
37 |
38 | class IncludeDirective(Directive):
39 | required_arguments = 1
40 | optional_arguments = 0
41 |
42 | def run(self):
43 | path = self.state.document.settings.env.config.fancy_include_path
44 | path = pathlib.Path(path)
45 | full_path = path / self.arguments[0]
46 |
47 | text = full_path.read_text()
48 |
49 | # add reference
50 | name = full_path.stem
51 | rst = [".. _example_{}:".format(name),
52 | "",
53 | ]
54 |
55 | # add docstring
56 | source = text.split('"""')
57 | doc = source[1].split("\n")
58 | doc.insert(1, "~" * len(doc[0])) # make title heading
59 |
60 | code = source[2].split("\n")
61 |
62 | for line in doc:
63 | rst.append(line)
64 |
65 | # image
66 | for ext in [".png", ".jpg"]:
67 | image_path = full_path.with_suffix(ext)
68 | if image_path.exists():
69 | break
70 | else:
71 | image_path = ""
72 | if image_path:
73 | rst.append(".. figure:: {}".format(image_path.as_posix()))
74 | rst.append("")
75 |
76 | # download file
77 | rst.append(":download:`{} <{}>`".format(
78 | full_path.name, full_path.as_posix()))
79 |
80 | # code
81 | rst.append("")
82 | rst.append(".. code-block:: python")
83 | rst.append(" :linenos:")
84 | rst.append("")
85 | for line in code:
86 | rst.append(" {}".format(line))
87 | rst.append("")
88 |
89 | vl = ViewList(rst, "fakefile.rst")
90 | # Create a node.
91 | node = nodes.section()
92 | node.document = self.state.document
93 | # Parse the rst.
94 | nested_parse_with_titles(self.state, vl, node)
95 | return node.children
96 |
97 |
98 | def setup(app):
99 | app.add_config_value('fancy_include_path', "../examples", 'html')
100 |
101 | app.add_directive('fancy_include', IncludeDirective)
102 |
103 | return {'version': '0.1'} # identifies the version of our extension
104 |
--------------------------------------------------------------------------------
/docs/extensions/github_changelog.py:
--------------------------------------------------------------------------------
1 | """Display changelog with links to GitHub issues
2 |
3 | Usage
4 | -----
5 | The directive
6 |
7 | .. include_changelog:: ../CHANGELOG
8 |
9 | adds the content of the changelog file into the current document.
10 | References to GitHub issues are identified as "(#XY)" (with parentheses
11 | and hash) and a link is inserted
12 |
13 | https://github.com/RI-imaging/{PROJECT}/issues/{XY}
14 |
15 | where PROJECT ist the `project` variable defined in conf.py.
16 | """
17 | import io
18 | import re
19 |
20 | from docutils.statemachine import ViewList
21 | from docutils.parsers.rst import Directive
22 | from sphinx.util.nodes import nested_parse_with_titles
23 | from docutils import nodes
24 |
25 |
26 | class IncludeDirective(Directive):
27 | required_arguments = 1
28 | optional_arguments = 0
29 |
30 | def run(self):
31 | full_path = self.arguments[0]
32 | project = self.state.document.settings.env.config.github_project
33 |
34 | def insert_github_link(reobj):
35 | line = reobj.string
36 | instr = line[reobj.start():reobj.end()]
37 | issue = instr.strip("#()")
38 | link = "https://github.com/{}/issues/".format(project)
39 | rstlink = "(`#{issue} <{link}{issue}>`_)".format(issue=issue,
40 | link=link)
41 | return rstlink
42 |
43 | with io.open(full_path, "r") as myfile:
44 | text = myfile.readlines()
45 |
46 | rst = []
47 | for line in text:
48 | line = line.strip("\n")
49 | if line.startswith(" ") and line.strip().startswith("-"):
50 | # list in list:
51 | rst.append("")
52 | if not line.startswith(" "):
53 | rst.append("")
54 | line = "version " + line
55 | rst.append(line)
56 | rst.append("-"*len(line))
57 | elif not line.strip():
58 | rst.append(line)
59 | else:
60 | line = re.sub(r"\(#[0-9]*\)", insert_github_link, line)
61 | rst.append(line)
62 |
63 | vl = ViewList(rst, "fakefile.rst")
64 | # Create a node.
65 | node = nodes.section()
66 | node.document = self.state.document
67 | # Parse the rst.
68 | nested_parse_with_titles(self.state, vl, node)
69 | return node.children
70 |
71 |
72 | def setup(app):
73 | app.add_config_value('github_project', "user/project", 'html')
74 | app.add_directive('include_changelog', IncludeDirective)
75 | return {'version': '0.1'} # identifies the version of our extension
76 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. _index:
2 |
3 | ODTbrain provides image reconstruction algorithms for Optical Diffraction
4 | Tomography with a Born and Rytov Approximation-based Inversion to compute
5 | the refractive index (n) in 2D and in 3D. This is the documentaion of
6 | ODTbrain version |release|.
7 |
8 |
9 | Documentation
10 | =============
11 |
12 | .. toctree::
13 | :maxdepth: 4
14 |
15 | sec_introduction
16 | sec_code_reference
17 | sec_examples
18 |
19 | .. toctree::
20 | :maxdepth: 1
21 |
22 | sec_changelog
23 | zsec_bib
24 |
25 |
26 | Indices and tables
27 | ==================
28 |
29 | * :ref:`genindex`
30 | * :ref:`modindex`
31 | * :ref:`search`
32 |
--------------------------------------------------------------------------------
/docs/odtbrain.bib:
--------------------------------------------------------------------------------
1 | % Encoding: UTF-8
2 |
3 | @Book{Kak2001,
4 | title = {{Principles of Computerized Tomographic Imaging}},
5 | publisher = {SIAM},
6 | year = {2001},
7 | author = {Kak, Aninash C. and Slaney, Malcom G.},
8 | editor = {O'Malley, Robert E.},
9 | address = {Philadelphia, USA},
10 | isbn = {089871494X},
11 | doi = {10.1137/1.9780898719277},
12 | keywords = {tomography},
13 | pages = {327},
14 | url = {http://www.slaney.org/pct/pct-toc.html},
15 | }
16 |
17 | @Article{Mueller2015tilted,
18 | author = {Müller, Paul and Schürmann, Mirjam and Chan, Chii J and Guck, Jochen},
19 | title = {{Single-cell diffraction tomography with optofluidic rotation about a tilted axis}},
20 | journal = {Proc. SPIE},
21 | year = {2015},
22 | volume = {9548},
23 | pages = {95480U--95480U--5},
24 | doi = {10.1117/12.2191501},
25 | }
26 |
27 | @Article{Mueller2015,
28 | author = {Müller, Paul and Schürmann, Mirjam and Guck, Jochen},
29 | title = {{ODTbrain: a Python library for full-view, dense diffraction tomography}},
30 | journal = {BMC Bioinformatics},
31 | year = {2015},
32 | volume = {16},
33 | number = {1},
34 | pages = {1--9},
35 | issn = {1471-2105},
36 | doi = {10.1186/s12859-015-0764-0},
37 | }
38 |
39 | @Article{Mueller2015arxiv,
40 | author = {Müller, Paul and Schürmann, Mirjam and Guck, Jochen},
41 | title = {{The Theory of Diffraction Tomography}},
42 | journal = {ArXiv e-prints},
43 | year = {2015},
44 | archiveprefix = {arXiv},
45 | arxivid = {q-bio.QM/1507.00466v2},
46 | eprint = {1507.00466v2},
47 | keywords = {81U40,J.2,Physics - Biological Physics,Physics - Optics,Quantitative Biology - Quantitative Methods},
48 | primaryclass = {q-bio.QM},
49 | }
50 |
51 | @Article{Tam1981,
52 | author = {Tam, K C and Perez-Mendez, V},
53 | title = {{Tomographical imaging with limited-angle input}},
54 | journal = {J. Opt. Soc. Am.},
55 | year = {1981},
56 | volume = {71},
57 | number = {5},
58 | pages = {582--592},
59 | doi = {10.1364/JOSA.71.000582},
60 | keywords = {tomography},
61 | publisher = {OSA},
62 | }
63 |
64 | @Article{Wolf1969,
65 | author = {Wolf, Emil},
66 | title = {{Three-dimensional structure determination of semi-transparent objects from holographic data}},
67 | journal = {Optics Communications},
68 | year = {1969},
69 | volume = {1},
70 | number = {4},
71 | pages = {153--156},
72 | month = {sep},
73 | issn = {00304018},
74 | doi = {10.1016/0030-4018(69)90052-2},
75 | keywords = {tomography},
76 | }
77 |
78 | @Article{Schuermann2017,
79 | author = {M. Schürmann and G. Cojoc and S. Girardo and E. Ulbricht and J. Guck and P. Müller},
80 | title = {Three-dimensional correlative single-cell imaging utilizing fluorescence and refractive index tomography},
81 | journal = {Journal of Biophotonics},
82 | year = {2017},
83 | volume = {11},
84 | number = {3},
85 | pages = {e201700145},
86 | month = {aug},
87 | doi = {10.1002/jbio.201700145},
88 | publisher = {Wiley-Blackwell},
89 | }
90 |
91 | @Article{Vertu2009,
92 | author = {Vertu, Stanislas and Delaunay, Jean-Jacques and Yamada, Ichiro and Haeberlé, Olivier},
93 | title = {{Diffraction microtomography with sample rotation: influence of a missing apple core in the recorded frequency space}},
94 | journal = {Central European Journal of Physics},
95 | year = {2009},
96 | volume = {7},
97 | number = {1},
98 | pages = {22--31},
99 | issn = {1895-1082},
100 | doi = {10.2478/s11534-008-0154-6},
101 | keywords = {Fourier optics,holographic interferometry,image reconstruction,tomography},
102 | publisher = {SP Versita},
103 | }
104 |
105 | @Comment{jabref-meta: databaseType:bibtex;}
106 |
--------------------------------------------------------------------------------
/docs/processing.rst:
--------------------------------------------------------------------------------
1 | Data conversion methods
2 | -----------------------
3 | .. currentmodule:: odtbrain
4 |
5 | .. autosummary::
6 | odt_to_ri
7 | opt_to_ri
8 | sinogram_as_radon
9 | sinogram_as_rytov
10 |
11 |
12 | Sinogram preparation
13 | ~~~~~~~~~~~~~~~~~~~~
14 | Tomographic data sets consist of detector images for different
15 | rotational positions :math:`\phi_0` of the object. Sinogram
16 | preparation means that the measured field :math:`u(\mathbf{r})`
17 | is transformed to either the Rytov approximation (diffraction tomography)
18 | or the Radon phase (classical tomography).
19 |
20 | .. autofunction:: sinogram_as_radon
21 | .. autofunction:: sinogram_as_rytov
22 |
23 |
24 | Translation of object function to refractive index
25 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
26 | To obtain the refractive index map :math:`n(\mathbf{r})`
27 | from an object function :math:`f(\mathbf{r})` returned
28 | by e.g. :func:`backpropagate_3d`, an additional conversion
29 | step is necessary. For diffraction based models, :func:`odt_to_ri`
30 | must be used whereas for Radon-based models :func:`opt_to_ri`
31 | must be used.
32 |
33 | .. autofunction:: odt_to_ri
34 | .. autofunction:: opt_to_ri
35 |
--------------------------------------------------------------------------------
/docs/recon_2d.rst:
--------------------------------------------------------------------------------
1 | 2D inversion
2 | ------------
3 | The first Born approximation for a 2D scattering problem with a plane
4 | wave
5 | :math:`u_0(\mathbf{r}) = a_0 \exp(-ik_\mathrm{m}\mathbf{s_0r})`
6 | reads:
7 |
8 | .. math::
9 | u_\mathrm{B}(\mathbf{r}) = \iint \!\! d^2r'
10 | G(\mathbf{r-r'}) f(\mathbf{r'}) u_0(\mathbf{r'})
11 |
12 | The Green's function in 2D is the zero-order Hankel function
13 | of the first kind:
14 |
15 | .. math::
16 | G(\mathbf{r-r'}) = \frac{i}{4}
17 | H_0^\mathrm{(1)}(k_\mathrm{m} \left| \mathbf{r-r'} \right|)
18 |
19 | Solving for :math:`f(\mathbf{r})` yields the Fourier diffraction theorem
20 | in 2D
21 |
22 | .. math::
23 | \widehat{F}(k_\mathrm{m}(\mathbf{s-s_0})) =
24 | - \sqrt{\frac{2}{\pi}}
25 | \frac{i k_\mathrm{m}}{a_0} M
26 | \widehat{U}_{\mathrm{B},\phi_0}(k_\mathrm{Dx})
27 | \exp \! \left(-i k_\mathrm{m} M l_\mathrm{D} \right)
28 |
29 | where
30 | :math:`\widehat{F}(k_\mathrm{x}, k_\mathrm{z})`
31 | is the Fourier transformed object function and
32 | :math:`\widehat{U}_{\mathrm{B}, \phi_0}(k_\mathrm{Dx})` is the
33 | Fourier transformed complex wave that travels along :math:`\mathbf{s_0}`
34 | (in the direction of :math:`\phi_0`) measured at the detector
35 | :math:`\mathbf{r_D}`.
36 |
37 |
38 | The following identities are used:
39 |
40 | .. math::
41 | k_\mathrm{m} (\mathbf{s-s_0}) &= k_\mathrm{Dx} \, \mathbf{t_\perp} +
42 | k_\mathrm{m}(M - 1) \, \mathbf{s_0}
43 |
44 | \mathbf{s_0} &= \left(p_0 , \, M_0 \right) =
45 | (-\sin\phi_0, \, \cos\phi_0)
46 |
47 | \mathbf{t_\perp} &= \left(- M_0 , \, p_0 \right) =
48 | (\cos\phi_0, \, \sin\phi_0)
49 |
50 | .. currentmodule:: odtbrain
51 |
52 |
53 | Method summary
54 | ~~~~~~~~~~~~~~
55 | .. autosummary::
56 | backpropagate_2d
57 | fourier_map_2d
58 | integrate_2d
59 |
60 |
61 | Backpropagation
62 | ~~~~~~~~~~~~~~~
63 | .. autofunction:: backpropagate_2d
64 |
65 | Fourier mapping
66 | ~~~~~~~~~~~~~~~
67 | .. autofunction:: fourier_map_2d
68 |
69 | Direct sum
70 | ~~~~~~~~~~
71 | .. autofunction:: integrate_2d
72 |
--------------------------------------------------------------------------------
/docs/recon_3d.rst:
--------------------------------------------------------------------------------
1 | 3D inversion
2 | ------------
3 | .. currentmodule:: odtbrain
4 |
5 |
6 | The first Born approximation for a 3D scattering problem with a plane
7 | wave
8 | :math:`u_0(\mathbf{r}) = a_0 \exp(-ik_\mathrm{m}\mathbf{s_0r})`
9 | reads:
10 |
11 |
12 | .. math::
13 | u_\mathrm{B}(\mathbf{r}) = \iiint \!\! d^3r'
14 | G(\mathbf{r-r'}) f(\mathbf{r'}) u_0(\mathbf{r'})
15 |
16 | The Green's function in 3D can be written as:
17 |
18 | .. math::
19 | G(\mathbf{r-r'}) = \frac{ik_\mathrm{m}}{8\pi^2} \iint \!\! dpdq
20 | \frac{1}{M} \exp\! \left \lbrace i k_\mathrm{m} \left[
21 | p(x-x') + q(y-y') + M(z-z') \right] \right \rbrace
22 |
23 | with
24 |
25 | .. math::
26 |
27 | M = \sqrt{1-p^2-q^2}
28 |
29 | Solving for :math:`f(\mathbf{r})` yields the Fourier diffraction theorem
30 | in 3D
31 |
32 | .. math::
33 | \widehat{F}(k_\mathrm{m}(\mathbf{s-s_0})) =
34 | - \sqrt{\frac{2}{\pi}}
35 | \frac{i k_\mathrm{m}}{a_0} M
36 | \widehat{U}_{\mathrm{B},\phi_0}(k_\mathrm{Dx}, k_\mathrm{Dy})
37 | \exp \! \left(-i k_\mathrm{m} M l_\mathrm{D} \right)
38 |
39 | where
40 | :math:`\widehat{F}(k_\mathrm{x}, k_\mathrm{y}, k_\mathrm{z})`
41 | is the Fourier transformed object function and
42 | :math:`\widehat{U}_{\mathrm{B}, \phi_0}(k_\mathrm{Dx}, k_\mathrm{Dy})`
43 | is the Fourier transformed complex wave that travels along
44 | :math:`\mathbf{s_0}`
45 | (in the direction of :math:`\phi_0`) measured at the detector
46 | :math:`\mathbf{r_D}`.
47 |
48 |
49 | The following identities are used:
50 |
51 | .. math::
52 | k_\mathrm{m} (\mathbf{s-s_0}) &= k_\mathrm{Dx} \, \mathbf{t_\perp} +
53 | k_\mathrm{m}(M - 1) \, \mathbf{s_0}
54 |
55 | \mathbf{s} &= (p, q, M)
56 |
57 | \mathbf{s_0} &= (p_0, q_0, M_0) = (-\sin\phi_0, \, 0, \, \cos\phi_0)
58 |
59 | \mathbf{t_\perp} &= \left(\cos\phi_0, \,
60 | \frac{k_\mathrm{Dy}}{k_\mathrm{Dx}}, \,
61 | \sin\phi_0 \right)^\top
62 |
63 | .. currentmodule:: odtbrain
64 |
65 |
66 | Method summary
67 | ~~~~~~~~~~~~~~
68 |
69 | .. autosummary::
70 | backpropagate_3d
71 | backpropagate_3d_tilted
72 |
73 |
74 | Backpropagation
75 | ~~~~~~~~~~~~~~~
76 | .. autofunction:: backpropagate_3d
77 |
78 |
79 | Backpropagation with tilted axis of rotation
80 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
81 | .. autofunction:: backpropagate_3d_tilted
82 |
83 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | sphinx
2 | sphinxcontrib.bibtex
3 | sphinx_rtd_theme
4 |
--------------------------------------------------------------------------------
/docs/sec_changelog.rst:
--------------------------------------------------------------------------------
1 | =========
2 | Changelog
3 | =========
4 | List of changes in-between ODTbrain releases.
5 |
6 | .. include_changelog:: ../CHANGELOG
7 |
--------------------------------------------------------------------------------
/docs/sec_code_reference.rst:
--------------------------------------------------------------------------------
1 | ==============
2 | Code reference
3 | ==============
4 |
5 | .. toctree::
6 | :maxdepth: 4
7 |
8 | processing
9 | recon_2d
10 | recon_3d
11 | apple
12 |
--------------------------------------------------------------------------------
/docs/sec_examples.rst:
--------------------------------------------------------------------------------
1 | ========
2 | Examples
3 | ========
4 |
5 | .. toctree::
6 | :maxdepth: 2
7 |
8 | ex2d
9 | ex3d
10 |
--------------------------------------------------------------------------------
/docs/sec_introduction.rst:
--------------------------------------------------------------------------------
1 | ============
2 | Introduction
3 | ============
4 |
5 | This package provides reconstruction algorithms for diffraction
6 | tomography in two and three dimensions.
7 |
8 | Installation
9 | ------------
10 | To install via the `Python Package Index (PyPI)`_, run:
11 |
12 | pip install odtbrain
13 |
14 |
15 | On some systems, the `FFTW3 library`_ might have to be
16 | installed manually before installing ODTbrain. All other
17 | dependencies are installed automatically.
18 | If the above command does not work, please refer to the
19 | installation instructions at the `GitHub repository`_ or
20 | `create an issue`_
21 |
22 | .. _`FFTW3 library`: http://fftw.org
23 | .. _`GitHub repository`: https://github.com/RI-imaging/ODTbrain
24 | .. _`Python Package Index (PyPI)`: https://pypi.python.org/pypi/odtbrain/
25 | .. _`create an issue`: https://github.com/RI-imaging/ODTbrain/issues
26 |
27 |
28 | Theoretical background
29 | ----------------------
30 | A detailed summary of the underlying theory is available
31 | in :cite:`Mueller2015arxiv`.
32 |
33 | The Fourier diffraction theorem states, that the Fourier transform
34 | :math:`\widehat{U}_{\mathrm{B},\phi_0}(\mathbf{k_\mathrm{D}})` of
35 | the scattered field :math:`u_\mathrm{B}(\mathbf{r_D})`, measured at
36 | a certain angle :math:`\phi_0`, is distributed along a circular arc
37 | (2D) or along a semi-spherical surface (3D) in Fourier space,
38 | synthesizing the Fourier transform
39 | :math:`\widehat{F}(\mathbf{k})` of the object function
40 | :math:`f(\mathbf{r})` :cite:`Kak2001`, :cite:`Wolf1969`.
41 |
42 | .. math::
43 |
44 | \widehat{F}(k_\mathrm{m}(\mathbf{s - s_0}))=
45 | - \sqrt{\frac{2}{\pi}} \frac{i k_\mathrm{m}}{a_0}
46 | M \widehat{U}_{\mathrm{B},\phi_0}(\mathbf{k_\mathrm{D}})
47 | \exp \! \left(-i k_\mathrm{m} M l_\mathrm{D} \right)
48 |
49 | In this notation,
50 | :math:`k_\mathrm{m}` is the wave number,
51 | :math:`\mathbf{s_0}` is the norm vector pointing at :math:`\phi_0`,
52 | :math:`M=\sqrt{1-s_\mathrm{x}^2}` (2D) and
53 | :math:`M=\sqrt{1-s_\mathrm{x}^2-s_\mathrm{y}^2}` (3D)
54 | enforces the spherical constraint, and
55 | :math:`l_\mathrm{D}` is the distance from the center of the object
56 | function :math:`f(\mathbf{r})` to the detector plane
57 | :math:`\mathbf{r_D}`.
58 |
59 |
60 | Fields of Application
61 | ---------------------
62 | The algorithms presented here are based on the (scalar) Helmholtz
63 | equation. Furthermore, the Born and Rytov approximations to the
64 | scattered wave :math:`u(\mathbf{r})` are used to linearize the
65 | problem for a straight-forward inversion.
66 |
67 | The package is intended for optical diffraction
68 | tomography to determine the refractive index of biological cells.
69 | Because the Helmholtz equation is only an approximation to the
70 | Maxwell equations, describing the propagation of light,
71 | :abbr:`FDTD (Finite Difference Time Domain)` simulations were performed
72 | to test the reconstruction algorithms within this package.
73 | The algorithms present in this package should also be valid for the
74 | following cases, but have not been tested appropriately:
75 |
76 | * tomographic measurements of absorbing materials (complex refractive
77 | index :math:`n(\mathbf{r})`)
78 |
79 | * ultrasonic diffraction tomography, which is correctly described by
80 | the Helmholtz equation
81 |
82 | How to cite
83 | -----------
84 | If you use ODTbrain in a scientific publication, please cite
85 | Müller et al., *BMC Bioinformatics* (2015) :cite:`Mueller2015`.
86 |
87 |
--------------------------------------------------------------------------------
/docs/zsec_bib.rst:
--------------------------------------------------------------------------------
1 | =============
2 | Bilbliography
3 | =============
4 |
5 | .. bibliography:: odtbrain.bib
6 |
--------------------------------------------------------------------------------
/examples/backprop_from_fdtd_2d.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RI-imaging/ODTbrain/f7bb8b792bad8ae78ea885758a801c52dfea44ad/examples/backprop_from_fdtd_2d.jpg
--------------------------------------------------------------------------------
/examples/backprop_from_fdtd_2d.py:
--------------------------------------------------------------------------------
1 | """FDTD cell phantom
2 |
3 | The *in silico* data set was created with the
4 | :abbr:`FDTD (Finite Difference Time Domain)` software `meep`_. The data
5 | are 1D projections of a 2D refractive index phantom. The
6 | reconstruction of the refractive index with the Rytov approximation
7 | is in good agreement with the phantom that was used in the
8 | simulation.
9 |
10 | .. note::
11 |
12 | In the initial version of this example, I did not notice that the
13 | 1D fields in the data file were actually not in-focus. Back then,
14 | I used an auto-focusing algorithm to numerically focus the sinogram
15 | data to the rotation center, and this distance was inaccurate.
16 | Upon closer inspection of the reconstructed image, it turned out that
17 | the focus position is still about 0.5 wavelengths away from the
18 | rotation center. I have added this information to the `fdtd_info.txt`
19 | file in the data archive.
20 |
21 | The take-home message is that for a proper tomographic reconstruction
22 | it is important that the distance between the rotation center and
23 | the focus of the sinogram is known. If this is not the case, it might
24 | make sense to sweep the reconstruction distance and use an
25 | appropriate metric to determine the best reconstruction.
26 |
27 | .. _`meep`: http://ab-initio.mit.edu/wiki/index.php/Meep
28 | """
29 | import matplotlib.pylab as plt
30 | import numpy as np
31 | import odtbrain as odt
32 |
33 | from example_helper import load_data
34 |
35 |
36 | sino, angles, phantom, cfg = load_data("fdtd_2d_sino_A100_R13.zip",
37 | f_angles="fdtd_angles.txt",
38 | f_sino_imag="fdtd_imag.txt",
39 | f_sino_real="fdtd_real.txt",
40 | f_info="fdtd_info.txt",
41 | f_phantom="fdtd_phantom.txt",
42 | )
43 |
44 | print("Example: Backpropagation from 2D FDTD simulations")
45 | print("Refractive index of medium:", cfg["nm"])
46 | print("Measurement position from object center in wavelengths:", cfg["lD"])
47 | print("Wavelength sampling:", cfg["res"])
48 | print("Performing backpropagation.")
49 |
50 | # Apply the Rytov approximation
51 | sino_rytov = odt.sinogram_as_rytov(sino)
52 |
53 | # perform backpropagation to obtain object function f
54 | f = odt.backpropagate_2d(uSin=sino_rytov,
55 | angles=angles,
56 | res=cfg["res"],
57 | nm=cfg["nm"],
58 | lD=cfg["lD"] * cfg["res"]
59 | )
60 |
61 | # compute refractive index n from object function
62 | n = odt.odt_to_ri(f, res=cfg["res"], nm=cfg["nm"])
63 |
64 | # compare phantom and reconstruction in plot
65 | fig, axes = plt.subplots(1, 3, figsize=(8, 2.8))
66 |
67 | axes[0].set_title("FDTD phantom")
68 | axes[0].imshow(phantom, vmin=phantom.min(), vmax=phantom.max())
69 | sino_phase = np.unwrap(np.angle(sino), axis=1)
70 |
71 | axes[1].set_title("phase sinogram")
72 | axes[1].imshow(sino_phase, vmin=sino_phase.min(), vmax=sino_phase.max(),
73 | aspect=sino.shape[1] / sino.shape[0],
74 | cmap="coolwarm")
75 | axes[1].set_xlabel("detector")
76 | axes[1].set_ylabel("angle [rad]")
77 |
78 | axes[2].set_title("reconstructed image")
79 | axes[2].imshow(n.real, vmin=phantom.min(), vmax=phantom.max())
80 |
81 | # set y ticks for sinogram
82 | labels = np.linspace(0, 2 * np.pi, len(axes[1].get_yticks()))
83 | labels = ["{:.2f}".format(i) for i in labels]
84 | axes[1].set_yticks(np.linspace(0, len(angles), len(labels)))
85 | axes[1].set_yticklabels(labels)
86 |
87 | plt.tight_layout()
88 | plt.show()
89 |
--------------------------------------------------------------------------------
/examples/backprop_from_fdtd_3d.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RI-imaging/ODTbrain/f7bb8b792bad8ae78ea885758a801c52dfea44ad/examples/backprop_from_fdtd_3d.jpg
--------------------------------------------------------------------------------
/examples/backprop_from_fdtd_3d.py:
--------------------------------------------------------------------------------
1 | """FDTD cell phantom
2 | The *in silico* data set was created with the
3 | :abbr:`FDTD (Finite Difference Time Domain)` software `meep`_. The data
4 | are 2D projections of a 3D refractive index phantom. The reconstruction
5 | of the refractive index with the Rytov approximation is in good
6 | agreement with the phantom that was used in the simulation. The data
7 | are downsampled by a factor of two. The rotational axis is the `y`-axis.
8 | A total of 180 projections are used for the reconstruction. A detailed
9 | description of this phantom is given in :cite:`Mueller2015`.
10 |
11 | .. _`meep`: http://ab-initio.mit.edu/wiki/index.php/Meep
12 | """
13 | import matplotlib.pylab as plt
14 | import numpy as np
15 |
16 | import odtbrain as odt
17 |
18 | from example_helper import load_data
19 |
20 |
21 | if __name__ == "__main__":
22 | sino, angles, phantom, cfg = \
23 | load_data("fdtd_3d_sino_A180_R6.500.tar.lzma")
24 |
25 | A = angles.shape[0]
26 |
27 | print("Example: Backpropagation from 3D FDTD simulations")
28 | print("Refractive index of medium:", cfg["nm"])
29 | print("Measurement position from object center:", cfg["lD"])
30 | print("Wavelength sampling:", cfg["res"])
31 | print("Number of projections:", A)
32 | print("Performing backpropagation.")
33 |
34 | # Apply the Rytov approximation
35 | sinoRytov = odt.sinogram_as_rytov(sino)
36 |
37 | # perform backpropagation to obtain object function f
38 | f = odt.backpropagate_3d(uSin=sinoRytov,
39 | angles=angles,
40 | res=cfg["res"],
41 | nm=cfg["nm"],
42 | lD=cfg["lD"]
43 | )
44 |
45 | # compute refractive index n from object function
46 | n = odt.odt_to_ri(f, res=cfg["res"], nm=cfg["nm"])
47 |
48 | sx, sy, sz = n.shape
49 | px, py, pz = phantom.shape
50 |
51 | sino_phase = np.angle(sino)
52 |
53 | # compare phantom and reconstruction in plot
54 | fig, axes = plt.subplots(2, 3, figsize=(8, 4))
55 | kwri = {"vmin": n.real.min(), "vmax": n.real.max()}
56 | kwph = {"vmin": sino_phase.min(), "vmax": sino_phase.max(),
57 | "cmap": "coolwarm"}
58 |
59 | # Phantom
60 | axes[0, 0].set_title("FDTD phantom center")
61 | rimap = axes[0, 0].imshow(phantom[px // 2], **kwri)
62 | axes[0, 0].set_xlabel("x")
63 | axes[0, 0].set_ylabel("y")
64 |
65 | axes[1, 0].set_title("FDTD phantom nucleolus")
66 | axes[1, 0].imshow(phantom[int(px / 2 + 2 * cfg["res"])], **kwri)
67 | axes[1, 0].set_xlabel("x")
68 | axes[1, 0].set_ylabel("y")
69 |
70 | # Sinogram
71 | axes[0, 1].set_title("phase projection")
72 | phmap = axes[0, 1].imshow(sino_phase[A // 2, :, :], **kwph)
73 | axes[0, 1].set_xlabel("detector x")
74 | axes[0, 1].set_ylabel("detector y")
75 |
76 | axes[1, 1].set_title("sinogram slice")
77 | axes[1, 1].imshow(sino_phase[:, :, sino.shape[2] // 2],
78 | aspect=sino.shape[1] / sino.shape[0], **kwph)
79 | axes[1, 1].set_xlabel("detector y")
80 | axes[1, 1].set_ylabel("angle [rad]")
81 | # set y ticks for sinogram
82 | labels = np.linspace(0, 2 * np.pi, len(axes[1, 1].get_yticks()))
83 | labels = ["{:.2f}".format(i) for i in labels]
84 | axes[1, 1].set_yticks(np.linspace(0, len(angles), len(labels)))
85 | axes[1, 1].set_yticklabels(labels)
86 |
87 | axes[0, 2].set_title("reconstruction center")
88 | axes[0, 2].imshow(n[sx // 2].real, **kwri)
89 | axes[0, 2].set_xlabel("x")
90 | axes[0, 2].set_ylabel("y")
91 |
92 | axes[1, 2].set_title("reconstruction nucleolus")
93 | axes[1, 2].imshow(n[int(sx / 2 + 2 * cfg["res"])].real, **kwri)
94 | axes[1, 2].set_xlabel("x")
95 | axes[1, 2].set_ylabel("y")
96 |
97 | # color bars
98 | cbkwargs = {"fraction": 0.045,
99 | "format": "%.3f"}
100 | plt.colorbar(phmap, ax=axes[0, 1], **cbkwargs)
101 | plt.colorbar(phmap, ax=axes[1, 1], **cbkwargs)
102 | plt.colorbar(rimap, ax=axes[0, 0], **cbkwargs)
103 | plt.colorbar(rimap, ax=axes[1, 0], **cbkwargs)
104 | plt.colorbar(rimap, ax=axes[0, 2], **cbkwargs)
105 | plt.colorbar(rimap, ax=axes[1, 2], **cbkwargs)
106 |
107 | plt.tight_layout()
108 | plt.show()
109 |
--------------------------------------------------------------------------------
/examples/backprop_from_fdtd_3d_tilted.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RI-imaging/ODTbrain/f7bb8b792bad8ae78ea885758a801c52dfea44ad/examples/backprop_from_fdtd_3d_tilted.jpg
--------------------------------------------------------------------------------
/examples/backprop_from_fdtd_3d_tilted.py:
--------------------------------------------------------------------------------
1 | """FDTD cell phantom with tilted axis of rotation
2 |
3 | The *in silico* data set was created with the
4 | :abbr:`FDTD (Finite Difference Time Domain)` software `meep`_. The data
5 | are 2D projections of a 3D refractive index phantom that is rotated
6 | about an axis which is tilted by 0.2 rad (11.5 degrees) with respect
7 | to the imaging plane. The example showcases the method
8 | :func:`odtbrain.backpropagate_3d_tilted` which takes into account
9 | such a tilted axis of rotation. The data are downsampled by a factor
10 | of two. A total of 220 projections are used for the reconstruction.
11 | Note that the information required for reconstruction decreases as the
12 | tilt angle increases. If the tilt angle is 90 degrees w.r.t. the
13 | imaging plane, then we get a rotating image of a cell (not images of a
14 | rotating cell) and tomographic reconstruction is impossible. A brief
15 | description of this algorithm is given in :cite:`Mueller2015tilted`.
16 |
17 |
18 | The first column shows the measured phase, visualizing the
19 | tilt (compare to other examples). The second column shows a
20 | reconstruction that does not take into account the tilted axis of
21 | rotation; the result is a blurry reconstruction. The third column
22 | shows the improved reconstruction; the known tilted axis of rotation
23 | is used in the reconstruction process.
24 |
25 | .. _`meep`: http://ab-initio.mit.edu/wiki/index.php/Meep
26 | """
27 | import matplotlib.pylab as plt
28 | import numpy as np
29 |
30 | import odtbrain as odt
31 |
32 | from example_helper import load_data
33 |
34 |
35 | if __name__ == "__main__":
36 | sino, angles, phantom, cfg = \
37 | load_data("fdtd_3d_sino_A220_R6.500_tiltyz0.2.tar.lzma")
38 |
39 | A = angles.shape[0]
40 |
41 | print("Example: Backpropagation from 3D FDTD simulations")
42 | print("Refractive index of medium:", cfg["nm"])
43 | print("Measurement position from object center:", cfg["lD"])
44 | print("Wavelength sampling:", cfg["res"])
45 | print("Axis tilt in y-z direction:", cfg["tilt_yz"])
46 | print("Number of projections:", A)
47 |
48 | print("Performing normal backpropagation.")
49 | # Apply the Rytov approximation
50 | sinoRytov = odt.sinogram_as_rytov(sino)
51 |
52 | # Perform naive backpropagation
53 | f_naiv = odt.backpropagate_3d(uSin=sinoRytov,
54 | angles=angles,
55 | res=cfg["res"],
56 | nm=cfg["nm"],
57 | lD=cfg["lD"]
58 | )
59 |
60 | print("Performing tilted backpropagation.")
61 | # Determine tilted axis
62 | tilted_axis = [0, np.cos(cfg["tilt_yz"]), np.sin(cfg["tilt_yz"])]
63 |
64 | # Perform tilted backpropagation
65 | f_tilt = odt.backpropagate_3d_tilted(uSin=sinoRytov,
66 | angles=angles,
67 | res=cfg["res"],
68 | nm=cfg["nm"],
69 | lD=cfg["lD"],
70 | tilted_axis=tilted_axis,
71 | )
72 |
73 | # compute refractive index n from object function
74 | n_naiv = odt.odt_to_ri(f_naiv, res=cfg["res"], nm=cfg["nm"])
75 | n_tilt = odt.odt_to_ri(f_tilt, res=cfg["res"], nm=cfg["nm"])
76 |
77 | sx, sy, sz = n_tilt.shape
78 | px, py, pz = phantom.shape
79 |
80 | sino_phase = np.angle(sino)
81 |
82 | # compare phantom and reconstruction in plot
83 | fig, axes = plt.subplots(2, 3, figsize=(8, 4.5))
84 | kwri = {"vmin": n_tilt.real.min(), "vmax": n_tilt.real.max()}
85 | kwph = {"vmin": sino_phase.min(), "vmax": sino_phase.max(),
86 | "cmap": "coolwarm"}
87 |
88 | # Sinogram
89 | axes[0, 0].set_title("phase projection")
90 | phmap = axes[0, 0].imshow(sino_phase[A // 2, :, :], **kwph)
91 | axes[0, 0].set_xlabel("detector x")
92 | axes[0, 0].set_ylabel("detector y")
93 |
94 | axes[1, 0].set_title("sinogram slice")
95 | axes[1, 0].imshow(sino_phase[:, :, sino.shape[2] // 2],
96 | aspect=sino.shape[1] / sino.shape[0], **kwph)
97 | axes[1, 0].set_xlabel("detector y")
98 | axes[1, 0].set_ylabel("angle [rad]")
99 | # set y ticks for sinogram
100 | labels = np.linspace(0, 2 * np.pi, len(axes[1, 1].get_yticks()))
101 | labels = ["{:.2f}".format(i) for i in labels]
102 | axes[1, 0].set_yticks(np.linspace(0, len(angles), len(labels)))
103 | axes[1, 0].set_yticklabels(labels)
104 |
105 | axes[0, 1].set_title("normal (center)")
106 | rimap = axes[0, 1].imshow(n_naiv[sx // 2].real, **kwri)
107 | axes[0, 1].set_xlabel("x")
108 | axes[0, 1].set_ylabel("y")
109 |
110 | axes[1, 1].set_title("normal (nucleolus)")
111 | axes[1, 1].imshow(n_naiv[int(sx / 2 + 2 * cfg["res"])].real, **kwri)
112 | axes[1, 1].set_xlabel("x")
113 | axes[1, 1].set_ylabel("y")
114 |
115 | axes[0, 2].set_title("tilt correction (center)")
116 | axes[0, 2].imshow(n_tilt[sx // 2].real, **kwri)
117 | axes[0, 2].set_xlabel("x")
118 | axes[0, 2].set_ylabel("y")
119 |
120 | axes[1, 2].set_title("tilt correction (nucleolus)")
121 | axes[1, 2].imshow(n_tilt[int(sx / 2 + 2 * cfg["res"])].real, **kwri)
122 | axes[1, 2].set_xlabel("x")
123 | axes[1, 2].set_ylabel("y")
124 |
125 | # color bars
126 | cbkwargs = {"fraction": 0.045,
127 | "format": "%.3f"}
128 | plt.colorbar(phmap, ax=axes[0, 0], **cbkwargs)
129 | plt.colorbar(phmap, ax=axes[1, 0], **cbkwargs)
130 | plt.colorbar(rimap, ax=axes[0, 1], **cbkwargs)
131 | plt.colorbar(rimap, ax=axes[1, 1], **cbkwargs)
132 | plt.colorbar(rimap, ax=axes[0, 2], **cbkwargs)
133 | plt.colorbar(rimap, ax=axes[1, 2], **cbkwargs)
134 |
135 | plt.tight_layout()
136 | plt.show()
137 |
--------------------------------------------------------------------------------
/examples/backprop_from_fdtd_3d_tilted2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RI-imaging/ODTbrain/f7bb8b792bad8ae78ea885758a801c52dfea44ad/examples/backprop_from_fdtd_3d_tilted2.jpg
--------------------------------------------------------------------------------
/examples/backprop_from_fdtd_3d_tilted2.py:
--------------------------------------------------------------------------------
1 | """FDTD cell phantom with tilted and rolled axis of rotation
2 |
3 | The *in silico* data set was created with the
4 | :abbr:`FDTD (Finite Difference Time Domain)` software `meep`_. The data
5 | are 2D projections of a 3D refractive index phantom that is rotated
6 | about an axis which is tilted by 0.2 rad (11.5 degrees) with respect to
7 | the imaging plane and rolled by -.42 rad (-24.1 degrees) within the
8 | imaging plane. The data are the same as were used in the previous
9 | example. A brief description of this algorithm is given in
10 | :cite:`Mueller2015tilted`.
11 |
12 | .. _`meep`: http://ab-initio.mit.edu/wiki/index.php/Meep
13 | """
14 | import matplotlib.pylab as plt
15 | import numpy as np
16 | from scipy.ndimage import rotate
17 |
18 | import odtbrain as odt
19 |
20 | from example_helper import load_data
21 |
22 |
23 | if __name__ == "__main__":
24 | sino, angles, phantom, cfg = \
25 | load_data("fdtd_3d_sino_A220_R6.500_tiltyz0.2.tar.lzma")
26 |
27 | # Perform titlt by -.42 rad in detector plane
28 | rotang = -0.42
29 | rotkwargs = {"mode": "constant",
30 | "order": 2,
31 | "reshape": False,
32 | }
33 | for ii in range(len(sino)):
34 | sino[ii].real = rotate(
35 | sino[ii].real, np.rad2deg(rotang), cval=1, **rotkwargs)
36 | sino[ii].imag = rotate(
37 | sino[ii].imag, np.rad2deg(rotang), cval=0, **rotkwargs)
38 |
39 | A = angles.shape[0]
40 |
41 | print("Example: Backpropagation from 3D FDTD simulations")
42 | print("Refractive index of medium:", cfg["nm"])
43 | print("Measurement position from object center:", cfg["lD"])
44 | print("Wavelength sampling:", cfg["res"])
45 | print("Axis tilt in y-z direction:", cfg["tilt_yz"])
46 | print("Number of projections:", A)
47 |
48 | # Apply the Rytov approximation
49 | sinoRytov = odt.sinogram_as_rytov(sino)
50 |
51 | # Determine tilted axis
52 | tilted_axis = [0, np.cos(cfg["tilt_yz"]), np.sin(cfg["tilt_yz"])]
53 | rotmat = np.array([
54 | [np.cos(rotang), -np.sin(rotang), 0],
55 | [np.sin(rotang), np.cos(rotang), 0],
56 | [0, 0, 1],
57 | ])
58 | tilted_axis = np.dot(rotmat, tilted_axis)
59 |
60 | print("Performing tilted backpropagation.")
61 | # Perform tilted backpropagation
62 | f_tilt = odt.backpropagate_3d_tilted(uSin=sinoRytov,
63 | angles=angles,
64 | res=cfg["res"],
65 | nm=cfg["nm"],
66 | lD=cfg["lD"],
67 | tilted_axis=tilted_axis,
68 | )
69 |
70 | # compute refractive index n from object function
71 | n_tilt = odt.odt_to_ri(f_tilt, res=cfg["res"], nm=cfg["nm"])
72 |
73 | sx, sy, sz = n_tilt.shape
74 | px, py, pz = phantom.shape
75 |
76 | sino_phase = np.angle(sino)
77 |
78 | # compare phantom and reconstruction in plot
79 | fig, axes = plt.subplots(1, 3, figsize=(8, 2.4))
80 | kwri = {"vmin": n_tilt.real.min(), "vmax": n_tilt.real.max()}
81 | kwph = {"vmin": sino_phase.min(), "vmax": sino_phase.max(),
82 | "cmap": "coolwarm"}
83 |
84 | # Sinogram
85 | axes[0].set_title("phase projection")
86 | phmap = axes[0].imshow(sino_phase[A // 2, :, :], **kwph)
87 | axes[0].set_xlabel("detector x")
88 | axes[0].set_ylabel("detector y")
89 |
90 | axes[1].set_title("sinogram slice")
91 | axes[1].imshow(sino_phase[:, :, sino.shape[2] // 2],
92 | aspect=sino.shape[1] / sino.shape[0], **kwph)
93 | axes[1].set_xlabel("detector y")
94 | axes[1].set_ylabel("angle [rad]")
95 | # set y ticks for sinogram
96 | labels = np.linspace(0, 2 * np.pi, len(axes[1].get_yticks()))
97 | labels = ["{:.2f}".format(i) for i in labels]
98 | axes[1].set_yticks(np.linspace(0, len(angles), len(labels)))
99 | axes[1].set_yticklabels(labels)
100 |
101 | axes[2].set_title("tilt correction (nucleolus)")
102 | rimap = axes[2].imshow(n_tilt[int(sx / 2 + 2 * cfg["res"])].real, **kwri)
103 | axes[2].set_xlabel("x")
104 | axes[2].set_ylabel("y")
105 |
106 | # color bars
107 | cbkwargs = {"fraction": 0.045,
108 | "format": "%.3f"}
109 | plt.colorbar(phmap, ax=axes[0], **cbkwargs)
110 | plt.colorbar(phmap, ax=axes[1], **cbkwargs)
111 | plt.colorbar(rimap, ax=axes[2], **cbkwargs)
112 |
113 | plt.tight_layout()
114 | plt.show()
115 |
--------------------------------------------------------------------------------
/examples/backprop_from_mie_2d_cylinder_offcenter.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RI-imaging/ODTbrain/f7bb8b792bad8ae78ea885758a801c52dfea44ad/examples/backprop_from_mie_2d_cylinder_offcenter.jpg
--------------------------------------------------------------------------------
/examples/backprop_from_mie_2d_cylinder_offcenter.py:
--------------------------------------------------------------------------------
1 | """Mie off-center cylinder
2 |
3 | The *in silico* data set was created with the
4 | softare `miefield `_.
5 | The data are 1D projections of an off-center cylinder of constant
6 | refractive index. The Born approximation is error-prone due to
7 | a relatively large radius of the cylinder (30 wavelengths) and
8 | a refractive index difference of 0.006 between cylinder and
9 | surrounding medium. The reconstruction of the refractive index
10 | with the Rytov approximation is in good agreement with the
11 | input data. When only 50 projections are used for the reconstruction,
12 | artifacts appear. These vanish when more projections are used for
13 | the reconstruction.
14 | """
15 | import matplotlib.pylab as plt
16 | import numpy as np
17 |
18 | import odtbrain as odt
19 |
20 | from example_helper import load_data
21 |
22 |
23 | # simulation data
24 | sino, angles, cfg = load_data("mie_2d_noncentered_cylinder_A250_R2.zip",
25 | f_sino_imag="sino_imag.txt",
26 | f_sino_real="sino_real.txt",
27 | f_angles="mie_angles.txt",
28 | f_info="mie_info.txt")
29 | A, size = sino.shape
30 |
31 | # background sinogram computed with Mie theory
32 | # miefield.GetSinogramCylinderRotation(radius, nmed, nmed, lD, lC, size, A,res)
33 | u0 = load_data("mie_2d_noncentered_cylinder_A250_R2.zip",
34 | f_sino_imag="u0_imag.txt",
35 | f_sino_real="u0_real.txt")
36 | # create 2d array
37 | u0 = np.tile(u0, size).reshape(A, size).transpose()
38 |
39 | # background field necessary to compute initial born field
40 | # u0_single = mie.GetFieldCylinder(radius, nmed, nmed, lD, size, res)
41 | u0_single = load_data("mie_2d_noncentered_cylinder_A250_R2.zip",
42 | f_sino_imag="u0_single_imag.txt",
43 | f_sino_real="u0_single_real.txt")
44 |
45 | print("Example: Backpropagation from 2D Mie simulations")
46 | print("Refractive index of medium:", cfg["nmed"])
47 | print("Measurement position from object center:", cfg["lD"])
48 | print("Wavelength sampling:", cfg["res"])
49 | print("Performing backpropagation.")
50 |
51 | # Set measurement parameters
52 | # Compute scattered field from cylinder
53 | radius = cfg["radius"] # wavelengths
54 | nmed = cfg["nmed"]
55 | ncyl = cfg["ncyl"]
56 |
57 | lD = cfg["lD"] # measurement distance in wavelengths
58 | lC = cfg["lC"] # displacement from center of image
59 | size = cfg["size"]
60 | res = cfg["res"] # px/wavelengths
61 | A = cfg["A"] # number of projections
62 |
63 | x = np.arange(size) - size / 2
64 | X, Y = np.meshgrid(x, x)
65 | rad_px = radius * res
66 | phantom = np.array(((Y - lC * res)**2 + X**2) < rad_px**2,
67 | dtype=float) * (ncyl - nmed) + nmed
68 |
69 | # Born
70 | u_sinB = (sino / u0 * u0_single - u0_single) # fake born
71 | fB = odt.backpropagate_2d(u_sinB, angles, res, nmed, lD * res)
72 | nB = odt.odt_to_ri(fB, res, nmed)
73 |
74 | # Rytov
75 | u_sinR = odt.sinogram_as_rytov(sino / u0)
76 | fR = odt.backpropagate_2d(u_sinR, angles, res, nmed, lD * res)
77 | nR = odt.odt_to_ri(fR, res, nmed)
78 |
79 | # Rytov 50
80 | u_sinR50 = odt.sinogram_as_rytov((sino / u0)[::5, :])
81 | fR50 = odt.backpropagate_2d(u_sinR50, angles[::5], res, nmed, lD * res)
82 | nR50 = odt.odt_to_ri(fR50, res, nmed)
83 |
84 | # Plot sinogram phase and amplitude
85 | ph = odt.sinogram_as_radon(sino / u0)
86 |
87 | am = np.abs(sino / u0)
88 |
89 | # prepare plot
90 | vmin = np.min(np.array([phantom, nB.real, nR50.real, nR.real]))
91 | vmax = np.max(np.array([phantom, nB.real, nR50.real, nR.real]))
92 |
93 | fig, axes = plt.subplots(2, 3, figsize=(8, 5))
94 | axes = np.array(axes).flatten()
95 |
96 | phantommap = axes[0].imshow(phantom, vmin=vmin, vmax=vmax)
97 | axes[0].set_title("phantom \n(non-centered cylinder)")
98 |
99 | amplmap = axes[1].imshow(am, cmap="gray")
100 | axes[1].set_title("amplitude sinogram \n(background-corrected)")
101 |
102 | phasemap = axes[2].imshow(ph, cmap="coolwarm")
103 | axes[2].set_title("phase sinogram [rad] \n(background-corrected)")
104 |
105 | axes[3].imshow(nB.real, vmin=vmin, vmax=vmax)
106 | axes[3].set_title("reconstruction (Born) \n(250 projections)")
107 |
108 | axes[4].imshow(nR50.real, vmin=vmin, vmax=vmax)
109 | axes[4].set_title("reconstruction (Rytov) \n(50 projections)")
110 |
111 | axes[5].imshow(nR.real, vmin=vmin, vmax=vmax)
112 | axes[5].set_title("reconstruction (Rytov) \n(250 projections)")
113 |
114 | # color bars
115 | cbkwargs = {"fraction": 0.045}
116 | plt.colorbar(phantommap, ax=axes[0], **cbkwargs)
117 | plt.colorbar(amplmap, ax=axes[1], **cbkwargs)
118 | plt.colorbar(phasemap, ax=axes[2], **cbkwargs)
119 | plt.colorbar(phantommap, ax=axes[3], **cbkwargs)
120 | plt.colorbar(phantommap, ax=axes[4], **cbkwargs)
121 | plt.colorbar(phantommap, ax=axes[5], **cbkwargs)
122 |
123 | plt.tight_layout()
124 | plt.show()
125 |
--------------------------------------------------------------------------------
/examples/backprop_from_mie_2d_incomplete_coverage.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RI-imaging/ODTbrain/f7bb8b792bad8ae78ea885758a801c52dfea44ad/examples/backprop_from_mie_2d_incomplete_coverage.jpg
--------------------------------------------------------------------------------
/examples/backprop_from_mie_2d_incomplete_coverage.py:
--------------------------------------------------------------------------------
1 | """Mie cylinder with incomplete angular coverage
2 |
3 | This example illustrates how the backpropagation algorithm of ODTbrain
4 | handles incomplete angular coverage. All examples use 100 projections
5 | at 100%, 60%, and 40% total angular coverage. The keyword argument
6 | `weight_angles` that invokes angular weighting is set to `True` by
7 | default. The *in silico* data set was created with the
8 | softare `miefield `_.
9 | The data are 1D projections of a non-centered cylinder of constant
10 | refractive index 1.339 embedded in water with refractive index 1.333.
11 | The first column shows the used sinograms (missing angles are displayed
12 | as zeros) that were created from the original sinogram with 250
13 | projections. The second column shows the reconstruction without angular
14 | weights and the third column shows the reconstruction with angular
15 | weights. The keyword argument `weight_angles` was introduced in version
16 | 0.1.1.
17 |
18 | A 180 degree coverage results in a good reconstruction of the object.
19 | Angular weighting as implemented in the backpropagation algorithm
20 | of ODTbrain automatically addresses uneven and incomplete angular
21 | coverage.
22 | """
23 | import matplotlib.pylab as plt
24 | import numpy as np
25 |
26 | import odtbrain as odt
27 |
28 | from example_helper import load_data
29 |
30 | sino, angles, cfg = load_data("mie_2d_noncentered_cylinder_A250_R2.zip",
31 | f_angles="mie_angles.txt",
32 | f_sino_real="sino_real.txt",
33 | f_sino_imag="sino_imag.txt",
34 | f_info="mie_info.txt")
35 | A, size = sino.shape
36 |
37 | # background sinogram computed with Mie theory
38 | # miefield.GetSinogramCylinderRotation(radius, nmed, nmed, lD, lC, size, A,res)
39 | u0 = load_data("mie_2d_noncentered_cylinder_A250_R2.zip",
40 | f_sino_imag="u0_imag.txt",
41 | f_sino_real="u0_real.txt")
42 | # create 2d array
43 | u0 = np.tile(u0, size).reshape(A, size).transpose()
44 |
45 | # background field necessary to compute initial born field
46 | # u0_single = mie.GetFieldCylinder(radius, nmed, nmed, lD, size, res)
47 | u0_single = load_data("mie_2d_noncentered_cylinder_A250_R2.zip",
48 | f_sino_imag="u0_single_imag.txt",
49 | f_sino_real="u0_single_real.txt")
50 |
51 | print("Example: Backpropagation from 2D FDTD simulations")
52 | print("Refractive index of medium:", cfg["nmed"])
53 | print("Measurement position from object center:", cfg["lD"])
54 | print("Wavelength sampling:", cfg["res"])
55 | print("Performing backpropagation.")
56 |
57 | # Set measurement parameters
58 | # Compute scattered field from cylinder
59 | radius = cfg["radius"] # wavelengths
60 | nmed = cfg["nmed"]
61 | ncyl = cfg["ncyl"]
62 |
63 | lD = cfg["lD"] # measurement distance in wavelengths
64 | lC = cfg["lC"] # displacement from center of image
65 | size = cfg["size"]
66 | res = cfg["res"] # px/wavelengths
67 | A = cfg["A"] # number of projections
68 |
69 | x = np.arange(size) - size / 2.0
70 | X, Y = np.meshgrid(x, x)
71 | rad_px = radius * res
72 | phantom = np.array(((Y - lC * res)**2 + X**2) < rad_px **
73 | 2, dtype=float) * (ncyl - nmed) + nmed
74 |
75 | u_sinR = odt.sinogram_as_rytov(sino / u0)
76 |
77 | # Rytov 100 projections evenly distributed
78 | removeeven = np.argsort(angles % .002)[:150]
79 | angleseven = np.delete(angles, removeeven, axis=0)
80 | u_sinReven = np.delete(u_sinR, removeeven, axis=0)
81 | pheven = odt.sinogram_as_radon(sino / u0)
82 | pheven[removeeven] = 0
83 |
84 | fReven = odt.backpropagate_2d(u_sinReven, angleseven, res, nmed, lD * res)
85 | nReven = odt.odt_to_ri(fReven, res, nmed)
86 | fRevennw = odt.backpropagate_2d(
87 | u_sinReven, angleseven, res, nmed, lD * res, weight_angles=False)
88 | nRevennw = odt.odt_to_ri(fRevennw, res, nmed)
89 |
90 | # Rytov 100 projections more than 180
91 | removemiss = 249 - \
92 | np.concatenate((np.arange(100), 100 + np.arange(150)[::3]))
93 | anglesmiss = np.delete(angles, removemiss, axis=0)
94 | u_sinRmiss = np.delete(u_sinR, removemiss, axis=0)
95 | phmiss = odt.sinogram_as_radon(sino / u0)
96 | phmiss[removemiss] = 0
97 |
98 | fRmiss = odt.backpropagate_2d(u_sinRmiss, anglesmiss, res, nmed, lD * res)
99 | nRmiss = odt.odt_to_ri(fRmiss, res, nmed)
100 | fRmissnw = odt.backpropagate_2d(
101 | u_sinRmiss, anglesmiss, res, nmed, lD * res, weight_angles=False)
102 | nRmissnw = odt.odt_to_ri(fRmissnw, res, nmed)
103 |
104 | # Rytov 100 projections less than 180
105 | removebad = 249 - np.arange(150)
106 | anglesbad = np.delete(angles, removebad, axis=0)
107 | u_sinRbad = np.delete(u_sinR, removebad, axis=0)
108 | phbad = odt.sinogram_as_radon(sino / u0)
109 | phbad[removebad] = 0
110 |
111 | fRbad = odt.backpropagate_2d(u_sinRbad, anglesbad, res, nmed, lD * res)
112 | nRbad = odt.odt_to_ri(fRbad, res, nmed)
113 | fRbadnw = odt.backpropagate_2d(
114 | u_sinRbad, anglesbad, res, nmed, lD * res, weight_angles=False)
115 | nRbadnw = odt.odt_to_ri(fRbadnw, res, nmed)
116 |
117 | # prepare plot
118 | kw_ri = {"vmin": np.min(np.array([phantom, nRmiss.real, nReven.real])),
119 | "vmax": np.max(np.array([phantom, nRmiss.real, nReven.real]))}
120 |
121 | kw_ph = {"vmin": np.min(np.array([pheven, phmiss])),
122 | "vmax": np.max(np.array([pheven, phmiss])),
123 | "cmap": "coolwarm",
124 | "interpolation": "none"}
125 |
126 | fig, axes = plt.subplots(3, 3, figsize=(8, 6.5))
127 |
128 | axes[0, 0].set_title("100% coverage ({} proj.)".format(angleseven.shape[0]))
129 | phmap = axes[0, 0].imshow(pheven, **kw_ph)
130 |
131 | axes[0, 1].set_title("RI without angular weights")
132 | rimap = axes[0, 1].imshow(nRevennw.real, **kw_ri)
133 |
134 | axes[0, 2].set_title("RI with angular weights")
135 | rimap = axes[0, 2].imshow(nReven.real, **kw_ri)
136 |
137 | axes[1, 0].set_title("60% coverage ({} proj.)".format(anglesmiss.shape[0]))
138 | axes[1, 0].imshow(phmiss, **kw_ph)
139 |
140 | axes[1, 1].set_title("RI without angular weights")
141 | axes[1, 1].imshow(nRmissnw.real, **kw_ri)
142 |
143 | axes[1, 2].set_title("RI with angular weights")
144 | axes[1, 2].imshow(nRmiss.real, **kw_ri)
145 |
146 | axes[2, 0].set_title("40% coverage ({} proj.)".format(anglesbad.shape[0]))
147 | axes[2, 0].imshow(phbad, **kw_ph)
148 |
149 | axes[2, 1].set_title("RI without angular weights")
150 | axes[2, 1].imshow(nRbadnw.real, **kw_ri)
151 |
152 | axes[2, 2].set_title("RI with angular weights")
153 | axes[2, 2].imshow(nRbad.real, **kw_ri)
154 |
155 | # color bars
156 | cbkwargs = {"fraction": 0.045,
157 | "format": "%.3f"}
158 | plt.colorbar(phmap, ax=axes[0, 0], **cbkwargs)
159 | plt.colorbar(phmap, ax=axes[1, 0], **cbkwargs)
160 | plt.colorbar(phmap, ax=axes[2, 0], **cbkwargs)
161 | plt.colorbar(rimap, ax=axes[0, 1], **cbkwargs)
162 | plt.colorbar(rimap, ax=axes[1, 1], **cbkwargs)
163 | plt.colorbar(rimap, ax=axes[2, 1], **cbkwargs)
164 | plt.colorbar(rimap, ax=axes[0, 2], **cbkwargs)
165 | plt.colorbar(rimap, ax=axes[1, 2], **cbkwargs)
166 | plt.colorbar(rimap, ax=axes[2, 2], **cbkwargs)
167 |
168 | plt.tight_layout()
169 | plt.show()
170 |
--------------------------------------------------------------------------------
/examples/backprop_from_mie_2d_weights_angles.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RI-imaging/ODTbrain/f7bb8b792bad8ae78ea885758a801c52dfea44ad/examples/backprop_from_mie_2d_weights_angles.jpg
--------------------------------------------------------------------------------
/examples/backprop_from_mie_2d_weights_angles.py:
--------------------------------------------------------------------------------
1 | """Mie cylinder with unevenly spaced angles
2 |
3 | Angular weighting can significantly improve reconstruction quality
4 | when the angular projections are sampled at non-equidistant
5 | intervals :cite:`Tam1981`. The *in silico* data set was created with
6 | the softare `miefield `_.
7 | The data are 1D projections of a non-centered cylinder of constant
8 | refractive index 1.339 embedded in water with refractive index 1.333.
9 | The first column shows the used sinograms (missing angles are displayed
10 | as zeros) that were created from the original sinogram with 250
11 | projections. The second column shows the reconstruction without angular
12 | weights and the third column shows the reconstruction with angular
13 | weights. The keyword argument `weight_angles` was introduced in version
14 | 0.1.1.
15 | """
16 | import matplotlib.pylab as plt
17 | import numpy as np
18 | from skimage.restoration import unwrap_phase
19 |
20 | import odtbrain as odt
21 |
22 | from example_helper import load_data
23 |
24 |
25 | sino, angles, cfg = load_data("mie_2d_noncentered_cylinder_A250_R2.zip",
26 | f_angles="mie_angles.txt",
27 | f_sino_real="sino_real.txt",
28 | f_sino_imag="sino_imag.txt",
29 | f_info="mie_info.txt")
30 | A, size = sino.shape
31 |
32 | # background sinogram computed with Mie theory
33 | # miefield.GetSinogramCylinderRotation(radius, nmed, nmed, lD, lC, size, A,res)
34 | u0 = load_data("mie_2d_noncentered_cylinder_A250_R2.zip",
35 | f_sino_imag="u0_imag.txt",
36 | f_sino_real="u0_real.txt")
37 | # create 2d array
38 | u0 = np.tile(u0, size).reshape(A, size).transpose()
39 |
40 | # background field necessary to compute initial born field
41 | # u0_single = mie.GetFieldCylinder(radius, nmed, nmed, lD, size, res)
42 | u0_single = load_data("mie_2d_noncentered_cylinder_A250_R2.zip",
43 | f_sino_imag="u0_single_imag.txt",
44 | f_sino_real="u0_single_real.txt")
45 |
46 |
47 | print("Example: Backpropagation from 2D FDTD simulations")
48 | print("Refractive index of medium:", cfg["nmed"])
49 | print("Measurement position from object center:", cfg["lD"])
50 | print("Wavelength sampling:", cfg["res"])
51 | print("Performing backpropagation.")
52 |
53 | # Set measurement parameters
54 | # Compute scattered field from cylinder
55 | radius = cfg["radius"] # wavelengths
56 | nmed = cfg["nmed"]
57 | ncyl = cfg["ncyl"]
58 |
59 | lD = cfg["lD"] # measurement distance in wavelengths
60 | lC = cfg["lC"] # displacement from center of image
61 | size = cfg["size"]
62 | res = cfg["res"] # px/wavelengths
63 | A = cfg["A"] # number of projections
64 |
65 | x = np.arange(size) - size / 2.0
66 | X, Y = np.meshgrid(x, x)
67 | rad_px = radius * res
68 | phantom = np.array(((Y - lC * res)**2 + X**2) < rad_px **
69 | 2, dtype=float) * (ncyl - nmed) + nmed
70 |
71 | u_sinR = odt.sinogram_as_rytov(sino / u0)
72 |
73 | # Rytov 200 projections
74 | # remove 50 projections from total of 250 projections
75 | remove200 = np.argsort(angles % .0002)[:50]
76 | angles200 = np.delete(angles, remove200, axis=0)
77 | u_sinR200 = np.delete(u_sinR, remove200, axis=0)
78 | ph200 = unwrap_phase(np.angle(sino / u0))
79 | ph200[remove200] = 0
80 |
81 | fR200 = odt.backpropagate_2d(u_sinR200, angles200, res, nmed, lD*res)
82 | nR200 = odt.odt_to_ri(fR200, res, nmed)
83 | fR200nw = odt.backpropagate_2d(u_sinR200, angles200, res, nmed, lD*res,
84 | weight_angles=False)
85 | nR200nw = odt.odt_to_ri(fR200nw, res, nmed)
86 |
87 | # Rytov 50 projections
88 | remove50 = np.argsort(angles % .0002)[:200]
89 | angles50 = np.delete(angles, remove50, axis=0)
90 | u_sinR50 = np.delete(u_sinR, remove50, axis=0)
91 | ph50 = unwrap_phase(np.angle(sino / u0))
92 | ph50[remove50] = 0
93 |
94 | fR50 = odt.backpropagate_2d(u_sinR50, angles50, res, nmed, lD*res)
95 | nR50 = odt.odt_to_ri(fR50, res, nmed)
96 | fR50nw = odt.backpropagate_2d(u_sinR50, angles50, res, nmed, lD*res,
97 | weight_angles=False)
98 | nR50nw = odt.odt_to_ri(fR50nw, res, nmed)
99 |
100 | # prepare plot
101 | kw_ri = {"vmin": 1.330,
102 | "vmax": 1.340}
103 |
104 | kw_ph = {"vmin": np.min(np.array([ph200, ph50])),
105 | "vmax": np.max(np.array([ph200, ph50])),
106 | "cmap": "coolwarm",
107 | "interpolation": "none"}
108 |
109 | fig, axes = plt.subplots(2, 3, figsize=(8, 4))
110 | axes = np.array(axes).flatten()
111 |
112 | phmap = axes[0].imshow(ph200, **kw_ph)
113 | axes[0].set_title("Phase sinogram (200 proj.)")
114 |
115 | rimap = axes[1].imshow(nR200nw.real, **kw_ri)
116 | axes[1].set_title("RI without angular weights")
117 |
118 | axes[2].imshow(nR200.real, **kw_ri)
119 | axes[2].set_title("RI with angular weights")
120 |
121 | axes[3].imshow(ph50, **kw_ph)
122 | axes[3].set_title("Phase sinogram (50 proj.)")
123 |
124 | axes[4].imshow(nR50nw.real, **kw_ri)
125 | axes[4].set_title("RI without angular weights")
126 |
127 | axes[5].imshow(nR50.real, **kw_ri)
128 | axes[5].set_title("RI with angular weights")
129 |
130 | # color bars
131 | cbkwargs = {"fraction": 0.045,
132 | "format": "%.3f"}
133 | plt.colorbar(phmap, ax=axes[0], **cbkwargs)
134 | plt.colorbar(phmap, ax=axes[3], **cbkwargs)
135 | plt.colorbar(rimap, ax=axes[1], **cbkwargs)
136 | plt.colorbar(rimap, ax=axes[2], **cbkwargs)
137 | plt.colorbar(rimap, ax=axes[5], **cbkwargs)
138 | plt.colorbar(rimap, ax=axes[4], **cbkwargs)
139 |
140 | plt.tight_layout()
141 | plt.show()
142 |
--------------------------------------------------------------------------------
/examples/backprop_from_mie_3d_sphere.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RI-imaging/ODTbrain/f7bb8b792bad8ae78ea885758a801c52dfea44ad/examples/backprop_from_mie_3d_sphere.jpg
--------------------------------------------------------------------------------
/examples/backprop_from_mie_3d_sphere.py:
--------------------------------------------------------------------------------
1 | r"""Mie sphere
2 | The *in silico* data set was created with the Mie calculation software
3 | `GMM-field`_. The data consist of a two-dimensional projection of a
4 | sphere with radius :math:`R=14\lambda`,
5 | refractive index :math:`n_\mathrm{sph}=1.006`,
6 | embedded in a medium of refractive index :math:`n_\mathrm{med}=1.0`
7 | onto a detector which is :math:`l_\mathrm{D} = 20\lambda` away from the
8 | center of the sphere.
9 |
10 | The package :mod:`nrefocus` must be used to numerically focus
11 | the detected field prior to the 3D backpropagation with ODTbrain.
12 | In :func:`odtbrain.backpropagate_3d`, the parameter `lD` must
13 | be set to zero (:math:`l_\mathrm{D}=0`).
14 |
15 | The figure shows the 3D reconstruction from Mie simulations of a
16 | perfect sphere using 200 projections. Missing angle artifacts are
17 | visible along the :math:`y`-axis due to the :math:`2\pi`-only
18 | coverage in 3D Fourier space.
19 |
20 | .. _`GMM-field`: https://code.google.com/p/scatterlib/wiki/Nearfield
21 | """
22 | import matplotlib.pylab as plt
23 | import nrefocus
24 | import numpy as np
25 |
26 | import odtbrain as odt
27 |
28 | from example_helper import load_data
29 |
30 |
31 | if __name__ == "__main__":
32 | Ex, cfg = load_data("mie_3d_sphere_field.zip",
33 | f_sino_imag="mie_sphere_imag.txt",
34 | f_sino_real="mie_sphere_real.txt",
35 | f_info="mie_info.txt")
36 |
37 | # Manually set number of angles:
38 | A = 200
39 |
40 | print("Example: Backpropagation from 3D Mie scattering")
41 | print("Refractive index of medium:", cfg["nm"])
42 | print("Measurement position from object center:", cfg["lD"])
43 | print("Wavelength sampling:", cfg["res"])
44 | print("Number of angles for reconstruction:", A)
45 | print("Performing backpropagation.")
46 |
47 | # Reconstruction angles
48 | angles = np.linspace(0, 2 * np.pi, A, endpoint=False)
49 |
50 | # Perform focusing
51 | Ex = nrefocus.refocus(Ex,
52 | d=-cfg["lD"]*cfg["res"],
53 | nm=cfg["nm"],
54 | res=cfg["res"],
55 | )
56 |
57 | # Create sinogram
58 | u_sin = np.tile(Ex.flat, A).reshape(A, int(cfg["size"]), int(cfg["size"]))
59 |
60 | # Apply the Rytov approximation
61 | u_sinR = odt.sinogram_as_rytov(u_sin)
62 |
63 | # Backpropagation
64 | fR = odt.backpropagate_3d(uSin=u_sinR,
65 | angles=angles,
66 | res=cfg["res"],
67 | nm=cfg["nm"],
68 | lD=0,
69 | padfac=2.1,
70 | save_memory=True)
71 |
72 | # RI computation
73 | nR = odt.odt_to_ri(fR, cfg["res"], cfg["nm"])
74 |
75 | # Plotting
76 | fig, axes = plt.subplots(2, 3, figsize=(8, 5))
77 | axes = np.array(axes).flatten()
78 | # field
79 | axes[0].set_title("Mie field phase")
80 | axes[0].set_xlabel("detector x")
81 | axes[0].set_ylabel("detector y")
82 | axes[0].imshow(np.angle(Ex), cmap="coolwarm")
83 | axes[1].set_title("Mie field amplitude")
84 | axes[1].set_xlabel("detector x")
85 | axes[1].set_ylabel("detector y")
86 | axes[1].imshow(np.abs(Ex), cmap="gray")
87 |
88 | # line plot
89 | axes[2].set_title("line plots")
90 | axes[2].set_xlabel("distance [px]")
91 | axes[2].set_ylabel("real refractive index")
92 | center = int(cfg["size"] / 2)
93 | x = np.arange(cfg["size"]) - center
94 | axes[2].plot(x, nR[:, center, center].real, label="x")
95 | axes[2].plot(x, nR[center, center, :].real, label="z")
96 | axes[2].plot(x, nR[center, :, center].real, label="y")
97 | axes[2].legend(loc=4)
98 | axes[2].set_xlim((-center, center))
99 | dn = abs(cfg["nsph"] - cfg["nm"])
100 | axes[2].set_ylim((cfg["nm"] - dn / 10, cfg["nsph"] + dn))
101 | axes[2].ticklabel_format(useOffset=False)
102 |
103 | # cross sections
104 | axes[3].set_title("RI reconstruction\nsection at x=0")
105 | axes[3].set_xlabel("z")
106 | axes[3].set_ylabel("y")
107 | axes[3].imshow(nR[center, :, :].real)
108 |
109 | axes[4].set_title("RI reconstruction\nsection at y=0")
110 | axes[4].set_xlabel("x")
111 | axes[4].set_ylabel("z")
112 | axes[4].imshow(nR[:, center, :].real)
113 |
114 | axes[5].set_title("RI reconstruction\nsection at z=0")
115 | axes[5].set_xlabel("y")
116 | axes[5].set_ylabel("x")
117 | axes[5].imshow(nR[:, :, center].real)
118 |
119 | plt.tight_layout()
120 | plt.show()
121 |
--------------------------------------------------------------------------------
/examples/backprop_from_qlsi_3d_hl60.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RI-imaging/ODTbrain/f7bb8b792bad8ae78ea885758a801c52dfea44ad/examples/backprop_from_qlsi_3d_hl60.jpg
--------------------------------------------------------------------------------
/examples/backprop_from_qlsi_3d_hl60.py:
--------------------------------------------------------------------------------
1 | """HL60 cell
2 |
3 | The quantitative phase data of an HL60 S/4 cell were recorded using
4 | :abbr:`QLSI (quadri-wave lateral shearing interferometry)`.
5 | The original dataset was used in a previous publication
6 | :cite:`Schuermann2017` to illustrate the capabilities of combined
7 | fluorescence and refractive index tomography.
8 |
9 | The example data set is already aligned and background-corrected as
10 | described in the original publication and the fluorescence data are
11 | not included. The lzma-archive contains the sinogram data stored in
12 | the :ref:`qpimage ` file format and the rotational
13 | positions of each sinogram image as a text file.
14 |
15 | The figure reproduces parts of figure 4 of the original manuscript.
16 | Note that minor deviations from the original figure can be attributed
17 | to the strong compression (scale offset filter) and due to the fact
18 | that the original sinogram images were cropped from 196x196 px to
19 | 140x140 px (which in particular affects the background-part of the
20 | refractive index histogram).
21 |
22 | The raw data is available
23 | `on figshare `
24 | (hl60_sinogram_qpi.h5).
25 | """
26 | import pathlib
27 | import tarfile
28 | import tempfile
29 |
30 | import matplotlib.pylab as plt
31 | import numpy as np
32 | import odtbrain as odt
33 | import qpimage
34 |
35 | from example_helper import get_file, extract_lzma
36 |
37 |
38 | if __name__ == "__main__":
39 | # ascertain the data
40 | path = get_file("qlsi_3d_hl60-cell_A140.tar.lzma")
41 | tarf = extract_lzma(path)
42 | tdir = tempfile.mkdtemp(prefix="odtbrain_example_")
43 |
44 | with tarfile.open(tarf) as tf:
45 | tf.extract("series.h5", path=tdir)
46 | angles = np.loadtxt(tf.extractfile("angles.txt"))
47 |
48 | # extract the complex field sinogram from the qpimage series data
49 | h5file = pathlib.Path(tdir) / "series.h5"
50 | with qpimage.QPSeries(h5file=h5file, h5mode="r") as qps:
51 | qp0 = qps[0]
52 | meta = qp0.meta
53 | sino = np.zeros((len(qps), qp0.shape[0], qp0.shape[1]),
54 | dtype=np.complex)
55 | for ii in range(len(qps)):
56 | sino[ii] = qps[ii].field
57 |
58 | # perform backpropagation
59 | u_sinR = odt.sinogram_as_rytov(sino)
60 | res = meta["wavelength"] / meta["pixel size"]
61 | nm = meta["medium index"]
62 |
63 | fR = odt.backpropagate_3d(uSin=u_sinR,
64 | angles=angles,
65 | res=res,
66 | nm=nm)
67 |
68 | ri = odt.odt_to_ri(fR, res, nm)
69 |
70 | # plot results
71 | ext = meta["pixel size"] * 1e6 * 70
72 | kw = {"vmin": ri.real.min(),
73 | "vmax": ri.real.max(),
74 | "extent": [-ext, ext, -ext, ext]}
75 | fig, axes = plt.subplots(1, 3, figsize=(8, 2.5))
76 | axes[0].imshow(ri[70, :, :].real, **kw)
77 | axes[0].set_xlabel("x [µm]")
78 | axes[0].set_ylabel("y [µm]")
79 |
80 | x = np.linspace(-ext, ext, 140)
81 | axes[1].plot(x, ri[70, :, 70], label="line plot x=0")
82 | axes[1].plot(x, ri[70, 70, :], label="line plot y=0")
83 | axes[1].set_xlabel("distance from center [µm]")
84 | axes[1].set_ylabel("refractive index")
85 | axes[1].legend()
86 |
87 | hist, xh = np.histogram(ri.real, bins=100)
88 | axes[2].plot(xh[1:], hist)
89 | axes[2].set_yscale('log')
90 | axes[2].set_xlabel("refractive index")
91 | axes[2].set_ylabel("histogram")
92 |
93 | plt.tight_layout()
94 | plt.show()
95 |
--------------------------------------------------------------------------------
/examples/backprop_from_rytov_3d_phantom_apple.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RI-imaging/ODTbrain/f7bb8b792bad8ae78ea885758a801c52dfea44ad/examples/backprop_from_rytov_3d_phantom_apple.jpg
--------------------------------------------------------------------------------
/examples/backprop_from_rytov_3d_phantom_apple.py:
--------------------------------------------------------------------------------
1 | """Missing apple core correction
2 |
3 | The missing apple core :cite:`Vertu2009` is a phenomenon in diffraction
4 | tomography that is a result of the fact the the Fourier space is not
5 | filled completely when the sample is rotated only about a single axis.
6 | The resulting artifacts include ringing and blurring in the
7 | reconstruction parallel to the original rotation axis. By enforcing
8 | constraints (refractive index real-valued and larger than the
9 | surrounding medium), these artifacts can be attenuated.
10 |
11 | This example generates an artificial sinogram using the Python
12 | library :ref:`cellsino ` (The example parameters
13 | are reused from :ref:`this example `).
14 | The sinogram is then reconstructed with the backpropagation algorithm
15 | and the missing apple core correction is applied.
16 |
17 | .. note::
18 | The missing apple core correction :func:`odtbrain.apple.correct`
19 | was implemented in version 0.3.0 and is thus not used in the
20 | older examples.
21 | """
22 | import matplotlib.pylab as plt
23 | import numpy as np
24 |
25 | import cellsino
26 | import odtbrain as odt
27 |
28 |
29 | if __name__ == "__main__":
30 | # number of sinogram angles
31 | num_ang = 160
32 | # sinogram acquisition angles
33 | angles = np.linspace(0, 2*np.pi, num_ang, endpoint=False)
34 | # detector grid size
35 | grid_size = (250, 250)
36 | # vacuum wavelength [m]
37 | wavelength = 550e-9
38 | # pixel size [m]
39 | pixel_size = 0.08e-6
40 | # refractive index of the surrounding medium
41 | medium_index = 1.335
42 |
43 | # initialize cell phantom
44 | phantom = cellsino.phantoms.SimpleCell()
45 |
46 | # initialize sinogram with geometric parameters
47 | sino = cellsino.Sinogram(phantom=phantom,
48 | wavelength=wavelength,
49 | pixel_size=pixel_size,
50 | grid_size=grid_size)
51 |
52 | # compute sinogram (field with Rytov approximation and fluorescence)
53 | sino = sino.compute(angles=angles, propagator="rytov", mode="field")
54 |
55 | # reconstruction of refractive index
56 | sino_rytov = odt.sinogram_as_rytov(sino)
57 | f = odt.backpropagate_3d(uSin=sino_rytov,
58 | angles=angles,
59 | res=wavelength/pixel_size,
60 | nm=medium_index)
61 |
62 | ri = odt.odt_to_ri(f=f,
63 | res=wavelength/pixel_size,
64 | nm=medium_index)
65 |
66 | # apple core correction
67 | fc = odt.apple.correct(f=f,
68 | res=wavelength/pixel_size,
69 | nm=medium_index,
70 | method="sh")
71 |
72 | ric = odt.odt_to_ri(f=fc,
73 | res=wavelength/pixel_size,
74 | nm=medium_index)
75 |
76 | # plotting
77 | idx = ri.shape[2] // 2
78 |
79 | # log-scaled power spectra
80 | ft = np.log(1 + np.abs(np.fft.fftshift(np.fft.fftn(ri))))
81 | ftc = np.log(1 + np.abs(np.fft.fftshift(np.fft.fftn(ric))))
82 |
83 | plt.figure(figsize=(7, 5.5))
84 |
85 | plotkwri = {"vmax": ri.real.max(),
86 | "vmin": ri.real.min(),
87 | "interpolation": "none",
88 | }
89 |
90 | plotkwft = {"vmax": ft.max(),
91 | "vmin": 0,
92 | "interpolation": "none",
93 | }
94 |
95 | ax1 = plt.subplot(221, title="plain refractive index")
96 | mapper = ax1.imshow(ri[:, :, idx].real, **plotkwri)
97 | plt.colorbar(mappable=mapper, ax=ax1)
98 |
99 | ax2 = plt.subplot(222, title="corrected refractive index")
100 | mapper = ax2.imshow(ric[:, :, idx].real, **plotkwri)
101 | plt.colorbar(mappable=mapper, ax=ax2)
102 |
103 | ax3 = plt.subplot(223, title="Fourier space (visible apple core)")
104 | mapper = ax3.imshow(ft[:, :, idx], **plotkwft)
105 | plt.colorbar(mappable=mapper, ax=ax3)
106 |
107 | ax4 = plt.subplot(224, title="Fourier space (with correction)")
108 | mapper = ax4.imshow(ftc[:, :, idx], **plotkwft)
109 | plt.colorbar(mappable=mapper, ax=ax4)
110 |
111 | plt.tight_layout()
112 | plt.show()
113 |
--------------------------------------------------------------------------------
/examples/data/fdtd_2d_sino_A100_R13.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RI-imaging/ODTbrain/f7bb8b792bad8ae78ea885758a801c52dfea44ad/examples/data/fdtd_2d_sino_A100_R13.zip
--------------------------------------------------------------------------------
/examples/data/fdtd_3d_sino_A180_R6.500.tar.lzma:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RI-imaging/ODTbrain/f7bb8b792bad8ae78ea885758a801c52dfea44ad/examples/data/fdtd_3d_sino_A180_R6.500.tar.lzma
--------------------------------------------------------------------------------
/examples/data/fdtd_3d_sino_A220_R6.500_tiltyz0.2.tar.lzma:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RI-imaging/ODTbrain/f7bb8b792bad8ae78ea885758a801c52dfea44ad/examples/data/fdtd_3d_sino_A220_R6.500_tiltyz0.2.tar.lzma
--------------------------------------------------------------------------------
/examples/data/mie_2d_noncentered_cylinder_A250_R2.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RI-imaging/ODTbrain/f7bb8b792bad8ae78ea885758a801c52dfea44ad/examples/data/mie_2d_noncentered_cylinder_A250_R2.zip
--------------------------------------------------------------------------------
/examples/data/mie_3d_sphere_field.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RI-imaging/ODTbrain/f7bb8b792bad8ae78ea885758a801c52dfea44ad/examples/data/mie_3d_sphere_field.zip
--------------------------------------------------------------------------------
/examples/data/qlsi_3d_hl60-cell_A140.tar.lzma:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RI-imaging/ODTbrain/f7bb8b792bad8ae78ea885758a801c52dfea44ad/examples/data/qlsi_3d_hl60-cell_A140.tar.lzma
--------------------------------------------------------------------------------
/examples/example_helper.py:
--------------------------------------------------------------------------------
1 | """Miscellaneous methods for example data handling"""
2 | import lzma
3 | import os
4 | import pathlib
5 | import tarfile
6 | import tempfile
7 | import warnings
8 | import zipfile
9 |
10 | import numpy as np
11 |
12 |
13 | datapath = pathlib.Path(__file__).parent / "data"
14 | webloc = "https://github.com/RI-imaging/ODTbrain/raw/master/examples/data/"
15 |
16 |
17 | def dl_file(url, dest, chunk_size=6553):
18 | """Download `url` to `dest`"""
19 | import urllib3
20 | http = urllib3.PoolManager()
21 | r = http.request('GET', url, preload_content=False)
22 | with dest.open('wb') as out:
23 | while True:
24 | data = r.read(chunk_size)
25 | if data is None or len(data) == 0:
26 | break
27 | out.write(data)
28 | r.release_conn()
29 |
30 |
31 | def extract_lzma(path):
32 | """Extract an lzma file and return the temporary file name"""
33 | tlfile = pathlib.Path(path)
34 | # open lzma file
35 | with tlfile.open("rb") as td:
36 | data = lzma.decompress(td.read())
37 | # write temporary tar file
38 | fd, tmpname = tempfile.mkstemp(prefix="odt_ex_", suffix=".tar")
39 | with open(fd, "wb") as fo:
40 | fo.write(data)
41 | return tmpname
42 |
43 |
44 | def get_file(fname, datapath=datapath):
45 | """Return path of an example data file
46 |
47 | Return the full path to an example data file name.
48 | If the file does not exist in the `datapath` directory,
49 | tries to download it from the ODTbrain GitHub repository.
50 | """
51 | # download location
52 | datapath = pathlib.Path(datapath)
53 | datapath.mkdir(parents=True, exist_ok=True)
54 |
55 | dlfile = datapath / fname
56 | if not dlfile.exists():
57 | print("Attempting to download file {} from {} to {}.".
58 | format(fname, webloc, datapath))
59 | try:
60 | dl_file(url=webloc+fname, dest=dlfile)
61 | except BaseException:
62 | warnings.warn("Download failed: {}".format(fname))
63 | raise
64 | return dlfile
65 |
66 |
67 | def load_data(fname, **kwargs):
68 | """Load example data"""
69 | fname = get_file(fname)
70 | if fname.suffix == ".lzma":
71 | return load_tar_lzma_data(fname)
72 | elif fname.suffix == ".zip":
73 | return load_zip_data(fname, **kwargs)
74 |
75 |
76 | def load_tar_lzma_data(tlfile):
77 | """Load example sinogram data from a .tar.lzma file"""
78 | tmpname = extract_lzma(tlfile)
79 |
80 | # open tar file
81 | fields_real = []
82 | fields_imag = []
83 | phantom = []
84 | parms = {}
85 |
86 | with tarfile.open(tmpname, "r") as t:
87 | members = t.getmembers()
88 | members.sort(key=lambda x: x.name)
89 |
90 | for m in members:
91 | n = m.name
92 | f = t.extractfile(m)
93 | if n.startswith("fdtd_info"):
94 | for ln in f.readlines():
95 | ln = ln.decode()
96 | if ln.count("=") == 1:
97 | key, val = ln.split("=")
98 | parms[key.strip()] = float(val.strip())
99 | elif n.startswith("phantom"):
100 | phantom.append(np.loadtxt(f))
101 | elif n.startswith("field"):
102 | if n.endswith("imag.txt"):
103 | fields_imag.append(np.loadtxt(f))
104 | elif n.endswith("real.txt"):
105 | fields_real.append(np.loadtxt(f))
106 |
107 | try:
108 | os.remove(tmpname)
109 | except OSError:
110 | pass
111 |
112 | phantom = np.array(phantom)
113 | sino = np.array(fields_real) + 1j * np.array(fields_imag)
114 | angles = np.linspace(0, 2 * np.pi, sino.shape[0], endpoint=False)
115 |
116 | return sino, angles, phantom, parms
117 |
118 |
119 | def load_zip_data(zipname, f_sino_real, f_sino_imag,
120 | f_angles=None, f_phantom=None, f_info=None):
121 | """Load example sinogram data from a .zip file"""
122 | ret = []
123 | with zipfile.ZipFile(str(zipname)) as arc:
124 | sino_real = np.loadtxt(arc.open(f_sino_real))
125 | sino_imag = np.loadtxt(arc.open(f_sino_imag))
126 | sino = sino_real + 1j * sino_imag
127 | ret.append(sino)
128 | if f_angles:
129 | angles = np.loadtxt(arc.open(f_angles))
130 | ret.append(angles)
131 | if f_phantom:
132 | phantom = np.loadtxt(arc.open(f_phantom))
133 | ret.append(phantom)
134 | if f_info:
135 | with arc.open(f_info) as info:
136 | cfg = {}
137 | for li in info.readlines():
138 | li = li.decode()
139 | if li.count("=") == 1:
140 | key, val = li.split("=")
141 | cfg[key.strip()] = float(val.strip())
142 | ret.append(cfg)
143 | return ret
144 |
--------------------------------------------------------------------------------
/examples/generate_example_images.py:
--------------------------------------------------------------------------------
1 | import os
2 | import os.path as op
3 | import sys
4 |
5 | import matplotlib.pylab as plt
6 |
7 | thisdir = op.dirname(op.abspath(__file__))
8 | sys.path.insert(0, op.dirname(thisdir))
9 |
10 | DPI = 80
11 |
12 |
13 | if __name__ == "__main__":
14 | # Do not display example plots
15 | plt.show = lambda: None
16 | files = os.listdir(thisdir)
17 | files = [f for f in files if f.endswith(".py")]
18 | files = [f for f in files if not f == op.basename(__file__)]
19 | files = sorted([op.join(thisdir, f) for f in files])
20 |
21 | for f in files:
22 | fname = f[:-3] + "_.jpg"
23 | if not op.exists(fname):
24 | exec_str = open(f).read()
25 | if exec_str.count("plt.show()"):
26 | exec(exec_str)
27 | plt.savefig(fname, dpi=DPI)
28 | print("Image created: '{}'".format(fname))
29 | else:
30 | print("No image: '{}'".format(fname))
31 | else:
32 | print("Image skipped (already exists): '{}'".format(fname))
33 | plt.close()
34 |
--------------------------------------------------------------------------------
/examples/requirements.txt:
--------------------------------------------------------------------------------
1 | cellsino
2 | nrefocus
3 | qpimage
4 | scikit-image
5 |
--------------------------------------------------------------------------------
/misc/Readme.md:
--------------------------------------------------------------------------------
1 | ## C++ Meep simulation files
2 | I wrote these [meep](http://ab-initio.mit.edu/wiki/index.php?title=Meep)
3 | simulation files to obtain the in-silico sinogram data used in the
4 | [original manuscript](https://dx.doi.org/10.1186/s12859-015-0764-0).
5 |
6 | ### Before you start
7 | Note that at the time of writing this, there is a Python interface for meep
8 | available for Ubuntu/Debian. If you are starting from scratch, it might be
9 | worth working with Python instead of wrapping these C++ scripts.
10 |
11 | ### Simulation workflow
12 | You will need a working meep installation (I recommend the parallel version)
13 | and a C++ compiler.
14 |
15 | I designed the files such that it is easy to write a wrapper and modify
16 | certain aspects. For instance, you can modify the incident angle of
17 | illumination simply by replacing the line starting with `#define ACQUISITION_PHI`.
18 | You can then create multiple subdirectories, one for each angle, and compile
19 | and run the simulation:
20 |
21 | ```
22 | # compile (using GNU C++ Compiler on linux)
23 | g++ -malign-double meep_phantom_2d.cpp -o meep_phantom_2d.bin -lmeep_mpi -lhdf5 -lz -lgsl -lharminv -llapack -lcblas -latlas -lfftw3 -lm
24 | # run with 4 cores (requires meep compiled with Open MPI support)
25 | mpirun -n 4 ./meep_phantom_2d.bin
26 | ```
27 |
--------------------------------------------------------------------------------
/misc/meep_phantom_2d.cpp:
--------------------------------------------------------------------------------
1 | // Scattering of a plane light wave at a 2D cell phantom
2 | // Author: Paul Mueller
3 | // DOI: https://dx.doi.org/10.1186/s12859-015-0764-0
4 |
5 | # include
6 | # include
7 | # include
8 | # include
9 | # include
10 | # include
11 | # include
12 |
13 | // Do not set this time to short. Wee need the system to equilibrate.
14 | // Very good agreement with "#define TIME 1000000"
15 | // This worked well with "#define TIME 100000"
16 | // This worked worse with "#define TIME 10000"
17 | #define TIME 15000
18 |
19 | // Coordinates
20 | // The cytoplasm is centered at the origin
21 | // All coordinates are in wavelengths
22 | // Assuming a wavelength of 500nm, 20 wavelengths is 10um
23 | // The cytoplasm and nucleus have major axis (A) and minor axis (B)
24 | // The angle of rotation PHI is angle between major axis (A) and x-axis.
25 |
26 | #define ACQUISITION_PHI .5
27 |
28 | // These SIZEs include the PML and are total domain sizes (not half)
29 | // LATERALSIZE can be large to grab all scattered fields
30 | #define LATERALSIZE 30.0
31 | // up to which distance in axial direction do we need the field?
32 | #define AXIALSIZE 20.0
33 |
34 | #define MEDIUM_RI 1.333
35 |
36 | #define CYTOPLASM_RI 1.365
37 | #define CYTOPLASM_A 8.5
38 | #define CYTOPLASM_B 7.0
39 |
40 | #define NUCLEUS_RI 1.360
41 | #define NUCLEUS_A 4.5
42 | #define NUCLEUS_B 3.5
43 | #define NUCLEUS_PHI 0.5
44 | #define NUCLEUS_X 2.0
45 | #define NUCLEUS_Y 1.0
46 |
47 | #define NUCLEOLUS_RI 1.387
48 | #define NUCLEOLUS_A 1.0
49 | #define NUCLEOLUS_B 1.0
50 | #define NUCLEOLUS_PHI 0.0
51 | #define NUCLEOLUS_X 2.0
52 | #define NUCLEOLUS_Y 2.0
53 |
54 | // Choosing the resolution
55 | // http://ab-initio.mit.edu/wiki/index.php/Meep_Tutorial
56 | // In general, at least 8 pixels/wavelength in the highest
57 | // dielectric is a good idea.
58 | #define SAMPLING 13. // How many pixels for a wavelength?
59 | // A PML thickness of about half the wavelength is ok.
60 | // http://www.mail-archive.com/meep-discuss@ab-initio.mit.edu/msg00525.html
61 | #define PMLTHICKNESS 0.5 // PML thickness in wavelengths
62 |
63 | #define ONLYMEDIUM false
64 | #define SAVEALLFRAMES false
65 | #define SAVELASTPERIODS 1 // If SAVEALLFRAMES is false, save such many
66 | // periods before end of simulation
67 |
68 | using namespace meep;
69 |
70 | double eps(const vec &p)
71 | {
72 | if (ONLYMEDIUM) {
73 | return pow(MEDIUM_RI,2.0);
74 | } else {
75 | double cox = p.x()-LATERALSIZE/2.0;
76 | double coy = p.y()-AXIALSIZE/2.0;
77 |
78 | double rottx = cox*cos(ACQUISITION_PHI) - coy*sin(ACQUISITION_PHI);
79 | double rotty = cox*sin(ACQUISITION_PHI) + coy*cos(ACQUISITION_PHI);
80 |
81 | // Cytoplasm
82 | double crotx = rottx;
83 | double croty = rotty;
84 |
85 | // Nucleus
86 | double nrotx = (rottx-NUCLEUS_X)*cos(NUCLEUS_PHI) - (rotty-NUCLEUS_Y)*sin(NUCLEUS_PHI);
87 | double nroty = (rottx-NUCLEUS_X)*sin(NUCLEUS_PHI) + (rotty-NUCLEUS_Y)*cos(NUCLEUS_PHI);
88 |
89 | // Nucleolus
90 | double nnrotx = (rottx-NUCLEOLUS_X)*cos(NUCLEOLUS_PHI) - (rotty-NUCLEOLUS_Y)*sin(NUCLEOLUS_PHI);
91 | double nnroty = (rottx-NUCLEOLUS_X)*sin(NUCLEOLUS_PHI) + (rotty-NUCLEOLUS_Y)*cos(NUCLEOLUS_PHI);
92 |
93 | // Nucleolus
94 | if ( pow(nnrotx,2.0)/pow(NUCLEOLUS_A,2.0) + pow(nnroty,2.0)/pow(NUCLEOLUS_B,2.0) <= 1 ) {
95 | return pow(NUCLEOLUS_RI,2.0);
96 | }
97 | // Nucleus
98 | else if ( pow(nrotx,2.0)/pow(NUCLEUS_A,2.0) + pow(nroty,2.0)/pow(NUCLEUS_B,2.0) <= 1 ) {
99 | return pow(NUCLEUS_RI,2.0);
100 | }
101 | // Cytoplasm
102 | else if ( pow(crotx,2.0)/pow(CYTOPLASM_A,2.0) + pow(croty,2.0)/pow(CYTOPLASM_B,2.0) <= 1 ) {
103 | return pow(CYTOPLASM_RI,2.0);
104 | }
105 | // Medium
106 | else {
107 | return pow(MEDIUM_RI,2.0);
108 | }
109 |
110 | }
111 | }
112 |
113 |
114 | complex one(const vec &p)
115 | {
116 | return 1.0;
117 | }
118 |
119 |
120 | int main(int argc, char **argv) {
121 |
122 | initialize mpi(argc, argv); // do this even for non-MPI Meep
123 |
124 | ////////////////////////////////////////////////////////////////////
125 | // CHECK THE eps() FUNCTION WHEN CHANGING THIS BLOCK.
126 | //
127 | // determine the size of the copmutational volume
128 | // 1. The wavelength defines the grid size. One wavelength
129 | // is sampled with SAMPLING pixels.
130 | double resolution = SAMPLING;
131 | // The lateral extension sr of the computational grid.
132 | // make sure sr is even
133 | double sr = LATERALSIZE;
134 | double sz = AXIALSIZE;
135 | // The axial extension is the same as the lateral extension,
136 | // since we rotate the sample.
137 |
138 | // Wavelength has size of one unit. c=1, so frequency is one as
139 | // well.
140 | double frequency = 1.;
141 | ////////////////////////////////////////////////////////////////////
142 |
143 | if (ONLYMEDIUM){
144 | // see eps() for more info
145 | master_printf("...Using empty structure \n");
146 | }
147 | else{
148 | // see eps() for more info
149 | master_printf("...Using phantom structure \n");
150 | }
151 |
152 | master_printf("...Lateral object size [wavelengths]: %f \n", 2.0 * CYTOPLASM_A);
153 | // take the largest number (A) here
154 | master_printf("...Axial object size [wavelengths]: %f \n", 2.0 * CYTOPLASM_B);
155 | master_printf("...PML thickness [wavelengths]: %f \n", PMLTHICKNESS);
156 | master_printf("...Medium RI: %f \n", MEDIUM_RI);
157 | master_printf("...Sampling per wavelength [px]: %f \n", SAMPLING);
158 | master_printf("...Radial extension [px]: %f \n", sr * SAMPLING);
159 | master_printf("...Axial extension [px]: %f \n", sz * SAMPLING);
160 |
161 | int clock0=clock();
162 |
163 | master_printf("...Initializing grid volume \n");
164 | grid_volume v = vol2d(sr,sz,resolution);
165 |
166 | master_printf("...Initializing structure \n");
167 | structure s(v, eps, pml(PMLTHICKNESS)); // structure
168 | s.set_epsilon(eps,true); //switch eps averaging on or off
169 |
170 | master_printf("...Initializing fields \n");
171 | fields f(&s);
172 |
173 | const char *dirname = make_output_directory(__FILE__);
174 | f.set_output_directory(dirname);
175 |
176 | master_printf("...Saving dielectric structure \n");
177 | f.output_hdf5(Dielectric, v.surroundings(),0,false,true,0);
178 |
179 | master_printf("...Adding light source \n");
180 | // Wavelength is one unit. Since c=1, frequency is also 1.
181 | continuous_src_time src(1.);
182 | // Light source is a plane at z = 2*PMLTHICKNESS
183 | // to not let the source sit in the PML
184 | volume src_plane(vec(0.0,2*PMLTHICKNESS),vec(sr,2*PMLTHICKNESS));
185 |
186 | f.add_volume_source(Ez,src,src_plane,one,1.0);
187 | master_printf("...Starting simulation \n");
188 |
189 |
190 | for (int i=0; i