Skip to content

Commit acb1694

Browse files
committed
feat: add utilities for flipping images
1 parent 0e73280 commit acb1694

File tree

2 files changed

+100
-2
lines changed

2 files changed

+100
-2
lines changed

src/arduino/app_utils/image/adjustments.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def letterbox(
6565
if frame.ndim == 2:
6666
# Greyscale
6767
if hasattr(color, "__len__"):
68-
color = color[0]
68+
raise ValueError("For greyscale images, color must be a scalar (int), not a tuple or list.")
6969
canvas = np.full((target_h, target_w), color, dtype=original_dtype)
7070
else:
7171
# Colored (BGR/BGRA)
@@ -108,6 +108,32 @@ def resize(frame: np.ndarray, target_size: Tuple[int, int], maintain_ratio: bool
108108
return cv2.resize(frame, (target_size[0], target_size[1]), interpolation=interpolation)
109109

110110

111+
def flip_h(frame: np.ndarray) -> np.ndarray:
112+
"""
113+
Flip frame horizontally.
114+
115+
Args:
116+
frame (np.ndarray): Input frame
117+
118+
Returns:
119+
np.ndarray: Horizontally flipped frame
120+
"""
121+
return frame[:, ::-1, ...]
122+
123+
124+
def flip_v(frame: np.ndarray) -> np.ndarray:
125+
"""
126+
Flip frame vertically.
127+
128+
Args:
129+
frame (np.ndarray): Input frame
130+
131+
Returns:
132+
np.ndarray: Vertically flipped frame
133+
"""
134+
return frame[::-1, :, ...]
135+
136+
111137
def adjust(frame: np.ndarray, brightness: float = 0.0, contrast: float = 1.0, saturation: float = 1.0, gamma: float = 1.0) -> np.ndarray:
112138
"""
113139
Apply image adjustments to a BGR or BGRA frame, preserving channel count
@@ -370,6 +396,26 @@ def resized(target_size: Tuple[int, int], maintain_ratio: bool = False, interpol
370396
return PipeableFunction(resize, target_size=target_size, maintain_ratio=maintain_ratio, interpolation=interpolation)
371397

372398

399+
def flipped_h():
400+
"""
401+
Pipeable horizontal flip function - flip frame horizontally with pipe operator support.
402+
403+
Returns:
404+
Function that takes a frame and returns horizontally flipped frame
405+
"""
406+
return PipeableFunction(flip_h)
407+
408+
409+
def flipped_v():
410+
"""
411+
Pipeable vertical flip function - flip frame vertically with pipe operator support.
412+
413+
Returns:
414+
Function that takes a frame and returns vertically flipped frame
415+
"""
416+
return PipeableFunction(flip_v)
417+
418+
373419
def adjusted(brightness: float = 0.0, contrast: float = 1.0, saturation: float = 1.0, gamma: float = 1.0):
374420
"""
375421
Pipeable adjust function - apply image adjustments with pipe operator support.

tests/arduino/app_utils/image/test_adjustments.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import numpy as np
66
import pytest
7-
from arduino.app_utils.image.adjustments import letterbox, resize, adjust, split_channels, greyscale
7+
from arduino.app_utils.image.adjustments import letterbox, resize, adjust, split_channels, greyscale, flip_h, flip_v
88

99

1010
# FIXTURES
@@ -440,3 +440,55 @@ def test_letterbox_color_tuple_error(frame_bgr_uint8):
440440
# BGRA (4-ch) frame with 3-ch padding
441441
frame_bgra = create_bgra_frame(np.uint8)
442442
letterbox(frame_bgra, (200, 200), color=(0, 0, 0))
443+
444+
445+
def test_flip_h_bgr(frame_bgr_uint8):
446+
"""Test horizontal flip for BGR image."""
447+
flipped = flip_h(frame_bgr_uint8)
448+
# Flipping twice should return the original
449+
assert np.array_equal(flip_h(flipped), frame_bgr_uint8)
450+
# Check that first column becomes last
451+
assert np.array_equal(flipped[:, 0, :], frame_bgr_uint8[:, -1, :])
452+
assert np.array_equal(flipped[:, -1, :], frame_bgr_uint8[:, 0, :])
453+
454+
455+
def test_flip_v_bgr(frame_bgr_uint8):
456+
"""Test vertical flip for BGR image."""
457+
flipped = flip_v(frame_bgr_uint8)
458+
# Flipping twice should return the original
459+
assert np.array_equal(flip_v(flipped), frame_bgr_uint8)
460+
# Check that first row becomes last
461+
assert np.array_equal(flipped[0, :, :], frame_bgr_uint8[-1, :, :])
462+
assert np.array_equal(flipped[-1, :, :], frame_bgr_uint8[0, :, :])
463+
464+
465+
def test_flip_h_greyscale(frame_grey_uint8):
466+
"""Test horizontal flip for greyscale image."""
467+
flipped = flip_h(frame_grey_uint8)
468+
assert np.array_equal(flip_h(flipped), frame_grey_uint8)
469+
assert np.array_equal(flipped[:, 0], frame_grey_uint8[:, -1])
470+
assert np.array_equal(flipped[:, -1], frame_grey_uint8[:, 0])
471+
472+
473+
def test_flip_v_greyscale(frame_grey_uint8):
474+
"""Test vertical flip for greyscale image."""
475+
flipped = flip_v(frame_grey_uint8)
476+
assert np.array_equal(flip_v(flipped), frame_grey_uint8)
477+
assert np.array_equal(flipped[0, :], frame_grey_uint8[-1, :])
478+
assert np.array_equal(flipped[-1, :], frame_grey_uint8[0, :])
479+
480+
481+
def test_flip_h_bgra(frame_bgra_uint8):
482+
"""Test horizontal flip for BGRA image."""
483+
flipped = flip_h(frame_bgra_uint8)
484+
assert np.array_equal(flip_h(flipped), frame_bgra_uint8)
485+
assert np.array_equal(flipped[:, 0, :], frame_bgra_uint8[:, -1, :])
486+
assert np.array_equal(flipped[:, -1, :], frame_bgra_uint8[:, 0, :])
487+
488+
489+
def test_flip_v_bgra(frame_bgra_uint8):
490+
"""Test vertical flip for BGRA image."""
491+
flipped = flip_v(frame_bgra_uint8)
492+
assert np.array_equal(flip_v(flipped), frame_bgra_uint8)
493+
assert np.array_equal(flipped[0, :, :], frame_bgra_uint8[-1, :, :])
494+
assert np.array_equal(flipped[-1, :, :], frame_bgra_uint8[0, :, :])

0 commit comments

Comments
 (0)