Skip to content

fix implementation mistakes and add conjugate gradients solver #7876

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f8f12b6
fix implementation mistakes
MrGranddy Jun 24, 2024
c856c31
Revert "fix implementation mistakes"
Jun 24, 2024
5d49fa9
fix implementation mistakes
MrGranddy Jun 24, 2024
df3b04c
fix edge penalty line and add test data
MrGranddy Jun 24, 2024
133c69f
update tests, add official code comparison test
MrGranddy Jun 24, 2024
d5d69f8
added conjugate gradient option for faster computation and tests
MrGranddy Jun 24, 2024
a90840c
add docs
MrGranddy Jun 24, 2024
abb2a26
add requirements, fix format
MrGranddy Jun 25, 2024
3111199
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 25, 2024
8278db3
fix capital letter variable name
MrGranddy Jun 25, 2024
bb696b4
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 25, 2024
3c414f4
Update monai/data/ultrasound_confidence_map.py
MrGranddy Jun 25, 2024
2d438cf
revert commented out requirement
MrGranddy Jun 25, 2024
3ff9fe9
remove residual examination code piece
MrGranddy Jun 25, 2024
da02700
add official code link to docstring
MrGranddy Jun 25, 2024
460c5e2
add additional information to docstring
MrGranddy Jun 25, 2024
2867b96
fix cg parameter name
MrGranddy Jun 25, 2024
a2b3881
Merge branch 'dev' into dev
MrGranddy Jun 25, 2024
6b409bc
fix long lines
MrGranddy Jun 25, 2024
661a0c3
Merge branch 'dev' of github.com:MrGranddy/MONAI into dev
MrGranddy Jun 25, 2024
2a66079
Merge branch 'dev' into dev
KumoLiu Jun 26, 2024
fcaac79
Merge branch 'dev' into dev
ericspod Jun 27, 2024
d965a9f
Merge branch 'dev' into dev
KumoLiu Jun 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ onnx>=1.13.0
onnxruntime; python_version <= '3.10'
zarr
huggingface_hub
pyamg>=5.0.0
2 changes: 1 addition & 1 deletion docs/source/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,6 @@ Since MONAI v0.2.0, the extras syntax such as `pip install 'monai[nibabel]'` is
```

which correspond to `nibabel`, `scikit-image`,`scipy`, `pillow`, `tensorboard`,
`gdown`, `pytorch-ignite`, `torchvision`, `itk`, `tqdm`, `lmdb`, `psutil`, `cucim`, `openslide-python`, `pandas`, `einops`, `transformers`, `mlflow`, `clearml`, `matplotlib`, `tensorboardX`, `tifffile`, `imagecodecs`, `pyyaml`, `fire`, `jsonschema`, `ninja`, `pynrrd`, `pydicom`, `h5py`, `nni`, `optuna`, `onnx`, `onnxruntime`, `zarr`, `lpips`, `nvidia-ml-py`, and `huggingface_hub` respectively.
`gdown`, `pytorch-ignite`, `torchvision`, `itk`, `tqdm`, `lmdb`, `psutil`, `cucim`, `openslide-python`, `pandas`, `einops`, `transformers`, `mlflow`, `clearml`, `matplotlib`, `tensorboardX`, `tifffile`, `imagecodecs`, `pyyaml`, `fire`, `jsonschema`, `ninja`, `pynrrd`, `pydicom`, `h5py`, `nni`, `optuna`, `onnx`, `onnxruntime`, `zarr`, `lpips`, `nvidia-ml-py`, `huggingface_hub` and `pyamg` respectively.

- `pip install 'monai[all]'` installs all the optional dependencies.
40 changes: 33 additions & 7 deletions monai/data/ultrasound_confidence_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
cv2, _ = optional_import("cv2")
csc_matrix, _ = optional_import("scipy.sparse", "1.7.1", min_version, "csc_matrix")
spsolve, _ = optional_import("scipy.sparse.linalg", "1.7.1", min_version, "spsolve")
cg, _ = optional_import("scipy.sparse.linalg", "1.7.1", min_version, "cg")
hilbert, _ = optional_import("scipy.signal", "1.7.1", min_version, "hilbert")
ruge_stuben_solver, _ = optional_import("pyamg", "5.0.0", min_version, "ruge_stuben_solver")


class UltrasoundConfidenceMap:
Expand All @@ -37,15 +39,31 @@ class UltrasoundConfidenceMap:
mode (str, optional): 'RF' or 'B' mode data. Defaults to 'B'.
sink_mode (str, optional): Sink mode. Defaults to 'all'. If 'mask' is selected, a mask must be when calling
the transform. Can be 'all', 'mid', 'min', or 'mask'.
use_cg (bool, optional): Use Conjugate Gradient method for solving the linear system. Defaults to False.
cg_tol (float, optional): Tolerance for the Conjugate Gradient method. Defaults to 1e-6.
cg_maxiter (int, optional): Maximum number of iterations for the Conjugate Gradient method. Defaults to 200.
"""

def __init__(self, alpha: float = 2.0, beta: float = 90.0, gamma: float = 0.05, mode="B", sink_mode="all"):
def __init__(
self,
alpha: float = 2.0,
beta: float = 90.0,
gamma: float = 0.05,
mode="B",
sink_mode="all",
use_cg=False,
cg_tol=1e-6,
cg_maxiter=200,
):
# The hyperparameters for confidence map estimation
self.alpha = alpha
self.beta = beta
self.gamma = gamma
self.mode = mode
self.sink_mode = sink_mode
self.use_cg = use_cg
self.cg_tol = cg_tol
self.cg_maxiter = cg_maxiter

# The precision to use for all computations
self.eps = np.finfo("float64").eps
Expand Down Expand Up @@ -228,17 +246,18 @@ def confidence_laplacian(self, padded_index: NDArray, padded_image: NDArray, bet
s = self.normalize(s)

# Horizontal penalty
s[:vertical_end] += gamma
# s[vertical_end:diagonal_end] += gamma * np.sqrt(2) # --> In the paper it is sqrt(2)
# since the diagonal edges are longer yet does not exist in the original code
s[vertical_end:] += gamma
# Here there is a difference between the official MATLAB code and the paper
# on the edge penalty. We directly implement what the official code does.

# Normalize differences
s = self.normalize(s)

# Gaussian weighting function
s = -(
(np.exp(-beta * s, dtype="float64")) + 1.0e-6
) # --> This epsilon changes results drastically default: 1.e-6
(np.exp(-beta * s, dtype="float64")) + 10e-6
) # --> This epsilon changes results drastically default: 10e-6
# Please notice that it is not 1e-6, it is 10e-6 which is actually different.

# Create Laplacian, diagonal missing
lap = csc_matrix((s, (i, j)))
Expand All @@ -256,7 +275,14 @@ def confidence_laplacian(self, padded_index: NDArray, padded_image: NDArray, bet
return lap

def _solve_linear_system(self, lap, rhs):
x = spsolve(lap, rhs)

if self.use_cg:
lap_sparse = lap.tocsr()
ml = ruge_stuben_solver(lap_sparse, coarse_solver="pinv")
m = ml.aspreconditioner(cycle="V")
x, _ = cg(lap, rhs, rtol=self.cg_tol, maxiter=self.cg_maxiter, M=m)
else:
x = spsolve(lap, rhs)

return x

Expand Down
22 changes: 20 additions & 2 deletions monai/transforms/intensity/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -2796,14 +2796,30 @@ class UltrasoundConfidenceMapTransform(Transform):
mode (str, optional): 'RF' or 'B' mode data. Defaults to 'B'.
sink_mode (str, optional): Sink mode. Defaults to 'all'. If 'mask' is selected, a mask must be when
calling the transform. Can be one of 'all', 'mid', 'min', 'mask'.
use_cg (bool, optional): Use Conjugate Gradient method for solving the linear system. Defaults to False.
cg_tol (float, optional): Tolerance for the Conjugate Gradient method. Defaults to 1e-6.
cg_maxiter (int, optional): Maximum number of iterations for the Conjugate Gradient method. Defaults to 200.
"""

def __init__(self, alpha: float = 2.0, beta: float = 90.0, gamma: float = 0.05, mode="B", sink_mode="all") -> None:
def __init__(
self,
alpha: float = 2.0,
beta: float = 90.0,
gamma: float = 0.05,
mode="B",
sink_mode="all",
use_cg=False,
cg_tol: float = 1.0e-6,
cg_maxiter: int = 200,
):
self.alpha = alpha
self.beta = beta
self.gamma = gamma
self.mode = mode
self.sink_mode = sink_mode
self.use_cg = use_cg
self.cg_tol = cg_tol
self.cg_maxiter = cg_maxiter

if self.mode not in ["B", "RF"]:
raise ValueError(f"Unknown mode: {self.mode}. Supported modes are 'B' and 'RF'.")
Expand All @@ -2813,7 +2829,9 @@ def __init__(self, alpha: float = 2.0, beta: float = 90.0, gamma: float = 0.05,
f"Unknown sink mode: {self.sink_mode}. Supported modes are 'all', 'mid', 'min' and 'mask'."
)

self._compute_conf_map = UltrasoundConfidenceMap(self.alpha, self.beta, self.gamma, self.mode, self.sink_mode)
self._compute_conf_map = UltrasoundConfidenceMap(
self.alpha, self.beta, self.gamma, self.mode, self.sink_mode, self.use_cg, self.cg_tol, self.cg_maxiter
)

def __call__(self, img: NdarrayOrTensor, mask: NdarrayOrTensor | None = None) -> NdarrayOrTensor:
"""Compute confidence map from an ultrasound image.
Expand Down
3 changes: 2 additions & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ mypy>=1.5.0
ninja
torchvision
psutil
cucim-cu12; platform_system == "Linux" and python_version >= "3.9" and python_version <= "3.10"
#cucim-cu12; platform_system == "Linux" and python_version >= "3.9" and python_version <= "3.10"
openslide-python
imagecodecs; platform_system == "Linux" or platform_system == "Darwin"
tifffile; platform_system == "Linux" or platform_system == "Darwin"
Expand Down Expand Up @@ -57,3 +57,4 @@ zarr
lpips==0.1.4
nvidia-ml-py
huggingface_hub
pyamg>=5.0.0
3 changes: 3 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ all =
lpips==0.1.4
nvidia-ml-py
huggingface_hub
pyamg>=5.0.0
nibabel =
nibabel
ninja =
Expand Down Expand Up @@ -162,6 +163,8 @@ pynvml =
# MetricsReloaded @ git+https://github.com/Project-MONAI/MetricsReloaded@monai-support#egg=MetricsReloaded
huggingface_hub =
huggingface_hub
pyamg =
pyamg>=5.0.0

[flake8]
select = B,C,E,F,N,P,T4,W,B9
Expand Down
Loading
Loading