Skip to content

Image Processing

Functions for converting images to sketches.

Overview

The image processing module converts raster images to vector strokes suitable for robot drawing.

graph LR
    A[Image File] --> B[Load & Grayscale]
    B --> C[Threshold]
    C --> D[Find Contours]
    D --> E[Simplify]
    E --> F[Normalize]
    F --> G[Optimize Order]
    G --> H[Sketch]

Main Function

image_to_sketch

image_to_sketch(image, config=None)

Convert an image to a Sketch (collection of Strokes).

Extracts contours from the image, simplifies them, normalizes coordinates to [0,1] range, and optimizes drawing order.

Parameters:

Name Type Description Default
image Union[str, Path, ndarray, Image]

Input image as file path, numpy array (H,W or H,W,C), or PIL Image.

required
config Optional[ImageConfig]

Image processing configuration. Uses defaults if None.

None

Returns:

Type Description
Sketch

Sketch object containing extracted strokes with normalized coordinates.

Sketch

(0,0) = top-left, (1,1) = bottom-right.

Raises:

Type Description
ImportError

If required dependencies (Pillow, scikit-image/opencv) are missing.

FileNotFoundError

If image path doesn't exist.

ValueError

If image format is unsupported.

Example

sketch = image_to_sketch("drawing.png") print(f"Extracted {len(sketch)} strokes")

Source code in pib3/image.py
def image_to_sketch(
    image: Union[str, Path, np.ndarray, "Image.Image"],
    config: Optional[ImageConfig] = None,
) -> Sketch:
    """
    Convert an image to a Sketch (collection of Strokes).

    Extracts contours from the image, simplifies them, normalizes coordinates
    to [0,1] range, and optimizes drawing order.

    Args:
        image: Input image as file path, numpy array (H,W or H,W,C), or PIL Image.
        config: Image processing configuration. Uses defaults if None.

    Returns:
        Sketch object containing extracted strokes with normalized coordinates.
        (0,0) = top-left, (1,1) = bottom-right.

    Raises:
        ImportError: If required dependencies (Pillow, scikit-image/opencv) are missing.
        FileNotFoundError: If image path doesn't exist.
        ValueError: If image format is unsupported.

    Example:
        >>> sketch = image_to_sketch("drawing.png")
        >>> print(f"Extracted {len(sketch)} strokes")
    """
    _check_dependencies()

    if config is None:
        config = ImageConfig()

    # Load image
    gray_array, (width, height) = _load_image_to_array(image)

    # Threshold to binary
    binary = _threshold_image(
        gray_array,
        threshold=config.threshold,
        auto_foreground=config.auto_foreground,
    )

    # Vectorize contours
    contours = _vectorize_contours(binary, config.simplify_tolerance)

    # Filter small contours (in pixel space)
    contours = _filter_small_contours(
        contours,
        min_length=config.min_contour_length,
        min_points=config.min_contour_points,
    )

    # Normalize to [0, 1]
    contours = _normalize_coordinates(contours, width, height, config.margin)

    # Optimize path order
    if config.optimize_path_order:
        contours = _optimize_path_order(contours)

    # Convert to Sketch
    strokes = [Stroke(points=points, closed=closed) for points, closed in contours]
    return Sketch(strokes=strokes, source_size=(width, height))

Basic Usage

import pib3

# From file path
sketch = pib3.image_to_sketch("drawing.png")

# From Path object
from pathlib import Path
sketch = pib3.image_to_sketch(Path("images/drawing.png"))

# With custom config
from pib3 import ImageConfig
config = ImageConfig(threshold=100, simplify_tolerance=3.0)
sketch = pib3.image_to_sketch("sketch.jpg", config)

Input Formats

import numpy as np
from PIL import Image
import pib3

# From file path (string or Path)
sketch = pib3.image_to_sketch("drawing.png")

# From NumPy array (grayscale)
gray = np.zeros((100, 100), dtype=np.uint8)
gray[25:75, 25:75] = 255
sketch = pib3.image_to_sketch(gray)

# From NumPy array (RGB)
rgb = np.zeros((100, 100, 3), dtype=np.uint8)
sketch = pib3.image_to_sketch(rgb)

# From NumPy array (RGBA with transparency)
rgba = np.zeros((100, 100, 4), dtype=np.uint8)
sketch = pib3.image_to_sketch(rgba)

# From PIL Image
pil_img = Image.open("drawing.png")
sketch = pib3.image_to_sketch(pil_img)

Processing Steps

  1. Load: Accept file path, numpy array, or PIL Image
  2. Grayscale: Convert to single channel
  3. Threshold: Binary black/white conversion
  4. Contours: Extract edges using scikit-image or OpenCV
  5. Simplify: Douglas-Peucker algorithm reduces points
  6. Filter: Remove small/short contours
  7. Normalize: Scale coordinates to [0, 1]
  8. Optimize: Reorder to minimize pen travel

Handling Different Images

from pib3 import ImageConfig, image_to_sketch

# Clean black-on-white
config = ImageConfig(threshold=128, auto_foreground=True)

# Light pencil sketch
config = ImageConfig(threshold=200, simplify_tolerance=3.0)

# High detail
config = ImageConfig(simplify_tolerance=0.5, min_contour_length=5)

# Noisy/low quality
config = ImageConfig(
    simplify_tolerance=4.0,
    min_contour_length=20,
    min_contour_points=5
)

Output

Returns a Sketch object containing:

  • List of Stroke objects with normalized coordinates
  • Source image dimensions (if available)
sketch = pib3.image_to_sketch("drawing.png")

print(f"Strokes extracted: {len(sketch)}")
print(f"Total points: {sketch.total_points()}")
print(f"Source size: {sketch.source_size}")

Dependencies

Image processing requires one of:

  • scikit-image (preferred): pip install scikit-image
  • OpenCV: pip install opencv-python

Install with image extras:

pip install "pib3[image] @ git+https://github.com/mamrehn/pib3.git"