People: Match markers with known faces and people #22
This commit is contained in:
parent
0cb44f7b45
commit
41cc0c596d
|
@ -71,6 +71,7 @@ var MarkerFixtures = MarkerMap{
|
|||
"1000003-5": Marker{
|
||||
ID: 5,
|
||||
FileID: 1000003,
|
||||
FaceID: FaceFixtures.Get("unknown").ID,
|
||||
RefUID: "",
|
||||
MarkerSrc: SrcImage,
|
||||
MarkerType: MarkerFace,
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
43
internal/query/markers_test.go
Normal file
43
internal/query/markers_test.go
Normal 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)
|
||||
}
|
Loading…
Reference in a new issue