Skip to content

Commit

Permalink
Merge branch 'dev/fix-opencv-headless-compatibility' into 'main'
Browse files Browse the repository at this point in the history
Fix opencv-python and opencv-python-headless compatibility

See merge request jatic/kitware/pybsm!105
  • Loading branch information
eveenhuis committed Sep 12, 2024
2 parents 8bf3e4d + 051d71b commit 47b4698
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 11 deletions.
6 changes: 6 additions & 0 deletions .gitlab-ci/.gitlab-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@
#
# 1) pybsm specific notebooks.
# 2) Added the original example as a job
# 3) Poetry install for opencv-python-headless
#
###############################################################################

.test-setup:
before_script:
- !reference [.shared-setup, before_script]
- poetry install --sync --only main,dev-testing --extras headless

notebooks:
parallel:
matrix:
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,30 @@ See [here for more installation documentation](
https://pybsm.readthedocs.io/en/latest/installation.html).
<!-- :auto installation: -->

Installing using the previous commands will not install OpenCV. While pyBSM will still function, there are a handful of functions that require OpenCV.

There are two options for installing pyBSM with OpenCV: graphics or headless. Graphics will install `opencv-python` as the OpenCV implementation while headless will install `opencv-python-headless` as the OpenCV implementation.

To install pybsm with `opencv-python`
* using pip:
```bash
pip install pybsm[graphics]
```
* using Poetry
```bash
poetry install --sync --with dev-linting,dev-testing,dev-docs --extras graphics
```

To install pybsm with `opencv-python-headless`
* using pip:
```bash
pip install pybsm[headless]
```
* using Poetry
```bash
poetry install --sync --with dev-linting,dev-testing,dev-docs --extras headless
```

<!-- :auto getting-started: -->
## Getting Started
We provide a number of examples based on Jupyter notebooks in the
Expand Down
8 changes: 8 additions & 0 deletions docs/release_notes/pending_release.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ CI/CD

* Updates to dependencies to support the new CI/CD.

* Changed `opencv-python` to an optional dependency.

* Added `opencv-python-headless` as an optional dependency.

* Added two extras (graphics and headless) for `opencv-python` and `opencv-python-headless` compatibility.

* Changed CI to use headless extra.

Documentation

* Added sphinx's autosummary template for recursively populating
Expand Down
37 changes: 34 additions & 3 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,13 @@ scipy = [
{version = "<1.11.1", python = "~3.8.1"}, # Can't satisfy CVE-2023-25399 because it is too restrictive
{version = ">=1.10.0,<1.14", python = ">=3.9"}, # CVE-2023-25399
]
opencv-python = ">=4.6"
setuptools = ">=65.6.1"
opencv-python = {version = ">=4.6", optional = true}
opencv-python-headless = {version = ">=4.6", optional = true}

[tool.poetry.extras]
graphics = ["opencv-python"]
headless = ["opencv-python-headless"]

# :auto dev-linting:
# Linting
Expand Down
28 changes: 22 additions & 6 deletions src/pybsm/otf/functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,12 @@
import warnings
from typing import Callable, Tuple

import cv2
try:
import cv2

is_usable = True
except ImportError:
is_usable = False

# 3rd party imports
import numpy as np
Expand Down Expand Up @@ -671,11 +676,10 @@ def polychromatic_turbulence_OTF( # noqa: N802

# calculate the coherence diameter over the band
r0_at_1um = coherence_diameter(1.0e-6, z_path, cn2)
r0_function = (
lambda wav: r0_at_1um # noqa: E731
* wav ** (6.0 / 5.0)
* (1e-6) ** (-6.0 / 5.0)
)

def r0_function(wav: float) -> float:
return r0_at_1um * wav ** (6.0 / 5.0) * (1e-6) ** (-6.0 / 5.0) # noqa: E731

r0_band = weighted_by_wavelength(wavelengths, weights, r0_function)

# calculate the turbulence OTF
Expand Down Expand Up @@ -1077,6 +1081,10 @@ def otf_to_psf(otf: np.ndarray, df: float, dx_out: float) -> np.ndarray:
if df or dx_out are 0
"""
if not is_usable:
raise ImportError(
"OpenCV not found. Please install 'pybsm[graphics]' or 'pybsm[headless]'."
)
# transform the psf
psf = np.real(np.fft.fftshift(np.fft.ifft2(np.fft.fftshift(otf))))

Expand Down Expand Up @@ -1419,6 +1427,10 @@ def apply_otf_to_image(
# this function. Therefore, we can calculate the instantaneous field of view
# (iFOV) of the assumed real camera, which is
# 2*arctan(ref_gsd/2/ref_range).
if not is_usable:
raise ImportError(
"OpenCV not found. Please install 'pybsm[graphics]' or 'pybsm[headless]'."
)
psf = otf_to_psf(otf, df, 2 * np.arctan(ref_gsd / 2 / ref_range))

# filter the image
Expand Down Expand Up @@ -1581,6 +1593,10 @@ def resample_2D( # noqa: N802
if dx_in is 0
"""
if not is_usable:
raise ImportError(
"OpenCV not found. Please install 'pybsm[graphics]' or 'pybsm[headless]'."
)
new_x = int(np.round(img_in.shape[1] * dx_in / dx_out))
new_y = int(np.round(img_in.shape[0] * dx_in / dx_out))
img_out = cv2.resize(img_in, (new_x, new_y))
Expand Down
20 changes: 19 additions & 1 deletion tests/otf/test_otf.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import unittest.mock as mock
from typing import Callable, Dict, Tuple

import cv2
import numpy as np
import pytest

from pybsm import otf
from pybsm.simulation import Scenario, Sensor

try:
import cv2

is_usable = True
except ImportError:
is_usable = False


@pytest.mark.skipif(
not is_usable,
reason="OpenCV not found. Please install 'pybsm[graphics]' or `pybsm[headless]`.",
)
class TestOTF:
@pytest.mark.parametrize(
("lambda0", "z_path", "cn2"),
Expand Down Expand Up @@ -1680,3 +1691,10 @@ def test_apply_otf_to_image(
)
assert np.isclose(output[0], expected[0], atol=5e-20).all()
assert np.isclose(output[1], expected[1], atol=5e-20).all()


@mock.patch("pybsm.otf.functional.is_usable", False)
def test_missing_deps() -> None:
"""Test that an exception is raised when required dependencies are not installed."""
with pytest.raises(ImportError, match=r"OpenCV not found"):
otf.resample_2D(np.ones((5, 5)), 1.0, 1.0)

0 comments on commit 47b4698

Please sign in to comment.