AgentStack
SKILL unreviewed MIT Self-run

Computer Vision

skill-ultroncore-claude-skill-vault-computer-vision · by UltronCore

>

No reviews yet
0 installs
4 views
0.0% view→install

Install

$ agentstack add skill-ultroncore-claude-skill-vault-computer-vision

Open-source listing — not yet scanned by AgentStack. Follow the source repository for install instructions.

Security review

⚠ Flagged

1 finding(s); flagged for manual review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures
  • high Dangerous shell/eval execution.

What it can access

  • Network access Used
  • Filesystem access Used
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution Used

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Computer Vision? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Computer Vision

When to Use

Use this skill for image loading/manipulation, object detection, OCR text extraction, image classification, manga panel/bubble processing, or building batch image pipelines. Covers PIL/Pillow, OpenCV, Tesseract, EasyOCR, YOLO, and Claude Vision API.


Core Rules

  • PIL/Pillow for image I/O and basic transforms; OpenCV for pixel-level operations and contour detection.
  • For OCR quality: Claude Vision API > EasyOCR > Tesseract (especially for manga/stylized fonts).
  • Always convert PIL images to numpy arrays for OpenCV (np.array(img)), and back with Image.fromarray(arr).
  • BGR vs RGB: OpenCV loads as BGR; PIL/most APIs expect RGB. Always convert: cv2.cvtColor(img, cv2.COLOR_BGR2RGB).
  • Batch pipelines: use pathlib.Path + generator patterns for memory efficiency on large sets.

PIL / Pillow Basics

from PIL import Image, ImageFilter, ImageEnhance, ImageDraw
import numpy as np

# Load, inspect, save
img = Image.open("photo.jpg")
print(img.size, img.mode)   # (width, height), 'RGB' / 'RGBA' / 'L'
img.save("output.png")

# Resize
img_resized = img.resize((800, 600))
img_thumb = img.copy(); img_thumb.thumbnail((256, 256))  # preserves aspect

# Convert modes
gray = img.convert("L")       # grayscale
rgba = img.convert("RGBA")    # add alpha channel

# Crop (left, upper, right, lower)
cropped = img.crop((100, 100, 500, 400))

# Rotate / flip
rotated = img.rotate(90, expand=True)
flipped = img.transpose(Image.FLIP_LEFT_RIGHT)

# Filters
blurred = img.filter(ImageFilter.GaussianBlur(radius=3))
sharp = img.filter(ImageFilter.SHARPEN)

# Enhance
enhancer = ImageEnhance.Contrast(img)
img_high_contrast = enhancer.enhance(2.0)

# Draw on image
draw = ImageDraw.Draw(img)
draw.rectangle([10, 10, 200, 200], outline="red", width=3)
draw.text((20, 20), "Label", fill="white")

# PIL  numpy
arr = np.array(img)          # PIL to numpy (RGB)
img_back = Image.fromarray(arr)  # numpy to PIL

OpenCV Fundamentals

import cv2
import numpy as np

# Read / write
img = cv2.imread("photo.jpg")          # BGR format
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # fix to RGB
cv2.imwrite("output.jpg", img)

# Resize
img_resized = cv2.resize(img, (800, 600))
img_half = cv2.resize(img, None, fx=0.5, fy=0.5)

# Grayscale + threshold
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY)
adaptive = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                  cv2.THRESH_BINARY, 11, 2)

# Blur
blurred = cv2.GaussianBlur(img, (5, 5), 0)
median = cv2.medianBlur(img, 5)

# Edge detection
edges = cv2.Canny(gray, 50, 150)

# Contour detection (e.g. panel borders)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
    area = cv2.contourArea(cnt)
    if area > 5000:
        x, y, w, h = cv2.boundingRect(cnt)
        cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)

# Morphological ops (useful for manga cleanup)
kernel = np.ones((3, 3), np.uint8)
dilated = cv2.dilate(binary, kernel, iterations=1)
eroded = cv2.erode(binary, kernel, iterations=1)
cleaned = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)

# Display (non-blocking for scripts)
cv2.imshow("Result", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

OCR with Tesseract (pytesseract)

# Install
pip install pytesseract pillow
brew install tesseract               # macOS
# sudo apt install tesseract-ocr    # Linux
import pytesseract
from PIL import Image
import cv2
import numpy as np

# Basic OCR
img = Image.open("document.png")
text = pytesseract.image_to_string(img)
print(text)

# With preprocessing for better accuracy
def preprocess_for_ocr(pil_img):
    gray = pil_img.convert("L")
    arr = np.array(gray)
    # Upscale if small
    if arr.shape[0]  0.5]

# On numpy array
img_arr = np.array(Image.open("document.png"))
results = reader.readtext(img_arr)

# Paragraph mode (merge nearby text)
results = reader.readtext(img_path, paragraph=True)

Claude Vision API for OCR (highest quality)

import anthropic
import base64
from pathlib import Path

client = anthropic.Anthropic()

def ocr_with_claude(image_path: str, prompt: str = None) -> str:
    """Use Claude Vision for OCR — best quality, especially for manga/stylized text."""
    img_data = Path(image_path).read_bytes()
    img_b64 = base64.standard_b64encode(img_data).decode("utf-8")

    # Detect media type
    suffix = Path(image_path).suffix.lower()
    media_map = {".jpg": "image/jpeg", ".jpeg": "image/jpeg",
                 ".png": "image/png", ".webp": "image/webp", ".gif": "image/gif"}
    media_type = media_map.get(suffix, "image/jpeg")

    if prompt is None:
        prompt = "Extract all text from this image exactly as written. Preserve formatting and line breaks."

    message = client.messages.create(
        model="claude-opus-4-5",
        max_tokens=2048,
        messages=[{
            "role": "user",
            "content": [
                {"type": "image", "source": {"type": "base64", "media_type": media_type, "data": img_b64}},
                {"type": "text", "text": prompt}
            ]
        }]
    )
    return message.content[0].text

# For manga speech bubbles
def ocr_manga_panel(image_path: str) -> dict:
    return ocr_with_claude(
        image_path,
        prompt="This is a manga panel. Extract all dialogue text from speech bubbles and thought bubbles. "
               "Return as JSON: {\"bubbles\": [{\"text\": \"...\", \"type\": \"speech|thought|narration\"}]}"
    )

YOLO Object Detection

pip install ultralytics
from ultralytics import YOLO
from PIL import Image
import cv2

# Load pre-trained model
model = YOLO("yolov8n.pt")   # nano: fast; yolov8s/m/l/x for more accuracy
# Downloads automatically on first run

# Detect on image
results = model("photo.jpg")

# Process results
for result in results:
    boxes = result.boxes
    for box in boxes:
        cls_id = int(box.cls[0])
        conf = float(box.conf[0])
        xyxy = box.xyxy[0].tolist()   # [x1, y1, x2, y2]
        label = model.names[cls_id]
        print(f"{label}: {conf:.2f} @ {xyxy}")

    # Save annotated image
    result.save("annotated.jpg")

# Batch detection
results = model(["img1.jpg", "img2.jpg", "img3.jpg"])

# Run on video
results = model("video.mp4", stream=True)
for result in results:
    frame = result.orig_img  # numpy BGR frame
    # process...

# Custom confidence threshold
results = model("photo.jpg", conf=0.4)

# Specific classes only (0=person, 2=car in COCO)
results = model("photo.jpg", classes=[0, 2])

Manga-Specific: Panel Detection

import cv2
import numpy as np
from PIL import Image
from pathlib import Path

def detect_manga_panels(image_path: str) -> list[dict]:
    """Detect individual panels in a manga page using contour detection."""
    img = cv2.imread(image_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # Manga pages usually have black panel borders on white background
    # Invert if background is dark
    mean_val = np.mean(gray)
    if mean_val  list[str]:
    """Extract each detected panel as a separate image."""
    from pathlib import Path
    img = Image.open(image_path)
    panels = detect_manga_panels(image_path)
    out = Path(output_dir)
    out.mkdir(exist_ok=True)
    saved = []
    for i, p in enumerate(panels):
        cropped = img.crop((p["x"], p["y"], p["x"]+p["w"], p["y"]+p["h"]))
        path = str(out / f"panel_{i:03d}.png")
        cropped.save(path)
        saved.append(path)
    return saved

Manga-Specific: Speech Bubble Detection

import cv2
import numpy as np

def detect_speech_bubbles(image_path: str) -> list[dict]:
    """Detect white speech bubbles using contour + circularity heuristics."""
    img = cv2.imread(image_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # Bubbles are typically white with dark outlines
    _, binary = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY)

    kernel = np.ones((2, 2), np.uint8)
    cleaned = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel, iterations=1)

    contours, _ = cv2.findContours(cleaned, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    bubbles = []
    h, w = img.shape[:2]
    for cnt in contours:
        area = cv2.contourArea(cnt)
        if area  (h * w * 0.4):
            continue

        # Circularity check: 4π·area / perimeter² (1.0 = perfect circle)
        perimeter = cv2.arcLength(cnt, True)
        if perimeter == 0:
            continue
        circularity = (4 * np.pi * area) / (perimeter ** 2)

        # Bubbles are roundish (not panel-shaped rectangles)
        if circularity > 0.3:
            x, y, bw, bh = cv2.boundingRect(cnt)
            bubbles.append({
                "x": x, "y": y, "w": bw, "h": bh,
                "circularity": circularity, "area": area
            })

    return sorted(bubbles, key=lambda b: (b["y"], b["x"]))

Batch Image Processing Pipeline

from pathlib import Path
from PIL import Image
import concurrent.futures

def process_image(path: Path, output_dir: Path) -> dict:
    """Process a single image — customize as needed."""
    try:
        img = Image.open(path).convert("RGB")
        # Example: resize + convert
        img.thumbnail((1024, 1024))
        out_path = output_dir / (path.stem + "_processed.jpg")
        img.save(out_path, "JPEG", quality=85, optimize=True)
        return {"file": path.name, "status": "ok", "out": str(out_path)}
    except Exception as e:
        return {"file": path.name, "status": "error", "error": str(e)}

def batch_process(input_dir: str, output_dir: str, workers: int = 4):
    """Batch process all images in a directory."""
    in_path = Path(input_dir)
    out_path = Path(output_dir)
    out_path.mkdir(parents=True, exist_ok=True)

    image_files = list(in_path.glob("**/*.{jpg,jpeg,png,webp}"))
    # Flatten globs for multiple extensions
    exts = ["*.jpg", "*.jpeg", "*.png", "*.webp", "*.JPG", "*.PNG"]
    image_files = [f for ext in exts for f in in_path.glob(ext)]

    print(f"Processing {len(image_files)} images...")
    results = []

    with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as executor:
        futures = {executor.submit(process_image, f, out_path): f for f in image_files}
        for future in concurrent.futures.as_completed(futures):
            result = future.result()
            results.append(result)
            status = result["status"]
            print(f"[{status}] {result['file']}")

    errors = [r for r in results if r["status"] == "error"]
    print(f"Done: {len(results) - len(errors)} OK, {len(errors)} errors")
    return results

Image Classification (torchvision / timm)

pip install torch torchvision timm pillow
import torch
import timm
from PIL import Image
from torchvision import transforms

# Load pretrained model
model = timm.create_model("efficientnet_b0", pretrained=True)
model.eval()

# Standard ImageNet preprocessing
transform = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

def classify(image_path: str, top_k: int = 5) -> list[tuple[str, float]]:
    img = Image.open(image_path).convert("RGB")
    tensor = transform(img).unsqueeze(0)  # add batch dim

    with torch.no_grad():
        logits = model(tensor)
        probs = torch.softmax(logits, dim=1)[0]
        top = torch.topk(probs, top_k)

    # Load ImageNet labels
    import urllib.request, json
    url = "https://storage.googleapis.com/download.tensorflow.org/data/imagenet_class_index.json"
    with urllib.request.urlopen(url) as r:
        class_idx = json.loads(r.read().decode())
    labels = {int(k): v[1] for k, v in class_idx.items()}

    return [(labels[idx.item()], prob.item()) for idx, prob in zip(top.indices, top.values)]

results = classify("photo.jpg")
for label, prob in results:
    print(f"{label}: {prob:.3f}")

Quick Reference

| Task | Best Tool | Notes | |------|-----------|-------| | Basic image I/O | PIL/Pillow | Easiest API | | Contour/edge detection | OpenCV | findContours, Canny | | OCR — printed text | Tesseract / EasyOCR | Preprocess first | | OCR — manga/stylized | Claude Vision API | Best accuracy | | Object detection | YOLOv8 (ultralytics) | Fast, pretrained | | Classification | timm + EfficientNet | Wide model zoo | | Manga panel detection | OpenCV contours | Custom thresholds | | Batch processing | ThreadPoolExecutor | I/O bound → threads |

Related Skills

  • diffusion-models — image generation models
  • multimodal-ai — multimodal systems
  • huggingface-llm-trainer — model training

GitNexus Index

This skill is indexed by GitNexus for knowledge graph traversal. Index path: /Users/localuser/.claude/skills/computer-vision/.gitnexus Last indexed: 2026-05-23

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.