mirror of
https://github.com/mudler/LocalAI.git
synced 2026-07-30 18:09:05 -04:00
Add the LocalAI Go backend that dlopens libfacedetect.so (the flat
facedetect_capi_* C-ABI) via purego, mirroring the sibling voice-detect
backend. Implements the Face subset of the Backend gRPC service:
- Embeddings(PredictOptions): Images[0] base64 -> temp file -> embed_path
-> L2-normalized ArcFace embedding.
- Detect(DetectOptions): src -> detect_path_json -> Detection boxes
(class_name "face", [x1,y1,x2,y2] -> x/y/w/h).
- FaceVerify(FaceVerifyRequest): two images + threshold + anti_spoof ->
verify_paths; best-effort img areas via detect.
- FaceAnalyze(FaceAnalyzeRequest): img -> analyze_path_json -> per-face
age + gender ("M"/"F" normalized to "Man"/"Woman").
The Makefile pins face-detect.cpp to 636a1963 and builds the shared lib
with ggml + vendored libjpeg-turbo static (PIC), so the .so is
ldd-clean (no libggml) and exports only facedetect_capi_* (no jpeg_
symbols). Gated Ginkgo e2e mirrors voice-detect.
Note for the gallery-wiring task: backend registration (index.yaml,
gallery, core/config/backend_capabilities.go) is intentionally not
touched here.
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Assisted-by: Claude:claude-opus-4-8 [Claude Code]
48 lines
1.2 KiB
Go
48 lines
1.2 KiB
Go
package main
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// defaultVerifyThreshold is the cosine-distance cutoff used when a request does
|
|
// not set one. Matches the insightface buffalo_l ArcFace R50 default the Python
|
|
// face backend ships with so the two implementations agree on verdicts out of
|
|
// the box.
|
|
const defaultVerifyThreshold float32 = 0.35
|
|
|
|
// loadOptions holds the parsed model-level options for face-detect.
|
|
type loadOptions struct {
|
|
verifyThreshold float32
|
|
modelName string
|
|
}
|
|
|
|
func splitOption(o string) (key, value string, ok bool) {
|
|
i := strings.Index(o, ":")
|
|
if i < 0 {
|
|
return "", "", false
|
|
}
|
|
return strings.TrimSpace(o[:i]), strings.TrimSpace(o[i+1:]), true
|
|
}
|
|
|
|
// parseOptions reads the backend "key:value" option slice. Unknown keys are
|
|
// ignored. Defaults: verify_threshold 0.35, model_name derived from the file.
|
|
func parseOptions(opts []string) loadOptions {
|
|
o := loadOptions{verifyThreshold: defaultVerifyThreshold}
|
|
for _, oo := range opts {
|
|
key, value, ok := splitOption(oo)
|
|
if !ok {
|
|
continue
|
|
}
|
|
switch key {
|
|
case "verify_threshold", "threshold":
|
|
if f, err := strconv.ParseFloat(value, 32); err == nil && f > 0 {
|
|
o.verifyThreshold = float32(f)
|
|
}
|
|
case "model_name":
|
|
o.modelName = value
|
|
}
|
|
}
|
|
return o
|
|
}
|