59 lines
1.4 KiB
Dart
59 lines
1.4 KiB
Dart
|
|
import '../native/phash_bindings.dart';
|
||
|
|
import '../native/phash_loader.dart';
|
||
|
|
import 'hash_library.dart';
|
||
|
|
|
||
|
|
class PhashMatch {
|
||
|
|
const PhashMatch({
|
||
|
|
required this.cardId,
|
||
|
|
required this.distance,
|
||
|
|
required this.score,
|
||
|
|
this.filename,
|
||
|
|
});
|
||
|
|
|
||
|
|
final String cardId;
|
||
|
|
final int distance;
|
||
|
|
final double score;
|
||
|
|
final String? filename;
|
||
|
|
|
||
|
|
bool get confident => score >= 75;
|
||
|
|
|
||
|
|
String? get assetPath =>
|
||
|
|
filename == null ? null : 'test_assets/$filename';
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Hamming ranking over a local hash library.
|
||
|
|
class PhashMatcher {
|
||
|
|
PhashMatcher({PhashNative? native}) : _native = native ?? PhashNative();
|
||
|
|
|
||
|
|
final PhashNative _native;
|
||
|
|
|
||
|
|
static double scoreFromDistance(int distance, {int bits = kPhashBits}) {
|
||
|
|
final raw = 100.0 - (distance / bits) * 100.0;
|
||
|
|
return double.parse(raw.toStringAsFixed(2));
|
||
|
|
}
|
||
|
|
|
||
|
|
List<PhashMatch> topN(
|
||
|
|
String queryHash,
|
||
|
|
HashLibrary library, {
|
||
|
|
int n = 3,
|
||
|
|
}) {
|
||
|
|
if (library.items.isEmpty || n <= 0) return const [];
|
||
|
|
|
||
|
|
final scored = <PhashMatch>[];
|
||
|
|
for (final item in library.items) {
|
||
|
|
final distance = _native.hamming(queryHash, item.hash);
|
||
|
|
scored.add(
|
||
|
|
PhashMatch(
|
||
|
|
cardId: item.cardId,
|
||
|
|
distance: distance,
|
||
|
|
score: scoreFromDistance(distance),
|
||
|
|
filename: item.filename,
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
scored.sort((a, b) => a.distance.compareTo(b.distance));
|
||
|
|
if (scored.length <= n) return scored;
|
||
|
|
return scored.sublist(0, n);
|
||
|
|
}
|
||
|
|
}
|