diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 07311775..a95328d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,6 @@ jobs: python-version: '3.11' - macos: test-xdist python-version: '3.11' - - linux: test-opencv-xdist - linux: test-cov-xdist coverage: 'codecov' test_downstream: diff --git a/CHANGES.rst b/CHANGES.rst index a4c29c77..5bfed34f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -22,6 +22,8 @@ Other - update minimum version of ``numpy`` to ``1.20`` and add minimum dependency testing to CI [#153] +- restore ``opencv-python`` to a hard dependency [#155] + 1.3.4 (2023-02-13) ================== diff --git a/pyproject.toml b/pyproject.toml index b991b1a2..32507ea2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ dependencies = [ 'astropy >=5.0.4', 'scipy >=1.6.0', 'numpy >=1.20', + 'opencv-python >=4.6.0.66', ] dynamic = ['version'] @@ -35,9 +36,6 @@ test = [ 'pytest-doctestplus', 'pytest-openfiles >=0.5.0', ] -opencv = [ - 'opencv-python >=4.6.0.66', -] [project.urls] 'repository' = 'https://github.com/spacetelescope/stcal' diff --git a/src/stcal/jump/jump.py b/src/stcal/jump/jump.py index 5ed1ca43..56ed16d2 100644 --- a/src/stcal/jump/jump.py +++ b/src/stcal/jump/jump.py @@ -3,19 +3,11 @@ import time import numpy as np +import cv2 as cv from . import constants from . import twopoint_difference as twopt -ELLIPSE_PACKAGE = None -try: - import cv2 as cv - - ELLIPSE_PACKAGE = 'opencv-python' -except (ImportError, ModuleNotFoundError): - ELLIPSE_PACKAGE_WARNING = '`opencv-python` must be installed (`pip install stcal[opencv]`) ' \ - 'in order to use ellipses' - log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) @@ -400,10 +392,7 @@ def extend_snowballs(plane, snowballs, sat_flag, jump_flag, expansion=1.5): center = (round(ceny), round(cenx)) extend_radius = round(jump_radius * expansion) color = (0, 0, 4) - if ELLIPSE_PACKAGE == 'opencv-python': - image = cv.circle(image, center, extend_radius, color, -1) - else: - raise ModuleNotFoundError(ELLIPSE_PACKAGE_WARNING) + image = cv.circle(image, center, extend_radius, color, -1) jump_circle = image[:, :, 2] saty, satx = np.where(sat_pix == 2) jump_circle[saty, satx] = 0 @@ -435,10 +424,7 @@ def extend_ellipses(plane, ellipses, sat_flag, jump_flag, expansion=1.1): center = (round(ceny), round(cenx)) axes = (round(axis1 / 2), round(axis2 / 2)) color = (0, 0, 4) - if ELLIPSE_PACKAGE == 'opencv-python': - image = cv.ellipse(image, center, axes, alpha, 0, 360, color, -1) - else: - raise ModuleNotFoundError(ELLIPSE_PACKAGE_WARNING) + image = cv.ellipse(image, center, axes, alpha, 0, 360, color, -1) jump_ellipse = image[:, :, 2] saty, satx = np.where(sat_pix == 2) jump_ellipse[saty, satx] = 0 @@ -450,12 +436,9 @@ def find_circles(dqplane, bitmask, min_area): # Using an input DQ plane this routine will find the groups of pixels with at least the minimum # area and return a list of the minimum enclosing circle parameters. pixels = np.bitwise_and(dqplane, bitmask) - if ELLIPSE_PACKAGE == 'opencv-python': - contours, hierarchy = cv.findContours(pixels, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) - bigcontours = [con for con in contours if cv.contourArea(con) >= min_area] - circles = [cv.minEnclosingCircle(con) for con in bigcontours] - else: - raise ModuleNotFoundError(ELLIPSE_PACKAGE_WARNING) + contours, hierarchy = cv.findContours(pixels, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) + bigcontours = [con for con in contours if cv.contourArea(con) >= min_area] + circles = [cv.minEnclosingCircle(con) for con in bigcontours] return circles @@ -463,14 +446,11 @@ def find_ellipses(dqplane, bitmask, min_area): # Using an input DQ plane this routine will find the groups of pixels with at least the minimum # area and return a list of the minimum enclosing ellipse parameters. pixels = np.bitwise_and(dqplane, bitmask) - if ELLIPSE_PACKAGE == 'opencv-python': - contours, hierarchy = cv.findContours(pixels, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) - bigcontours = [con for con in contours if cv.contourArea(con) > min_area] - # minAreaRect is used becuase fitEllipse requires 5 points and it is possible to have a contour - # with just 4 points. - ellipses = [cv.minAreaRect(con) for con in bigcontours] - else: - raise ModuleNotFoundError(ELLIPSE_PACKAGE_WARNING) + contours, hierarchy = cv.findContours(pixels, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) + bigcontours = [con for con in contours if cv.contourArea(con) > min_area] + # minAreaRect is used becuase fitEllipse requires 5 points and it is possible to have a contour + # with just 4 points. + ellipses = [cv.minAreaRect(con) for con in bigcontours] return ellipses diff --git a/tests/test_jump.py b/tests/test_jump.py index f797cd3c..b579fe96 100644 --- a/tests/test_jump.py +++ b/tests/test_jump.py @@ -6,14 +6,6 @@ DQFLAGS = {'JUMP_DET': 4, 'SATURATED': 2, 'DO_NOT_USE': 1} -try: - import cv2 as cv # noqa: F401 - - OPENCV_INSTALLED = True -except ImportError: - OPENCV_INSTALLED = False - - @pytest.fixture(scope='function') def setup_cube(): def _cube(ngroups, readnoise=10): @@ -31,7 +23,6 @@ def _cube(ngroups, readnoise=10): return _cube -@pytest.mark.xfail(not OPENCV_INSTALLED, reason="`opencv-python` not installed") def test_find_simple_circle(): plane = np.zeros(shape=(5, 5), dtype=np.uint8) plane[2, 2] = DQFLAGS['JUMP_DET'] @@ -43,7 +34,6 @@ def test_find_simple_circle(): assert circle[0][1] == pytest.approx(1.0, 1e-3) -@pytest.mark.xfail(not OPENCV_INSTALLED, reason="`opencv-python` not installed") def test_find_simple_ellipse(): plane = np.zeros(shape=(5, 5), dtype=np.uint8) plane[2, 2] = DQFLAGS['JUMP_DET'] diff --git a/tox.ini b/tox.ini index 7dafa35d..76de5ad7 100644 --- a/tox.ini +++ b/tox.ini @@ -44,7 +44,6 @@ commands = [testenv] description = run tests - opencv: requiring opencv-python jwst: of JWST pipeline romancal: of Romancal pipeline oldestdeps: with the oldest supported version of key dependencies @@ -53,7 +52,6 @@ description = xdist: using parallel processing extras = test - opencv: opencv deps = xdist: pytest-xdist jwst: jwst[test] @ git+https://github.com/spacetelescope/jwst.git @@ -78,7 +76,6 @@ commands = jwst: --pyargs jwst --ignore-glob=timeconversion --ignore-glob=associations --ignore-glob=*/scripts/* \ romancal: --pyargs romancal \ cov: --cov=. --cov-config=pyproject.toml --cov-report=term-missing --cov-report=xml \ - opencv: -- tests/test_jump.py \ {posargs} [testenv:build-docs]