diff --git a/pyproject.toml b/pyproject.toml index 6b48ec4..70a8f24 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ ] dependencies = [ "appdirs == 1.4.4", - "cmapy == 0.6.6", # TODO(ecyoung3): Replace with own implementation + "matplotlib == 3.8.*", "numpy == 1.26.*", "opencv-python == 4.9.0.*", "PyQt6 == 6.7.*", diff --git a/requirements-dev.txt b/requirements-dev.txt index 8b371b7..336e2bf 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,45 +1,41 @@ # This file was autogenerated by uv via the following command: # uv pip compile pyproject.toml -o requirements-dev.txt --extra dev appdirs==1.4.4 -cmapy==0.6.6 colorama==0.4.6 # via pytest -contourpy==1.1.1 +contourpy==1.2.1 # via matplotlib cycler==0.12.1 # via matplotlib exceptiongroup==1.2.1 # via pytest -fonttools==4.43.1 +fonttools==4.51.0 # via matplotlib iniconfig==2.0.0 # via pytest kiwisolver==1.4.5 # via matplotlib -matplotlib==3.8.0 - # via cmapy +matplotlib==3.8.4 mypy==1.10.0 mypy-extensions==1.0.0 # via mypy numpy==1.26.1 # via - # cmapy # contourpy # matplotlib # opencv-python # pyqtgraph # scipy opencv-python==4.9.0.80 - # via cmapy packaging==23.2 # via # matplotlib # pytest -pillow==10.1.0 +pillow==10.3.0 # via matplotlib pluggy==1.5.0 # via pytest -pyparsing==3.1.1 +pyparsing==3.1.2 # via matplotlib pyqt6==6.7.0 pyqt6-qt6==6.7.0 @@ -48,7 +44,7 @@ pyqt6-sip==13.6.0 # via pyqt6 pyqtgraph==0.13.3 pytest==8.2.0 -python-dateutil==2.8.2 +python-dateutil==2.9.0.post0 # via matplotlib ruff==0.4.3 scipy==1.13.0 diff --git a/requirements.txt b/requirements.txt index 07dcde6..9372d1d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,32 +1,28 @@ # This file was autogenerated by uv via the following command: # uv pip compile pyproject.toml -o requirements.txt appdirs==1.4.4 -cmapy==0.6.6 -contourpy==1.1.1 +contourpy==1.2.1 # via matplotlib cycler==0.12.1 # via matplotlib -fonttools==4.43.1 +fonttools==4.51.0 # via matplotlib kiwisolver==1.4.5 # via matplotlib -matplotlib==3.8.0 - # via cmapy +matplotlib==3.8.4 numpy==1.26.1 # via - # cmapy # contourpy # matplotlib # opencv-python # pyqtgraph # scipy opencv-python==4.9.0.80 - # via cmapy -packaging==23.2 +packaging==24.0 # via matplotlib -pillow==10.1.0 +pillow==10.3.0 # via matplotlib -pyparsing==3.1.1 +pyparsing==3.1.2 # via matplotlib pyqt6==6.7.0 pyqt6-qt6==6.7.0 @@ -34,7 +30,7 @@ pyqt6-qt6==6.7.0 pyqt6-sip==13.6.0 # via pyqt6 pyqtgraph==0.13.3 -python-dateutil==2.8.2 +python-dateutil==2.9.0.post0 # via matplotlib scipy==1.13.0 six==1.16.0 diff --git a/src/frheed/image_processing.py b/src/frheed/image_processing.py index ef2a15a..d979534 100644 --- a/src/frheed/image_processing.py +++ b/src/frheed/image_processing.py @@ -2,7 +2,6 @@ Assorted image processing operations. """ -import cmapy import cv2 import numpy as np from matplotlib import pyplot as plt @@ -42,12 +41,9 @@ def normalize(arr: np.ndarray) -> np.ndarray: return arr.astype(np.uint8, copy=True) -def apply_cmap(arr: np.ndarray, cmap: str) -> np.ndarray: +def apply_cmap(arr: np.ndarray, cmap_name: str, bgr_order: bool = False) -> np.ndarray: """ - Apply a named colormap to an array. This function uses the cmapy library - to convert matplotlib colormaps to cv2 colormaps, since cv2.applyColormap is - approximately 5x faster than using matplotlib/numpy methods - (tested using uint8 2048 x 1536 arrays, ~30ms vs ~6ms). + Apply a named colormap to an array. Parameters ---------- @@ -55,6 +51,8 @@ def apply_cmap(arr: np.ndarray, cmap: str) -> np.ndarray: The array to apply the colormap to. The array must be single-channel (not RGB). cmap : str The name of the colormap to apply (any valid matplotlib colormap). + rgbA_order : bool + Whether the provided array is in BGR order instead of RGB order. Returns ------- @@ -65,8 +63,20 @@ def apply_cmap(arr: np.ndarray, cmap: str) -> np.ndarray: and width of the input array. """ - colorized_arr: np.ndarray = cmapy.colorize(normalize(arr), cmap, rgb_order=True) - return colorized_arr + cmap = plt.get_cmap(cmap_name, 256) + rgba_data = plt.cm.ScalarMappable(cmap=cmap).to_rgba(np.arange(0, 1, 1 / 256), bytes=True) + rgba_data = rgba_data[:, 0:-1].reshape((256, 1, 3)) + + # Convert to 3-channel RGB/BGR uint8 for OpenCV + cmap_data = np.zeros((256, 1, 3), np.uint8) + + # Remove the alpha channel and optionally reverse RGB to BGR + if bgr_order: + cmap_data[:, :, :] = rgba_data[:, :, ::-1] + else: + cmap_data[:, :, :] = rgba_data[:, :, :] + + return cv2.applyColorMap(arr, cmap_data) def to_grayscale(array: np.ndarray) -> np.ndarray: @@ -124,4 +134,4 @@ def extend_image(image: np.ndarray, new_col: np.ndarray) -> np.ndarray: def get_valid_colormaps() -> list[str]: - return plt.colormaps() + return list(plt.colormaps())