joyjonesmark's picture
Initial deploy with models
e5abc2e
"""
Data augmentation utilities for the Emotion Recognition System.
"""
import numpy as np
from typing import Tuple, Optional
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import sys
from pathlib import Path
sys.path.append(str(Path(__file__).parent.parent.parent))
from src.config import AUGMENTATION_CONFIG
def get_augmentation_generator(
rotation_range: int = AUGMENTATION_CONFIG["rotation_range"],
width_shift_range: float = AUGMENTATION_CONFIG["width_shift_range"],
height_shift_range: float = AUGMENTATION_CONFIG["height_shift_range"],
horizontal_flip: bool = AUGMENTATION_CONFIG["horizontal_flip"],
zoom_range: float = AUGMENTATION_CONFIG["zoom_range"],
brightness_range: Tuple[float, float] = AUGMENTATION_CONFIG["brightness_range"],
fill_mode: str = AUGMENTATION_CONFIG["fill_mode"],
rescale: float = 1./255
) -> ImageDataGenerator:
"""
Create an ImageDataGenerator with augmentation settings.
Args:
rotation_range: Degree range for random rotations
width_shift_range: Fraction for horizontal shifts
height_shift_range: Fraction for vertical shifts
horizontal_flip: Whether to randomly flip horizontally
zoom_range: Range for random zoom
brightness_range: Range for brightness adjustment
fill_mode: Points outside boundaries fill method
rescale: Rescaling factor
Returns:
Configured ImageDataGenerator
"""
return ImageDataGenerator(
rescale=rescale,
rotation_range=rotation_range,
width_shift_range=width_shift_range,
height_shift_range=height_shift_range,
horizontal_flip=horizontal_flip,
zoom_range=zoom_range,
brightness_range=brightness_range,
fill_mode=fill_mode
)
def augment_image(
image: np.ndarray,
num_augmentations: int = 5,
generator: Optional[ImageDataGenerator] = None
) -> np.ndarray:
"""
Generate augmented versions of a single image.
Args:
image: Input image array of shape (height, width, channels)
num_augmentations: Number of augmented images to generate
generator: Optional ImageDataGenerator, creates default if None
Returns:
Array of augmented images of shape (num_augmentations, height, width, channels)
"""
if generator is None:
generator = get_augmentation_generator(rescale=1.0) # No rescale for single images
# Reshape for generator (needs batch dimension)
image_batch = np.expand_dims(image, axis=0)
# Generate augmented images
augmented_images = []
aug_iter = generator.flow(image_batch, batch_size=1)
for _ in range(num_augmentations):
augmented = next(aug_iter)[0]
augmented_images.append(augmented)
return np.array(augmented_images)
def create_balanced_augmentation(
images: np.ndarray,
labels: np.ndarray,
target_samples_per_class: int
) -> Tuple[np.ndarray, np.ndarray]:
"""
Create a balanced dataset through augmentation of minority classes.
Args:
images: Array of images
labels: Array of one-hot encoded labels
target_samples_per_class: Target number of samples per class
Returns:
Tuple of (augmented_images, augmented_labels)
"""
generator = get_augmentation_generator(rescale=1.0)
# Convert one-hot to class indices
class_indices = np.argmax(labels, axis=1)
unique_classes = np.unique(class_indices)
augmented_images = []
augmented_labels = []
for class_idx in unique_classes:
# Get images of this class
class_mask = class_indices == class_idx
class_images = images[class_mask]
class_labels = labels[class_mask]
current_count = len(class_images)
# Add original images
augmented_images.extend(class_images)
augmented_labels.extend(class_labels)
# Generate more if needed
if current_count < target_samples_per_class:
needed = target_samples_per_class - current_count
for i in range(needed):
# Select random image from class
idx = np.random.randint(0, current_count)
original = class_images[idx]
# Generate one augmented version
aug = augment_image(original, num_augmentations=1, generator=generator)[0]
augmented_images.append(aug)
augmented_labels.append(class_labels[idx])
return np.array(augmented_images), np.array(augmented_labels)
def get_augmentation_preview(
image: np.ndarray,
num_samples: int = 9
) -> np.ndarray:
"""
Generate a preview of augmentations for visualization.
Args:
image: Original image
num_samples: Number of augmented samples to generate
Returns:
Array including original + augmented images
"""
augmented = augment_image(image, num_augmentations=num_samples - 1)
# Add original as first image
original = np.expand_dims(image, axis=0)
return np.concatenate([original, augmented], axis=0)