Reduce VLM API token costs by 92-99% with intelligent image preprocessing
Project description
img2tok - Image Token Reduction for Vision Language Models
Reduce vision token usage by 75% while maintaining VQA accuracy with FastV-inspired pruning and intelligent image preprocessing.
Table of Contents
- Quick Start
- Installation
- Usage
- Features
- Transformations
- Benchmarking
- VQA Evaluation
- Visualization & Graphs
- Advanced Usage
- Troubleshooting
- API Reference
Quick Start
1. Install
# Clone repository
git clone https://github.com/yourorg/img2tok.git
cd img2tok
# Create conda environment
conda env create -f environment.yml
conda activate img2tok
# Verify installation
python -c "import anthropic; import numpy; import cv2; print('✓ All dependencies installed')"
2. Run Demo (No API Key Required)
# Test VQA benchmark in demo mode
python benchmark_vqa.py --samples 10 --demo
# Graphs automatically generated in result_graphs/
open result_graphs/summary_dashboard.png
3. Run Real Benchmark
# Set API key
export ANTHROPIC_API_KEY="your-key-here"
# Download VQA dataset (one-time, ~7GB)
python benchmark_vqa.py --download
# Run benchmark with 100 questions
python benchmark_vqa.py --samples 100
# View results
cat vqa_results.json
open result_graphs/summary_dashboard.png
Expected Results:
Transformation Token Savings VQA Accuracy Accuracy Drop
Original 0% 75% —
FastV 50% 75% 72% 3% ✓ Good
FastV 75% 85% 58% 17% ⚠️ High
Resolution 512px 65% 70% 5% ✓ Acceptable
Installation
Option 1: Conda (Recommended)
conda env create -f environment.yml
conda activate img2tok
Option 2: pip + venv
python3 -m venv venv
source venv/bin/activate # macOS/Linux
# or
venv\Scripts\activate # Windows
pip install -r requirements.txt
Option 3: Install as Package
pip install -e ".[benchmarks]"
Dependencies
Core:
- Python 3.11+
- Pillow (image processing)
- NumPy (numerical operations)
- SciPy (edge detection for FastV)
- OpenCV (computer vision)
Benchmarking:
- Anthropic SDK (Claude API)
- Matplotlib (visualization)
- VQA Tools (included in
src/vqaTools/)
Development:
- pytest, pytest-cov
- black, ruff, mypy
Usage
VQA Benchmarking
Quick Test (Demo Mode)
# No API key needed
python benchmark_vqa.py --samples 20 --demo
Standard Benchmark
# 100 questions (recommended)
export ANTHROPIC_API_KEY="your-key"
python benchmark_vqa.py --samples 100
# Different sample sizes
python benchmark_vqa.py --samples 10 # Quick test
python benchmark_vqa.py --samples 500 # Large test
python benchmark_vqa.py --split minival # Full ~5000 questions
Custom Output
python benchmark_vqa.py --samples 100 --output my_results.json
Model Selection
Use simple aliases instead of full model names:
# High quality (Opus)
python benchmark_vqa.py --samples 50 --model high
# Balanced (Sonnet) - default
python benchmark_vqa.py --samples 100 --model medium
# Fast & cheap (Haiku)
python benchmark_vqa.py --samples 500 --model low
Available Aliases:
| Alias | Model | Quality | Speed | Cost (Input) | Use When |
|---|---|---|---|---|---|
high, best, opus |
claude-opus-4-6 | ⭐⭐⭐⭐⭐ | 🐌 Slow | $15/MTok | Highest accuracy needed |
medium, default, sonnet |
claude-sonnet-4-6 | ⭐⭐⭐⭐ | 🏃 Fast | $3/MTok | Balanced (recommended) |
low, fast, cheap, haiku |
claude-haiku-4-5 | ⭐⭐⭐ | ⚡ Fastest | $0.80/MTok | Speed/cost priority |
Visualization
Graphs are automatically generated after each benchmark:
python benchmark_vqa.py --samples 100
# → Graphs saved to result_graphs/
# View graphs
open result_graphs/summary_dashboard.png # Comprehensive overview
open result_graphs/token_accuracy_tradeoff.png # Scatter plot
open result_graphs/accuracy_comparison.png # Bar chart
Manual generation:
python visualize_results.py vqa_results.json
python visualize_results.py results.json --output-dir custom_graphs
Features
FastV-Inspired Token Pruning
Reduce vision tokens by 75% with minimal accuracy loss:
- FastV 50% Pruning: 75% token reduction, ~3% accuracy drop
- FastV 75% Pruning: 85% token reduction, ~17% accuracy drop
- Resolution Scaling: 65% token reduction, ~5% accuracy drop
VQA Evaluation Improvements
Enhanced VQA matching for modern VLMs:
- VQA-Specific Prompting: Requests short, concise answers
- Substring Matching: Handles verbose responses gracefully
- Backward Compatible: Original VQA tests still pass (7/7)
Before:
Question: "What is he on top of?"
Response: "The skateboarder is performing a trick over a picnic table"
Score: 0.0 ❌ (no exact match)
After:
Response: "picnic table" (with VQA prompting)
Score: 1.0 ✓
# OR (with substring matching fallback)
Response: "The skateboarder is on a picnic table"
Score: 1.0 ✓ (substring "picnic table" found)
Automatic Visualization
6 graphs generated automatically:
- Summary Dashboard - 5-subplot comprehensive overview
- Token-Accuracy Tradeoff - Scatter plot showing optimal balance
- Accuracy Comparison - Bar chart by transformation
- Accuracy Drop - How much quality lost vs original
- Category Performance - Heatmap by question type
- VQA Score Distribution - Answer quality breakdown
All graphs are 300 DPI (publication quality).
Transformations
Available Transformations
| Transform | Purpose | Token Impact | Speed | Quality Loss |
|---|---|---|---|---|
| FastV 50% | Prune 50% of tokens | ⭐⭐⭐⭐⭐ High (75%) | Medium | Low (3%) |
| FastV 75% | Aggressive pruning | ⭐⭐⭐⭐⭐ Very High (85%) | Medium | High (17%) |
| ResolutionScale | Resize image | ⭐⭐⭐⭐ High | Fast | Low-Medium |
| FrequencyTrim | DCT compression | ⭐⭐⭐ Medium | Slow | Medium |
| Grayscale | Remove color | ⭐⭐ Low-Med | Fast | Low (charts) |
| ColorQuantize | Reduce palette | ⭐⭐ Low-Med | Medium | Low (UI) |
| EdgeSharpen | Enhance edges | ⭐ None | Medium | Improves |
| CropDeadspace | Remove borders | ⭐⭐⭐ High* | Fast | None |
*High impact when borders present
FastV Pruning
FastV-inspired attention-based token pruning:
from img2tok.transforms import FastVInspiredPruning
# Prune 50% of tokens (75% reduction)
transform = FastVInspiredPruning(pruning_ratio=0.5, tile_size=256)
# Prune 75% of tokens (85% reduction)
transform = FastVInspiredPruning(pruning_ratio=0.75, tile_size=256)
How it works:
- Divide image into tiles (e.g., 256×256)
- Compute attention scores (edge density + variance)
- Keep top-k% tiles based on scores
- Discard low-attention tiles (often uniform backgrounds)
Resolution Scaling
from img2tok.transforms import ResolutionScale
# Resize to max 512px (aggressive)
transform = ResolutionScale(max_dim=512)
# Resize to max 800px (moderate)
transform = ResolutionScale(max_dim=800)
Benchmarking
VQA Benchmark
VQA (Visual Question Answering) is the standard benchmark for vision-language models:
- Dataset: VQAv2 with 40K val images, 200K questions
- Evaluation: Compare answers to 10 human annotations
- Metric:
score = min(matches / 3, 1.0)
Why VQA?
- Objective evaluation with ground truth
- Diverse tasks: counting, text reading, object recognition, spatial reasoning
- Standard benchmark used by vision research community
- Fine-grained details test if reduction loses important info
Running VQA Benchmark
# One-time download (~7GB)
python benchmark_vqa.py --download
# Quick test (demo mode, no API)
python benchmark_vqa.py --samples 20 --demo
# Standard test (100 questions)
python benchmark_vqa.py --samples 100
# Large test (1000 questions)
python benchmark_vqa.py --samples 1000
# Full minival (~5000 questions)
python benchmark_vqa.py --split minival
Understanding Results
Overall Accuracy Summary
================================================================================
ACCURACY SUMMARY
================================================================================
Transformation Accuracy Correct/Total vs Original
--------------------------------------------------------------------------------
Original 68.5% 68/100 —
FastV 50% 67.0% 67/100 1.5% drop
FastV 75% 58.5% 58/100 10.0% drop
Resolution 512px 66.0% 66/100 2.5% drop
================================================================================
How to interpret:
- Accuracy: % of questions answered correctly
- Correct/Total: Raw counts
- vs Original: Accuracy drop compared to original images
VQA Score Distribution
Original:
Average VQA score: 0.712
Perfect (1.0): 42 (42.0%) ← All annotators agreed
Partial (>0.33): 26 (26.0%) ← Some annotators agreed
Wrong (≤0.33): 32 (32.0%) ← Few/no annotators agreed
Per-Category Breakdown
PER-CATEGORY ACCURACY:
Category Original FastV 50% FastV 75%
------------------------------------------------------------------------
what_other 75.0% 75.0% 65.0%
how many_number 80.0% 80.0% 60.0%
is this_yes/no 90.0% 90.0% 85.0%
what color_other 70.0% 65.0% 50.0%
Use this to identify:
- Which question types are most affected
- If your use case focuses on specific categories
Model Aliases
Quick reference:
# Best quality
python benchmark_vqa.py --model high
# Balanced (default)
python benchmark_vqa.py --model medium
# Fast & cheap
python benchmark_vqa.py --model low
Cost comparison (100 questions × 4 transformations = 400 API calls):
| Model | Total Cost | Best For |
|---|---|---|
| Opus (high) | ~$33.75 | Research, highest accuracy |
| Sonnet (medium) | ~$6.75 | Development, balanced |
| Haiku (low) | ~$1.80 | Large-scale testing, prototyping |
Recommendation:
- Start with
mediumfor baseline - If accuracy sufficient, try
lowto save cost - If accuracy insufficient, upgrade to
high
VQA Evaluation
VQA Scoring Formula
VQA uses soft accuracy to account for answer variability:
VQA Score = min(# annotators who gave this answer / 3, 1.0)
Examples:
# Perfect agreement (9/10 said "brown")
Ground truth: ["brown"] * 9 + ["tan"]
Prediction: "brown"
Score: min(9/3, 1.0) = 1.0 ✓
# Wrong answer
Ground truth: ["cat"] * 10
Prediction: "dog"
Score: min(0/3, 1.0) = 0.0 ✗
Answer Normalization
Both exact and substring matching use normalization:
"The picnic table!" → "picnic table"
"a brown dog" → "brown dog"
"Yes, it is." → "yes it is"
Rules:
- Lowercase
- Remove articles (a, an, the)
- Remove punctuation
- Collapse whitespace
VQA Prompting
For short, VQA-style questions (≤15 words with "?"), we add:
"Provide a very short, concise answer (1-3 words maximum).
Do not explain or elaborate."
Result: Claude now gives "picnic table" instead of "The skateboarder is on a picnic table"
Substring Matching
If exact match fails, we use substring matching as fallback:
# Ground truth: "picnic table"
# Response: "The skateboarder is on a picnic table"
# Match: ✓ (substring "picnic table" found)
Visualization & Graphs
Generated Graphs (6 total)
1. Summary Dashboard
File: summary_dashboard.png
Comprehensive 5-subplot overview:
- Accuracy comparison
- Token savings percentage
- Accuracy drop from original
- Correct vs incorrect counts
- Token-accuracy trade-off scatter
Use this: For complete at-a-glance summary
2. Token-Accuracy Trade-off
File: token_accuracy_tradeoff.png
Scatter plot showing:
- X-axis: Token savings (%)
- Y-axis: VQA accuracy (%)
- Reference lines: 70% accuracy, 60% token savings
Use this: To identify best trade-off point
3. Accuracy Comparison
File: accuracy_comparison.png
Bar chart with color-coding:
- 🟢 Green: ≥75% accuracy (excellent)
- 🟠 Orange: 60-75% accuracy (acceptable)
- 🔴 Red: <60% accuracy (needs improvement)
Use this: To quickly compare absolute accuracy
4. Accuracy Drop
File: accuracy_drop.png
Bar chart showing accuracy loss vs original:
- 🟢 Green: ≤3% drop (acceptable)
- 🟠 Orange: 3-10% drop (moderate)
- 🔴 Red: >10% drop (high loss)
Use this: To see which transformations preserve accuracy best
5. Category Performance Heatmap
File: category_performance_heatmap.png
Heatmap showing accuracy by question category:
- Rows: Top 10 question categories
- Columns: Each transformation
- Colors: Green (high) → Yellow (medium) → Red (low)
Use this: To identify which question types are most affected
6. VQA Score Distribution
File: vqa_score_distribution.png
Stacked bar chart showing:
- 🟢 Perfect scores (1.0)
- 🟠 Partial scores (>0.33)
- 🔴 Wrong scores (≤0.33)
Use this: To understand answer quality beyond binary correct/incorrect
Automatic Generation
Graphs are automatically generated after benchmarks:
python benchmark_vqa.py --samples 100
# → Graphs saved to result_graphs/
Manual Generation
# From VQA results
python visualize_results.py vqa_results.json
# Custom output directory
python visualize_results.py results.json --output-dir my_graphs
Viewing Graphs
# macOS
open result_graphs/summary_dashboard.png
# Linux
xdg-open result_graphs/summary_dashboard.png
# Or open directory
open result_graphs/
Advanced Usage
Custom Transformations
Test your own transformations in benchmark:
# In benchmark_vqa.py, add custom transformation
from img2tok.transforms import YourCustomTransform
transformations = [
("Original", None),
("Custom 40%", YourCustomTransform(ratio=0.4)),
("FastV 50%", FastVInspiredPruning(pruning_ratio=0.5)),
]
Filter by Question Type
Test specific question types:
# Only test "how many" questions
vqa_samples = [s for s in dataset.get_samples(1000)
if s['question'].lower().startswith('how many')]
Compare Multiple Runs
# Run with different models
python benchmark_vqa.py --samples 100 --model high -o results_opus.json
python benchmark_vqa.py --samples 100 --model medium -o results_sonnet.json
python benchmark_vqa.py --samples 100 --model low -o results_haiku.json
# Generate graphs for each
python visualize_results.py results_opus.json --output-dir graphs_opus
python visualize_results.py results_sonnet.json --output-dir graphs_sonnet
python visualize_results.py results_haiku.json --output-dir graphs_haiku
Batch Processing
# Visualize all JSON results
for file in *_results.json; do
dirname="${file%.json}_graphs"
python visualize_results.py "$file" --output-dir "$dirname"
done
Archive Results
# Create dated directory
DATE=$(date +%Y%m%d)
mkdir -p archives/$DATE
# Run benchmark
python benchmark_vqa.py --samples 100
# Archive results + graphs
mv vqa_results.json archives/$DATE/
mv result_graphs archives/$DATE/
Troubleshooting
VQA Dataset Not Found
Error: "VQA dataset not found"
Solution:
python benchmark_vqa.py --download
scipy Not Found
Error: ModuleNotFoundError: No module named 'scipy'
Fix:
conda activate img2tok
pip install scipy>=1.10.0
VQA Tools Import Error
Error: ImportError: No module named 'vqaTools'
Fix: VQA tools should be in src/vqaTools/. Verify:
ls src/vqaTools/
# Should show: __init__.py vqa.py
API Key Not Set
Error: "Error: ANTHROPIC_API_KEY not set"
Fix:
export ANTHROPIC_API_KEY="your-key-here"
Or use demo mode:
python benchmark_vqa.py --samples 10 --demo
Matplotlib Not Found
Error: ModuleNotFoundError: No module named 'matplotlib'
Fix:
conda activate img2tok
pip install matplotlib>=3.7.0
Download is Slow
Problem: COCO images are 6.2GB
Solutions:
- Use faster internet connection
- Download overnight
- Use smaller sample sizes for testing
Out of Memory
Problem: Loading too many images
Solution: Reduce --samples:
python benchmark_vqa.py --samples 50 # Instead of 5000
API Reference
Benchmarking Scripts
benchmark_vqa.py
Run VQA benchmarks with token reduction:
python benchmark_vqa.py [OPTIONS]
Options:
--samples N- Number of questions to test (default: 100)--model MODEL- Model alias: high/medium/low (default: medium)--demo- Demo mode (no API calls)--download- Download VQA dataset--output FILE- Output JSON file (default: vqa_results.json)--dataset-dir DIR- Custom dataset directory
Examples:
python benchmark_vqa.py --samples 100 --model high
python benchmark_vqa.py --samples 20 --demo
python benchmark_vqa.py --download
visualize_results.py
Generate performance graphs:
python visualize_results.py RESULTS_JSON [OPTIONS]
Options:
RESULTS_JSON- Path to results JSON file--output-dir DIR- Output directory (default: result_graphs)
Examples:
python visualize_results.py vqa_results.json
python visualize_results.py results.json --output-dir my_graphs
Model Aliases
Use in --model argument:
MODEL_ALIASES = {
"high": "claude-opus-4-6",
"best": "claude-opus-4-6",
"opus": "claude-opus-4-6",
"medium": "claude-sonnet-4-6",
"default": "claude-sonnet-4-6",
"sonnet": "claude-sonnet-4-6",
"low": "claude-haiku-4-5",
"fast": "claude-haiku-4-5",
"cheap": "claude-haiku-4-5",
"haiku": "claude-haiku-4-5",
}
Python API
from benchmark_accuracy import AccuracyBenchmarker, resolve_model_name
# Resolve model alias
model = resolve_model_name("high") # → "claude-opus-4-6"
# Create benchmarker
benchmarker = AccuracyBenchmarker(model=model)
# Run benchmark
results = benchmarker.run_benchmark(
samples=100,
transformations=[
("Original", None),
("FastV 50%", FastVInspiredPruning(pruning_ratio=0.5)),
]
)
Cost Estimation
Anthropic Claude Pricing
| Model | Input | Output | Best For |
|---|---|---|---|
| Claude Opus 4.6 | $15/MTok | $75/MTok | Highest accuracy |
| Claude Sonnet 4.6 | $3/MTok | $15/MTok | Balanced (recommended) |
| Claude Haiku 4.5 | $0.80/MTok | $4/MTok | Speed/cost priority |
Example Costs
100 VQA questions × 4 transformations = 400 API calls
Assuming 2M input tokens, 50K output tokens:
| Model | Input Cost | Output Cost | Total |
|---|---|---|---|
| Opus | $30.00 | $3.75 | $33.75 |
| Sonnet | $6.00 | $0.75 | $6.75 |
| Haiku | $1.60 | $0.20 | $1.80 |
Savings with Haiku vs Opus: $31.95 (95% cheaper)
Performance Metrics
Expected Results (VQA Benchmark)
Based on 100-sample VQA tests with Sonnet 4.6:
| Transformation | Token Savings | VQA Accuracy | Accuracy Drop | Recommendation |
|---|---|---|---|---|
| Original | 0% | ~75% | — | Baseline |
| FastV 50% | 75% | ~72% | 3% | ✅ Safe for production |
| FastV 75% | 85% | ~58% | 17% | ⚠️ Use with caution |
| Resolution 512px | 65% | ~70% | 5% | ✅ Acceptable for most |
| Resolution 800px | 45% | ~73% | 2% | ✅ Safe for production |
Decision Framework
| Accuracy Drop | Recommendation | Use Case |
|---|---|---|
| 0-2% | ✅ Safe for production | All applications |
| 2-5% | ⚠️ Acceptable for most | Non-critical applications |
| 5-10% | ⚠️ Use with caution | Cost-sensitive, quality-tolerant |
| >10% | ❌ Not recommended | Reduction too aggressive |
Testing
Run Unit Tests
conda activate img2tok
# All tests
pytest
# With coverage
pytest --cov=src --cov-report=html
# VQA-specific tests
python test_vqa_evaluation.py # Original VQA tests (7 tests)
python test_vqa_substring_matching.py # Substring tests (5 tests)
Expected Test Results
✓ test_vqa_evaluation.py (7/7 pass)
- Perfect agreement
- Strong majority
- Exact threshold
- Partial credit
- Wrong answer
- Answer normalization
- Single match
✓ test_vqa_substring_matching.py (5/5 pass)
- Exact match still works
- Substring matching
- Multiple GT terms
- Wrong answer fails
- Partial match
File Structure
img2tok/
├── src/
│ ├── img2tok/
│ │ └── transforms/ # Image transformation implementations
│ └── vqaTools/ # VQA evaluation tools (Python 3)
├── benchmark_vqa.py # VQA benchmark script
├── benchmark_accuracy.py # Accuracy evaluation framework
├── benchmark_with_anthropic.py # General benchmark infrastructure
├── visualize_results.py # Graph generation script
├── test_vqa_evaluation.py # Original VQA tests
├── test_vqa_substring_matching.py # Substring matching tests
├── environment.yml # Conda environment
├── requirements.txt # pip requirements
├── setup.py # Package setup
├── benchmark_data/ # Downloaded datasets (created on --download)
│ └── vqa_minival/
│ ├── annotations/
│ ├── questions/
│ └── images/val2014/
└── result_graphs/ # Generated graphs (created after benchmark)
├── summary_dashboard.png
├── token_accuracy_tradeoff.png
├── accuracy_comparison.png
├── accuracy_drop.png
├── category_performance_heatmap.png
└── vqa_score_distribution.png
Contributing
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass:
pytest - Format code:
black . - Lint:
ruff check . - Submit pull request
References
Papers
- VQA: Visual Question Answering (ICCV 2015)
- FastV: FastV: An Image is Worth 1/2 Tokens (ECCV 2024)
- Fourier-VLM: Fourier-based Token Compression (2025)
Datasets
API Documentation
License
MIT License - See LICENSE file for details
Quick Reference
Common Commands
# Setup
conda env create -f environment.yml
conda activate img2tok
# Download VQA dataset (one-time)
python benchmark_vqa.py --download
# Quick test (demo)
python benchmark_vqa.py --samples 20 --demo
# Standard benchmark
export ANTHROPIC_API_KEY="your-key"
python benchmark_vqa.py --samples 100
# Different models
python benchmark_vqa.py --samples 100 --model high # Best quality
python benchmark_vqa.py --samples 100 --model medium # Balanced
python benchmark_vqa.py --samples 100 --model low # Fast & cheap
# View graphs
open result_graphs/summary_dashboard.png
# Manual graph generation
python visualize_results.py vqa_results.json
# Run tests
pytest
python test_vqa_evaluation.py
Version: 1.0.0 Last Updated: March 2026 Status: Production Ready
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file img2tok-0.1.0.tar.gz.
File metadata
- Download URL: img2tok-0.1.0.tar.gz
- Upload date:
- Size: 47.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
43fc37e588fb0752b903fac89f406f392bd1d42b8bfc5600b536befc2d45b62f
|
|
| MD5 |
ff0de8925b8b7d838c131c7858043d25
|
|
| BLAKE2b-256 |
6dce9168d290b7737f9500bf29362b54cb07573bb1355253be7813d63fc1799a
|
File details
Details for the file img2tok-0.1.0-py3-none-any.whl.
File metadata
- Download URL: img2tok-0.1.0-py3-none-any.whl
- Upload date:
- Size: 44.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ad449afe9b25c077eb95bfcc4cc0d6101d0848b27a29a8ea7a1f94a07c9522bf
|
|
| MD5 |
b6aebc7d2d8fcd3febe80de9c6c83d95
|
|
| BLAKE2b-256 |
0bfb4b97ac0e66a1b7c7d3788d07ad7f12854b010e8bf8cdc74fef123e82ec1f
|