← Selectie van werk

In opdracht · 2026 · Starling Studio · #web #ai

Storyboard

De film, met ondertiteling
De film, met ondertiteling

In één middag van een handgetekende schets naar een korte film.

Een groep vertelt samen één verhaal in vijf scènes. Per scène beantwoord je een paar korte vragen en teken je een schets op een voorgedrukt vel, dat je daarna met je telefoon fotografeert; de app maakt van die foto eerst een beeld en vervolgens een clip. Aan het eind van de middag staat er een film met titel, vertelstem, ondertiteling en aftiteling, die je samen op het grote scherm bekijkt. Op de AI in de Stad Ecosysteemdag in Breda draaiden vijf van deze workshops tegelijk, met twintig tot dertig mensen per zaal.

Ik heb de applicatie gebouwd en uitgerold, van backend en frontend tot beeldketen en hosting. Als een foto binnenkomt zoekt de server de hoekmarkeringen op, trekt het vel recht, vlakt de belichting uit, corrigeert de kleur aan de hand van een gedrukte kleurbalk en snijdt het tekenvlak uit. Een taalmodel schrijft daarna de opdrachten voor beeld, beweging en vertelstem, waarna FLUX van de schets een beeld maakt en van dat beeld een clip; vanaf scène twee gaat het laatste frame van de vorige clip mee, zodat de film doorloopt. Omdat alle clips hetzelfde formaat hebben, kan ffmpeg ze zonder hercoderen aan elkaar zetten. Beeld, video, tekst en stem zitten elk achter een eigen aanbieder-schakelaar, de wachtrij is afgestemd op dertig groepen die tegelijk renderen, en elke scène mag één keer gratis en daarna drie keer opnieuw. Het geheel draait als één container op Scaleway met een beheerde database en privé-opslag: Django 5 met 107 tests aan de serverkant, TypeScript-webcomponenten met 29 tests aan de voorkant.

Colofon

  • Getoond: AI in de Stad Ecosysteemdag, Breda, 23 september 2026
  • Studio: Starling Studio
  • Mijn aandeel: projectmanagement, klantcontact, backend, frontend, beeldketen, hosting

Code

Het rechtzetten van het schetsvel, ingekort: de gevonden hoekmarkeringen worden met hun bekende plek op het vel (in millimeters) gekoppeld, RANSAC rekent daar een homografie uit die slechte detecties negeert, en de foto wordt op het vlakke vel gelegd. Daarna volgen belichting, kleur en uitsnede.

def detect_and_warp(image_rgb, reference):
    scale = DPI / MM_PER_INCH
    page_w, page_h = reference["page_mm"]
    canvas = (round(page_w * scale), round(page_h * scale))
    known = reference["marker_corners_mm"]
    found = _detect_markers(detector, gray, known, factors=(1.0, 1.5, 2.0))

    src, dst = [], []
    for marker_id, quad in sorted(found.items()):
        # detector en referentie geven hoeken in dezelfde volgorde: index voor index
        src.extend(quad)
        dst.extend(np.array(known[str(marker_id)], dtype=np.float64) * scale)

    if len(found) < MIN_MARKERS:
        raise PreprocessError("Te weinig markeringen gevonden; maak de foto opnieuw.")

    homography, inlier_mask = cv2.findHomography(
        np.asarray(src), np.asarray(dst), cv2.RANSAC, RANSAC_THRESHOLD_PX
    )
    return cv2.warpPerspective(
        image_rgb, homography, canvas,
        flags=cv2.INTER_CUBIC, borderValue=(255, 255, 255),
    )