| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
some refactor and code | 4 年前 | |
Tests directory linting pass | 5 年前 | |
Tests directory linting pass | 5 年前 | |
Tests directory linting pass | 5 年前 | |
added changes to tests to work on master branch | 7 年前 | |
Tests directory linting pass | 5 年前 | |
Add computeCOR: Python parity with MATLAB/Utilities/computeCOR.m The MATLAB toolbox has had computeCOR.m (a port of SophiaBeads' centre_geom.m; T. Liu, Optical Engineering 2009) for years; the Python bindings have no centre-of-rotation estimator. This adds one, checked against the MATLAB implementation line by line - the arrays are transposed relative to it throughout (MATLAB holds (detector, angle), Python (angle, detector)), and the module docstring records the mapping. One deliberate divergence, matching MATLAB's intent rather than a literal mask translation: MATLAB's length(find(test > 0)) is the count of valid pixels, so its score is a mean over valid conjugate rays. A boolean-mask len() would be the angle count - constant across candidates - which turns the search into a sum minimisation that rewards candidates whose reflected rays fall off the detector. Validated by recovery: a COR planted via geo.COR at 0 / +-0.5 / +1.5 / -2.0 mm comes back within 0.1 mm (test tolerance 0.2 mm). Tests: Python/tests/test_compute_cor.py (6). | 1 个月前 | |
Tests directory linting pass | 5 年前 | |
fix(python): avoid duplicate full-turn demo projections | 2 个月前 | |
fix: raise ValueError when im_3d_denoise receives 2D input | 3 个月前 | |
gpuids=None means every visible GPU, not no GPU Every compiled entry point declares gpuids=None in its OWN signature - _Ax_ext, _Atb_ext, minTV, AwminTV, tvdenoise, add_poisson - and all of them convert it through one helper, convert_to_c_gpuids in _gpuUtils.pxd. That helper turned None into a VALID pointer holding m_iCount = 0 and a NULL device list, and handed it to CUDA to run on zero devices. The algorithm classes hide it. They all do 'if self.gpuids is None: self.gpuids = GpuIds()' before calling down, so tigre.Ax/Atb and every algs.* function accept None happily - which is why this never showed up in normal use. The kernels reachable directly do not: minTV(img, alpha, iters, None) ends the process outright - no exception, no message, no traceback. Fixed in the helper rather than in seven call sites, since all seven already route through it. None now resolves to GpuIds(), whose no-argument form means 'all visible GPUs' - the same thing the algorithm classes were doing by hand. Also added the missing 'raise' to the malloc check beside it, which constructed a MemoryError and discarded it. Tests: Python/tests/test_gpuids_default.py (5). Note a regression there does not fail the tests, it aborts the interpreter running them - that is the nature of the bug. | 1 个月前 | |
Krylov: call l2norm directly, trim docstrings to what the code does Review follow-up on #774: - drop the _norm wrapper in krylov_subspace_algorithms.py; every call site uses tigre.utilities.im3Dnorm.l2norm directly (all were 2-norms) - CGLS.run_main_iter docstring now describes the two-loop structure and the sentinel, not the history; the stray string literals in LSQR/LSMR become a one-line comment - im3Dnorm: finish the l2norm sentence, fix indentation in inner - tests follow the rename - krylov_subspace_algorithms.py is LF throughout again (upstream is LF) | 29 天前 | |
IRN_TV_CGLS: give it a real adjoint and a divergence guard It diverged at 512^3 (mean 8469, max 5e7) and reproduces on a 64^3 phantom in seconds - correlation 0.06 against FDK's 0.42, voxel values reaching 7e5. Two causes, both fixed here. 1. L AND L^T WERE NOT AN ADJOINT PAIR (measured asymmetry 6.4e-2) CGLS is applied to the stacked operator [A; sqrt(lmbda) W D], and it assumes the transpose it is handed IS the adjoint. This one was wrong twice over: * `Dxx = np.copy(img)` followed by writing only `[0:-2]` left the LAST TWO slices holding raw image VALUES rather than differences. So D did not annihilate a constant image - it penalised intensity there, a Tikhonov term smuggled into part of the volume - and `D(1) != 0` is trivially checkable, which is now a test. * D^T was off by one at index n-2, leaving `Wx[n-2]` unreduced. Replaced with a derived pair, D u[i] = u[i]-u[i+1] (0 at i=n-1) and its exact transpose, applied per axis via swapaxes so the three directions share one implementation. Asymmetry is now 0.0e0 and D(constant) is exactly zero. 2. THE INNER CGLS HAD NO DIVERGENCE GUARD Plain CGLS has always had one; this loop ran niter_outer * niter unguarded steps. On the phantom the residual starts rising at step 2 and compounds: 9.7e3 -> 6.9e5 over 20 steps. The guard undoes the bad step and falls out to the next outer reweighting, which is this algorithm's natural restart. The comparison is scoped WITHIN one outer iteration on purpose. Across a reweighting the two residuals belong to different weighted problems, so a first step that looks worse than the previous outer's last is not evidence of anything - a first attempt that compared across the boundary stopped the solver after two productive steps. A separate check stops the run when a whole reweighting fails to improve the best residual, i.e. the outer loop stalled. Also removed `self.res = res0` at the top of the outer loop: res0 is reassigned inside the inner loop, so it restored the second-to-last iterate rather than any meaningful starting point. RESULT on the 64^3 phantom (FDK reference 0.4163): lmbda niter/outer before after 1 5/1 0.1420 0.4264 1 10/2 0.1044 0.4197 1 20/4 0.0276 0.3902 100 10/2 0.4509 0.4697 Every configuration is now stable, monotone across outer iterations, and in a physical value range - and at lmbda=100 it is the best method tested on this phantom, ahead of FDK 0.4163, FISTA 0.4204, ISTA 0.4379 and ASD-POCS 0.4101. Note lmbda=1, the default, is far too small for this scaling: ||Ax|| = 5.6e4 against ||Dx|| = 71 for the same image, so the TV block carries ~4% of the data block's weight. That is a tuning question, not a correctness one, and is left to the caller now that neither setting explodes. Tests: the adjoint identity and D(constant)=0 (no GPU), and irn_tv_cgls joins the end-to-end smoke test it previously could not pass. 22 pass. | 30 天前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 4 年前 | ||
| 5 年前 | ||
| 5 年前 | ||
| 5 年前 | ||
| 7 年前 | ||
| 5 年前 | ||
| 1 个月前 | ||
| 5 年前 | ||
| 2 个月前 | ||
| 3 个月前 | ||
| 1 个月前 | ||
| 29 天前 | ||
| 30 天前 |