Understanding the MegaWrapper Preprocessing Pipeline and Image Transforms in stable-worldmodel
The MegaWrapper class composes a sequence of specialized Gymnasium wrappers—AddPixelsWrapper, EverythingToInfoWrapper, EnsureInfoKeysWrapper, and ResizeGoalWrapper—to standardize any environment into a pixel-based reinforcement learning interface with configurable image preprocessing.
The stable-worldmodel library provides a robust preprocessing stack for reinforcement learning researchers working with pixel observations. The MegaWrapper preprocessing pipeline serves as the central orchestrator, automatically rendering frames, applying transforms, and restructuring environment outputs into a consistent format suitable for world model training.
The MegaWrapper Architecture
Located in stable_worldmodel/wrapper/default.py, the MegaWrapper class implements a four-stage wrapping strategy that transforms raw Gymnasium environments into fully pixel-based interfaces. Each stage handles a specific concern, from rendering to validation.
AddPixelsWrapper and Image Rendering
The pipeline begins with AddPixelsWrapper, which calls env.render() to generate RGB arrays and resizes them to the target image_shape. According to the source code in default.py, this wrapper optionally applies a torchvision-style transform via the pixels_transform parameter before storing the result under the pixels key in the info dictionary.
# Conceptual flow from AddPixelsWrapper (default.py lines 95-108)
pixels = env.render() # RGB array
pixels = resize(pixels, image_shape, resample) # bilinear/nearest
if pixels_transform:
pixels = pixels_transform(pixels)
info['pixels'] = pixels
EverythingToInfoWrapper for Unified Data Flow
Next, EverythingToInfoWrapper moves all observation data—including raw observations, rewards, and termination flags—into the info dictionary. This normalization allows downstream components to treat all environment outputs uniformly as metadata, simplifying the interface for pixel-based agents.
EnsureInfoKeysWrapper Validation
Before completing the wrapping sequence, EnsureInfoKeysWrapper validates that required keys exist in the info dictionary. By default, it checks for keys matching the pattern ^pixels(?:\..*)?$, raising a clear error if pixel data is missing. This prevents silent failures when preprocessing pipelines misconfigure.
ResizeGoalWrapper for Goal-Conditioned Tasks
For environments that provide goal images, ResizeGoalWrapper (optional) processes these targets through the same resizing logic and an optional separate goal_transform. This ensures goal observations match the observation space dimensions, maintaining consistency in goal-conditioned reinforcement learning setups.
The MegaWrapper.__init__ method wires these components together sequentially:
# From MegaWrapper.__init__ in default.py
if add_pixels:
env = AddPixelsWrapper(env, image_shape, pixels_transform, resample)
env = EverythingToInfoWrapper(env)
env = EnsureInfoKeysWrapper(env, req_keys)
if add_pixels:
env = ResizeGoalWrapper(env, image_shape, goal_transform, resample)
Image Transform Utilities and Visual Augmentations
The stable_worldmodel/wrapper/visual.py module extends the preprocessing pipeline with pixel-level augmentations. Each wrapper inherits from _PixelTransform, which automatically applies transformations to env.render() outputs and any info['pixels*'] entries (supporting multi-camera setups like pixels.camera0).
Available Augmentation Wrappers
The library includes the following visual disturbance wrappers, each implementing specific domain randomization techniques:
- NoiseWrapper: Adds step-dependent Gaussian noise with schedulable standard deviation (supports
linear,cosine,exponential,sinusoidal, or custom callables) - ColorJitterWrapper: Applies random brightness, contrast, saturation, and hue shifts sampled once per episode
- BlurWrapper: Applies Gaussian blur with configurable kernel size to simulate motion blur or low-quality sensors
- OcclusionWrapper: Renders random rectangular patches with solid colors to simulate missing data
- MovingPatchWrapper: Creates drifting colored patches that move smoothly across consecutive frames
- RandomShiftWrapper: Implements DrQ-style random translation with replicate padding
- CutoutWrapper: Masks random rectangles each frame for regularization
- RandomConvWrapper: Applies freshly sampled random convolutional filters per episode for extreme domain randomization
- GrayscaleWrapper: Converts RGB to grayscale with optional channel broadcasting
- ResolutionWrapper: Downsamples then upsamples to simulate low-resolution sensors
- ChromaKeyWrapper: Replaces keyed colors (e.g., green-screen) with static images or looping video backgrounds
All wrappers maintain the standard Gymnasium API (reset(), step(), render()), allowing unlimited compositional stacking either before MegaWrapper or within custom pipelines.
Implementation Examples
Basic MegaWrapper Configuration
The following example demonstrates wrapping a standard CartPole environment with pixel rendering and noise augmentation:
import gymnasium as gym
from stable_worldmodel.wrapper.default import MegaWrapper
from stable_worldmodel.wrapper.visual import NoiseWrapper
# Create base environment
base_env = gym.make("CartPole-v1", render_mode="rgb_array")
# Add exponential decay noise schedule
noisy_env = NoiseWrapper(
env=base_env,
std=lambda step: 5.0 * (0.99 ** step),
seed=42,
)
# Apply MegaWrapper with 84x84 resolution
env = MegaWrapper(
env=noisy_env,
image_shape=(84, 84),
pixels_transform=None,
goal_transform=None,
required_keys=None,
separate_goal=True,
image_resample="bilinear",
add_pixels=True,
)
obs, info = env.reset()
print(f"Pixel shape: {info['pixels'].shape}") # Output: (84, 84, 3)
Stacking Visual Augmentations
Visual wrappers compose sequentially. This example adds color jittering and blurring:
from stable_worldmodel.wrapper.visual import ColorJitterWrapper, BlurWrapper
# Stack augmentations
env = ColorJitterWrapper(env, brightness=0.3, contrast=0.3,
saturation=0.3, hue=0.1, seed=42)
env = BlurWrapper(env, kernel=3, sigma=0.0)
obs, info = env.reset()
# info["pixels"] now contains color-jittered, blurred frames
Integrating Torchvision Transforms
For normalization and tensor conversion, pass torchvision transforms directly to MegaWrapper:
import torchvision.transforms as T
from stable_worldmodel.wrapper.default import MegaWrapper
transform = T.Compose([
T.RandomHorizontalFlip(p=0.5),
T.ToTensor(), # Converts to C×H×W float tensor
T.Normalize(mean=[0.5]*3, std=[0.5]*3),
])
env = MegaWrapper(
env=gym.make("Breakout-v0", render_mode="rgb_array"),
image_shape=(84, 84),
pixels_transform=transform,
add_pixels=True,
)
Background Substitution with ChromaKeyWrapper
Replace green-screen backgrounds with video loops for synthetic data generation:
from stable_worldmodel.wrapper.visual import ChromaKeyWrapper
env = ChromaKeyWrapper(
env=gym.make("Custom3DEnv", render_mode="rgb_array"),
key_color=(0, 255, 0),
media="background.mp4",
tolerance=30.0,
)
Summary
- The MegaWrapper preprocessing pipeline in
stable_worldmodel/wrapper/default.pystandardizes Gymnasium environments through a four-stage wrapping process: pixel rendering, info restructuring, validation, and goal resizing. - AddPixelsWrapper handles frame rendering and optional torchvision-compatible transforms via the
pixels_transformparameter. - Visual augmentation wrappers in
stable_worldmodel/wrapper/visual.pyinherit from_PixelTransformand support scheduled noise, color jittering, blurring, and chroma-key effects. - All wrappers maintain Gymnasium API compatibility, allowing flexible stacking before or after the main preprocessing pipeline.
Frequently Asked Questions
What is the purpose of MegaWrapper in stable-worldmodel?
MegaWrapper serves as the central preprocessing orchestrator that converts any Gymnasium environment into a consistent pixel-based interface. It ensures all observations, rewards, and termination flags reside in the info dictionary while guaranteeing that pixel data exists under standardized keys like pixels, making downstream world model training code environment-agnostic.
How does AddPixelsWrapper handle image transforms?
AddPixelsWrapper applies transforms after resizing but before storage. According to default.py, it first renders the environment, resizes the output to image_shape using the specified resampling method (bilinear or nearest), then optionally applies the pixels_transform callable (typically a torchvision transform) before inserting the result into info['pixels'].
Can I stack multiple visual augmentation wrappers?
Yes, visual wrappers compose arbitrarily. Each augmentation in stable_worldmodel/wrapper/visual.py inherits from Gymnasium's wrapper base and the internal _PixelTransform class, allowing you to chain NoiseWrapper, ColorJitterWrapper, BlurWrapper, and others either before MegaWrapper (affecting raw renders) or after (affecting resized pixels).
Where are the wrapper classes defined in the source code?
The core preprocessing logic resides in two files: MegaWrapper, AddPixelsWrapper, and the info-management wrappers are implemented in stable_worldmodel/wrapper/default.py, while pixel-level augmentations like NoiseWrapper and ChromaKeyWrapper are defined in stable_worldmodel/wrapper/visual.py. Utility functions such as get_in for nested dictionary access are located in stable_worldmodel/utils.py.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →