Source: backend/models/modelOcr.js

/**
 * @fileoverview Diese Datei enthält Funktionen zur Durchführung von OCR auf Bildern.
 * Sie ermöglicht die Vorverarbeitung von Bildern und die Texterkennung mithilfe von Tesseract.js.
 * 
 * @author Ilyass
 * Mithilfe von Tesseract.js und Github-Copilot.
 * @module modelOcr
 */

const path = require('path');
const fs = require('fs').promises;
const Tesseract = require('tesseract.js');

let Jimp;
try {
    Jimp = require('jimp');
} catch (error) {
    console.warn('Failed to load Jimp. Falling back to basic image processing.');
}

/**
 * Führt eine Vorverarbeitung des Bildes durch, um die Texterkennung zu verbessern.
 * Falls Jimp nicht verfügbar ist, wird das Originalbild ohne Verarbeitung zurückgegeben.
 * 
 * @async
 * @function preprocessImage
 * @param {Buffer} imageBuffer - Der Bildpuffer, der verarbeitet werden soll.
 * @returns {Promise<Buffer>} Der vorverarbeitete Bildpuffer oder das Originalbild, falls Jimp nicht verfügbar ist.
 * @throws {Error} Falls ein Fehler während der Verarbeitung auftritt.
 * @example
 * const processedImage = await preprocessImage(imageBuffer);
 */
async function preprocessImage(imageBuffer) {
    try {
        if (Jimp && typeof Jimp.read === 'function') {
            const image = await Jimp.read(imageBuffer);
            return image
                .greyscale()
                .contrast(0.1)
                .normalize()
                .getBufferAsync(Jimp.MIME_JPEG);
        } else {
            // Fallback: return the original buffer if Jimp is not available
            console.log('Using original image without preprocessing.');
            return imageBuffer;
        }
    } catch (error) {
        console.error('Error in preprocessImage:', error);
        // Fallback: return the original buffer if preprocessing fails
        return imageBuffer;
    }
}

/**
 * Führt OCR auf einem Bild durch und extrahiert den enthaltenen Text mithilfe von Tesseract.js.
 * 
 * @async
 * @function performOCR
 * @param {Buffer} imageBuffer - Der Bildpuffer, aus dem der Text extrahiert werden soll.
 * @param {string} filename - Der Name der Datei (für Logging-Zwecke).
 * @returns {Promise<{ success: boolean, text?: string, error?: string }>} 
 * Ein Objekt mit dem erkannten Text oder einer Fehlermeldung.
 * @throws {Error} Falls ein Fehler bei der OCR-Erkennung auftritt.
 * @example
 * const result = await performOCR(imageBuffer, 'document.png');
 * if (result.success) {
 *     console.log('Extrahierter Text:', result.text);
 * } else {
 *     console.error('Fehler:', result.error);
 * }
 */
async function performOCR(imageBuffer, filename) {
    console.log('Starting OCR process for:', filename);
    try {
        console.log('Preprocessing image...');
        const processedImageBuffer = await preprocessImage(imageBuffer);

        console.log('Recognizing image...');
        const { data: { text } } = await Tesseract.recognize(processedImageBuffer, 'deu', {
            tessedit_pageseg_mode: Tesseract.PSM.AUTO,
            tessedit_ocr_engine_mode: Tesseract.OEM.LSTM_ONLY,
            tessjs_create_pdf: '0',
            tessjs_create_hocr: '0',
            tessjs_create_tsv: '0',
            preserve_interword_spaces: '1',
            tessedit_char_whitelist: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzÄÖÜäöüß0123456789.,!?()-:;"\' ',
        });

        console.log('OCR completed. Extracted text length:', text.length);

        // Post-process the text
        const processedText = text
            .replace(/[\n\r]+/g, ' ')  // Replace multiple newlines with a single space
            .replace(/\s+/g, ' ')      // Replace multiple spaces with a single space
            .trim();                   // Trim leading and trailing whitespace

        console.log(`OCR performed successfully for ${filename}`);
        return { success: true, text: processedText };
    } catch (error) {
        console.error('Error performing OCR:', error);
        return { success: false, error: error.message };
    }
}

module.exports = {
    performOCR
};