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.
Files changed (36) hide show
  1. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/Makefile +5 -3
  2. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/PKG-INFO +25 -27
  3. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/README.md +22 -22
  4. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/example/demo_redbird_basic.py +1 -1
  5. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/example/demo_redbird_forward.py +1 -1
  6. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/example/demo_redbird_forward_expert.py +1 -1
  7. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/example/demo_redbird_forward_heterogeneous.py +1 -1
  8. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/example/demo_redbird_forward_layered.py +1 -1
  9. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/example/demo_redbird_recon.py +1 -1
  10. redbirdpy-0.2.0/example/demo_redbird_recon_expert.py +186 -0
  11. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/example/demo_redbird_recon_widefield.py +1 -1
  12. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/example/demo_redbird_widefield.py +1 -1
  13. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/pyproject.toml +3 -4
  14. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/redbirdpy/__init__.py +1 -1
  15. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/redbirdpy/solver.py +183 -23
  16. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/setup.py +3 -4
  17. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/test/test_init.py +1 -1
  18. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/test/test_solver.py +66 -66
  19. redbirdpy-0.1.0/example/demo_redbird_recon_expert.py +0 -179
  20. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/LICENSE.txt +0 -0
  21. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/MANIFEST.in +0 -0
  22. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/doc/images/redbird_banner.png +0 -0
  23. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/redbirdpy/analytical.py +0 -0
  24. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/redbirdpy/forward.py +0 -0
  25. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/redbirdpy/property.py +0 -0
  26. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/redbirdpy/recon.py +0 -0
  27. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/redbirdpy/utility.py +0 -0
  28. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/redbirdpy.egg-info/SOURCES.txt +0 -0
  29. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/setup.cfg +0 -0
  30. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/test/test_analytical.py +0 -0
  31. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/test/test_forward.py +0 -0
  32. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/test/test_integration.py +0 -0
  33. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/test/test_property.py +0 -0
  34. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/test/test_recon.py +0 -0
  35. {redbirdpy-0.1.0 → redbirdpy-0.2.0}/test/test_utility.py +0 -0
  36. {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.1.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 :: 3 - Alpha
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
- Provides-Extra: mesh
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
- ![Redbird Banner](./doc/images/redbird_banner.png)
54
+ ![Redbird Banner](https://raw.githubusercontent.com/fangq/redbird/refs/heads/master/doc/images/redbird_banner.png)
57
55
 
58
56
  # RedbirdPy - A Model-Based Diffuse Optical Imaging Toolbox for Python
59
57
 
60
- * **Copyright**: (C) Qianqian Fang (2005–2025) \<q.fang at neu.edu>
58
+ * **Copyright**: (C) Qianqian Fang (2005–2026) \<q.fang at neu.edu>
61
59
  * **License**: GNU Public License V3 or later
62
- * **Version**: 0.1.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
- **Redbird-Python** 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.
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—**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).
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.8+
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 add the `redbirdpy` folder to your Python path.
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
- | `get_solver_info()` | Query available solver backends |
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': np.array([[0, 0, 1, 1], [0.012, 1.1, 0, 1.37]]),
399
- '830': np.array([[0, 0, 1, 1], [0.008, 0.9, 0, 1.37]])
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/redbirdpy).
594
+ Python translation based on the [Redbird MATLAB toolbox](https://github.com/fangq/redbird).
@@ -1,10 +1,10 @@
1
- ![Redbird Banner](./doc/images/redbird_banner.png)
1
+ ![Redbird Banner](https://raw.githubusercontent.com/fangq/redbird/refs/heads/master/doc/images/redbird_banner.png)
2
2
 
3
3
  # RedbirdPy - A Model-Based Diffuse Optical Imaging Toolbox for Python
4
4
 
5
- * **Copyright**: (C) Qianqian Fang (2005–2025) \<q.fang at neu.edu>
5
+ * **Copyright**: (C) Qianqian Fang (2005–2026) \<q.fang at neu.edu>
6
6
  * **License**: GNU Public License V3 or later
7
- * **Version**: 0.1.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
- **Redbird-Python** 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.
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—**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).
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.8+
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 add the `redbirdpy` folder to your Python path.
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
- | `get_solver_info()` | Query available solver backends |
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': np.array([[0, 0, 1, 1], [0.012, 1.1, 0, 1.37]]),
344
- '830': np.array([[0, 0, 1, 1], [0.008, 0.9, 0, 1.37]])
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/redbirdpy).
541
+ Python translation based on the [Redbird MATLAB toolbox](https://github.com/fangq/redbird).
@@ -196,7 +196,7 @@ if __name__ == "__main__":
196
196
  subplot=133,
197
197
  parent=hh,
198
198
  )
199
- plt.show()
199
+ plt.show(block=(len(sys.argv) == 1))
200
200
 
201
201
  except Exception as e:
202
202
  print(f"Plotting skipped: {e}")
@@ -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
@@ -215,4 +215,4 @@ ax.set_ylabel("log10(Detector Value)")
215
215
  ax.set_title("Detector Readings vs Distance")
216
216
  ax.legend()
217
217
 
218
- plt.show()
218
+ plt.show(block=(len(sys.argv) == 1))
@@ -197,4 +197,4 @@ ax2.set_title("Forward solution from detector")
197
197
  plt.colorbar(tc2, ax=ax2, label="log10(fluence)")
198
198
 
199
199
  plt.tight_layout()
200
- plt.show()
200
+ plt.show(block=(len(sys.argv) == 1))
@@ -219,4 +219,4 @@ plt.colorbar(tc3, ax=ax3, label="log10(fluence)")
219
219
  ax3.legend()
220
220
 
221
221
  plt.tight_layout()
222
- plt.show()
222
+ plt.show(block=(len(sys.argv) == 1))
@@ -169,4 +169,4 @@ ax.set_ylabel("Residual")
169
169
  ax.set_title("Reconstruction Convergence")
170
170
  ax.grid(True)
171
171
 
172
- plt.show()
172
+ plt.show(block=(len(sys.argv) == 1))
@@ -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))
@@ -167,4 +167,4 @@ i2m.plotmesh(
167
167
  parent=hh,
168
168
  )
169
169
 
170
- plt.show()
170
+ plt.show(block=(len(sys.argv) == 1))
@@ -261,4 +261,4 @@ if HAS_MCX and fcw is not None:
261
261
  ]
262
262
  ax.legend(handles=legend_elements, loc="upper right")
263
263
 
264
- plt.show()
264
+ 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.1.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 :: 3 - Alpha",
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
 
@@ -52,7 +52,7 @@ Author: Translated from Redbird MATLAB toolbox by Qianqian Fang (q.fang <at> neu
52
52
  License: GPL version 3
53
53
  """
54
54
 
55
- __version__ = "0.1.0"
55
+ __version__ = "0.2.0"
56
56
  __author__ = "Qianqian Fang"
57
57
 
58
58
  from . import forward