redbirdpy 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/Makefile +5 -3
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/PKG-INFO +25 -27
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/README.md +22 -22
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/example/demo_redbird_basic.py +1 -1
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/example/demo_redbird_forward.py +1 -1
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/example/demo_redbird_forward_expert.py +1 -1
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/example/demo_redbird_forward_heterogeneous.py +1 -1
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/example/demo_redbird_forward_layered.py +1 -1
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/example/demo_redbird_recon.py +1 -1
- redbirdpy-0.2.0/example/demo_redbird_recon_expert.py +186 -0
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/example/demo_redbird_recon_widefield.py +1 -1
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/example/demo_redbird_widefield.py +1 -1
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/pyproject.toml +3 -4
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/redbirdpy/__init__.py +1 -1
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/redbirdpy/solver.py +183 -23
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/setup.py +3 -4
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/test/test_init.py +1 -1
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/test/test_solver.py +66 -66
- redbirdpy-0.1.0/example/demo_redbird_recon_expert.py +0 -179
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/LICENSE.txt +0 -0
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/MANIFEST.in +0 -0
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/doc/images/redbird_banner.png +0 -0
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/redbirdpy/analytical.py +0 -0
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/redbirdpy/forward.py +0 -0
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/redbirdpy/property.py +0 -0
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/redbirdpy/recon.py +0 -0
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/redbirdpy/utility.py +0 -0
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/redbirdpy.egg-info/SOURCES.txt +0 -0
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/setup.cfg +0 -0
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/test/test_analytical.py +0 -0
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/test/test_forward.py +0 -0
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/test/test_integration.py +0 -0
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/test/test_property.py +0 -0
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/test/test_recon.py +0 -0
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/test/test_utility.py +0 -0
- {redbirdpy-0.1.0 → redbirdpy-0.2.0}/test/test_widefield.py +0 -0
|
@@ -81,7 +81,11 @@ docs:
|
|
|
81
81
|
|
|
82
82
|
# Format code (requires black)
|
|
83
83
|
format:
|
|
84
|
-
$(PYTHON) -m black redbirdpy/ test/
|
|
84
|
+
$(PYTHON) -m black redbirdpy/ test/ example/*.py setup.py
|
|
85
|
+
|
|
86
|
+
# Run all demo scripts
|
|
87
|
+
demo:
|
|
88
|
+
cd example && for demo in demo*.py; do echo $$demo; time $(PYTHON) $$demo 0; done
|
|
85
89
|
|
|
86
90
|
# Type checking (requires mypy)
|
|
87
91
|
typecheck:
|
|
@@ -99,5 +103,3 @@ info:
|
|
|
99
103
|
version:
|
|
100
104
|
@$(PYTHON) -c "import redbirdpy; print(redbirdpy.__version__)"
|
|
101
105
|
|
|
102
|
-
pretty:
|
|
103
|
-
$(PYTHON) -m black test/*.py example/*.py redbirdpy/*.py setup.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: redbirdpy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A Python toolbox for Diffuse Optical Tomography (DOT) and Near-Infrared Spectroscopy (NIRS)
|
|
5
5
|
Home-page: https://github.com/fangq/redbirdpy
|
|
6
6
|
Author: Qianqian Fang
|
|
@@ -14,7 +14,7 @@ Project-URL: Repository, https://github.com/fangq/redbirdpy
|
|
|
14
14
|
Project-URL: Bug Tracker, https://github.com/fangq/redbirdpy/issues
|
|
15
15
|
Keywords: Diffuse Optical Tomography,DOT,NIRS,Near-Infrared Spectroscopy,FEM,Finite Element Method,Biomedical Optics,Image Reconstruction,Inverse Problem,Photon Migration,Diffusion Equation,Tissue Optics
|
|
16
16
|
Platform: any
|
|
17
|
-
Classifier: Development Status ::
|
|
17
|
+
Classifier: Development Status :: 4 - Beta
|
|
18
18
|
Classifier: Intended Audience :: Science/Research
|
|
19
19
|
Classifier: Intended Audience :: Developers
|
|
20
20
|
Classifier: Intended Audience :: Healthcare Industry
|
|
@@ -37,14 +37,12 @@ Description-Content-Type: text/markdown
|
|
|
37
37
|
License-File: LICENSE.txt
|
|
38
38
|
Requires-Dist: numpy>=1.15.0
|
|
39
39
|
Requires-Dist: scipy>=1.0.0
|
|
40
|
-
|
|
41
|
-
Requires-Dist: iso2mesh>=0.5.4; extra == "mesh"
|
|
40
|
+
Requires-Dist: iso2mesh>=0.5.0
|
|
42
41
|
Provides-Extra: dev
|
|
43
42
|
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
44
43
|
Requires-Dist: pytest-cov>=2.0; extra == "dev"
|
|
45
44
|
Requires-Dist: flake8>=3.0; extra == "dev"
|
|
46
45
|
Provides-Extra: all
|
|
47
|
-
Requires-Dist: iso2mesh>=0.5.4; extra == "all"
|
|
48
46
|
Requires-Dist: matplotlib>=3.0; extra == "all"
|
|
49
47
|
Dynamic: author
|
|
50
48
|
Dynamic: home-page
|
|
@@ -53,13 +51,13 @@ Dynamic: maintainer
|
|
|
53
51
|
Dynamic: platform
|
|
54
52
|
Dynamic: requires-python
|
|
55
53
|
|
|
56
|
-

|
|
57
55
|
|
|
58
56
|
# RedbirdPy - A Model-Based Diffuse Optical Imaging Toolbox for Python
|
|
59
57
|
|
|
60
|
-
* **Copyright**: (C) Qianqian Fang (2005–
|
|
58
|
+
* **Copyright**: (C) Qianqian Fang (2005–2026) \<q.fang at neu.edu>
|
|
61
59
|
* **License**: GNU Public License V3 or later
|
|
62
|
-
* **Version**: 0.
|
|
60
|
+
* **Version**: 0.2.0 (Flamingo)
|
|
63
61
|
* **GitHub**: [https://github.com/fangq/redbirdpy](https://github.com/fangq/redbirdpy)
|
|
64
62
|
* **Acknowledgement**: This project is supported by the US National Institute of Health (NIH)
|
|
65
63
|
grant [R01-CA204443](https://reporter.nih.gov/project-details/10982160)
|
|
@@ -88,7 +86,7 @@ Dynamic: requires-python
|
|
|
88
86
|
|
|
89
87
|
## Introduction
|
|
90
88
|
|
|
91
|
-
**
|
|
89
|
+
**RedbirdPy** is a Python translation of the [Redbird MATLAB toolbox](https://github.com/fangq/redbird) for diffuse optical imaging (DOI) and diffuse optical tomography (DOT). It provides a fast, experimentally-validated forward solver for the diffusion equation using the finite-element method (FEM), along with advanced non-linear image reconstruction algorithms.
|
|
92
90
|
|
|
93
91
|
Redbird is the result of over two decades of active research in DOT and image reconstruction. It has been the core data analysis tool in numerous publications related to optical breast imaging, prior-guided reconstruction techniques, multi-modal imaging, and wide-field DOT systems.
|
|
94
92
|
|
|
@@ -104,7 +102,7 @@ Redbird is the result of over two decades of active research in DOT and image re
|
|
|
104
102
|
|
|
105
103
|
### Validation
|
|
106
104
|
|
|
107
|
-
The forward solver is carefully validated against Monte Carlo solvers
|
|
105
|
+
The forward solver is carefully validated against Monte Carlo solvers - **MCX** and **MMC**. The diffusion approximation is valid in high-scattering media where the reduced scattering coefficient (μs') is much greater than the absorption coefficient (μa).
|
|
108
106
|
|
|
109
107
|
---
|
|
110
108
|
|
|
@@ -112,15 +110,15 @@ The forward solver is carefully validated against Monte Carlo solvers—**MCX**
|
|
|
112
110
|
|
|
113
111
|
### Requirements
|
|
114
112
|
|
|
115
|
-
- Python 3.
|
|
113
|
+
- Python 3.6+
|
|
116
114
|
- NumPy
|
|
117
115
|
- SciPy
|
|
116
|
+
- Iso2Mesh
|
|
118
117
|
|
|
119
118
|
### Basic Installation
|
|
120
119
|
|
|
121
120
|
```bash
|
|
122
121
|
pip install numpy scipy
|
|
123
|
-
# For mesh generation
|
|
124
122
|
pip install iso2mesh # or from https://github.com/NeuroJSON/pyiso2mesh
|
|
125
123
|
```
|
|
126
124
|
|
|
@@ -131,7 +129,7 @@ pip install iso2mesh # or from https://github.com/NeuroJSON/pyiso2mesh
|
|
|
131
129
|
pip install numba # JIT compilation
|
|
132
130
|
|
|
133
131
|
# For accelerated solvers
|
|
134
|
-
pip install blocksolver # or from https://github.com/fangq/
|
|
132
|
+
pip install blocksolver # or from https://github.com/fangq/blit
|
|
135
133
|
|
|
136
134
|
# For other linear solvers
|
|
137
135
|
pip install pypardiso # Intel MKL PARDISO (fastest direct solver)
|
|
@@ -147,7 +145,7 @@ cd redbirdpy
|
|
|
147
145
|
pip install -e .
|
|
148
146
|
```
|
|
149
147
|
|
|
150
|
-
Or simply
|
|
148
|
+
Or simply import `redbirdpy` from inside the repository's top folder.
|
|
151
149
|
|
|
152
150
|
---
|
|
153
151
|
|
|
@@ -201,12 +199,12 @@ Redbird performs two main tasks:
|
|
|
201
199
|
|
|
202
200
|
Redbird supports four types of image reconstructions:
|
|
203
201
|
|
|
204
|
-
| Mode | Description
|
|
205
|
-
|
|
206
|
-
| **Bulk Fitting** | Estimate single set of properties for entire domain
|
|
207
|
-
| **Segmented** | One property set per labeled tissue segment ("hard-prior")
|
|
202
|
+
| Mode | Description |
|
|
203
|
+
|------|---------------------------------------------------------------|
|
|
204
|
+
| **Bulk Fitting** | Estimate single set of properties for the entire domain |
|
|
205
|
+
| **Segmented** | One property set per labeled tissue segment ("hard-prior") |
|
|
208
206
|
| **Soft-Prior** | Spatial priors as soft constraints (Laplacian, compositional) |
|
|
209
|
-
| **Unconstrained** | Independent properties per node with Tikhonov regularization
|
|
207
|
+
| **Unconstrained** | Independent properties per node with Tikhonov regularization |
|
|
210
208
|
|
|
211
209
|
---
|
|
212
210
|
|
|
@@ -265,9 +263,9 @@ Redbird supports four types of image reconstructions:
|
|
|
265
263
|
| Function | Description |
|
|
266
264
|
|----------|-------------|
|
|
267
265
|
| `femsolve(A, b, method)` | Solve linear system with auto-selection |
|
|
268
|
-
| `
|
|
266
|
+
| `solverinfo()` | Query available solver backends |
|
|
269
267
|
|
|
270
|
-
Supported solvers: `pardiso`, `umfpack`, `cholmod`, `superlu`, `blqmr`, `cg`, `cg+amg`, `gmres`, `bicgstab`
|
|
268
|
+
Supported solvers: `pardiso`, `umfpack`, `cholmod`, `superlu`, `blqmr`, `cg`, `cg+amg`, `gmres`, `minres`, `minres+amg`, `qmr`, `bicgstab`
|
|
271
269
|
|
|
272
270
|
### `redbirdpy.analytical` - Analytical Solutions
|
|
273
271
|
|
|
@@ -395,8 +393,8 @@ Configure similarly using `dettype`, `detparam1`, `detparam2`, `detpattern`.
|
|
|
395
393
|
|
|
396
394
|
```python
|
|
397
395
|
cfg['prop'] = {
|
|
398
|
-
'690':
|
|
399
|
-
'830':
|
|
396
|
+
'690': [[0, 0, 1, 1], [0.012, 1.1, 0, 1.37]],
|
|
397
|
+
'830': [[0, 0, 1, 1], [0.008, 0.9, 0, 1.37]]
|
|
400
398
|
}
|
|
401
399
|
```
|
|
402
400
|
|
|
@@ -498,7 +496,7 @@ recon['prop'] = np.tile(cfg['prop'][1,:], (recon['node'].shape[0], 1))
|
|
|
498
496
|
newrecon, resid = rb.run(cfg, recon, detphi0, lambda_=1e-4)[:2]
|
|
499
497
|
```
|
|
500
498
|
|
|
501
|
-
### Wide-Field Reconstruction
|
|
499
|
+
### Wide-Field Forward and Reconstruction
|
|
502
500
|
|
|
503
501
|
```python
|
|
504
502
|
# Create illumination patterns
|
|
@@ -585,12 +583,12 @@ If you use Redbird in your research, please cite:
|
|
|
585
583
|
|
|
586
584
|
## License
|
|
587
585
|
|
|
588
|
-
GNU General Public License v3.0 or later - see [LICENSE](LICENSE) file for details.
|
|
586
|
+
GNU General Public License v3.0 or later - see [LICENSE](LICENSE.txt) file for details.
|
|
589
587
|
|
|
590
588
|
## Author
|
|
591
589
|
|
|
592
590
|
**Qianqian Fang** (q.fang@neu.edu)
|
|
593
|
-
Computational Optics & Translational Imaging Lab
|
|
591
|
+
Computational Optics & Translational Imaging (COTI) Lab
|
|
594
592
|
Northeastern University
|
|
595
593
|
|
|
596
|
-
Python translation based on the [Redbird MATLAB toolbox](https://github.com/fangq/
|
|
594
|
+
Python translation based on the [Redbird MATLAB toolbox](https://github.com/fangq/redbird).
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-

|
|
2
2
|
|
|
3
3
|
# RedbirdPy - A Model-Based Diffuse Optical Imaging Toolbox for Python
|
|
4
4
|
|
|
5
|
-
* **Copyright**: (C) Qianqian Fang (2005–
|
|
5
|
+
* **Copyright**: (C) Qianqian Fang (2005–2026) \<q.fang at neu.edu>
|
|
6
6
|
* **License**: GNU Public License V3 or later
|
|
7
|
-
* **Version**: 0.
|
|
7
|
+
* **Version**: 0.2.0 (Flamingo)
|
|
8
8
|
* **GitHub**: [https://github.com/fangq/redbirdpy](https://github.com/fangq/redbirdpy)
|
|
9
9
|
* **Acknowledgement**: This project is supported by the US National Institute of Health (NIH)
|
|
10
10
|
grant [R01-CA204443](https://reporter.nih.gov/project-details/10982160)
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
|
|
34
34
|
## Introduction
|
|
35
35
|
|
|
36
|
-
**
|
|
36
|
+
**RedbirdPy** is a Python translation of the [Redbird MATLAB toolbox](https://github.com/fangq/redbird) for diffuse optical imaging (DOI) and diffuse optical tomography (DOT). It provides a fast, experimentally-validated forward solver for the diffusion equation using the finite-element method (FEM), along with advanced non-linear image reconstruction algorithms.
|
|
37
37
|
|
|
38
38
|
Redbird is the result of over two decades of active research in DOT and image reconstruction. It has been the core data analysis tool in numerous publications related to optical breast imaging, prior-guided reconstruction techniques, multi-modal imaging, and wide-field DOT systems.
|
|
39
39
|
|
|
@@ -49,7 +49,7 @@ Redbird is the result of over two decades of active research in DOT and image re
|
|
|
49
49
|
|
|
50
50
|
### Validation
|
|
51
51
|
|
|
52
|
-
The forward solver is carefully validated against Monte Carlo solvers
|
|
52
|
+
The forward solver is carefully validated against Monte Carlo solvers - **MCX** and **MMC**. The diffusion approximation is valid in high-scattering media where the reduced scattering coefficient (μs') is much greater than the absorption coefficient (μa).
|
|
53
53
|
|
|
54
54
|
---
|
|
55
55
|
|
|
@@ -57,15 +57,15 @@ The forward solver is carefully validated against Monte Carlo solvers—**MCX**
|
|
|
57
57
|
|
|
58
58
|
### Requirements
|
|
59
59
|
|
|
60
|
-
- Python 3.
|
|
60
|
+
- Python 3.6+
|
|
61
61
|
- NumPy
|
|
62
62
|
- SciPy
|
|
63
|
+
- Iso2Mesh
|
|
63
64
|
|
|
64
65
|
### Basic Installation
|
|
65
66
|
|
|
66
67
|
```bash
|
|
67
68
|
pip install numpy scipy
|
|
68
|
-
# For mesh generation
|
|
69
69
|
pip install iso2mesh # or from https://github.com/NeuroJSON/pyiso2mesh
|
|
70
70
|
```
|
|
71
71
|
|
|
@@ -76,7 +76,7 @@ pip install iso2mesh # or from https://github.com/NeuroJSON/pyiso2mesh
|
|
|
76
76
|
pip install numba # JIT compilation
|
|
77
77
|
|
|
78
78
|
# For accelerated solvers
|
|
79
|
-
pip install blocksolver # or from https://github.com/fangq/
|
|
79
|
+
pip install blocksolver # or from https://github.com/fangq/blit
|
|
80
80
|
|
|
81
81
|
# For other linear solvers
|
|
82
82
|
pip install pypardiso # Intel MKL PARDISO (fastest direct solver)
|
|
@@ -92,7 +92,7 @@ cd redbirdpy
|
|
|
92
92
|
pip install -e .
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
Or simply
|
|
95
|
+
Or simply import `redbirdpy` from inside the repository's top folder.
|
|
96
96
|
|
|
97
97
|
---
|
|
98
98
|
|
|
@@ -146,12 +146,12 @@ Redbird performs two main tasks:
|
|
|
146
146
|
|
|
147
147
|
Redbird supports four types of image reconstructions:
|
|
148
148
|
|
|
149
|
-
| Mode | Description
|
|
150
|
-
|
|
151
|
-
| **Bulk Fitting** | Estimate single set of properties for entire domain
|
|
152
|
-
| **Segmented** | One property set per labeled tissue segment ("hard-prior")
|
|
149
|
+
| Mode | Description |
|
|
150
|
+
|------|---------------------------------------------------------------|
|
|
151
|
+
| **Bulk Fitting** | Estimate single set of properties for the entire domain |
|
|
152
|
+
| **Segmented** | One property set per labeled tissue segment ("hard-prior") |
|
|
153
153
|
| **Soft-Prior** | Spatial priors as soft constraints (Laplacian, compositional) |
|
|
154
|
-
| **Unconstrained** | Independent properties per node with Tikhonov regularization
|
|
154
|
+
| **Unconstrained** | Independent properties per node with Tikhonov regularization |
|
|
155
155
|
|
|
156
156
|
---
|
|
157
157
|
|
|
@@ -210,9 +210,9 @@ Redbird supports four types of image reconstructions:
|
|
|
210
210
|
| Function | Description |
|
|
211
211
|
|----------|-------------|
|
|
212
212
|
| `femsolve(A, b, method)` | Solve linear system with auto-selection |
|
|
213
|
-
| `
|
|
213
|
+
| `solverinfo()` | Query available solver backends |
|
|
214
214
|
|
|
215
|
-
Supported solvers: `pardiso`, `umfpack`, `cholmod`, `superlu`, `blqmr`, `cg`, `cg+amg`, `gmres`, `bicgstab`
|
|
215
|
+
Supported solvers: `pardiso`, `umfpack`, `cholmod`, `superlu`, `blqmr`, `cg`, `cg+amg`, `gmres`, `minres`, `minres+amg`, `qmr`, `bicgstab`
|
|
216
216
|
|
|
217
217
|
### `redbirdpy.analytical` - Analytical Solutions
|
|
218
218
|
|
|
@@ -340,8 +340,8 @@ Configure similarly using `dettype`, `detparam1`, `detparam2`, `detpattern`.
|
|
|
340
340
|
|
|
341
341
|
```python
|
|
342
342
|
cfg['prop'] = {
|
|
343
|
-
'690':
|
|
344
|
-
'830':
|
|
343
|
+
'690': [[0, 0, 1, 1], [0.012, 1.1, 0, 1.37]],
|
|
344
|
+
'830': [[0, 0, 1, 1], [0.008, 0.9, 0, 1.37]]
|
|
345
345
|
}
|
|
346
346
|
```
|
|
347
347
|
|
|
@@ -443,7 +443,7 @@ recon['prop'] = np.tile(cfg['prop'][1,:], (recon['node'].shape[0], 1))
|
|
|
443
443
|
newrecon, resid = rb.run(cfg, recon, detphi0, lambda_=1e-4)[:2]
|
|
444
444
|
```
|
|
445
445
|
|
|
446
|
-
### Wide-Field Reconstruction
|
|
446
|
+
### Wide-Field Forward and Reconstruction
|
|
447
447
|
|
|
448
448
|
```python
|
|
449
449
|
# Create illumination patterns
|
|
@@ -530,12 +530,12 @@ If you use Redbird in your research, please cite:
|
|
|
530
530
|
|
|
531
531
|
## License
|
|
532
532
|
|
|
533
|
-
GNU General Public License v3.0 or later - see [LICENSE](LICENSE) file for details.
|
|
533
|
+
GNU General Public License v3.0 or later - see [LICENSE](LICENSE.txt) file for details.
|
|
534
534
|
|
|
535
535
|
## Author
|
|
536
536
|
|
|
537
537
|
**Qianqian Fang** (q.fang@neu.edu)
|
|
538
|
-
Computational Optics & Translational Imaging Lab
|
|
538
|
+
Computational Optics & Translational Imaging (COTI) Lab
|
|
539
539
|
Northeastern University
|
|
540
540
|
|
|
541
|
-
Python translation based on the [Redbird MATLAB toolbox](https://github.com/fangq/
|
|
541
|
+
Python translation based on the [Redbird MATLAB toolbox](https://github.com/fangq/redbird).
|
|
@@ -80,7 +80,7 @@ node_with_data = np.column_stack([cfg["node"], phi_log])
|
|
|
80
80
|
# plotmesh with slice 'y>60'
|
|
81
81
|
plotmesh(node_with_data, cfg["elem"], "y>60")
|
|
82
82
|
|
|
83
|
-
plt.show()
|
|
83
|
+
plt.show(block=(len(sys.argv) == 1))
|
|
84
84
|
|
|
85
85
|
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
86
86
|
# % Print summary
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Redbird - A Diffusion Solver for Diffuse Optical Tomography
|
|
3
|
+
Copyright Qianqian Fang, 2018
|
|
4
|
+
|
|
5
|
+
Continuous-Wave (CW) reconstruction of absorption (mua) target
|
|
6
|
+
(explicit iterative loop to show internal steps of rbrun)
|
|
7
|
+
|
|
8
|
+
This file is part of Redbird URL: https://github.com/fangq/redbirdpy
|
|
9
|
+
|
|
10
|
+
INDEX CONVENTION: iso2mesh returns 1-based elem/face indices.
|
|
11
|
+
tsearchn returns 1-based element indices to match MATLAB convention.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
import time
|
|
16
|
+
import sys
|
|
17
|
+
import os
|
|
18
|
+
|
|
19
|
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
|
20
|
+
|
|
21
|
+
import redbirdpy as rb
|
|
22
|
+
from redbirdpy import forward
|
|
23
|
+
from redbirdpy.recon import reginv, matreform
|
|
24
|
+
import iso2mesh as i2m
|
|
25
|
+
import matplotlib.pyplot as plt
|
|
26
|
+
|
|
27
|
+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
28
|
+
# % prepare simulation input
|
|
29
|
+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
30
|
+
|
|
31
|
+
if __name__ == "__main__":
|
|
32
|
+
s0 = [70, 50, 20]
|
|
33
|
+
|
|
34
|
+
nobbx, fcbbx, _ = i2m.meshabox(
|
|
35
|
+
[40, 0, 0], [160, 120, 60], 10
|
|
36
|
+
) # elem/face are 1-based
|
|
37
|
+
nosp, fcsp, _ = i2m.meshasphere(s0, 5, 1)
|
|
38
|
+
no, fc = i2m.mergemesh(nobbx, fcbbx, nosp, fcsp[:, :3])
|
|
39
|
+
|
|
40
|
+
node, elem, _ = i2m.s2m(
|
|
41
|
+
no, fc[:, :3], 1, 10, "tetgen", [[41, 1, 1], s0]
|
|
42
|
+
) # elem is 1-based
|
|
43
|
+
|
|
44
|
+
xi, yi = np.meshgrid(np.arange(60, 141, 20), np.arange(20, 101, 20))
|
|
45
|
+
|
|
46
|
+
cfg0 = {
|
|
47
|
+
"node": node,
|
|
48
|
+
"elem": elem[:, :4], # 1-based
|
|
49
|
+
"seg": elem[:, 4].astype(int),
|
|
50
|
+
"srcpos": np.c_[xi.flat, yi.flat, np.zeros(xi.size)],
|
|
51
|
+
"detpos": np.c_[xi.flat, yi.flat, 60 * np.ones(xi.size)],
|
|
52
|
+
"srcdir": [0, 0, 1],
|
|
53
|
+
"detdir": [0, 0, -1],
|
|
54
|
+
"prop": np.array([[0, 0, 1, 1], [0.008, 1, 0, 1.37], [0.016, 1, 0, 1.37]]),
|
|
55
|
+
"omega": 0,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
cfg = {k: v.copy() if hasattr(v, "copy") else v for k, v in cfg0.items()}
|
|
59
|
+
cfg0 = rb.meshprep(cfg0)[0]
|
|
60
|
+
|
|
61
|
+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
62
|
+
# % Run forward for the heterogeneous domain
|
|
63
|
+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
64
|
+
|
|
65
|
+
detphi0 = rb.run(cfg0)[0]
|
|
66
|
+
|
|
67
|
+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
68
|
+
# % Reset the domain to a homogeneous medium for recon
|
|
69
|
+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
70
|
+
|
|
71
|
+
node, face, elem = i2m.meshabox([40, 0, 0], [160, 120, 60], 10) # 1-based
|
|
72
|
+
cfg = rb.setmesh(cfg, node, elem, cfg["prop"], np.ones(node.shape[0], dtype=int))
|
|
73
|
+
|
|
74
|
+
sd = rb.sdmap(cfg)
|
|
75
|
+
|
|
76
|
+
# create coarse reconstruction mesh
|
|
77
|
+
recon = {}
|
|
78
|
+
recon["node"], _, recon["elem"] = i2m.meshabox(
|
|
79
|
+
[40, 0, 0], [160, 120, 60], 20
|
|
80
|
+
) # 1-based
|
|
81
|
+
recon["mapid"], recon["mapweight"] = i2m.tsearchn(
|
|
82
|
+
recon["node"], recon["elem"], cfg["node"]
|
|
83
|
+
) # mapid is 0-based
|
|
84
|
+
|
|
85
|
+
# Check for NaN (points outside recon mesh)
|
|
86
|
+
nan_count = np.isnan(recon["mapid"]).sum()
|
|
87
|
+
if nan_count > 0:
|
|
88
|
+
print(f"WARNING: {nan_count} forward nodes outside recon mesh")
|
|
89
|
+
|
|
90
|
+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
91
|
+
# % Initialize properties for reconstruction
|
|
92
|
+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
93
|
+
|
|
94
|
+
maxiter = 10
|
|
95
|
+
resid = np.zeros(maxiter)
|
|
96
|
+
|
|
97
|
+
# initialize reconstruction to homogeneous (label=1)
|
|
98
|
+
recon["prop"] = np.tile(cfg["prop"][1, :], (recon["node"].shape[0], 1))
|
|
99
|
+
cfg["prop"] = np.tile(cfg["prop"][1, :], (cfg["node"].shape[0], 1))
|
|
100
|
+
cfg.pop("seg", None)
|
|
101
|
+
|
|
102
|
+
# prepare mesh for forward solver
|
|
103
|
+
cfg = rb.meshprep(cfg)[0]
|
|
104
|
+
|
|
105
|
+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
106
|
+
# % Explicit iterative reconstruction
|
|
107
|
+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
108
|
+
|
|
109
|
+
for i in range(maxiter):
|
|
110
|
+
tic = time.perf_counter()
|
|
111
|
+
|
|
112
|
+
# run forward on forward mesh
|
|
113
|
+
detphi, phi = forward.runforward(cfg, sd=sd, method="blqmr", rhsblock=4)
|
|
114
|
+
# build Jacobian for mua (cfg["elem"] is 1-based, jac handles conversion internally)
|
|
115
|
+
# Jmua shape: (nsd, nn_forward)
|
|
116
|
+
Jmua, _ = forward.jac(sd, phi, cfg["deldotdel"], cfg["elem"], cfg["evol"])
|
|
117
|
+
|
|
118
|
+
# remap Jacobian to coarse recon mesh
|
|
119
|
+
Jmua_recon = i2m.meshremap(
|
|
120
|
+
Jmua.T,
|
|
121
|
+
recon["mapid"],
|
|
122
|
+
recon["mapweight"],
|
|
123
|
+
recon["elem"],
|
|
124
|
+
recon["node"].shape[0],
|
|
125
|
+
).T
|
|
126
|
+
|
|
127
|
+
# create inverse problem (real-valued formulation)
|
|
128
|
+
Jmua_recon, misfit, _ = matreform(
|
|
129
|
+
Jmua_recon, detphi0.flatten(), detphi.flatten(), "real"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# store residual
|
|
133
|
+
resid[i] = np.sum(np.abs(misfit))
|
|
134
|
+
|
|
135
|
+
# normalize Jacobian
|
|
136
|
+
blockscale = 1.0 / np.sqrt(np.sum(Jmua_recon**2))
|
|
137
|
+
Jmua_recon = Jmua_recon * blockscale
|
|
138
|
+
|
|
139
|
+
# solve for update on recon mesh
|
|
140
|
+
dmu_recon = reginv(Jmua_recon, misfit, 1e-3)
|
|
141
|
+
|
|
142
|
+
# de-normalize
|
|
143
|
+
dmu_recon = dmu_recon * blockscale
|
|
144
|
+
|
|
145
|
+
# update recon mesh mua
|
|
146
|
+
recon["prop"][:, 0] = recon["prop"][:, 0] + dmu_recon
|
|
147
|
+
|
|
148
|
+
# interpolate update to forward mesh
|
|
149
|
+
# meshinterp: interpolate from recon nodes to forward nodes
|
|
150
|
+
cfg["prop"] = i2m.meshinterp(
|
|
151
|
+
recon["prop"],
|
|
152
|
+
recon["mapid"],
|
|
153
|
+
recon["mapweight"],
|
|
154
|
+
recon["elem"],
|
|
155
|
+
cfg["prop"],
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
elapsed = time.perf_counter() - tic
|
|
159
|
+
print(
|
|
160
|
+
f"iter [{i+1:4d}]: residual={resid[i]:.6e}, relres={resid[i]/resid[0]:.6e} (time={elapsed:.4f} s)"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
164
|
+
# % Plotting results
|
|
165
|
+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
166
|
+
|
|
167
|
+
cutpos, cutval, facedata = i2m.qmeshcut(
|
|
168
|
+
cfg["elem"][:, :4], cfg["node"][:, :3], cfg["prop"][:, 0], "z = 20"
|
|
169
|
+
)[:3]
|
|
170
|
+
hh = i2m.plotmesh(
|
|
171
|
+
np.c_[cutpos, np.log10(np.abs(cutval) + 1e-20)],
|
|
172
|
+
facedata.tolist(),
|
|
173
|
+
subplot=121,
|
|
174
|
+
hold="on",
|
|
175
|
+
)
|
|
176
|
+
cutpos, cutval, facedata = i2m.qmeshcut(
|
|
177
|
+
cfg["elem"][:, :4], cfg["node"][:, :3], cfg["prop"][:, 0], "x = 70"
|
|
178
|
+
)[:3]
|
|
179
|
+
i2m.plotmesh(
|
|
180
|
+
np.c_[cutpos, np.log10(np.abs(cutval) + 1e-20)],
|
|
181
|
+
facedata.tolist(),
|
|
182
|
+
subplot=122,
|
|
183
|
+
parent=hh,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
plt.show(block=(len(sys.argv) == 1))
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "redbirdpy"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "A Python toolbox for Diffuse Optical Tomography (DOT) and Near-Infrared Spectroscopy (NIRS)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "GPL-3.0"}
|
|
@@ -29,7 +29,7 @@ keywords = [
|
|
|
29
29
|
"Tissue Optics",
|
|
30
30
|
]
|
|
31
31
|
classifiers = [
|
|
32
|
-
"Development Status ::
|
|
32
|
+
"Development Status :: 4 - Beta",
|
|
33
33
|
"Intended Audience :: Science/Research",
|
|
34
34
|
"Intended Audience :: Developers",
|
|
35
35
|
"Intended Audience :: Healthcare Industry",
|
|
@@ -52,17 +52,16 @@ requires-python = ">=3.6"
|
|
|
52
52
|
dependencies = [
|
|
53
53
|
"numpy>=1.15.0",
|
|
54
54
|
"scipy>=1.0.0",
|
|
55
|
+
"iso2mesh>=0.5.0",
|
|
55
56
|
]
|
|
56
57
|
|
|
57
58
|
[project.optional-dependencies]
|
|
58
|
-
mesh = ["iso2mesh>=0.5.4"]
|
|
59
59
|
dev = [
|
|
60
60
|
"pytest>=6.0",
|
|
61
61
|
"pytest-cov>=2.0",
|
|
62
62
|
"flake8>=3.0",
|
|
63
63
|
]
|
|
64
64
|
all = [
|
|
65
|
-
"iso2mesh>=0.5.4",
|
|
66
65
|
"matplotlib>=3.0",
|
|
67
66
|
]
|
|
68
67
|
|