PyPotteryTrace is a web-based interactive tool for converting archaeological pottery drawings into clean, structured SVG vectors. Using Meta's SAM2 (Segment Anything Model 2) for AI-powered segmentation, it allows archaeologists to precisely segment pottery elements, assign archaeological categories, and generate publication-ready vector graphics with automatic mirroring and intelligent profile connections.
PyPotteryTrace is a web-based Flask application that combines AI-powered segmentation (SAM2) with specialized vectorization algorithms to convert archaeological pottery drawings into structured SVG files. Unlike generic vectorization tools, PyPotteryTrace understands archaeological pottery conventions and automates tasks like:
- Profile mirroring around a rotation axis (with automatic construction lines)
- Running element connection to profiles with asymmetric logic
- Category-based organization of SVG layers
- Outer contour extraction for profiles and running elements
- Project management for iterative work on complex drawings
- Upload pottery drawing image
- Segment elements using AI (click-based prompts)
- Categorize elements (Profile, Running_Element, Decoration, etc.)
- Define rotation center for automatic mirroring
- Vectorize with intelligent category-specific processing
- Export organized SVG with proper layer structure
- Point prompts: Click to add/remove areas from segmentation
- Real-time feedback: Instant visual segmentation updates
- Precise boundaries: AI-detected element edges with sub-pixel accuracy
- Multiple elements: Segment multiple pottery elements independently
Specialized categories matching archaeological drawing conventions:
| Category | Description | Vectorization | Mirroring |
|---|---|---|---|
| Profile | Vessel profile/outline | Outer contour + construction lines (Symmetry_Line, Diameter) | ✅ Auto-mirror |
| Running_Element | Ridges, grooves, bands | Outer contour (open path) | ✅ Auto-mirror |
| Prospectus | Front/rear view | Standard vectorization | ❌ No mirror |
| Application | Applied elements | Standard vectorization | ❌ No mirror |
| Handle | Handles, spouts | Standard vectorization | ❌ No mirror |
| Decoration | Surface decoration | Standard vectorization | ❌ No mirror |
| Detail | Additional details | Standard vectorization | ❌ No mirror |
- Automatic profile mirroring around user-defined rotation center
- Asymmetric connection logic for Running_Element ↔ Profile:
- Right side (original): Running_Element extends to touch Profile (elements stay separate)
- Left side (mirrored): Profile_Mirrored merges with Running_Element_Mirrored (unified curve)
- Construction lines automatically generated for Profile (Symmetry_Line, Diameter)
- No construction lines for Running_Element (cleaner output)
- Contour extraction from binary masks
- Douglas-Peucker (RDP) simplification for cleaner paths
- Bézier curve smoothing for natural line appearance
- Open vs closed paths based on category logic
- Distance-based merging for profile fusion (proximity threshold)
- Endpoint extension for visual element connection
- Session persistence: Save/load complete project state
- Organized workspace: Automatic folder structure (masks, originals, outputs)
- Version control: Iterate on complex drawings without data loss
- Batch processing: Vectorize multiple elements in one pass
- Organized SVG: Category-grouped layers with proper z-ordering
- COCO format: ML-compatible annotations (bounding boxes, polygons)
- Individual masks: PNG masks for each segmented element
- Project archive: Complete workspace for collaboration
Frontend: HTML5 Canvas + Vanilla JavaScript (ES6 modules)
Backend: Flask (Python 3.9+)
AI Model: SAM2 (Segment Anything Model 2) via PyTorch
Image: OpenCV + NumPy + Pillow + scikit-image
Vector: svgwrite + rdp (Douglas-Peucker) + Bézier smoothing
Storage: File-based project structure + JSON metadata
main.py # Flask app, routes, orchestration
├── sam2_handler.py # SAM2 model loading & segmentation
├── vectorization_handler.py # Mask→SVG conversion, mirroring, merging
├── project_manager.py # Project state persistence
├── ml_export_handler.py # COCO format export
└── config_template.py # Configuration defaults
Key Modules:
main.py: Flask routes (/upload,/segment,/vectorize,/save_project), session managementsam2_handler.py: Loads SAM2 model, processes point/box prompts, returns segmentation masksvectorization_handler.py:- Extracts contours from masks
- Creates SVG paths with RDP simplification
- Handles mirroring logic (Profile + Running_Element)
- Implements asymmetric connection (
extend_running_element_to_profile,merge_profile_with_running_element)
project_manager.py: Saves/loads project state JSON, manages file structureml_export_handler.py: Converts masks to COCO annotation format
app.js # Main application controller
├── modules/
│ ├── canvasManager.js # Canvas rendering, zoom/pan
│ ├── imageGrid.js # Thumbnail grid for elements
│ ├── segmentation.js # SAM2 interaction (prompts)
│ ├── projectManager.js # Save/load UI
│ └── postprocess.js # Batch export, stroke widths
Workflow:
- User uploads image →
canvasManagerdisplays on canvas - User clicks →
segmentationsends prompts to/segmentendpoint - SAM2 returns mask → rendered as overlay on canvas
- User assigns category → stored in element metadata
- "Vectorize All" →
/vectorizeprocesses all elements - Backend generates SVG → returned to frontend for download
1. Mask (PNG binary image)
↓
2. cv2.findContours() → extract boundary points
↓
3. Filter contours (largest = outer contour)
↓
4. rdp() simplification (epsilon tolerance)
↓
5. smooth_path_to_bezier() [optional]
↓
6. Create SVG path element with category styling
↓
7. Mirror if Profile or Running_Element
↓
8. Apply connection logic (extend/merge)
↓
9. Export organized SVG with grouped layers
Profile Category:
- Extract outer contour from mask
- Create Symmetry_Line (vertical through rotation center)
- Create Diameter line (horizontal at max width)
- Mirror outer contour around rotation center → Profile_Mirrored
- Group in SVG: Profile, Symmetry_Line, Diameter
Running_Element Category:
- Extract outer contour (open path, no closing)
- If Profile exists: extend endpoints to touch Profile
- Mirror outer contour → Running_Element_Mirrored
- Right side: Keep extended Running_Element separate
- Left side: Merge Profile_Mirrored + Running_Element_Mirrored using distance matrix
- Remove Running_Element_Mirrored after merge (unified curve)
- Python 3.9+ (3.10 or 3.11 recommended)
- CUDA-capable GPU (recommended for SAM2, but CPU works)
- Git for cloning and SAM2 installation
git clone https://github.com/lrncrd/PyPotteryTrace.git
cd PyPotteryTrace# Windows
python -m venv .venv
.venv\Scripts\activate
# macOS/Linux
python3 -m venv .venv
source .venv/bin/activate# Install PyTorch (adjust for your CUDA version if needed)
# Visit https://pytorch.org for specific commands
# CUDA 11.8 example:
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118
# CPU only:
pip install torch torchvision
# Install SAM2 from GitHub
pip install git+https://github.com/facebookresearch/segment-anything-2.git
# Install remaining requirements
pip install -r requirements.txtModels are downloaded automatically on first run, but you can pre-download:
cd interactive_app/models
# Small model (~180MB, faster):
wget https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_small.pt
# Base+ model (~319MB, better quality):
wget https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_base_plus.pt
# Tiny model (~108MB, fastest):
wget https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_tiny.ptcd interactive_app
python main.pyOpen browser at: http://localhost:5000
cd interactive_app
python main.pyNavigate to http://localhost:5000 in your browser.
- Click "Select Image" button
- Choose your pottery drawing (JPG, PNG, TIFF, BMP)
- Image appears on canvas
- Click on pottery element (e.g., profile outline)
- SAM2 generates segmentation mask automatically
- Refine segmentation (if needed):
- Click inside = add area (positive prompt)
- Right-click = remove area (negative prompt)
- Click "Confirm Segment" when satisfied
- Select category from dropdown (Profile, Running_Element, Decoration, etc.)
- Element appears in thumbnail grid with assigned category
- Repeat steps 3-4 for each element
- Click "Set Rotation Center" button
- Click on canvas where vertical symmetry axis should be
- Center point appears as red circle
- Required for Profile and Running_Element mirroring
- Set stroke widths for each category
- Configure vectorization parameters (epsilon, smoothing)
- Click "Vectorize All" button
- Backend processes all elements with category-specific logic
- Progress bar shows completion
- Download SVG: Get organized vector file
- Save Project: Store complete session (masks, state, settings)
- Load Project: Resume work on saved project
1. Upload pottery drawing with profile and ridge
2. Segment profile outline → Assign "Profile" category
3. Segment ridge/band → Assign "Running_Element" category
4. Set rotation center on vertical axis
5. Vectorize All
→ Profile generates: Profile, Profile_Mirrored, Symmetry_Line, Diameter
→ Running_Element generates: Running_Element extended to Profile (right)
→ Left side: Profile_Mirrored merges with Running_Element_Mirrored
6. Download organized SVG with proper layers
Adjust in the web interface or via config_template.py:
| Parameter | Description | Default | Range |
|---|---|---|---|
epsilon |
RDP simplification tolerance (higher = simpler paths) | 2.0 | 0.5-10.0 |
smoothing_factor |
Bézier curve smoothing intensity | 0.3 | 0.0-1.0 |
min_contour_points |
Minimum points to keep a contour | 10 | 5-50 |
proximity_threshold |
Distance (px) for profile merging | 50.0 | 10-200 |
Default stroke widths by category (adjustable in UI):
| Category | Default Width | Adjustable |
|---|---|---|
| Profile | 1.5 pt | ✅ |
| Profile_Mirrored | 1.5 pt | ✅ |
| Symmetry_Line | 0.5 pt | ✅ |
| Diameter | 0.8 pt | ✅ |
| Running_Element | 1.2 pt | ✅ |
| Application | 1.2 pt | ✅ |
| Handle | 1.0 pt | ✅ |
| Decoration | 0.8 pt | ✅ |
| Detail | 0.6 pt | ✅ |
Choose model size in sam2_handler.py:
| Model | Size | Speed | Quality | Use Case |
|---|---|---|---|---|
tiny |
~108MB | Fastest | Good | Quick testing |
small |
~180MB | Fast | Better | Default (recommended) |
base_plus |
~319MB | Slower | Best | High-precision work |
When you save a project, PyPotteryTrace creates an organized folder:
projects/
└── PROJECTNAME_YYYYMMDD_HHMMSS/
├── project_state.json # Complete project metadata
├── project_vectorized.svg # Final organized SVG output
├── COCO_annotations.json # ML export format
├── original/
│ └── input_image.jpg # Original uploaded image
└── masks/
├── Profile_1.png # Individual element masks
├── Running_Element_1.png
├── Decoration_1.png
└── ...
Organized SVG with category-based groups:
<svg>
<g id="Profile">
<path d="M..." /> <!-- Profile outer contour -->
</g>
<g id="Symmetry_Line">
<path d="M..." /> <!-- Vertical construction line -->
</g>
<g id="Diameter">
<path d="M..." /> <!-- Horizontal construction line -->
</g>
<g id="Profile_Mirrored">
<path d="M..." /> <!-- Mirrored profile (merged with Running_Element_Mirrored) -->
</g>
<g id="Running_Element">
<path d="M..." /> <!-- Running element (extended to Profile) -->
</g>
<!-- Running_Element_Mirrored removed after merge -->
<g id="Decoration">
<path d="M..." />
</g>
</svg>Key Features:
- Organized layers: Easy to manipulate in Illustrator/Inkscape
- Construction lines: Separate groups for Symmetry_Line and Diameter
- Merged profiles: Left side shows unified Profile_Mirrored
- Stroke styling: Category-specific widths and colors
Complete project state for resuming work:
{
"project_id": "PROJECTNAME_20251111_123456",
"created_at": "2025-11-11T12:34:56",
"image": {
"filename": "pottery_drawing.jpg",
"width": 2000,
"height": 1500
},
"rotation_center": {"x": 500, "y": 750},
"elements": [
{
"id": "elem_001",
"category": "Profile",
"mask_path": "masks/Profile_1.png",
"vectorized": true,
"vectorization_params": {...}
},
...
]
}Machine learning compatible format:
{
"images": [...],
"annotations": [
{
"id": 1,
"image_id": 1,
"category_id": 1,
"segmentation": [[x1,y1,x2,y2,...]],
"bbox": [x, y, width, height],
"area": 12345
},
...
],
"categories": [
{"id": 1, "name": "Profile"},
{"id": 2, "name": "Running_Element"},
...
]
}You can use the vectorization engine programmatically:
from interactive_app.vectorization_handler import VectorizationHandler
import numpy as np
# Initialize handler
handler = VectorizationHandler()
# Vectorize a mask
mask = cv2.imread('mask.png', cv2.IMREAD_GRAYSCALE)
svg_path = handler.vectorize_from_png(
mask_path='mask.png',
output_path='output.svg',
category='Profile',
epsilon=2.0,
smoothing_factor=0.3
)
# Create mirrored profile
rotation_center_x = 500
handler.create_profile_svg(
outer_contour=contour_points,
output_path='profile.svg',
center_x=rotation_center_x,
image_height=1000
)To add custom categories, edit vectorization_handler.py:
CATEGORIES = {
'YourCategory': {
'color': '#FF5733',
'stroke_width': 1.0,
'fill': 'none',
'description': 'Your custom category'
}
}Then update interactive_app/templates/index.html category dropdown.
PyPotteryTrace is open for contributions! Here's how:
- Fork repository on GitHub
- Create feature branch:
git checkout -b feature/your-improvement - Make changes with clear, documented code
- Test thoroughly with real pottery drawings
- Commit with descriptive messages
- Open Pull Request with:
- Clear description of changes
- Before/after examples (if UI/algorithm changes)
- Test results
- Keep functions small, focused, and well-documented
- Follow existing code style (PEP 8 for Python)
- Avoid adding heavy dependencies without discussion
- Include docstrings for new functions
- Update README if adding user-facing features
- Algorithm improvements: Better path simplification, smoothing
- UI enhancements: Accessibility, mobile responsiveness
- Documentation: Tutorials, examples, translations
- Testing: Unit tests, integration tests
- Performance: Optimization for large images
