Skip to main content

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

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:

  1. VQA-Specific Prompting: Requests short, concise answers
  2. Substring Matching: Handles verbose responses gracefully
  3. 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:

  1. Summary Dashboard - 5-subplot comprehensive overview
  2. Token-Accuracy Tradeoff - Scatter plot showing optimal balance
  3. Accuracy Comparison - Bar chart by transformation
  4. Accuracy Drop - How much quality lost vs original
  5. Category Performance - Heatmap by question type
  6. 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:

  1. Divide image into tiles (e.g., 256×256)
  2. Compute attention scores (edge density + variance)
  3. Keep top-k% tiles based on scores
  4. 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?

  1. Objective evaluation with ground truth
  2. Diverse tasks: counting, text reading, object recognition, spatial reasoning
  3. Standard benchmark used by vision research community
  4. 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:

  1. Start with medium for baseline
  2. If accuracy sufficient, try low to save cost
  3. 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:

  1. Use faster internet connection
  2. Download overnight
  3. 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:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass: pytest
  5. Format code: black .
  6. Lint: ruff check .
  7. Submit pull request

References

Papers

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

img2tok-0.1.0.tar.gz (47.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

img2tok-0.1.0-py3-none-any.whl (44.7 kB view details)

Uploaded Python 3

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

Hashes for img2tok-0.1.0.tar.gz
Algorithm Hash digest
SHA256 43fc37e588fb0752b903fac89f406f392bd1d42b8bfc5600b536befc2d45b62f
MD5 ff0de8925b8b7d838c131c7858043d25
BLAKE2b-256 6dce9168d290b7737f9500bf29362b54cb07573bb1355253be7813d63fc1799a

See more details on using hashes here.

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

Hashes for img2tok-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ad449afe9b25c077eb95bfcc4cc0d6101d0848b27a29a8ea7a1f94a07c9522bf
MD5 b6aebc7d2d8fcd3febe80de9c6c83d95
BLAKE2b-256 0bfb4b97ac0e66a1b7c7d3788d07ad7f12854b010e8bf8cdc74fef123e82ec1f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page