4NK_IA_front/backend/enhancedOcr.js

247 lines
7.4 KiB
JavaScript

const sharp = require('sharp')
const path = require('path')
const fs = require('fs')
const { execSync } = require('child_process')
const {
isCNIDocument,
enhanceCNIPreprocessing,
processCNIWithZones,
decodeMRZ,
} = require('./cniOcrEnhancer')
/**
* OCR amélioré avec support spécialisé pour les CNI
* Combine Tesseract avec des techniques de preprocessing avancées
*/
// Fonction pour exécuter Tesseract avec des paramètres optimisés
async function runTesseractOCR(imageBuffer, options = {}) {
try {
const tempInput = path.join(__dirname, 'temp_input.png')
const tempOutput = path.join(__dirname, 'temp_output')
// Sauvegarder l'image temporaire
fs.writeFileSync(tempInput, imageBuffer)
// Paramètres Tesseract optimisés
const tesseractOptions = {
language: options.language || 'fra',
psm: options.psm || '6', // Mode uniforme de bloc de texte
oem: options.oem || '3', // Mode par défaut
...options,
}
// Construire la commande Tesseract
const cmd = `tesseract "${tempInput}" "${tempOutput}" -l ${tesseractOptions.language} --psm ${tesseractOptions.psm} --oem ${tesseractOptions.oem}`
console.log(`[TESSERACT] Commande: ${cmd}`)
// Exécuter Tesseract
execSync(cmd, { stdio: 'pipe' })
// Lire le résultat
const resultText = fs.readFileSync(`${tempOutput}.txt`, 'utf8')
// Nettoyer les fichiers temporaires
try {
fs.unlinkSync(tempInput)
fs.unlinkSync(`${tempOutput}.txt`)
} catch (cleanupError) {
console.warn(`[TESSERACT] Erreur nettoyage: ${cleanupError.message}`)
}
console.log(`[TESSERACT] Texte extrait: ${resultText.length} caractères`)
return {
text: resultText.trim(),
confidence: 0.8, // Estimation
method: 'tesseract_enhanced',
}
} catch (error) {
console.error(`[TESSERACT] Erreur OCR:`, error.message)
throw error
}
}
// Fonction pour extraire le texte d'une image avec améliorations CNI
async function extractTextFromImageEnhanced(inputPath) {
try {
console.log(`[ENHANCED_OCR] Début extraction: ${path.basename(inputPath)}`)
// Vérifier si c'est une CNI
const isCNI = await isCNIDocument(inputPath)
console.log(`[ENHANCED_OCR] CNI détectée: ${isCNI}`)
if (isCNI) {
return await extractTextFromCNI(inputPath)
} else {
return await extractTextFromStandardDocument(inputPath)
}
} catch (error) {
console.error(`[ENHANCED_OCR] Erreur extraction:`, error.message)
throw error
}
}
// Fonction spécialisée pour l'extraction de texte des CNI
async function extractTextFromCNI(inputPath) {
try {
console.log(`[CNI_OCR] Traitement CNI: ${path.basename(inputPath)}`)
// Améliorer le preprocessing pour les CNI
const enhancedImage = await enhanceCNIPreprocessing(inputPath)
if (!enhancedImage) {
throw new Error('Échec du preprocessing CNI')
}
// Traitement par zones
const cniZones = await processCNIWithZones(inputPath)
let combinedText = ''
let mrzData = null
// Extraire le texte de l'image améliorée
const mainText = await runTesseractOCR(enhancedImage, {
language: 'fra',
psm: '6', // Mode uniforme de bloc de texte
})
combinedText += mainText.text + '\n'
// Si on a des zones, traiter chaque zone séparément
if (cniZones && cniZones.zones) {
for (const [zoneName, zoneImage] of Object.entries(cniZones.zones)) {
try {
const zoneText = await runTesseractOCR(zoneImage, {
language: 'fra',
psm: '8', // Mode mot unique
})
combinedText += `[${zoneName.toUpperCase()}] ${zoneText.text}\n`
console.log(`[CNI_OCR] Zone ${zoneName}: ${zoneText.text}`)
} catch (zoneError) {
console.warn(`[CNI_OCR] Erreur zone ${zoneName}:`, zoneError.message)
}
}
}
// Traiter la MRZ si disponible
if (cniZones && cniZones.mrz) {
try {
const mrzText = await runTesseractOCR(cniZones.mrz, {
language: 'eng', // La MRZ est en anglais
psm: '8', // Mode mot unique
})
combinedText += `[MRZ] ${mrzText.text}\n`
// Décoder la MRZ
mrzData = decodeMRZ(mrzText.text)
if (mrzData) {
combinedText += `[MRZ_DECODED] Nom: ${mrzData.surname}, Prénom: ${mrzData.givenNames}, Numéro: ${mrzData.documentNumber}\n`
}
} catch (mrzError) {
console.warn(`[CNI_OCR] Erreur MRZ:`, mrzError.message)
}
}
// Post-traitement du texte pour corriger les erreurs communes
const processedText = postProcessCNIText(combinedText)
console.log(`[CNI_OCR] Texte final: ${processedText.length} caractères`)
return {
text: processedText,
confidence: 0.85, // Confiance élevée pour les CNI traitées
method: 'cni_enhanced',
mrzData: mrzData,
zones: cniZones ? Object.keys(cniZones.zones || {}) : [],
}
} catch (error) {
console.error(`[CNI_OCR] Erreur traitement CNI:`, error.message)
throw error
}
}
// Fonction pour l'extraction de texte des documents standards
async function extractTextFromStandardDocument(inputPath) {
try {
console.log(`[STANDARD_OCR] Traitement document standard: ${path.basename(inputPath)}`)
// Preprocessing standard
const image = sharp(inputPath)
const metadata = await image.metadata()
const processedImage = await image
.resize({
width: Math.min(metadata.width * 2, 2000),
height: Math.min(metadata.height * 2, 2000),
fit: 'inside',
withoutEnlargement: false,
})
.grayscale()
.normalize()
.sharpen()
.png()
.toBuffer()
// OCR standard
const result = await runTesseractOCR(processedImage, {
language: 'fra',
psm: '6',
})
return {
text: result.text,
confidence: result.confidence,
method: 'standard_enhanced',
}
} catch (error) {
console.error(`[STANDARD_OCR] Erreur traitement standard:`, error.message)
throw error
}
}
// Fonction pour post-traiter le texte des CNI
function postProcessCNIText(text) {
try {
let processedText = text
// Corrections communes pour les CNI
const corrections = [
// Corrections de caractères corrompus
{ from: /RÉPUBLIQUE FRANCATSEN/g, to: 'RÉPUBLIQUE FRANÇAISE' },
{ from: /CARTE NATIONALE DIDENTITE/g, to: "CARTE NATIONALE D'IDENTITÉ" },
{ from: /Ne :/g, to: 'N° :' },
{ from: /Fe - 0/g, to: 'Féminin' },
{ from: /Mele:/g, to: 'Mâle:' },
{ from: /IDFRACANTUCCKKLLLLKLLLLLLLLLLLK/g, to: 'IDFRA' },
// Nettoyage des caractères parasites
{
from: /[^\w\sÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ.,;:!?()\-'"]/g,
to: ' ',
},
// Normalisation des espaces
{ from: /\s+/g, to: ' ' },
{ from: /^\s+|\s+$/g, to: '' },
]
// Appliquer les corrections
for (const correction of corrections) {
processedText = processedText.replace(correction.from, correction.to)
}
console.log(`[POST_PROCESS] Texte post-traité: ${processedText.length} caractères`)
return processedText
} catch (error) {
console.error(`[POST_PROCESS] Erreur post-traitement:`, error.message)
return text
}
}
module.exports = {
extractTextFromImageEnhanced,
extractTextFromCNI,
extractTextFromStandardDocument,
runTesseractOCR,
postProcessCNIText,
}