Merge pull request 'fix: improve OCR CIP extraction to filter common TSI card text' (#10) from fix/ocr-barcode-extraction into main
Build & Push Docker Images / test-backend (push) Successful in 35s
Build & Push Docker Images / test-frontend (push) Successful in 32s
Build & Push Docker Images / deploy (push) Successful in 6s
Build & Push Docker Images / build-backend (push) Successful in 17s
Build & Push Docker Images / build-frontend (push) Successful in 16s
Build & Push Docker Images / test-backend (push) Successful in 35s
Build & Push Docker Images / test-frontend (push) Successful in 32s
Build & Push Docker Images / deploy (push) Successful in 6s
Build & Push Docker Images / build-backend (push) Successful in 17s
Build & Push Docker Images / build-frontend (push) Successful in 16s
Reviewed-on: #10
This commit was merged in pull request #10.
This commit is contained in:
+62
-5
@@ -531,12 +531,63 @@ const upload = multer({
|
||||
// Extract CIP from OCR text — looks for a long alphanumeric token (16 chars typical for CIP)
|
||||
function extractCip(text) {
|
||||
const cleaned = text.replace(/\s+/g, '');
|
||||
// Try exact 16-char alphanumeric match first
|
||||
|
||||
// Common Spanish text on TSI cards that should NOT be treated as CIP
|
||||
const CARD_TEXT_PATTERNS = [
|
||||
/TARJETASANITARIA/i,
|
||||
/TARJETASANIT/i,
|
||||
/TARJETASEGURIDAD/i,
|
||||
/SISTEMASALUD/i,
|
||||
/SERVICIOANDALUZ/i,
|
||||
/SALUD/i,
|
||||
/NUMERO/i,
|
||||
/NUM/i,
|
||||
/CIP/i,
|
||||
/CODIGO/i,
|
||||
/IDENTIFICACION/i,
|
||||
/PERSONAL/i,
|
||||
/TITULAR/i,
|
||||
/FECHA/i,
|
||||
/NACIMIENTO/i,
|
||||
/CADUCIDAD/i,
|
||||
/VALIDA/i,
|
||||
/ESPANA/i,
|
||||
/ESPAÑA/i,
|
||||
];
|
||||
|
||||
// Check if OCR result is just common card text
|
||||
const isCardText = (candidate) => {
|
||||
const upper = candidate.toUpperCase();
|
||||
return CARD_TEXT_PATTERNS.some(pattern => pattern.test(upper));
|
||||
};
|
||||
|
||||
// Try to find barcodes first (typically numeric-heavy)
|
||||
// TSI barcodes are usually numeric or alphanumeric with mostly numbers
|
||||
const numericMatches = cleaned.match(/\d{10,30}/g) || [];
|
||||
for (const match of numericMatches) {
|
||||
if (match.length >= 10 && match.length <= 30) {
|
||||
return match.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
// Try exact 16-char alphanumeric match, but filter card text
|
||||
const exact = cleaned.match(/[A-Z0-9]{16}/i);
|
||||
if (exact) return exact[0].toUpperCase();
|
||||
// Fallback: 8–30 alphanumeric chars
|
||||
const loose = cleaned.match(/[A-Z0-9]{8,30}/i);
|
||||
if (loose) return loose[0].toUpperCase();
|
||||
if (exact) {
|
||||
const candidate = exact[0].toUpperCase();
|
||||
if (!isCardText(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: 8–30 alphanumeric chars, filter card text
|
||||
const looseMatches = cleaned.match(/[A-Z0-9]{8,30}/gi) || [];
|
||||
for (const match of looseMatches) {
|
||||
const candidate = match.toUpperCase();
|
||||
if (!isCardText(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -546,11 +597,17 @@ app.post('/api/tsi/ocr', upload.single('photo'), async (req, res) => {
|
||||
return res.status(400).json({ error: 'No image file provided' });
|
||||
}
|
||||
|
||||
console.log('[OCR] Processing image, size:', req.file.size, 'bytes');
|
||||
|
||||
const worker = await createWorker('spa+eng');
|
||||
const { data } = await worker.recognize(req.file.buffer);
|
||||
await worker.terminate();
|
||||
|
||||
console.log('[OCR] Raw text:', data.text);
|
||||
console.log('[OCR] Words:', data.words?.length || 0, 'words detected');
|
||||
if (data.words && data.words.length > 0) {
|
||||
console.log('[OCR] First 10 words:', data.words.slice(0, 10).map(w => `${w.text}(${w.confidence?.toFixed(2)})`).join(', '));
|
||||
}
|
||||
const cip = extractCip(data.text);
|
||||
console.log('[OCR] Extracted CIP:', cip);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user