People: Match markers with known faces and people #22

This commit is contained in:
Michael Mayer 2021-08-14 20:48:38 +02:00
parent 0cb44f7b45
commit 41cc0c596d
4 changed files with 94 additions and 11 deletions

View file

@ -71,6 +71,7 @@ var MarkerFixtures = MarkerMap{
"1000003-5": Marker{
ID: 5,
FileID: 1000003,
FaceID: FaceFixtures.Get("unknown").ID,
RefUID: "",
MarkerSrc: SrcImage,
MarkerType: MarkerFace,

View file

@ -173,12 +173,18 @@ func (w *Faces) Start() (err error) {
// Skip clustering if index contains no new face markers.
if n := query.CountNewFaceMarkers(); n < 1 {
log.Debugf("faces: no new markers, matching known faces")
log.Debugf("faces: no new markers, matching people and faces")
if m, err := query.MatchKnownFaces(); err != nil {
if affected, err := query.MatchMarkersWithPeople(); err != nil {
log.Errorf("faces: %s (create people from markers)", err)
} else if affected > 0 {
log.Infof("faces: matched %d markers with people", affected)
}
if matched, err := query.MatchKnownFaces(); err != nil {
return err
} else if m > 0 {
log.Infof("faces: matched %d markers", m)
} else if matched > 0 {
log.Infof("faces: matched %d markers to known faces", matched)
}
return nil
@ -313,7 +319,7 @@ func (w *Faces) Start() (err error) {
log.Errorf("faces: failed adding %s", txt.Quote(marker.MarkerLabel))
} else if f, ok := faceMap[faceId]; ok {
faceMap[faceId] = faceMatch{Embedding: f.Embedding, PersonUID: person.PersonUID}
entity.Db().Model(&entity.Face{}).Where("id = ?", faceId).Update("PersonUID", person.PersonUID)
entity.Db().Model(&entity.Face{}).Where("id = ? AND person_uid = ''", faceId).Update("PersonUID", person.PersonUID)
}
// Existing person?

View file

@ -2,6 +2,7 @@ package query
import (
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/pkg/txt"
)
// MarkerByID returns a Marker based on the ID.
@ -16,22 +17,23 @@ func MarkerByID(id uint) (marker entity.Marker, err error) {
// Markers finds a list of file markers filtered by type, embeddings, and sorted by id.
func Markers(limit, offset int, markerType string, embeddings, unmatched bool) (result entity.Markers, err error) {
stmt := Db()
db := Db()
if markerType != "" {
stmt = stmt.Where("marker_type = ?", markerType)
db = db.Where("marker_type = ?", markerType)
}
if embeddings {
stmt = stmt.Where("embeddings <> ''")
db = db.Where("embeddings <> ''")
}
if unmatched {
stmt = stmt.Where("ref_uid = ''")
db = db.Where("ref_uid = ''")
}
stmt = stmt.Order("id").Limit(limit).Offset(offset)
err = stmt.Find(&result).Error
db = db.Order("id").Limit(limit).Offset(offset)
err = db.Find(&result).Error
return result, err
}
@ -64,3 +66,34 @@ func Embeddings(single bool) (result entity.Embeddings, err error) {
return result, nil
}
func MatchMarkersWithPeople() (affected int, err error) {
var markers entity.Markers
if err := Db().
Where("face_id <> '' AND ref_uid = '' AND ref_src = ''").
Where("marker_invalid = 0 AND marker_type = ?", entity.MarkerFace).
Where("marker_label <> ''").
Order("marker_label").
Find(&markers).Error; err != nil {
return affected, err
} else if len(markers) == 0 {
return affected, nil
}
for _, m := range markers {
if p := entity.NewPerson(m.MarkerLabel, entity.SrcMarker, 1); p == nil {
log.Errorf("faces: person should not be nil - bug?")
} else if p = entity.FirstOrCreatePerson(p); p == nil {
log.Errorf("faces: failed adding person %s for marker %d", txt.Quote(m.MarkerLabel), m.ID)
} else if err := m.Updates(entity.Val{"RefUID": p.PersonUID, "RefSrc": entity.SrcPeople, "FaceID": ""}); err != nil {
return affected, err
} else if err := Db().Model(&entity.Face{}).Where("id = ? AND person_uid = ''", m.FaceID).Update("PersonUID", p.PersonUID).Error; err != nil {
return affected, err
} else {
affected++
}
}
return affected, nil
}

View file

@ -0,0 +1,43 @@
package query
import (
"testing"
"github.com/photoprism/photoprism/internal/entity"
"github.com/stretchr/testify/assert"
)
func TestMarkers(t *testing.T) {
results, err := Markers(3, 0, entity.MarkerFace, false, false)
if err != nil {
t.Fatal(err)
}
assert.GreaterOrEqual(t, len(results), 1)
for _, val := range results {
assert.IsType(t, entity.Marker{}, val)
}
}
func TestEmbeddings(t *testing.T) {
results, err := Embeddings(false)
if err != nil {
t.Fatal(err)
}
assert.GreaterOrEqual(t, len(results), 1)
for _, val := range results {
assert.IsType(t, entity.Embedding{}, val)
}
}
func TestMatchMarkersWithPeople(t *testing.T) {
affected, err := MatchMarkersWithPeople()
assert.NoError(t, err)
assert.GreaterOrEqual(t, affected, 1)
}