Grid Maker for Drawing

Turn reference photos into printable drawing grids

Welcome to another official UVF IT beginner build. Today, we will build your own Grid Maker for Drawing inspired by the popular tool found on Product Hunt. This utility helps artists transfer reference images onto canvas by overlaying a grid, making scaling and proportioning easier.

We will use Python and the Pillow library to create a minimal, script-based clone. This approach avoids complex web frameworks, focusing instead on core image processing logic. By the end of this 45-minute session, you will have a working tool that takes any image and outputs a version with a customizable grid overlay, ready for printing.

Prerequisites & Notes

System Architecture
LOCAL ENVIRONMENT Read Process Write Input Image PNG/JPG grid_maker.py Python Script Pillow Library Image Processing Output Image Grid Overlay
1

Phase 1: Scaffold Project

Create a new directory for the project and initialize a Python virtual environment to isolate dependencies. This ensures that your system Python remains clean and that you can reproduce the environment easily.

Install the Pillow library, which provides image processing capabilities for Python. We will use this library to load, modify, and save images with the grid overlay.

mkdir grid-maker-clone
cd grid-maker-clone
python3 -m venv venv
source venv/bin/activate
pip install Pillow
2

Phase 2: Core Feature 1 (Image Loading)

Create the main Python script grid_maker.py. We will start by importing the necessary modules from Pillow and setting up the basic structure of our script.

Implement logic to load an image from a file path using Pillow. We will use Image.open() to load the image and Image.new() to create a transparent overlay for the grid lines.

grid_maker.py

from PIL import Image, ImageDraw
import sys
import os

def load_image(path):
    """Load an image from the given path."""
    if not os.path.exists(path):
        print(f"Error: File '{path}' not found.")
        sys.exit(1)
    try:
        img = Image.open(path).convert("RGBA")
        return img
    except Exception as e:
        print(f"Error loading image: {e}")
        sys.exit(1)

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python grid_maker.py <input_image> [output_image] [cell_size]")
        sys.exit(1)

    input_path = sys.argv[1]
    img = load_image(input_path)
    print(f"Loaded image: {input_path} (Size: {img.size})")
3

Phase 3: Core Feature 2 (Grid Overlay)

Add a function to draw a grid on the image. The grid should be evenly spaced, with configurable cell size. We will create a transparent overlay and draw lines on it.

Use Pillow's ImageDraw to draw lines. We will iterate over the image width and height, drawing vertical and horizontal lines at intervals defined by the cell size.

grid_maker.py

from PIL import Image, ImageDraw
import sys
import os

def load_image(path):
    """Load an image from the given path."""
    if not os.path.exists(path):
        print(f"Error: File '{path}' not found.")
        sys.exit(1)
    try:
        img = Image.open(path).convert("RGBA")
        return img
    except Exception as e:
        print(f"Error loading image: {e}")
        sys.exit(1)

def draw_grid(img, cell_size=50, color=(255, 255, 255, 128)):
    """Draw a grid on the image."""
    width, height = img.size
    overlay = Image.new("RGBA", img.size, (0, 0, 0, 0))
    draw = ImageDraw.Draw(overlay)

    # Draw vertical lines
    for x in range(0, width, cell_size):
        draw.line([(x, 0), (x, height)], fill=color, width=1)

    # Draw horizontal lines
    for y in range(0, height, cell_size):
        draw.line([(0, y), (width, y)], fill=color, width=1)

    # Composite the overlay onto the original image
    img_with_grid = Image.alpha_composite(img, overlay)
    return img_with_grid

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python grid_maker.py <input_image> [output_image] [cell_size]")
        sys.exit(1)

    input_path = sys.argv[1]
    output_path = sys.argv[2] if len(sys.argv) > 2 else "output_grid.png"
    cell_size = int(sys.argv[3]) if len(sys.argv) > 3 else 50

    img = load_image(input_path)
    img_with_grid = draw_grid(img, cell_size)
    img_with_grid.save(output_path)
    print(f"Saved grid image to: {output_path}")
4

Phase 4: Run Locally

To test our script, we first need a sample image. You can use any photo from your computer, or download a placeholder image for testing purposes.

Run the script by passing the input image path and the desired output file name. The script will process the image and save the gridded version to your directory.

# Download a sample image for testing if you don't have one
curl -o test.png https://via.placeholder.com/800x600

# Run the grid maker script
# Usage: python grid_maker.py <input_image> <output_image> [cell_size]
python grid_maker.py test.png output_grid.png 50
5

Phase 5: Verify and Extend

Check your project directory for the new file output_grid.png. Open it with your default image viewer to ensure the grid lines are visible and evenly spaced.

Experiment with different cell sizes by changing the last argument in the command. Smaller numbers create denser grids, while larger numbers create fewer, bigger squares.

# Verify the output file exists and check its size
ls -lh output_grid.png

# Try a different grid size (e.g., 100 pixels per cell)
python grid_maker.py test.png output_grid_large.png 100

# Try a very fine grid (e.g., 20 pixels per cell)
python grid_maker.py test.png output_grid_fine.png 20

Verifying the Stack

CheckCommand / Why it matters
Virtual environment created and activatedsource venv/bin/activate
Pillow library installedpip show Pillow
Script runs without errorspython grid_maker.py test.png
Output image generated with gridls -lh output_grid.png
Grid size is configurablepython grid_maker.py test.png output_grid_8.png 8

Next Steps

You have built a minimal Grid Maker for Drawing. To extend this project, consider adding a web interface using Flask or Streamlit, supporting more image formats, or allowing users to adjust grid line opacity and color.