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
}