Phash distance filter (#3596)

* Add phash_distance filter criterion
* Add distance to phash filter in UI
This commit is contained in:
WithoutPants
2023-04-17 15:36:51 +10:00
committed by GitHub
parent 62a1bc22c9
commit dcc73c4873
13 changed files with 184 additions and 17 deletions

View File

@@ -29,6 +29,7 @@ func (d *CustomSQLiteDriver) Open(dsn string) (driver.Conn, error) {
"regexp": regexFn,
"durationToTinyInt": durationToTinyIntFn,
"basename": basenameFn,
"phash_distance": phashDistanceFn,
}
for name, fn := range funcs {

10
pkg/sqlite/phash.go Normal file
View File

@@ -0,0 +1,10 @@
package sqlite
import "github.com/corona10/goimagehash"
func phashDistanceFn(phash1 int64, phash2 int64) (int64, error) {
hash1 := goimagehash.NewImageHash(uint64(phash1), goimagehash.PHash)
hash2 := goimagehash.NewImageHash(uint64(phash2), goimagehash.PHash)
distance, _ := hash1.Distance(hash2)
return int64(distance), nil
}

View File

@@ -882,17 +882,16 @@ func (qb *SceneStore) makeFilter(ctx context.Context, sceneFilter *models.SceneF
query.handleCriterion(ctx, criterionHandlerFunc(func(ctx context.Context, f *filterBuilder) {
if sceneFilter.Phash != nil {
qb.addSceneFilesTable(f)
f.addLeftJoin(fingerprintTable, "fingerprints_phash", "scenes_files.file_id = fingerprints_phash.file_id AND fingerprints_phash.type = 'phash'")
value, _ := utils.StringToPhash(sceneFilter.Phash.Value)
intCriterionHandler(&models.IntCriterionInput{
Value: int(value),
// backwards compatibility
scenePhashDistanceCriterionHandler(qb, &models.PhashDistanceCriterionInput{
Value: sceneFilter.Phash.Value,
Modifier: sceneFilter.Phash.Modifier,
}, "fingerprints_phash.fingerprint", nil)(ctx, f)
})(ctx, f)
}
}))
query.handleCriterion(ctx, scenePhashDistanceCriterionHandler(qb, sceneFilter.PhashDistance))
query.handleCriterion(ctx, intCriterionHandler(sceneFilter.Rating100, "scenes.rating", nil))
// legacy rating handler
query.handleCriterion(ctx, rating5CriterionHandler(sceneFilter.Rating, "scenes.rating", nil))
@@ -1382,6 +1381,45 @@ INNER JOIN (` + valuesClause + `) t ON t.column2 = pt.tag_id
}
}
func scenePhashDistanceCriterionHandler(qb *SceneStore, phashDistance *models.PhashDistanceCriterionInput) criterionHandlerFunc {
return func(ctx context.Context, f *filterBuilder) {
if phashDistance != nil {
qb.addSceneFilesTable(f)
f.addLeftJoin(fingerprintTable, "fingerprints_phash", "scenes_files.file_id = fingerprints_phash.file_id AND fingerprints_phash.type = 'phash'")
value, _ := utils.StringToPhash(phashDistance.Value)
distance := 0
if phashDistance.Distance != nil {
distance = *phashDistance.Distance
}
if distance == 0 {
// use the default handler
intCriterionHandler(&models.IntCriterionInput{
Value: int(value),
Modifier: phashDistance.Modifier,
}, "fingerprints_phash.fingerprint", nil)(ctx, f)
}
switch {
case phashDistance.Modifier == models.CriterionModifierEquals && distance > 0:
// needed to avoid a type mismatch
f.addWhere("typeof(fingerprints_phash.fingerprint) = 'integer'")
f.addWhere("phash_distance(fingerprints_phash.fingerprint, ?) < ?", value, distance)
case phashDistance.Modifier == models.CriterionModifierNotEquals && distance > 0:
// needed to avoid a type mismatch
f.addWhere("typeof(fingerprints_phash.fingerprint) = 'integer'")
f.addWhere("phash_distance(fingerprints_phash.fingerprint, ?) > ?", value, distance)
default:
intCriterionHandler(&models.IntCriterionInput{
Value: int(value),
Modifier: phashDistance.Modifier,
}, "fingerprints_phash.fingerprint", nil)(ctx, f)
}
}
}
}
func (qb *SceneStore) setSceneSort(query *queryBuilder, findFilter *models.FindFilterType) {
if findFilter == nil || findFilter.Sort == nil || *findFilter.Sort == "" {
return