Skip to content

Commit

Permalink
remove usage of opencv-python entirely
Browse files Browse the repository at this point in the history
  • Loading branch information
zacharyburnett committed May 1, 2024
1 parent ddb6758 commit 9b2cba5
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 86 deletions.
6 changes: 0 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,6 @@ jobs:
- toxenv: test-numpy120
os: ubuntu-latest
python-version: '3.8'
- toxenv: test-opencv-xdist
os: ubuntu-latest
python-version: '3.10'
- toxenv: test-scikitimage-xdist
os: ubuntu-latest
python-version: '3.10'
- toxenv: test-jwst-xdist-cov
os: ubuntu-latest
python-version: '3.x'
Expand Down
9 changes: 2 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ classifiers = [
]
dependencies = [
'astropy >=5.0.4',
'scikit-image',
'scipy >=1.6.0',
'shapely',
'numpy >=1.17',
]
dynamic = ['version']
Expand All @@ -35,13 +37,6 @@ test = [
'pytest-doctestplus',
'pytest-openfiles >=0.5.0',
]
ellipse = [
'scikit-image',
'shapely',
]
opencv = [
'opencv-python>=4.6.0.66',
]

[project.urls]
'repository' = 'https://github.com/spacetelescope/stcal'
Expand Down
96 changes: 28 additions & 68 deletions src/stcal/jump/jump.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,11 @@
from . import constants
from . import twopoint_difference as twopt

ELLIPSE_PACKAGE = None
try:
import cv2 as cv

ELLIPSE_PACKAGE = 'opencv-python'
except (ImportError, ModuleNotFoundError):
try:
import shapely.geometry
import skimage.draw
import skimage.measure

from .circle import Circle

ELLIPSE_PACKAGE = 'scikit-image'
ELLIPSE_PACKAGE_WARNING = '`opencv-python` not installed; ' \
'using `scikit-image` + `shapely` for ellipse construction'
except (ImportError, ModuleNotFoundError):
ELLIPSE_PACKAGE_WARNING = 'an image processing package (either `opencv-python` or `scikit-image` + `shapely`)' \
'must be installed in order to use ellipses'
import shapely.geometry
import skimage.draw
import skimage.measure

from .circle import Circle

log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
Expand Down Expand Up @@ -413,14 +399,9 @@ 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)
elif ELLIPSE_PACKAGE == 'scikit-image':
warnings.warn(ELLIPSE_PACKAGE_WARNING)
disk = skimage.draw.disk(center, extend_radius)
image[disk] = color
else:
raise ModuleNotFoundError(ELLIPSE_PACKAGE_WARNING)
warnings.warn(ELLIPSE_PACKAGE_WARNING)
disk = skimage.draw.disk(center, extend_radius)
image[disk] = color
jump_circle = image[:, :, 2]
saty, satx = np.where(sat_pix == 2)
jump_circle[saty, satx] = 0
Expand Down Expand Up @@ -452,14 +433,9 @@ 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)
elif ELLIPSE_PACKAGE == 'scikit-image':
warnings.warn(ELLIPSE_PACKAGE_WARNING)
ellipse = skimage.draw.ellipse(*center, *axes, rotation=alpha)
image[ellipse] = color
else:
raise ModuleNotFoundError(ELLIPSE_PACKAGE_WARNING)
warnings.warn(ELLIPSE_PACKAGE_WARNING)
ellipse = skimage.draw.ellipse(*center, *axes, rotation=alpha)
image[ellipse] = color
jump_ellipse = image[:, :, 2]
saty, satx = np.where(sat_pix == 2)
jump_ellipse[saty, satx] = 0
Expand All @@ -471,16 +447,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]
elif ELLIPSE_PACKAGE == 'scikit-image':
contours = [shapely.geometry.Polygon(con) for con in skimage.measure.find_contours(pixels)]
bigcontours = [con for con in contours if con.area > min_area]
circles = [Circle.from_points(numpy.stack(con.exterior.xy, axis=1)) for con in bigcontours]
else:
raise ModuleNotFoundError(ELLIPSE_PACKAGE_WARNING)
contours = [shapely.geometry.Polygon(con) for con in skimage.measure.find_contours(pixels)]
bigcontours = [con for con in contours if con.area > min_area]
circles = [Circle.from_points(numpy.stack(con.exterior.xy, axis=1)) for con in bigcontours]
return circles


Expand All @@ -489,29 +458,20 @@ def find_ellipses(dqplane, bitmask, min_area):
# 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]
elif ELLIPSE_PACKAGE == 'scikit-image':
contours = [shapely.geometry.Polygon(con) for con in skimage.measure.find_contours(pixels)]
bigcontours = [con for con in contours if con.area > min_area]
rectangles = [
numpy.flip(numpy.stack(con.minimum_rotated_rectangle.exterior.xy, axis=1), axis=1)
for con in bigcontours
]
ellipses = [
(
numpy.mean(rectangle[[0, 2], :], axis=0),
numpy.hypot(*numpy.diff(rectangle[[0, 1, 2], :], axis=0)),
numpy.degrees(numpy.arctan2(*numpy.flip(numpy.diff(rectangle[[3, 0], :], axis=0)[0])))
)
for rectangle in rectangles
]
else:
raise ModuleNotFoundError(ELLIPSE_PACKAGE_WARNING)
contours = [shapely.geometry.Polygon(con) for con in skimage.measure.find_contours(pixels)]
bigcontours = [con for con in contours if con.area > min_area]
rectangles = [
numpy.flip(numpy.stack(con.minimum_rotated_rectangle.exterior.xy, axis=1), axis=1)
for con in bigcontours
]
ellipses = [
(
numpy.mean(rectangle[[0, 2], :], axis=0),
numpy.hypot(*numpy.diff(rectangle[[0, 1, 2], :], axis=0)),
numpy.degrees(numpy.arctan2(*numpy.flip(numpy.diff(rectangle[[3, 0], :], axis=0)[0])))
)
for rectangle in rectangles
]

return ellipses

Expand Down
5 changes: 0 additions & 5 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,11 @@ description =
run tests
jwst: of JWST pipeline
romancal: of Romancal pipeline
opencv: requiring opencv-python
scikitimage: requiring scikit-image
warnings: treating warnings as errors
cov: with coverage
xdist: using parallel processing
extras =
test
scikitimage: ellipse
opencv: opencv
deps =
xdist: pytest-xdist
jwst: jwst[test] @ git+https://github.com/spacetelescope/jwst.git
Expand All @@ -79,7 +75,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,scikitimage: -- tests/test_jump.py \
{posargs}

[testenv:build-docs]
Expand Down

0 comments on commit 9b2cba5

Please sign in to comment.