Spaces:
Build error
Build error
| # Image Compression | |
| Headroom automatically compresses images in your LLM requests, reducing token usage by **40-90%** while maintaining answer accuracy. | |
| ## Overview | |
| Vision models charge by the token, and images are expensive: | |
| - A 1024x1024 image costs ~765 tokens (OpenAI) | |
| - A 2048x2048 image costs ~2,900 tokens | |
| Headroom's image compression uses a **trained ML router** to analyze your query and automatically select the optimal compression technique: | |
| | Technique | Savings | When Used | | |
| |-----------|---------|-----------| | |
| | `full_low` | ~87% | General questions ("What is this?") | | |
| | `preserve` | 0% | Fine details needed ("Count the whiskers") | | |
| | `crop` | 50-90% | Region-specific ("What's in the corner?") | | |
| | `transcode` | ~99% | Text extraction ("Read the sign") | | |
| ## How It Works | |
| ``` | |
| User uploads image + asks question | |
| ↓ | |
| [Query Analysis] | |
| TrainedRouter (MiniLM from HuggingFace) | |
| Classifies: "What animal is this?" → full_low | |
| ↓ | |
| [Image Analysis] | |
| SigLIP analyzes image properties | |
| (has text? complex? fine details?) | |
| ↓ | |
| [Apply Compression] | |
| OpenAI: detail="low" | |
| Anthropic: Resize to 512px | |
| Google: Resize to 768px | |
| ↓ | |
| Compressed request to LLM | |
| ``` | |
| ## Quick Start | |
| ### With Headroom Proxy (Zero Code Changes) | |
| ```bash | |
| # Start the proxy | |
| headroom proxy --port 8787 | |
| # Connect your client | |
| ANTHROPIC_BASE_URL=http://localhost:8787 claude | |
| ``` | |
| Images are automatically compressed based on your queries. | |
| ### With HeadroomClient | |
| ```python | |
| from headroom import HeadroomClient | |
| client = HeadroomClient(provider="openai") | |
| response = client.chat.completions.create( | |
| model="gpt-4o", | |
| messages=[{ | |
| "role": "user", | |
| "content": [ | |
| {"type": "text", "text": "What animal is this?"}, | |
| {"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,..."}} | |
| ] | |
| }] | |
| ) | |
| # Image automatically compressed with detail="low" (87% savings) | |
| ``` | |
| ### Direct API | |
| ```python | |
| from headroom.image import ImageCompressor | |
| compressor = ImageCompressor() | |
| # Compress images in messages | |
| compressed_messages = compressor.compress(messages, provider="openai") | |
| # Check savings | |
| print(f"Saved {compressor.last_savings:.0f}% tokens") | |
| print(f"Technique: {compressor.last_result.technique.value}") | |
| ``` | |
| ## Configuration | |
| ### Proxy Configuration | |
| ```bash | |
| # Enable image compression (default: true) | |
| headroom proxy --image-optimize | |
| # Disable image compression | |
| headroom proxy --no-image-optimize | |
| ``` | |
| ### Programmatic Configuration | |
| ```python | |
| from headroom.image import ImageCompressor | |
| compressor = ImageCompressor( | |
| model_id="chopratejas/technique-router", # HuggingFace model | |
| use_siglip=True, # Enable image analysis | |
| device="cuda", # Use GPU if available | |
| ) | |
| ``` | |
| ## Provider Support | |
| | Provider | Detection | Compression Method | | |
| |----------|-----------|-------------------| | |
| | **OpenAI** | `image_url` | Sets `detail="low"` | | |
| | **Anthropic** | `image` with `source` | Resizes to 512px | | |
| | **Google** | `inlineData` | Resizes to 768px (tile-optimized) | | |
| ### OpenAI | |
| Uses the native `detail` parameter: | |
| ```python | |
| # Before | |
| {"type": "image_url", "image_url": {"url": "data:..."}} | |
| # After (full_low technique) | |
| {"type": "image_url", "image_url": {"url": "data:...", "detail": "low"}} | |
| ``` | |
| ### Anthropic | |
| Resizes the image using PIL: | |
| ```python | |
| # Before: 1024x1024 image (~1,398 tokens) | |
| # After: 512x512 image (~349 tokens) - 75% savings | |
| ``` | |
| ### Google Gemini | |
| Resizes to 768px (optimal for Gemini's 768x768 tile system): | |
| ```python | |
| # Before: 1536x1536 image (4 tiles × 258 = 1,032 tokens) | |
| # After: 768x768 image (1 tile × 258 = 258 tokens) - 75% savings | |
| ``` | |
| ## Techniques Explained | |
| ### `full_low` (87% savings) | |
| Best for general understanding questions: | |
| - "What is this?" | |
| - "Describe the scene" | |
| - "Is this indoors or outdoors?" | |
| The model doesn't need fine details to answer these questions. | |
| ### `preserve` (0% savings) | |
| Required when fine details matter: | |
| - "Count the whiskers" | |
| - "What brand is shown?" | |
| - "Read the serial number" | |
| - "What time does the clock show?" | |
| ### `crop` (50-90% savings) | |
| For region-specific queries: | |
| - "What's in the top-right corner?" | |
| - "Focus on the background" | |
| - "Zoom into the left side" | |
| *Note: Currently implemented as resize. True cropping coming soon.* | |
| ### `transcode` (99% savings) | |
| For text extraction (converts image to text): | |
| - "Read the sign" | |
| - "What does it say?" | |
| - "Transcribe the document" | |
| *Note: Requires vision model call. Currently falls back to preserve.* | |
| ## The Trained Router | |
| The routing decision is made by a fine-tuned **MiniLM** classifier: | |
| - **Model**: `chopratejas/technique-router` on HuggingFace | |
| - **Size**: ~128MB | |
| - **Accuracy**: 93.7% on validation set | |
| - **Training data**: 1,157 examples across 4 techniques | |
| The model is downloaded automatically on first use and cached locally. | |
| ### Training Data Examples | |
| | Query | Technique | | |
| |-------|-----------| | |
| | "What animal is this?" | `full_low` | | |
| | "Count the spots" | `preserve` | | |
| | "Read the text on the sign" | `transcode` | | |
| | "What's in the corner?" | `crop` | | |
| ## Performance | |
| ### Token Savings by Query Type | |
| | Query Type | Before | After | Savings | | |
| |------------|--------|-------|---------| | |
| | General ("What is this?") | 765 | 85 | 89% | | |
| | Detail ("Count items") | 765 | 765 | 0% | | |
| | Region ("Top corner?") | 765 | 85 | 89% | | |
| | Text ("Read the sign") | 765 | 85 | 89% | | |
| ### Latency | |
| - Router inference: ~10ms (CPU), ~2ms (GPU) | |
| - Image resize: ~5-20ms depending on size | |
| - First request: +2-3s (model download, cached after) | |
| ## Troubleshooting | |
| ### Model Download Issues | |
| The HuggingFace model downloads on first use: | |
| ```python | |
| # Force a specific cache directory | |
| import os | |
| os.environ["HF_HOME"] = "/path/to/cache" | |
| from headroom.image import ImageCompressor | |
| compressor = ImageCompressor() | |
| ``` | |
| ### GPU Memory | |
| SigLIP requires ~400MB GPU memory. To use CPU only: | |
| ```python | |
| compressor = ImageCompressor(device="cpu") | |
| ``` | |
| ### Disable Image Compression | |
| ```python | |
| # Proxy | |
| headroom proxy --no-image-optimize | |
| # Direct | |
| # Simply don't call compress() | |
| ``` | |
| ## API Reference | |
| ### `ImageCompressor` | |
| ```python | |
| class ImageCompressor: | |
| def __init__( | |
| self, | |
| model_id: str = "chopratejas/technique-router", | |
| use_siglip: bool = True, | |
| device: str | None = None, | |
| ): ... | |
| def has_images(self, messages: list[dict]) -> bool: | |
| """Check if messages contain images.""" | |
| def compress( | |
| self, | |
| messages: list[dict], | |
| provider: str = "openai", | |
| ) -> list[dict]: | |
| """Compress images in messages.""" | |
| @property | |
| def last_result(self) -> CompressionResult | None: | |
| """Result of last compression.""" | |
| @property | |
| def last_savings(self) -> float: | |
| """Savings percentage from last compression.""" | |
| ``` | |
| ### `CompressionResult` | |
| ```python | |
| @dataclass | |
| class CompressionResult: | |
| technique: Technique # full_low, preserve, crop, transcode | |
| original_tokens: int # Estimated tokens before | |
| compressed_tokens: int # Estimated tokens after | |
| confidence: float # Router confidence (0-1) | |
| @property | |
| def savings_percent(self) -> float: | |
| """Percentage of tokens saved.""" | |
| ``` | |
| ### `Technique` | |
| ```python | |
| class Technique(Enum): | |
| FULL_LOW = "full_low" # 87% savings | |
| PRESERVE = "preserve" # 0% savings | |
| CROP = "crop" # 50-90% savings | |
| TRANSCODE = "transcode" # 99% savings | |
| ``` | |
| ## See Also | |
| - [Compression Guide](compression.md) - Text compression techniques | |
| - [CCR Guide](ccr.md) - Reversible compression with retrieval | |
| - [Proxy Guide](proxy.md) - Zero-code deployment | |
| - [Architecture](ARCHITECTURE.md) - System design | |