4NK_IA_front/backend/imagePreprocessing.js
Nicolas Cantu a5a0421b32 backend
2025-09-16 01:04:57 +02:00

216 lines
6.1 KiB
JavaScript

/**
* Module de préprocessing d'image pour améliorer l'OCR
* Optimisé pour les documents d'identité et CNI
*/
const sharp = require('sharp')
const fs = require('fs')
const path = require('path')
/**
* Prétraite une image pour améliorer la qualité de l'OCR
* @param {string} inputPath - Chemin vers l'image d'entrée
* @param {string} outputPath - Chemin vers l'image de sortie (optionnel)
* @param {Object} options - Options de préprocessing
* @returns {Promise<Buffer>} - Buffer de l'image préprocessée
*/
async function preprocessImageForOCR(inputPath, outputPath = null, options = {}) {
console.log(`[PREPROCESSING] Début du préprocessing de: ${path.basename(inputPath)}`)
try {
// Options par défaut optimisées pour les documents d'identité
const defaultOptions = {
// Redimensionnement
width: 2000, // Largeur cible
height: null, // Hauteur automatique (maintient le ratio)
// Amélioration du contraste
contrast: 1.5, // Augmente le contraste
brightness: 1.1, // Légère augmentation de la luminosité
// Filtres
sharpen: true, // Amélioration de la netteté
denoise: true, // Réduction du bruit
// Conversion
grayscale: true, // Conversion en niveaux de gris
threshold: null, // Seuil pour binarisation (optionnel)
// Format de sortie
format: 'png', // Format PNG pour meilleure qualité
quality: 100 // Qualité maximale
}
const config = { ...defaultOptions, ...options }
console.log(`[PREPROCESSING] Configuration:`, {
width: config.width,
contrast: config.contrast,
brightness: config.brightness,
grayscale: config.grayscale,
sharpen: config.sharpen
})
// Lecture de l'image
let image = sharp(inputPath)
// Redimensionnement
if (config.width || config.height) {
image = image.resize(config.width, config.height, {
fit: 'inside',
withoutEnlargement: false
})
console.log(`[PREPROCESSING] Redimensionnement appliqué`)
}
// Conversion en niveaux de gris
if (config.grayscale) {
image = image.grayscale()
console.log(`[PREPROCESSING] Conversion en niveaux de gris`)
}
// Amélioration du contraste et de la luminosité
if (config.contrast !== 1 || config.brightness !== 1) {
image = image.modulate({
brightness: config.brightness,
contrast: config.contrast
})
console.log(`[PREPROCESSING] Contraste (${config.contrast}) et luminosité (${config.brightness}) appliqués`)
}
// Amélioration de la netteté
if (config.sharpen) {
image = image.sharpen({
sigma: 1.0,
flat: 1.0,
jagged: 2.0
})
console.log(`[PREPROCESSING] Amélioration de la netteté appliquée`)
}
// Réduction du bruit
if (config.denoise) {
image = image.median(3)
console.log(`[PREPROCESSING] Réduction du bruit appliquée`)
}
// Binarisation (seuil) si demandée
if (config.threshold) {
image = image.threshold(config.threshold)
console.log(`[PREPROCESSING] Binarisation avec seuil ${config.threshold}`)
}
// Application des modifications et conversion
const processedBuffer = await image
.png({ quality: config.quality })
.toBuffer()
console.log(`[PREPROCESSING] Image préprocessée: ${processedBuffer.length} bytes`)
// Sauvegarde optionnelle
if (outputPath) {
await fs.promises.writeFile(outputPath, processedBuffer)
console.log(`[PREPROCESSING] Image sauvegardée: ${outputPath}`)
}
return processedBuffer
} catch (error) {
console.error(`[PREPROCESSING] Erreur lors du préprocessing:`, error.message)
throw error
}
}
/**
* Prétraite une image avec plusieurs configurations et retourne la meilleure
* @param {string} inputPath - Chemin vers l'image d'entrée
* @returns {Promise<Buffer>} - Buffer de la meilleure image préprocessée
*/
async function preprocessImageMultipleConfigs(inputPath) {
console.log(`[PREPROCESSING] Test de plusieurs configurations pour: ${path.basename(inputPath)}`)
const configs = [
{
name: 'Standard',
options: {
width: 2000,
contrast: 1.5,
brightness: 1.1,
grayscale: true,
sharpen: true,
denoise: true
}
},
{
name: 'Haute résolution',
options: {
width: 3000,
contrast: 1.8,
brightness: 1.2,
grayscale: true,
sharpen: true,
denoise: false
}
},
{
name: 'Contraste élevé',
options: {
width: 2000,
contrast: 2.0,
brightness: 1.0,
grayscale: true,
sharpen: true,
denoise: true
}
},
{
name: 'Binarisation',
options: {
width: 2000,
contrast: 1.5,
brightness: 1.1,
grayscale: true,
sharpen: true,
denoise: true,
threshold: 128
}
}
]
// Pour l'instant, on utilise la configuration standard
// Dans une version avancée, on pourrait tester toutes les configs
const bestConfig = configs[0]
console.log(`[PREPROCESSING] Utilisation de la configuration: ${bestConfig.name}`)
return await preprocessImageForOCR(inputPath, null, bestConfig.options)
}
/**
* Analyse les métadonnées d'une image
* @param {string} imagePath - Chemin vers l'image
* @returns {Promise<Object>} - Métadonnées de l'image
*/
async function analyzeImageMetadata(imagePath) {
try {
const metadata = await sharp(imagePath).metadata()
console.log(`[PREPROCESSING] Métadonnées de ${path.basename(imagePath)}:`, {
format: metadata.format,
width: metadata.width,
height: metadata.height,
channels: metadata.channels,
density: metadata.density,
size: `${(metadata.size / 1024).toFixed(1)} KB`
})
return metadata
} catch (error) {
console.error(`[PREPROCESSING] Erreur lors de l'analyse des métadonnées:`, error.message)
return null
}
}
module.exports = {
preprocessImageForOCR,
preprocessImageMultipleConfigs,
analyzeImageMetadata
}