Skip to main content...
Python + Classical CV — the Catalog Tool
25 min

Day 12: Building the Catalog Tool pipeline + CLI

Composing the pipeline

Days 8-11 gave you four separable operations: color-space conversion, thresholding/edges, contour/morphology cleanup, and GrabCut segmentation. Today they compose into one pipeline function — background removal, resize, normalize — plus a CLI so it's runnable as a real tool, not a notebook cell.

src/catalog/pipeline.py — composing the week into one function
import cv2
import numpy as np

def remove_background(img_bgr: np.ndarray) -> np.ndarray:
    mask = np.zeros(img_bgr.shape[:2], np.uint8)
    bgd_model = np.zeros((1, 65), np.float64)
    fgd_model = np.zeros((1, 65), np.float64)
    h, w = img_bgr.shape[:2]
    rect = (int(w * 0.05), int(h * 0.05), int(w * 0.9), int(h * 0.9))

    cv2.grabCut(img_bgr, mask, rect, bgd_model, fgd_model, 5, cv2.GC_INIT_WITH_RECT)
    fg_mask = np.where((mask == 1) | (mask == 3), 255, 0).astype("uint8")

    # Day 10 cleanup: close small holes, open away stray noise
    kernel = np.ones((5, 5), np.uint8)
    fg_mask = cv2.morphologyEx(fg_mask, cv2.MORPH_CLOSE, kernel)
    fg_mask = cv2.morphologyEx(fg_mask, cv2.MORPH_OPEN, kernel)

    b, g, r = cv2.split(img_bgr)
    return cv2.merge([b, g, r, fg_mask])   # BGRA — mask becomes the alpha channel

def resize_and_normalize(img_rgba: np.ndarray, target: int = 1024) -> np.ndarray:
    h, w = img_rgba.shape[:2]
    scale = target / max(h, w)
    resized = cv2.resize(img_rgba, (int(w * scale), int(h * scale)), interpolation=cv2.INTER_AREA)

    # pad to a square canvas so every catalog image has consistent dimensions
    canvas = np.zeros((target, target, 4), dtype=np.uint8)
    y_off = (target - resized.shape[0]) // 2
    x_off = (target - resized.shape[1]) // 2
    canvas[y_off:y_off + resized.shape[0], x_off:x_off + resized.shape[1]] = resized
    return canvas

def process_image(path: str, target: int = 1024) -> np.ndarray:
    img = cv2.imread(path)
    cutout = remove_background(img)
    return resize_and_normalize(cutout, target)

The CLI

argparse (stdlib) is the pragmatic choice for a tool this size — no new dependency, and every backend engineer already knows the shape of a CLI (flags, positional args, --help). Iterate over a directory, process each image, report successes and failures rather than crashing on the first bad file.

src/catalog/cli.py
import argparse
from pathlib import Path
import cv2
from .pipeline import process_image

def main():
    parser = argparse.ArgumentParser(description="FitXpert Catalog Tool")
    parser.add_argument("input_dir", type=Path)
    parser.add_argument("output_dir", type=Path)
    parser.add_argument("--size", type=int, default=1024)
    args = parser.parse_args()

    args.output_dir.mkdir(parents=True, exist_ok=True)
    ok, failed = 0, []

    for path in args.input_dir.glob("*.jpg"):
        try:
            result = process_image(str(path), args.size)
            cv2.imwrite(str(args.output_dir / f"{path.stem}.png"), result)
            ok += 1
        except Exception as e:
            failed.append((path.name, str(e)))

    print(f"Processed {ok} images, {len(failed)} failed")
    for name, err in failed:
        print(f"  {name}: {err}")

if __name__ == "__main__":
    main()

Fail loud per-item, not for the whole batch

A batch tool that crashes on image 3 of 20 and silently loses images 4-20's results is worse than one that logs 17 successes and 3 named failures. This pattern — catch per-item, report at the end — is the same instinct as a good batch job in any backend system, just applied to a CV pipeline.

Key terms

argparse
Python's standard-library module for building command-line interfaces with flags, positional arguments, and --help.
BGRA
BGR color channels plus an Alpha (transparency) channel — how a background-removed image is typically saved as a PNG.

Processing a directory of 20 images, image #7 is corrupt and raises an exception inside the pipeline. What should the CLI do?

We use cookies

We use cookies to enhance your browsing experience, serve personalized content, and analyze our traffic. By clicking "Accept All", you consent to our use of cookies. Learn more

    Day 12: Building the Catalog Tool pipeline + CLI | RBTechIconX