| |
| """ |
| Comprehensive test script for transforms.py |
| Tests all functions and classes independently |
| Run from project root: python -c "from src.data.transforms import *; exec(open('test_transforms_main.py').read())" |
| """ |
|
|
| import torch |
| import numpy as np |
| from PIL import Image |
| import matplotlib.pyplot as plt |
| from typing import Dict, List |
| import traceback |
| import time |
|
|
|
|
| |
| def create_test_images() -> Dict[str, Image.Image]: |
| """Create various test images""" |
| test_images = {} |
|
|
| |
| test_images['standard'] = Image.fromarray( |
| np.random.randint(0, 255, (300, 300, 3), dtype=np.uint8) |
| ) |
|
|
| |
| test_images['small'] = Image.fromarray( |
| np.random.randint(0, 255, (50, 50, 3), dtype=np.uint8) |
| ) |
|
|
| |
| test_images['large'] = Image.fromarray( |
| np.random.randint(0, 255, (800, 600, 3), dtype=np.uint8) |
| ) |
|
|
| |
| test_images['rectangle'] = Image.fromarray( |
| np.random.randint(0, 255, (400, 200, 3), dtype=np.uint8) |
| ) |
|
|
| |
| gray_array = np.random.randint(0, 255, (200, 200), dtype=np.uint8) |
| test_images['grayscale'] = Image.fromarray(gray_array, 'L').convert('RGB') |
|
|
| return test_images |
|
|
|
|
| def test_basic_transforms(): |
| """Test basic transform functions""" |
| print("=" * 60) |
| print("TESTING BASIC TRANSFORMS") |
| print("=" * 60) |
|
|
| test_images = create_test_images() |
| results = {} |
|
|
| |
| input_sizes = [224, 256, 384] |
|
|
| for size in input_sizes: |
| print(f"\n--- Testing input size: {size} ---") |
|
|
| |
| try: |
| for level in ['light', 'medium', 'heavy']: |
| transform = get_train_transforms(input_size=size, augmentation_level=level) |
|
|
| for img_name, img in test_images.items(): |
| tensor = transform(img) |
| expected_shape = (3, size, size) |
| assert tensor.shape == expected_shape, f"Wrong shape: {tensor.shape} vs {expected_shape}" |
| assert tensor.dtype == torch.float32, f"Wrong dtype: {tensor.dtype}" |
|
|
| print(f"β get_train_transforms({level}) works for all test images") |
|
|
| results[f'train_transforms_{size}'] = "PASS" |
|
|
| except Exception as e: |
| print(f"β get_train_transforms failed: {e}") |
| results[f'train_transforms_{size}'] = f"FAIL: {e}" |
|
|
| |
| try: |
| val_transform = get_val_transforms(input_size=size) |
|
|
| for img_name, img in test_images.items(): |
| tensor = val_transform(img) |
| expected_shape = (3, size, size) |
| assert tensor.shape == expected_shape, f"Wrong shape: {tensor.shape} vs {expected_shape}" |
| assert tensor.dtype == torch.float32, f"Wrong dtype: {tensor.dtype}" |
|
|
| print(f"β get_val_transforms works for all test images") |
| results[f'val_transforms_{size}'] = "PASS" |
|
|
| except Exception as e: |
| print(f"β get_val_transforms failed: {e}") |
| results[f'val_transforms_{size}'] = f"FAIL: {e}" |
|
|
| |
| try: |
| inference_transform = get_inference_transforms(224) |
| tensor = inference_transform(test_images['standard']) |
| assert tensor.shape == (3, 224, 224), f"Wrong inference shape: {tensor.shape}" |
| print("β get_inference_transforms works") |
| results['inference_transforms'] = "PASS" |
| except Exception as e: |
| print(f"β get_inference_transforms failed: {e}") |
| results['inference_transforms'] = f"FAIL: {e}" |
|
|
| return results |
|
|
|
|
| def test_model_specific_transforms(): |
| """Test model-specific transform function""" |
| print("\n" + "=" * 60) |
| print("TESTING MODEL-SPECIFIC TRANSFORMS") |
| print("=" * 60) |
|
|
| results = {} |
|
|
| |
| test_models = [ |
| ('resnet50', 224), |
| ('efficientnet-b2', 260), |
| ('convnext-tiny-384', 384), |
| ('unknown_model', 224) |
| ] |
|
|
| for model_name, expected_size in test_models: |
| print(f"\n--- Testing model: {model_name} ---") |
|
|
| try: |
| |
| train_transform = get_transforms_for_model(model_name, is_training=True) |
| val_transform = get_transforms_for_model(model_name, is_training=False) |
|
|
| test_img = Image.fromarray(np.random.randint(0, 255, (300, 300, 3), dtype=np.uint8)) |
|
|
| train_tensor = train_transform(test_img) |
| val_tensor = val_transform(test_img) |
|
|
| print(f"β Train transform output: {train_tensor.shape}") |
| print(f"β Val transform output: {val_tensor.shape}") |
|
|
| |
| assert train_tensor.shape[0] == 3, "Wrong number of channels" |
| assert val_tensor.shape[0] == 3, "Wrong number of channels" |
| assert train_tensor.shape[1] == train_tensor.shape[2], "Not square" |
| assert val_tensor.shape[1] == val_tensor.shape[2], "Not square" |
|
|
| results[f'model_{model_name}'] = "PASS" |
|
|
| except Exception as e: |
| print(f"β Model {model_name} failed: {e}") |
| results[f'model_{model_name}'] = f"FAIL: {e}" |
|
|
| return results |
|
|
|
|
| def test_advanced_augmentations(): |
| """Test MixUp, CutMix, and RandAugment""" |
| print("\n" + "=" * 60) |
| print("TESTING ADVANCED AUGMENTATIONS") |
| print("=" * 60) |
|
|
| results = {} |
|
|
| |
| print("\n--- Testing MixUp ---") |
| try: |
| mixup = MixUpTransform(alpha=1.0, num_classes=5) |
|
|
| batch_size = 4 |
| images = torch.randn(batch_size, 3, 224, 224) |
| labels = torch.tensor([0, 1, 2, 3]) |
|
|
| mixed_images, labels_a, labels_b, lam = mixup(images, labels) |
|
|
| assert mixed_images.shape == images.shape, f"MixUp shape mismatch: {mixed_images.shape}" |
| assert 0 <= lam <= 1, f"Lambda out of range: {lam}" |
| assert labels_a.shape == labels.shape, "Labels_a shape mismatch" |
| assert labels_b.shape == labels.shape, "Labels_b shape mismatch" |
|
|
| print(f"β MixUp works: lambda={lam:.3f}, output_shape={mixed_images.shape}") |
| results['mixup'] = "PASS" |
|
|
| except Exception as e: |
| print(f"β MixUp failed: {e}") |
| results['mixup'] = f"FAIL: {e}" |
|
|
| |
| print("\n--- Testing CutMix ---") |
| try: |
| cutmix = CutMixTransform(alpha=1.0, num_classes=5) |
|
|
| batch_size = 4 |
| images = torch.randn(batch_size, 3, 224, 224) |
| labels = torch.tensor([0, 1, 2, 3]) |
|
|
| mixed_images, labels_a, labels_b, lam = cutmix(images, labels) |
|
|
| assert mixed_images.shape == images.shape, f"CutMix shape mismatch: {mixed_images.shape}" |
| assert 0 <= lam <= 1, f"Lambda out of range: {lam}" |
| assert labels_a.shape == labels.shape, "Labels_a shape mismatch" |
| assert labels_b.shape == labels.shape, "Labels_b shape mismatch" |
|
|
| print(f"β CutMix works: lambda={lam:.3f}, output_shape={mixed_images.shape}") |
| results['cutmix'] = "PASS" |
|
|
| except Exception as e: |
| print(f"β CutMix failed: {e}") |
| results['cutmix'] = f"FAIL: {e}" |
|
|
| |
| print("\n--- Testing RandAugment ---") |
| try: |
| randaugment = RandAugmentTransform(n=2, m=10) |
|
|
| test_img = Image.fromarray(np.random.randint(0, 255, (224, 224, 3), dtype=np.uint8)) |
| augmented_img = randaugment(test_img) |
|
|
| assert isinstance(augmented_img, Image.Image), "RandAugment should return PIL Image" |
| assert augmented_img.size == test_img.size, "RandAugment changed image size" |
|
|
| print(f"β RandAugment works: input_size={test_img.size}, output_size={augmented_img.size}") |
| results['randaugment'] = "PASS" |
|
|
| except Exception as e: |
| print(f"β RandAugment failed: {e}") |
| results['randaugment'] = f"FAIL: {e}" |
|
|
| return results |
|
|
|
|
| def test_advanced_train_transforms(): |
| """Test advanced training transforms with RandAugment""" |
| print("\n" + "=" * 60) |
| print("TESTING ADVANCED TRAIN TRANSFORMS") |
| print("=" * 60) |
|
|
| results = {} |
|
|
| try: |
| |
| transform_normal = get_advanced_train_transforms( |
| input_size=224, |
| use_randaugment=False |
| ) |
|
|
| |
| transform_rand = get_advanced_train_transforms( |
| input_size=224, |
| use_randaugment=True, |
| randaugment_n=2, |
| randaugment_m=5 |
| ) |
|
|
| test_img = Image.fromarray(np.random.randint(0, 255, (300, 300, 3), dtype=np.uint8)) |
|
|
| tensor_normal = transform_normal(test_img) |
| tensor_rand = transform_rand(test_img) |
|
|
| assert tensor_normal.shape == (3, 224, 224), f"Normal transform wrong shape: {tensor_normal.shape}" |
| assert tensor_rand.shape == (3, 224, 224), f"RandAugment transform wrong shape: {tensor_rand.shape}" |
|
|
| print(f"β Advanced transforms work: normal={tensor_normal.shape}, randaugment={tensor_rand.shape}") |
| results['advanced_transforms'] = "PASS" |
|
|
| except Exception as e: |
| print(f"β Advanced transforms failed: {e}") |
| results['advanced_transforms'] = f"FAIL: {e}" |
|
|
| return results |
|
|
|
|
| def test_denormalize(): |
| """Test denormalization utility""" |
| print("\n" + "=" * 60) |
| print("TESTING DENORMALIZATION") |
| print("=" * 60) |
|
|
| results = {} |
|
|
| try: |
| |
| transform = get_val_transforms(224) |
| test_img = Image.fromarray(np.random.randint(0, 255, (224, 224, 3), dtype=np.uint8)) |
| normalized_tensor = transform(test_img) |
|
|
| |
| denorm = DeNormalize() |
| denormalized_tensor = denorm(normalized_tensor) |
|
|
| assert denormalized_tensor.shape == normalized_tensor.shape, "Denorm changed shape" |
| assert denormalized_tensor.dtype == torch.float32, "Denorm changed dtype" |
|
|
| |
| min_val = denormalized_tensor.min().item() |
| max_val = denormalized_tensor.max().item() |
|
|
| print(f"β Denormalization works: shape={denormalized_tensor.shape}") |
| print(f" Normalized range: [{normalized_tensor.min():.3f}, {normalized_tensor.max():.3f}]") |
| print(f" Denormalized range: [{min_val:.3f}, {max_val:.3f}]") |
|
|
| results['denormalize'] = "PASS" |
|
|
| except Exception as e: |
| print(f"β Denormalization failed: {e}") |
| results['denormalize'] = f"FAIL: {e}" |
|
|
| return results |
|
|
|
|
| def test_performance(): |
| """Test transform performance""" |
| print("\n" + "=" * 60) |
| print("TESTING PERFORMANCE") |
| print("=" * 60) |
|
|
| results = {} |
|
|
| try: |
| transform = get_train_transforms(224, 'medium') |
| test_img = Image.fromarray(np.random.randint(0, 255, (224, 224, 3), dtype=np.uint8)) |
|
|
| |
| n_iterations = 100 |
| start_time = time.time() |
|
|
| for _ in range(n_iterations): |
| _ = transform(test_img) |
|
|
| end_time = time.time() |
| avg_time = (end_time - start_time) / n_iterations * 1000 |
|
|
| print(f"β Performance test: {avg_time:.2f}ms per transform (avg of {n_iterations} iterations)") |
|
|
| if avg_time < 50: |
| results['performance'] = "PASS" |
| else: |
| results['performance'] = f"SLOW: {avg_time:.2f}ms" |
|
|
| except Exception as e: |
| print(f"β Performance test failed: {e}") |
| results['performance'] = f"FAIL: {e}" |
|
|
| return results |
|
|
|
|
| def test_edge_cases(): |
| """Test edge cases and error handling""" |
| print("\n" + "=" * 60) |
| print("TESTING EDGE CASES") |
| print("=" * 60) |
|
|
| results = {} |
|
|
| |
| try: |
| small_img = Image.fromarray(np.random.randint(0, 255, (10, 10, 3), dtype=np.uint8)) |
| transform = get_val_transforms(224) |
| tensor = transform(small_img) |
| assert tensor.shape == (3, 224, 224), "Small image transform failed" |
| print("β Very small image (10x10) handled correctly") |
| results['small_image'] = "PASS" |
| except Exception as e: |
| print(f"β Small image test failed: {e}") |
| results['small_image'] = f"FAIL: {e}" |
|
|
| |
| try: |
| wide_img = Image.fromarray(np.random.randint(0, 255, (50, 500, 3), dtype=np.uint8)) |
| transform = get_val_transforms(224) |
| tensor = transform(wide_img) |
| assert tensor.shape == (3, 224, 224), "Wide image transform failed" |
| print("β Extreme aspect ratio (1:10) handled correctly") |
| results['aspect_ratio'] = "PASS" |
| except Exception as e: |
| print(f"β Aspect ratio test failed: {e}") |
| results['aspect_ratio'] = f"FAIL: {e}" |
|
|
| |
| try: |
| transform = get_train_transforms(224, 'invalid_level') |
| |
| print("β Invalid augmentation level handled (fallback to medium)") |
| results['invalid_aug_level'] = "PASS" |
| except Exception as e: |
| print(f"β Invalid augmentation level test failed: {e}") |
| results['invalid_aug_level'] = f"FAIL: {e}" |
|
|
| return results |
|
|
|
|
| def print_final_summary(all_results: Dict[str, Dict]): |
| """Print comprehensive test summary""" |
| print("\n" + "=" * 60) |
| print("FINAL TEST SUMMARY") |
| print("=" * 60) |
|
|
| total_tests = 0 |
| passed_tests = 0 |
| failed_tests = [] |
|
|
| for test_category, results in all_results.items(): |
| print(f"\n{test_category.upper()}:") |
| for test_name, result in results.items(): |
| total_tests += 1 |
| if result == "PASS": |
| passed_tests += 1 |
| print(f" β {test_name}") |
| else: |
| failed_tests.append(f"{test_category}.{test_name}: {result}") |
| print(f" β {test_name}: {result}") |
|
|
| print(f"\n" + "=" * 60) |
| print(f"OVERALL RESULTS:") |
| print(f"Total tests: {total_tests}") |
| print(f"Passed: {passed_tests}") |
| print(f"Failed: {len(failed_tests)}") |
| print(f"Success rate: {passed_tests / total_tests * 100:.1f}%") |
|
|
| if failed_tests: |
| print(f"\nFAILED TESTS:") |
| for failure in failed_tests: |
| print(f" - {failure}") |
|
|
| return len(failed_tests) == 0 |
|
|
|
|
| def main(): |
| """Main test function""" |
| print("TRANSFORMS.PY COMPREHENSIVE TEST SUITE") |
| print("=" * 60) |
| print("Testing all functions and classes in transforms.py") |
|
|
| all_results = {} |
|
|
| try: |
| |
| all_results['basic_transforms'] = test_basic_transforms() |
| all_results['model_specific'] = test_model_specific_transforms() |
| all_results['advanced_augmentations'] = test_advanced_augmentations() |
| all_results['advanced_transforms'] = test_advanced_train_transforms() |
| all_results['denormalize'] = test_denormalize() |
| all_results['performance'] = test_performance() |
| all_results['edge_cases'] = test_edge_cases() |
|
|
| |
| success = print_final_summary(all_results) |
|
|
| if success: |
| print("\nπ ALL TESTS PASSED! transforms.py is working correctly.") |
| return 0 |
| else: |
| print("\nβ SOME TESTS FAILED! Check the issues above.") |
| return 1 |
|
|
| except Exception as e: |
| print(f"\nπ₯ CRITICAL ERROR during testing: {e}") |
| print(f"Traceback:\n{traceback.format_exc()}") |
| return 1 |
|
|
|
|
| if __name__ == "__main__": |
| exit_code = main() |
| print(f"\nTest completed with exit code: {exit_code}") |