Compare commits
No commits in common. "8a5ec11573971fde1921488d08760ed1952ece7f" and "e5cb8569e574336ac13d60f68ae66519bc854160" have entirely different histories.
8a5ec11573
...
e5cb8569e5
3 changed files with 23 additions and 55 deletions
|
|
@ -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;
|
||||||
workerPromise = null;
|
|
||||||
numberWorkerPromise = null;
|
|
||||||
await Promise.all(
|
|
||||||
workers.filter(Boolean).map(async (promise) => {
|
|
||||||
try {
|
try {
|
||||||
const worker = await promise;
|
const worker = await workerPromise;
|
||||||
await worker.terminate();
|
await worker.terminate();
|
||||||
} catch {
|
} catch {
|
||||||
// ignore teardown errors
|
// ignore teardown errors
|
||||||
}
|
}
|
||||||
})
|
workerPromise = null;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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'}`);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((uploadError) => {
|
|
||||||
console.warn('Scan image upload failed:', uploadError);
|
console.warn('Scan image upload failed:', uploadError);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCardScanned(buildScannedCardPayload(finalCard, { imageData, scanImageUrl, ocrMeta }));
|
||||||
|
finishTrackedCard(cardTracker);
|
||||||
};
|
};
|
||||||
|
|
||||||
const showScanNotice = (message) => {
|
const showScanNotice = (message) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue