Compare commits

..

No commits in common. "8a5ec11573971fde1921488d08760ed1952ece7f" and "e5cb8569e574336ac13d60f68ae66519bc854160" have entirely different histories.

3 changed files with 23 additions and 55 deletions

View file

@ -96,26 +96,9 @@ export async function recognizeCardNameStrip(imageDataUrl) {
/** /**
* OCR name strip + bottom collector-number strip. * OCR name strip + bottom collector-number strip.
* Runs both strips in parallel using two dedicated workers (~50% faster on mobile).
* @param {string} imageDataUrl * @param {string} imageDataUrl
* @returns {Promise<{ nameText: string, nameConfidence: number, cardNumber: string, numberConfidence: number }>} * @returns {Promise<{ nameText: string, nameConfidence: number, cardNumber: string, numberConfidence: number }>}
*/ */
let numberWorkerPromise = null;
async function getNumberWorker() {
if (!numberWorkerPromise) {
numberWorkerPromise = (async () => {
const { createWorker } = await import('tesseract.js');
const worker = await createWorker('eng', 1, {
logger: () => {},
});
await worker.setParameters({ tessedit_pageseg_mode: NUMBER_STRIP_PSM });
return worker;
})();
}
return numberWorkerPromise;
}
export async function recognizeCardFields(imageDataUrl) { export async function recognizeCardFields(imageDataUrl) {
if (typeof window === 'undefined') { if (typeof window === 'undefined') {
return { nameText: '', nameConfidence: 0, cardNumber: '', numberConfidence: 0 }; return { nameText: '', nameConfidence: 0, cardNumber: '', numberConfidence: 0 };
@ -127,17 +110,12 @@ export async function recognizeCardFields(imageDataUrl) {
cropCardStrip(imageDataUrl, { fromTop: false, heightRatio: NUMBER_STRIP_HEIGHT_RATIO }), cropCardStrip(imageDataUrl, { fromTop: false, heightRatio: NUMBER_STRIP_HEIGHT_RATIO }),
]); ]);
const [nameWorker, numberWorker] = await Promise.all([ const worker = await getWorker();
getWorker(), await worker.setParameters({ tessedit_pageseg_mode: NAME_STRIP_PSM });
getNumberWorker(), const nameResult = await worker.recognize(nameStripUrl);
]);
await nameWorker.setParameters({ tessedit_pageseg_mode: NAME_STRIP_PSM }); await worker.setParameters({ tessedit_pageseg_mode: NUMBER_STRIP_PSM });
const numberResult = await worker.recognize(numberStripUrl);
const [nameResult, numberResult] = await Promise.all([
nameWorker.recognize(nameStripUrl),
numberWorker.recognize(numberStripUrl),
]);
const cardNumber = extractCollectorNumberCandidate(numberResult.data.text || ''); const cardNumber = extractCollectorNumberCandidate(numberResult.data.text || '');
@ -154,17 +132,12 @@ export async function recognizeCardFields(imageDataUrl) {
} }
export async function terminateOcrWorker() { export async function terminateOcrWorker() {
const workers = [workerPromise, numberWorkerPromise]; if (!workerPromise) return;
try {
const worker = await workerPromise;
await worker.terminate();
} catch {
// ignore teardown errors
}
workerPromise = null; workerPromise = null;
numberWorkerPromise = null;
await Promise.all(
workers.filter(Boolean).map(async (promise) => {
try {
const worker = await promise;
await worker.terminate();
} catch {
// ignore teardown errors
}
})
);
} }

View file

@ -15,13 +15,13 @@ export const TRACK_STALE_MS = 3000;
export const SHAPE_DETECTION_INTERVAL_MS = 200; export const SHAPE_DETECTION_INTERVAL_MS = 200;
/** Verification polling interval in the camera scanner hook. */ /** Verification polling interval in the camera scanner hook. */
export const VERIFICATION_INTERVAL_MS = 500; export const VERIFICATION_INTERVAL_MS = 1000;
/** Minimum stable frames before a tracked card is sent for identification. */ /** Minimum stable frames before a tracked card is sent for identification. */
export const MIN_STABLE_COUNT_FOR_VERIFY = 3; export const MIN_STABLE_COUNT_FOR_VERIFY = 3;
/** Minimum time (ms) a card must be tracked before verification. */ /** Minimum time (ms) a card must be tracked before verification. */
export const MIN_FIRST_SEEN_MS_FOR_VERIFY = 500; export const MIN_FIRST_SEEN_MS_FOR_VERIFY = 800;
/** Delay after camera start before detection loops begin. */ /** Delay after camera start before detection loops begin. */
export const DETECTION_START_DELAY_MS = 300; export const DETECTION_START_DELAY_MS = 300;

View file

@ -101,22 +101,17 @@ export function useScannerIdentification({
document.title = originalTitle; document.title = originalTitle;
}, 3000); }, 3000);
// Show result immediately — don't block on upload let scanImageUrl = null;
onCardScanned(buildScannedCardPayload(finalCard, { imageData, scanImageUrl: null, ocrMeta }));
finishTrackedCard(cardTracker);
// Upload capture in background (fire-and-forget)
if (imageData) { if (imageData) {
uploadScanCapture(imageData) try {
.then((url) => { scanImageUrl = await uploadScanCapture(imageData);
if (debugLog && isDebugMode) { } catch (uploadError) {
debugLog('📤', `Background upload complete: ${url ? 'success' : '429'}`); console.warn('Scan image upload failed:', uploadError);
} }
})
.catch((uploadError) => {
console.warn('Scan image upload failed:', uploadError);
});
} }
onCardScanned(buildScannedCardPayload(finalCard, { imageData, scanImageUrl, ocrMeta }));
finishTrackedCard(cardTracker);
}; };
const showScanNotice = (message) => { const showScanNotice = (message) => {