2021-08-14 18:13:03 +02:00
|
|
|
package query
|
|
|
|
|
|
|
|
import (
|
2021-08-22 21:06:44 +02:00
|
|
|
"fmt"
|
|
|
|
|
2021-08-14 18:13:03 +02:00
|
|
|
"github.com/photoprism/photoprism/internal/entity"
|
|
|
|
)
|
|
|
|
|
2021-08-19 23:12:51 +02:00
|
|
|
// Faces returns all (known) faces from the index.
|
2021-08-22 21:06:44 +02:00
|
|
|
func Faces(knownOnly bool, src string) (result entity.Faces, err error) {
|
|
|
|
stmt := Db()
|
|
|
|
|
|
|
|
if src == "" {
|
|
|
|
stmt = stmt.Where("face_src <> ?", entity.SrcDefault)
|
|
|
|
} else {
|
|
|
|
stmt = stmt.Where("face_src = ?", src)
|
|
|
|
}
|
2021-08-14 18:13:03 +02:00
|
|
|
|
|
|
|
if knownOnly {
|
2021-08-22 21:06:44 +02:00
|
|
|
stmt = stmt.Where("subject_uid <> ''").Order("subject_uid, samples DESC")
|
|
|
|
} else {
|
2021-08-23 16:22:01 +02:00
|
|
|
stmt = stmt.Order("samples DESC")
|
2021-08-14 18:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
err = stmt.Find(&result).Error
|
|
|
|
|
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
2021-08-19 23:12:51 +02:00
|
|
|
// MatchFaceMarkers matches markers with known faces.
|
|
|
|
func MatchFaceMarkers() (affected int64, err error) {
|
2021-08-22 21:06:44 +02:00
|
|
|
faces, err := Faces(true, "")
|
2021-08-14 18:13:03 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return affected, err
|
|
|
|
}
|
|
|
|
|
2021-08-23 16:22:01 +02:00
|
|
|
for _, f := range faces {
|
2021-08-14 18:13:03 +02:00
|
|
|
if res := Db().Model(&entity.Marker{}).
|
2021-08-23 16:22:01 +02:00
|
|
|
Where("face_id = ?", f.ID).
|
2021-08-19 21:12:38 +02:00
|
|
|
Where("subject_src = ?", entity.SrcAuto).
|
2021-08-23 16:22:01 +02:00
|
|
|
Where("subject_uid <> ?", f.SubjectUID).
|
|
|
|
Updates(entity.Values{"SubjectUID": f.SubjectUID}); res.Error != nil {
|
2021-08-14 18:13:03 +02:00
|
|
|
return affected, err
|
|
|
|
} else if res.RowsAffected > 0 {
|
|
|
|
affected += res.RowsAffected
|
|
|
|
}
|
2021-08-23 16:22:01 +02:00
|
|
|
|
|
|
|
if err := f.UpdateMatchTime(); err != nil {
|
|
|
|
return affected, err
|
|
|
|
}
|
2021-08-14 18:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return affected, nil
|
|
|
|
}
|
|
|
|
|
2021-08-22 16:14:34 +02:00
|
|
|
// RemoveAnonymousFaceClusters removes anonymous faces from the index.
|
|
|
|
func RemoveAnonymousFaceClusters() (removed int64, err error) {
|
|
|
|
res := UnscopedDb().Delete(
|
2021-08-14 18:13:03 +02:00
|
|
|
entity.Face{},
|
2021-08-22 16:14:34 +02:00
|
|
|
"face_src = ? AND subject_uid = ''", entity.SrcAuto)
|
|
|
|
|
|
|
|
return res.RowsAffected, res.Error
|
2021-08-14 19:52:49 +02:00
|
|
|
}
|
|
|
|
|
2021-08-22 16:14:34 +02:00
|
|
|
// RemoveAutoFaceClusters removes automatically added face clusters from the index.
|
|
|
|
func RemoveAutoFaceClusters() (removed int64, err error) {
|
|
|
|
res := UnscopedDb().
|
|
|
|
Delete(entity.Face{}, "id <> ? AND face_src = ?", entity.UnknownFace.ID, entity.SrcAuto)
|
|
|
|
|
|
|
|
return res.RowsAffected, res.Error
|
2021-08-15 14:14:27 +02:00
|
|
|
}
|
|
|
|
|
2021-08-23 16:22:01 +02:00
|
|
|
// CountNewFaceMarkers counts the number of new face markers in the index.
|
2021-08-14 19:52:49 +02:00
|
|
|
func CountNewFaceMarkers() (n int) {
|
|
|
|
var f entity.Face
|
|
|
|
|
2021-08-24 15:20:05 +02:00
|
|
|
if err := Db().Where("face_src = ?", entity.SrcAuto).
|
|
|
|
Order("created_at DESC").Limit(1).Take(&f).Error; err != nil {
|
2021-08-15 14:14:27 +02:00
|
|
|
log.Debugf("faces: no existing clusters")
|
2021-08-14 19:52:49 +02:00
|
|
|
}
|
|
|
|
|
2021-08-23 16:22:01 +02:00
|
|
|
q := Db().Model(&entity.Markers{}).
|
|
|
|
Where("marker_type = ?", entity.MarkerFace).
|
|
|
|
Where("face_id = '' AND marker_invalid = 0 AND embeddings_json <> ''")
|
2021-08-14 19:52:49 +02:00
|
|
|
|
|
|
|
if !f.CreatedAt.IsZero() {
|
|
|
|
q = q.Where("created_at > ?", f.CreatedAt)
|
|
|
|
}
|
|
|
|
|
2021-08-23 16:22:01 +02:00
|
|
|
if err := q.Count(&n).Error; err != nil {
|
2021-08-14 19:52:49 +02:00
|
|
|
log.Errorf("faces: %s (count new markers)", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return n
|
2021-08-14 18:13:03 +02:00
|
|
|
}
|
2021-08-22 21:06:44 +02:00
|
|
|
|
2021-08-23 16:22:01 +02:00
|
|
|
// CountUnmatchedFaceMarkers counts the number of unmatched face markers in the index.
|
|
|
|
func CountUnmatchedFaceMarkers() (n int) {
|
|
|
|
var f entity.Face
|
|
|
|
|
2021-08-24 15:20:05 +02:00
|
|
|
if err := Db().Where("face_src = ?", entity.SrcAuto).Order("matched_at ASC").Limit(1).Take(&f).Error; err != nil {
|
2021-08-23 16:22:01 +02:00
|
|
|
log.Debugf("faces: no unmatched clusters")
|
|
|
|
}
|
|
|
|
|
2021-08-24 15:20:05 +02:00
|
|
|
q := Db().Model(&entity.Markers{}).
|
|
|
|
Where("marker_type = ?", entity.MarkerFace).
|
|
|
|
Where("face_id = '' AND subject_src = '' AND marker_invalid = 0 AND embeddings_json <> ''")
|
2021-08-23 16:22:01 +02:00
|
|
|
|
|
|
|
if f.MatchedAt != nil {
|
|
|
|
q = q.Where("updated_at > ?", f.MatchedAt)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := q.Count(&n).Error; err != nil {
|
|
|
|
log.Errorf("faces: %s (count unmatched markers)", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
2021-08-22 21:06:44 +02:00
|
|
|
// MergeFaces returns a new face that replaces multiple others.
|
|
|
|
func MergeFaces(merge entity.Faces) (merged *entity.Face, err error) {
|
|
|
|
if len(merge) < 2 {
|
|
|
|
// Nothing to merge.
|
|
|
|
return merged, fmt.Errorf("at least two faces required for merging")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create merged face cluster.
|
|
|
|
if merged = entity.NewFace(merge[0].SubjectUID, merge[0].FaceSrc, merge.Embeddings()); merged == nil {
|
|
|
|
return merged, fmt.Errorf("merged face must not be nil")
|
|
|
|
} else if err := merged.Create(); err != nil {
|
|
|
|
return merged, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update marker matches.
|
|
|
|
if err := Db().Model(&entity.Marker{}).Where("face_id IN (?)", merge.IDs()).
|
|
|
|
Updates(entity.Values{"face_id": merged.ID, "subject_uid": merged.SubjectUID}).Error; err != nil {
|
|
|
|
return merged, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete merged faces.
|
|
|
|
if err := Db().Where("id IN (?) AND id <> ?", merge.IDs(), merged.ID).Delete(&entity.Face{}).Error; err != nil {
|
|
|
|
return merged, err
|
|
|
|
}
|
|
|
|
|
2021-08-23 16:22:01 +02:00
|
|
|
// Find and reference additional matching markers.
|
|
|
|
if err := merged.MatchMarkers(); err != nil {
|
2021-08-22 21:06:44 +02:00
|
|
|
return merged, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return merged, err
|
|
|
|
}
|