UNPKG

ppu-paddle-ocr

Version:

A lightweight, type safe, PaddleOCR implementation in Bun/Node.js for text detection and recognition in JavaScript environments.

7 lines 4.63 kB
export class PaddleOcrService{static instance=null;options;detectionSession=null;recognitionSession=null;constructor(options){this.options={...DEFAULT_PADDLE_OPTIONS,...options}}log(message){if(this.options.debugging?.verbose){console.log(`[DetectionService] ${message}`)}}async initialize(overrideOptions){try{let effectiveOptions={...this.options,...overrideOptions};let resolvedDetectionPath=path.resolve(process.cwd(),effectiveOptions.model.detection);let resolvedRecognitionPath=path.resolve(process.cwd(),effectiveOptions.model.recognition);let resolvedCharactersPath=path.resolve(process.cwd(),effectiveOptions.model.charactersDictionary);this.log(`Loading Detection ONNX model from: ${resolvedDetectionPath}`);let detModelBuffer=readFileSync(resolvedDetectionPath).buffer;this.detectionSession=await ort.InferenceSession.create(detModelBuffer);await new Promise((resolve)=>setImmediate(resolve));this.log(`Detection ONNX model loaded successfully Loading Recognition ONNX model from: ${resolvedRecognitionPath}`);this.log(`input: ${this.detectionSession.inputNames} output: ${this.detectionSession.outputNames}`);let recModelBuffer=readFileSync(resolvedRecognitionPath).buffer;this.recognitionSession=await ort.InferenceSession.create(recModelBuffer);await new Promise((resolve)=>setImmediate(resolve));this.log(`Recognition ONNX model loaded successfully input: ${this.recognitionSession.inputNames} output: ${this.recognitionSession.outputNames}`);this.log(`Loading character dictionary from: ${resolvedCharactersPath}`);let charactersDictionary=readFileSync(resolvedCharactersPath,"utf-8").split(` `);if(!charactersDictionary.length){throw new Error(`Character dictionary at ${resolvedCharactersPath} is empty or not found.`)}this.options.recognition.charactersDictionary=charactersDictionary;this.log(`Character dictionary loaded with ${this.options.recognition?.charactersDictionary.length||0} entries.`)}catch(error){console.error("Failed to initialize PaddleOcrService:",error);throw error}}static async getInstance(options){if(!PaddleOcrService.instance){PaddleOcrService.instance=new PaddleOcrService(options);await PaddleOcrService.instance.initialize()}else if(options){await PaddleOcrService.instance.initialize(options)}return PaddleOcrService.instance}isInitialized(){return this.detectionSession!==null&&this.recognitionSession!==null}static async changeModel(options){if(!PaddleOcrService.instance){PaddleOcrService.instance=new PaddleOcrService(options);await PaddleOcrService.instance.initialize()}else{await PaddleOcrService.instance.destroy();await PaddleOcrService.instance.initialize(options)}return PaddleOcrService.instance}static async createInstance(options){let instance=new PaddleOcrService(options);await instance.initialize();return instance}async recognize(image,options){await ImageProcessor.initRuntime();let detector=new DetectionService(this.detectionSession,this.options.detection,this.options.debugging);let recognitor=new RecognitionService(this.recognitionSession,this.options.recognition,this.options.debugging);let detection=await detector.run(image);let recognition=await recognitor.run(image,detection);let processed=this.processRecognition(recognition);if(options?.flatten){return{text:processed.text,results:recognition,confidence:processed.confidence}}return processed}processRecognition(recognition){let result={text:"",lines:[],confidence:0};if(!recognition.length){return result}let totalConfidence=recognition.reduce((sum,r)=>sum+r.confidence,0);result.confidence=totalConfidence/recognition.length;let currentLine=[recognition[0]];let fullText=recognition[0].text;let avgHeight=recognition[0].box.height;for(let i=1;i<recognition.length;i++){let current=recognition[i];let previous=recognition[i-1];let verticalGap=Math.abs(current.box.y-previous.box.y);let threshold=avgHeight*0.5;if(verticalGap<=threshold){currentLine.push(current);fullText+=` ${current.text}`;avgHeight=currentLine.reduce((sum,r)=>sum+r.box.height,0)/currentLine.length}else{result.lines.push([...currentLine]);fullText+=` ${current.text}`;currentLine=[current];avgHeight=current.box.height}}if(currentLine.length>0){result.lines.push([...currentLine])}result.text=fullText;return result}async destroy(){await this.detectionSession?.release();await this.recognitionSession?.release()}}import{readFileSync}from"fs";import*as ort from"onnxruntime-node";import*as path from"path";import{ImageProcessor}from"ppu-ocv";import{DEFAULT_PADDLE_OPTIONS}from"../constants";import{DetectionService}from"./detection.service";import{RecognitionService}from"./recognition.service";export default PaddleOcrService;