Compose Multiple Transforms
Problem Statement
Use Compose to chain multiple transforms together into a single transform pipeline.
Background
In practice, you often need to apply multiple transforms in sequence. Compose lets you define a pipeline where the output of each transform becomes the input to the next one.
Your Task
Write a function create_transform_pipeline() that creates a Compose pipeline to:
- Convert the input to a tensor
- Normalize it with mean=0.5 and std=0.5 for each of 3 channels
The function should return a dictionary with the output's shape, value range, and mean when applied to an image.
Output Format
Return a dictionary with keys: "shape" (list), "min_val" (float, 2 decimals), "max_val" (float, 2 decimals), "mean" (float, 2 decimals).
Example:
numpy array of shape (2, 2, 3) with all values 127 (mid-gray)
{"shape": [3, 2, 2], "min_val": -0.0, "max_val": -0.0, "mean": -0.0}ToTensor converts 127 to ~0.498, then Normalize: (0.498-0.5)/0.5 ā -0.004 ā -0.0
Constraints:
- Use torchvision.transforms.Compose, ToTensor, Normalize
- Use mean=[0.5, 0.5, 0.5] and std=[0.5, 0.5, 0.5] for Normalize
- Round all numeric values to 2 decimal places
- Input will be a numpy array of shape (H, W, 3)
1. Background Knowledge
torchvision.transforms is PyTorch's standard library for image preprocessing in computer vision tasks. Key components:
- ToTensor(): Converts PIL Image or NumPy array (H, W, C) ā PyTorch tensor (C, H, W) with values in [0.0, 1.0]
# Input: np.array shape (H, W, 3), uint8 [0, 255]
# Output: torch.Tensor shape (3, H, W), float32 [0.0, 1.0]
- Normalize(mean, std): Applies per-channel normalization:
For mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]:
-
Input [0.0, 1.0] ā Output [-1.0, 1.0]
-
Compose([transforms]): Chains transforms sequentially. Output of one becomes input to next.
Key Prerequisite: Input is NumPy (H, W, 3) uint8 [0, 255]. After pipeline: tensor (3, H, W) [-1.0, 1.0].
2. Algorithm Approach
Pipeline Design Pattern: Create custom callable class that wraps Compose + post-processing statistics.
NumPy (H,W,3) [0,255]
ā ToTensor()
Tensor (3,H,W) [0,1]
ā Normalize([0.5]*3, [0.5]*3)
Tensor (3,H,W) [-1,1]
ā Custom stats computation
Dict {"shape": [...], "min_val": -1.0, "max_val": 1.0, "mean": 0.0}
Core Math:
- Mid-gray 127/255 ā 0.498 ā After normalization: (0.498-0.5)/0.5 ā -0.004 ā -0.00
3. Step-by-Step Strategy
- Create standard pipeline:
pipeline = Compose([
ToTensor(),
Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
])
- Define custom transform class inheriting from nn.Module or using callable:
class TransformWithStats:
def __init__(self):
self.pipeline = pipeline
def __call__(self, img):
tensor = self.pipeline(img) # Apply transforms
return {
"shape": list(tensor.shape),
"min_val": round(tensor.min().item(), 2),
"max_val": round(tensor.max().item(), 2),
"mean": round(tensor.mean().item(), 2)
}
- Return instance: return TransformWithStats()
4. Common Pitfalls
| Pitfall | Fix |
|---|---|
| Wrong tensor shape: Forgetting ToTensor() permutes (H,W,C) ā (C,H,W) | Always use ToTensor() first |
| Incorrect normalization range: Using mean=0.5 without ToTensor() | ToTensor() ā [0,1] then normalize ā [-1,1] |
| Shape as tuple instead of list: tensor.shape returns tuple | list(tensor.shape) |
| Rounding precision: Using round(x, 2) on tensor directly | .item() to get Python scalar first |
| Channel order: mean=[0.5,0.5,0.5] assumes RGB order | Matches constraint: input (H,W,3) |
| Mutable default args: mean=[] in function | Use [0.5, 0.5, 0.5] literals |
Sample verification:
np.full((2,2,3), 127) ā ToTensor() ā [0.498,...] ā Normalize ā [-0.004,...]
ā {"shape": [3,2,2], "min_val": -0.0, "max_val": -0.0, "mean": -0.0}
5. Time & Space Complexity
Time: O(Hā Wā C) where C=3
- ToTensor: O(HWC) copy + scaling
- Normalize: O(HWC) element-wise ops
- Statistics: O(HWC) min/max/mean
Space: O(HWC)
- Input NumPy + output tensor + temporary results
- Pipeline stateless (no extra space beyond input/output)
Complete Solution:
from torchvision.transforms import Compose, ToTensor, Normalize
import torch
def create_transform_pipeline():
pipeline = Compose([
ToTensor(),
Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
])
class TransformWithStats:
def __call__(self, img):
tensor = pipeline(img)
return {
"shape": list(tensor.shape),
"min_val": round(float(tensor.min()), 2),
"max_val": round(float(tensor.max()), 2),
"mean": round(float(tensor.mean()), 2)
}
return TransformWithStats()
This pattern is production-ready for PyTorch DataLoader transforms with metadata extraction.