This commit is contained in:
Chris Kruining 2026-02-09 16:35:08 +01:00
parent 83ab4df537
commit b4e4ff73ec
No known key found for this signature in database
GPG key ID: EB894A3560CCCAD2
8 changed files with 427 additions and 10586 deletions

View file

@ -20,6 +20,12 @@ import {
HASH_BITS,
} from "./perceptualHash";
import { resizeImage, rotateImage } from "./imageUtils";
import {
isDebugEnabled,
saveDebugImage,
drawCorners,
resetDebugPrefix,
} from "./debugSaver";
export interface RecognitionOptions {
/** Enable card detection and perspective correction. */
@ -30,6 +36,8 @@ export interface RecognitionOptions {
minConfidence?: number;
/** Maximum Hamming distance to accept a match. */
matchThreshold?: number;
/** Enable debug image saving at each pipeline step. */
debug?: boolean;
}
export interface CardMatch {
@ -65,6 +73,7 @@ const DEFAULT_OPTIONS: Required<RecognitionOptions> = {
enableRotationMatching: true,
minConfidence: 0.85,
matchThreshold: MATCH_THRESHOLD,
debug: false,
};
/**
@ -93,8 +102,18 @@ export function recognizeCard(
): RecognitionResult {
const startTime = performance.now();
const opts = { ...DEFAULT_OPTIONS, ...options };
// Reset debug prefix for this recognition run
if (opts.debug && isDebugEnabled()) {
resetDebugPrefix();
}
try {
// Save input image
if (opts.debug && isDebugEnabled()) {
saveDebugImage("01_input", pixels, width, height);
}
if (cardHashes.length === 0) {
return {
success: false,
@ -111,7 +130,19 @@ export function recognizeCard(
// Step 1: Detect and extract card (if enabled)
if (opts.enableCardDetection) {
detection = detectCard(pixels, width, height);
// Save detection visualization
if (opts.debug && isDebugEnabled() && detection.found) {
const detectionViz = drawCorners(
pixels,
width,
height,
detection.corners,
[0, 255, 0]
);
saveDebugImage("03_detection", detectionViz, width, height);
}
if (detection.found) {
const warped = warpPerspective(
pixels,
@ -122,6 +153,11 @@ export function recognizeCard(
cardPixels = warped.pixels;
cardWidth = warped.width;
cardHeight = warped.height;
// Save perspective corrected image
if (opts.debug && isDebugEnabled()) {
saveDebugImage("04_perspective", cardPixels, cardWidth, cardHeight);
}
}
}
@ -196,9 +232,19 @@ function findBestMatchWithRotations(
// Apply CLAHE
const clahePixels = applyCLAHE(rotatedPixels, rotatedWidth, rotatedHeight);
// Save CLAHE output (only for rotation 0 to avoid spam)
if (opts.debug && isDebugEnabled() && rotation === 0) {
saveDebugImage("05_clahe", clahePixels, rotatedWidth, rotatedHeight);
}
// Resize to 32x32
const resized = resizeImage(clahePixels, rotatedWidth, rotatedHeight, 32, 32);
// Save final 32x32 (only for rotation 0)
if (opts.debug && isDebugEnabled() && rotation === 0) {
saveDebugImage("06_final_32x32", resized, 32, 32);
}
// Compute hash
const queryHash = computeColorHash(resized);
@ -243,9 +289,19 @@ function findBestMatchSingle(
): CardMatch | null {
// Apply CLAHE
const clahePixels = applyCLAHE(pixels, width, height);
// Save CLAHE output
if (opts.debug && isDebugEnabled()) {
saveDebugImage("05_clahe", clahePixels, width, height);
}
// Resize to 32x32
const resized = resizeImage(clahePixels, width, height, 32, 32);
// Save final 32x32
if (opts.debug && isDebugEnabled()) {
saveDebugImage("06_final_32x32", resized, 32, 32);
}
// Compute hash
const queryHash = computeColorHash(resized);