builtin-programs/recognition/sam2-contours.folk

set cc [C]
$cc cflags -I.
$cc include "vendor/CContour.c"

# Returns contours for a SAM2 mask byte string. Each contour is a Tcl list of
# {x y} pairs, with the same epsilon/scale/minLength behavior as
# findImageContours.
$cc proc findMaskContours {int width int height Jim_Obj* mask
                           double epsilon
                           double scaleX double scaleY} Jim_Obj* {
    int w = width;
    int h = height;
    if (w < 3 || h < 3) return Jim_NewListObj(interp, NULL, 0);

    int maskLen = 0;
    const uint8_t *maskData = (const uint8_t *)Jim_GetString(mask, &maskLen);
    FOLK_ENSURE(maskLen == w * h);

    int *F = malloc(sizeof(int) * (size_t)w * (size_t)h);
    for (int y = 0; y < h; y++) {
        int *Frow = F + (size_t)y * w;
        const uint8_t *maskRow = maskData + (size_t)y * w;
        for (int x = 0; x < w; x++) {
            Frow[x] = maskRow[x] ? 1 : 0;
        }
    }

    Contour *contours = findContours(F, w, h);
    free(F);

    Jim_Obj *outer = Jim_NewListObj(interp, NULL, 0);
    for (ptrdiff_t c = 0; c < arrlen(contours); c++) {
        Point *pts = (epsilon > 0)
            ? approxPolyDP(contours[c].points, (float)epsilon)
            : contours[c].points;
        ptrdiff_t n = arrlen(pts);

        size_t cap = (size_t)n * 40 + 1;
        char *buf = malloc(cap);
        size_t off = 0;
        for (ptrdiff_t k = 0; k < n; k++) {
            off += snprintf(buf + off, cap - off,
                            k == 0 ? "{%g %g}" : " {%g %g}",
                            pts[k].x * scaleX, pts[k].y * scaleY);
        }
        Jim_ListAppendElement(interp, outer,
            Jim_NewStringObjNoAlloc(interp, buf, (int)off));

        if (epsilon > 0) arrfree(pts);
    }
    freeContours(contours);
    return outer;
}
Claim the SAM2 contours library is [$cc compile]

When /someone/ wishes /obj/ has contours in /v/ &\
     /obj/ has mask /mask/ in /v/ with /...maskOpts/ &\
     /v/ has resolved geometry /geom/ &\
     the SAM2 contours library is /sam2ContoursLib/ {

    set width [dict get $maskOpts width]
    set height [dict get $maskOpts height]
    set scaleX [expr {double($geom(width)) / double($width)}]
    set scaleY [expr {double($geom(height)) / double($height)}]
    set epsilon 1.0

    Claim $obj has contours \
        [$sam2ContoursLib findMaskContours $width $height $mask \
             $epsilon $scaleX $scaleY] \
        in $v
}