People: Add face clustering and matching to meta worker #22
This commit is contained in:
parent
05daa9f7be
commit
42027962aa
6 changed files with 49 additions and 28 deletions
4
go.mod
4
go.mod
|
@ -63,9 +63,9 @@ require (
|
||||||
github.com/ugorji/go v1.2.6 // indirect
|
github.com/ugorji/go v1.2.6 // indirect
|
||||||
github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6
|
github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6
|
||||||
github.com/urfave/cli v1.22.5
|
github.com/urfave/cli v1.22.5
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e
|
||||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect
|
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect
|
||||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d
|
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d
|
||||||
gonum.org/v1/gonum v0.9.3 // indirect
|
gonum.org/v1/gonum v0.9.3 // indirect
|
||||||
google.golang.org/protobuf v1.27.1 // indirect
|
google.golang.org/protobuf v1.27.1 // indirect
|
||||||
gopkg.in/photoprism/go-tz.v2 v2.1.1
|
gopkg.in/photoprism/go-tz.v2 v2.1.1
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -314,6 +314,8 @@ golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPh
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e h1:VvfwVmMH40bpMeizC9/K7ipM5Qjucuu16RWfneFPyhQ=
|
||||||
|
golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
@ -378,6 +380,8 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
|
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
|
||||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
|
||||||
|
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
|
|
@ -21,7 +21,7 @@ var FacesCommand = cli.Command{
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "index",
|
Name: "index",
|
||||||
Usage: "Performs face clustering and recognition",
|
Usage: "Performs face clustering and matching",
|
||||||
Action: facesIndexAction,
|
Action: facesIndexAction,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -58,7 +58,7 @@ func facesStatsAction(ctx *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// facesIndexAction performs face clustering and recognition.
|
// facesIndexAction performs face clustering and matching.
|
||||||
func facesIndexAction(ctx *cli.Context) error {
|
func facesIndexAction(ctx *cli.Context) error {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
"github.com/mpraski/clusters"
|
"github.com/mpraski/clusters"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Faces represents a worker for face clustering and recognition.
|
// Faces represents a worker for face clustering and matching.
|
||||||
type Faces struct {
|
type Faces struct {
|
||||||
conf *config.Config
|
conf *config.Config
|
||||||
}
|
}
|
||||||
|
@ -143,19 +143,22 @@ func (w *Faces) Analyze() (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start face clustering and recognition.
|
// Disabled tests if facial recognition is disabled.
|
||||||
|
func (w *Faces) Disabled() bool {
|
||||||
|
return !(w.conf.Experimental() && w.conf.Settings().Features.People)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start face clustering and matching.
|
||||||
func (w *Faces) Start() (err error) {
|
func (w *Faces) Start() (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
err = fmt.Errorf("faces: %s (panic)\nstack: %s", r, debug.Stack())
|
err = fmt.Errorf("%s (panic)\nstack: %s", r, debug.Stack())
|
||||||
log.Error(err)
|
log.Errorf("faces: %s", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if !w.conf.Experimental() {
|
if w.Disabled() {
|
||||||
return fmt.Errorf("faces: experimental features disabled")
|
return fmt.Errorf("facial recognition is disabled")
|
||||||
} else if !w.conf.Settings().Features.People {
|
|
||||||
return fmt.Errorf("faces: disabled in settings")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := mutex.MainWorker.Start(); err != nil {
|
if err := mutex.MainWorker.Start(); err != nil {
|
||||||
|
@ -266,7 +269,7 @@ func (w *Faces) Start() (err error) {
|
||||||
|
|
||||||
for _, marker := range markers {
|
for _, marker := range markers {
|
||||||
if mutex.MainWorker.Canceled() {
|
if mutex.MainWorker.Canceled() {
|
||||||
return fmt.Errorf("faces: worker canceled")
|
return fmt.Errorf("worker canceled")
|
||||||
}
|
}
|
||||||
|
|
||||||
var faceId string
|
var faceId string
|
||||||
|
|
|
@ -32,8 +32,8 @@ func NewMoments(conf *config.Config) *Moments {
|
||||||
func (w *Moments) Start() (err error) {
|
func (w *Moments) Start() (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
err = fmt.Errorf("moments: %s (panic)\nstack: %s", r, debug.Stack())
|
err = fmt.Errorf("%s (panic)\nstack: %s", r, debug.Stack())
|
||||||
log.Error(err)
|
log.Errorf("moments: %s", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
@ -14,23 +14,23 @@ import (
|
||||||
"github.com/photoprism/photoprism/internal/query"
|
"github.com/photoprism/photoprism/internal/query"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Meta represents a background metadata maintenance worker.
|
// Meta represents a background metadata optimization worker.
|
||||||
type Meta struct {
|
type Meta struct {
|
||||||
conf *config.Config
|
conf *config.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMeta returns a new background metadata maintenance worker.
|
// NewMeta returns a new Meta worker.
|
||||||
func NewMeta(conf *config.Config) *Meta {
|
func NewMeta(conf *config.Config) *Meta {
|
||||||
return &Meta{conf: conf}
|
return &Meta{conf: conf}
|
||||||
}
|
}
|
||||||
|
|
||||||
// originalsPath returns the original media files path as string.
|
// originalsPath returns the original media files path as string.
|
||||||
func (worker *Meta) originalsPath() string {
|
func (m *Meta) originalsPath() string {
|
||||||
return worker.conf.OriginalsPath()
|
return m.conf.OriginalsPath()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts the metadata worker.
|
// Start metadata optimization routine.
|
||||||
func (worker *Meta) Start(delay time.Duration) (err error) {
|
func (m *Meta) Start(delay time.Duration) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
err = fmt.Errorf("metadata: %s (panic)\nstack: %s", r, debug.Stack())
|
err = fmt.Errorf("metadata: %s (panic)\nstack: %s", r, debug.Stack())
|
||||||
|
@ -46,13 +46,14 @@ func (worker *Meta) Start(delay time.Duration) (err error) {
|
||||||
|
|
||||||
log.Debugf("metadata: starting routine check")
|
log.Debugf("metadata: starting routine check")
|
||||||
|
|
||||||
settings := worker.conf.Settings()
|
settings := m.conf.Settings()
|
||||||
done := make(map[string]bool)
|
done := make(map[string]bool)
|
||||||
|
|
||||||
limit := 50
|
limit := 50
|
||||||
offset := 0
|
offset := 0
|
||||||
optimized := 0
|
optimized := 0
|
||||||
|
|
||||||
|
// Run index optimization.
|
||||||
for {
|
for {
|
||||||
photos, err := query.PhotosCheck(limit, offset, delay)
|
photos, err := query.PhotosCheck(limit, offset, delay)
|
||||||
|
|
||||||
|
@ -86,9 +87,9 @@ func (worker *Meta) Start(delay time.Duration) (err error) {
|
||||||
log.Debugf("metadata: optimized photo %s", photo.String())
|
log.Debugf("metadata: optimized photo %s", photo.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, m := range merged {
|
for _, p := range merged {
|
||||||
log.Infof("metadata: merged %s", m.PhotoUID)
|
log.Infof("metadata: merged %s", p.PhotoUID)
|
||||||
done[m.PhotoUID] = true
|
done[p.PhotoUID] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,20 +106,33 @@ func (worker *Meta) Start(delay time.Duration) (err error) {
|
||||||
log.Infof("metadata: optimized %d photos", optimized)
|
log.Infof("metadata: optimized %d photos", optimized)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Explicitly set quality of photos without primary file to -1.
|
||||||
if err := query.ResetPhotoQuality(); err != nil {
|
if err := query.ResetPhotoQuality(); err != nil {
|
||||||
log.Warnf("metadata: %s (reset photo quality)", err.Error())
|
log.Warnf("metadata: %s (reset photo quality)", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update photo counts for labels and places.
|
||||||
if err := entity.UpdatePhotoCounts(); err != nil {
|
if err := entity.UpdatePhotoCounts(); err != nil {
|
||||||
log.Warnf("metadata: %s (update photo counts)", err.Error())
|
log.Warnf("metadata: %s (update photo counts)", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
moments := photoprism.NewMoments(worker.conf)
|
// Run moments worker.
|
||||||
|
if w := photoprism.NewMoments(m.conf); w == nil {
|
||||||
if err := moments.Start(); err != nil {
|
log.Errorf("moments: failed creating worker")
|
||||||
|
} else if err := w.Start(); err != nil {
|
||||||
log.Warnf("moments: %s", err)
|
log.Warnf("moments: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run faces worker.
|
||||||
|
if w := photoprism.NewFaces(m.conf); w == nil {
|
||||||
|
log.Errorf("faces: failed creating worker")
|
||||||
|
} else if w.Disabled() {
|
||||||
|
// Do nothing.
|
||||||
|
} else if err := w.Start(); err != nil {
|
||||||
|
log.Warnf("faces: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run garbage collection.
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Reference in a new issue