AI: Adjust face recognition config defaults #5167

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer
2025-10-29 17:24:11 +01:00
parent 38770b6892
commit fb79e58468
11 changed files with 63 additions and 36 deletions

View File

@@ -17,19 +17,19 @@ var (
// ScoreThreshold is the base minimum face score accepted by the detector.
ScoreThreshold = 9.0
// ClusterScoreThreshold is the minimum score required for faces that contribute to automatic clustering.
ClusterScoreThreshold = 15
ClusterScoreThreshold = 20
// SizeThreshold is the minimum detected face size, in pixels.
SizeThreshold = 25
// ClusterSizeThreshold is the minimum face size, in pixels, for faces considered when forming clusters.
ClusterSizeThreshold = 50
ClusterSizeThreshold = 60
// ClusterDist is the similarity distance threshold that defines the cluster core.
ClusterDist = 0.64
// ClusterRadius is the maximum normalized distance for cluster samples.
ClusterRadius = 0.42
// MatchDist is the distance offset threshold used to match new faces with existing clusters.
MatchDist = 0.42
MatchDist = 0.4
// CollisionDist is the minimum distance under which embeddings cannot be distinguished.
CollisionDist = 0.1
CollisionDist = 0.05
// ClusterCore is the minimum number of faces required to seed a cluster core.
ClusterCore = 4
// SampleThreshold is the number of faces required before automatic clustering begins.
@@ -37,7 +37,7 @@ var (
// Epsilon is the numeric tolerance used during cluster comparisons.
Epsilon = 0.01
// SkipChildren controls whether the clustering step omits faces from child samples by default.
SkipChildren = true
SkipChildren = false
// IgnoreBackground determines whether background faces are ignored when generating matches.
IgnoreBackground = true
)

File diff suppressed because one or more lines are too long

View File

@@ -15,6 +15,14 @@ func TestRandomDist(t *testing.T) {
}
func TestRandomEmbeddings(t *testing.T) {
skipChildren := SkipChildren
ignoreBackground := IgnoreBackground
t.Cleanup(func() {
SkipChildren = skipChildren
IgnoreBackground = ignoreBackground
})
SkipChildren = true
IgnoreBackground = true
t.Run("Regular", func(t *testing.T) {
e := RandomEmbeddings(2, RegularFace)
for i := range e {

File diff suppressed because one or more lines are too long

View File

@@ -364,8 +364,8 @@ func (c *Config) Propagate() {
face.ClusterRadius = c.FaceClusterRadius()
face.ClusterDist = c.FaceClusterDist()
face.MatchDist = c.FaceMatchDist()
face.SkipChildren = !c.FaceMatchChildren()
face.IgnoreBackground = !c.FaceMatchBackground()
face.SkipChildren = c.FaceSkipChildren()
face.IgnoreBackground = !c.FaceAllowBackground()
face.DetectionAngles = c.FaceAngles()
if err := face.ConfigureEngine(face.EngineSettings{
Name: c.FaceEngine(),

View File

@@ -260,22 +260,22 @@ func (c *Config) FaceMatchDist() float64 {
return c.options.FaceMatchDist
}
// FaceMatchChildren reports whether child embeddings should be matched automatically.
func (c *Config) FaceMatchChildren() bool {
// FaceSkipChildren reports whether child embeddings should be skipped when matching.
func (c *Config) FaceSkipChildren() bool {
if c == nil {
return false
return face.SkipChildren
}
return c.options.FaceMatchChildren
return c.options.FaceSkipChildren
}
// FaceMatchBackground reports whether background embeddings should be matched.
func (c *Config) FaceMatchBackground() bool {
// FaceAllowBackground reports whether background embeddings should not be ignored.
func (c *Config) FaceAllowBackground() bool {
if c == nil {
return false
return !face.IgnoreBackground
}
return c.options.FaceMatchBackground
return c.options.FaceAllowBackground
}
// FaceAngles returns the set of detection angles in radians.

View File

@@ -288,18 +288,18 @@ func TestConfig_FaceMatchDist(t *testing.T) {
assert.Equal(t, face.MatchDist, c.FaceMatchDist())
}
func TestConfig_FaceMatchChildren(t *testing.T) {
func TestConfig_FaceSkipChildren(t *testing.T) {
c := NewConfig(CliTestContext())
assert.False(t, c.FaceMatchChildren())
c.options.FaceMatchChildren = true
assert.True(t, c.FaceMatchChildren())
assert.False(t, c.FaceSkipChildren())
c.options.FaceSkipChildren = true
assert.True(t, c.FaceSkipChildren())
}
func TestConfig_FaceMatchBackground(t *testing.T) {
func TestConfig_FaceAllowBackground(t *testing.T) {
c := NewConfig(CliTestContext())
assert.False(t, c.FaceMatchBackground())
c.options.FaceMatchBackground = true
assert.True(t, c.FaceMatchBackground())
assert.False(t, c.FaceAllowBackground())
c.options.FaceAllowBackground = true
assert.True(t, c.FaceAllowBackground())
}
func TestConfig_FaceAngles(t *testing.T) {

View File

@@ -1252,14 +1252,14 @@ var Flags = CliFlags{
EnvVars: EnvVars("FACE_MATCH_DIST"),
}}, {
Flag: &cli.BoolFlag{
Name: "face-match-children",
Usage: "enable automatic matching for child face embeddings",
EnvVars: EnvVars("FACE_MATCH_CHILDREN"),
Name: "face-skip-children",
Usage: "skips automatic matching of child face embeddings",
EnvVars: EnvVars("FACE_SKIP_CHILDREN"),
}}, {
Flag: &cli.BoolFlag{
Name: "face-match-background",
Usage: "enable automatic matching for background embeddings",
EnvVars: EnvVars("FACE_MATCH_BACKGROUND"),
Name: "face-allow-background",
Usage: "allows matching of probable background embeddings",
EnvVars: EnvVars("FACE_ALLOW_BACKGROUND"),
}}, {
Flag: &cli.StringFlag{
Name: "pid-filename",

View File

@@ -246,8 +246,8 @@ type Options struct {
FaceCollisionDist float64 `yaml:"-" json:"-" flag:"face-collision-dist"`
FaceEpsilonDist float64 `yaml:"-" json:"-" flag:"face-epsilon-dist"`
FaceMatchDist float64 `yaml:"-" json:"-" flag:"face-match-dist"`
FaceMatchChildren bool `yaml:"-" json:"-" flag:"face-match-children"`
FaceMatchBackground bool `yaml:"-" json:"-" flag:"face-match-background"`
FaceSkipChildren bool `yaml:"-" json:"-" flag:"face-skip-children"`
FaceAllowBackground bool `yaml:"-" json:"-" flag:"face-allow-background"`
PIDFilename string `yaml:"PIDFilename" json:"-" flag:"pid-filename"`
LogFilename string `yaml:"LogFilename" json:"-" flag:"log-filename"`
DetachServer bool `yaml:"DetachServer" json:"-" flag:"detach-server"`

View File

@@ -327,8 +327,8 @@ func (c *Config) Report() (rows [][]string, cols []string) {
{"face-collision-dist", fmt.Sprintf("%f", c.FaceCollisionDist())},
{"face-epsilon-dist", fmt.Sprintf("%f", c.FaceEpsilonDist())},
{"face-match-dist", fmt.Sprintf("%f", c.FaceMatchDist())},
{"face-match-children", fmt.Sprintf("%t", c.FaceMatchChildren())},
{"face-match-background", fmt.Sprintf("%t", c.FaceMatchBackground())},
{"face-skip-children", fmt.Sprintf("%t", c.FaceSkipChildren())},
{"face-allow-background", fmt.Sprintf("%t", c.FaceAllowBackground())},
// Daemon Mode.
{"pid-filename", c.PIDFilename()},

View File

@@ -35,10 +35,10 @@ func TestBuildFaceCandidates(t *testing.T) {
regular := entity.NewFace("", entity.SrcAuto, face.RandomEmbeddings(3, face.RegularFace))
require.NotNil(t, regular)
children := entity.NewFace("", entity.SrcAuto, face.RandomEmbeddings(3, face.ChildrenFace))
require.NotNil(t, children)
background := entity.NewFace("", entity.SrcAuto, face.RandomEmbeddings(3, face.BackgroundFace))
require.NotNil(t, background)
faces := entity.Faces{*regular, *children}
faces := entity.Faces{*regular, *background}
index := buildFaceIndex(faces)