monocr 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # monocr (JavaScript/Node.js)
1
+ # monocr
2
2
 
3
- Mon language OCR using ONNX Runtime for Node.js applications.
3
+ Mon language (mnw) OCR for Node.js.
4
4
 
5
5
  ## Installation
6
6
 
@@ -13,107 +13,65 @@ npm install monocr
13
13
  ```javascript
14
14
  const { read_image } = require("monocr");
15
15
 
16
- // Auto-downloads model on first run
17
- const text = await read_image("path/to/image.jpg");
16
+ // Automatically downloads model on first run
17
+ const text = await read_image("image.jpg");
18
18
  console.log(text);
19
19
  ```
20
20
 
21
21
  ## API
22
22
 
23
- ### read_image(imagePath, [modelPath], [charsetPath])
23
+ ### `read_image(imagePath, [modelPath], [charsetPath])`
24
24
 
25
- Recognize text from an image file.
25
+ Recognizes text from an image file.
26
26
 
27
- **Parameters:**
27
+ - `imagePath` (string): Path to image file.
28
+ - `modelPath` (string, optional): Path to ONNX model. Defaults to `~/.monocr/models/monocr.onnx`.
29
+ - `charsetPath` (string, optional): Path to charset file. Defaults to bundled charset.
28
30
 
29
- - `imagePath` - Path to image file (jpg, png)
30
- - `modelPath` - (Optional) Path to ONNX model. Defaults to auto-downloaded model.
31
- - `charsetPath` - (Optional) Path to charset file. Defaults to auto-downloaded file.
31
+ Returns: `Promise<string>`
32
32
 
33
- **Returns:** `Promise<string>` - Recognized text
33
+ ### `read_pdf(pdfPath, [modelPath], [charsetPath])`
34
34
 
35
- ### read_pdf(pdfPath, modelPath, charsetPath)
35
+ Recognizes text from a PDF file.
36
36
 
37
- Recognize text from a PDF file.
37
+ - `pdfPath` (string): Path to PDF file.
38
+ - `modelPath` (string, optional): As above.
39
+ - `charsetPath` (string, optional): As above.
38
40
 
39
- **Parameters:**
41
+ Returns: `Promise<string[]>` (Array of text per page)
40
42
 
41
- - `pdfPath` - Path to PDF file
42
- - `modelPath` - Path to ONNX model (optional)
43
- - `charsetPath` - Path to charset file (optional)
43
+ ### `read_image_with_accuracy(imagePath, groundTruth, [modelPath], [charsetPath])`
44
44
 
45
- **Returns:** `Promise<string[]>` - Array of text per page
45
+ Recognizes text and calculates accuracy against ground truth.
46
46
 
47
- ### read_image_with_accuracy(imagePath, groundTruth, modelPath, charsetPath)
47
+ - `imagePath` (string): Path to image file.
48
+ - `groundTruth` (string): Expected text.
48
49
 
49
- Recognize text with accuracy measurement.
50
+ Returns: `Promise<{text: string, accuracy: number}>`
50
51
 
51
- **Parameters:**
52
+ ## CLI Usage
52
53
 
53
- - `imagePath` - Path to image file
54
- - `groundTruth` - Expected text for accuracy calculation
55
- - `modelPath` - Path to ONNX model (optional)
56
- - `charsetPath` - Path to charset file (optional)
57
-
58
- **Returns:** `Promise<{text: string, accuracy: number}>` - Text and accuracy percentage
59
-
60
- ### MonOCR Class
61
-
62
- For advanced usage, use the `MonOCR` class directly:
63
-
64
- ```javascript
65
- const { MonOCR } = require("monocr");
66
-
67
- const ocr = new MonOCR("model.onnx", "charset.txt");
68
- await ocr.init();
69
-
70
- // Single line
71
- const text = await ocr.predictLine(imageSource);
72
-
73
- // Full page (with line segmentation)
74
- const results = await ocr.predictPage(imagePath);
75
- ```
76
-
77
- ## CLI
78
-
79
- The package includes a command-line tool:
54
+ The package includes a `monocr` command-line tool.
80
55
 
81
56
  ```bash
82
- # Single image
83
- monocr image path/to/image.jpg
84
-
85
- # PDF файл
86
- monocr pdf path/to/document.pdf
87
-
88
- # Batch processing
89
- monocr batch path/to/images/ -o results.json
90
- ```
91
-
92
- ## Examples
93
-
94
- See the `examples/` directory for detailed usage examples:
57
+ # Download model to cache (optional, happens automatically on first use)
58
+ monocr download
95
59
 
96
- - `simple.js` - Basic image OCR
97
- - `with-accuracy.js` - OCR with accuracy measurement
98
- - `batch.js` - Batch processing
60
+ # Recognize single image
61
+ monocr image input.jpg
99
62
 
100
- Run examples:
63
+ # Recognize PDF
64
+ monocr pdf document.pdf
101
65
 
102
- ```bash
103
- node examples/simple.js
66
+ # Batch process directory
67
+ monocr batch ./images -o results.json
104
68
  ```
105
69
 
106
70
  ## Model Files
107
71
 
108
- Models are **automatically downloaded** on first use to `~/.monocr/models/`.
109
-
110
- You can also trigger a manual download:
111
-
112
- ```bash
113
- monocr download
114
- ```
72
+ The ONNX model (`monocr.onnx`) is downloaded automatically to `~/.monocr/models/` on first use. The charset file is bundled with the package.
115
73
 
116
- Can also specify custom model paths if you prefer offline usage without the default cache.
74
+ To use a custom model, provide the `modelPath` argument to the API functions or CLI.
117
75
 
118
76
  ## License
119
77
 
package/bin/monocr.js CHANGED
@@ -14,7 +14,7 @@ program
14
14
  .command('image <path>')
15
15
  .description('Recognize text from an image file')
16
16
  .option('-m, --model <path>', 'Path to ONNX model (optional, auto-downloads)')
17
- .option('-c, --charset <path>', 'Path to charset file (optional, auto-downloads)')
17
+ .option('-c, --charset <path>', 'Path to charset file (optional)')
18
18
  .action(async (imagePath, options) => {
19
19
  try {
20
20
  const text = await read_image(imagePath, options.model, options.charset);
@@ -29,7 +29,7 @@ program
29
29
  .command('pdf <path>')
30
30
  .description('Recognize text from a PDF file')
31
31
  .option('-m, --model <path>', 'Path to ONNX model (optional, auto-downloads)')
32
- .option('-c, --charset <path>', 'Path to charset file (optional, auto-downloads)')
32
+ .option('-c, --charset <path>', 'Path to charset file (optional)')
33
33
  .action(async (pdfPath, options) => {
34
34
  try {
35
35
  const pages = await read_pdf(pdfPath, options.model, options.charset);
@@ -48,7 +48,7 @@ program
48
48
  .command('batch <directory>')
49
49
  .description('Process all images in a directory')
50
50
  .option('-m, --model <path>', 'Path to ONNX model (optional, auto-downloads)')
51
- .option('-c, --charset <path>', 'Path to charset file (optional, auto-downloads)')
51
+ .option('-c, --charset <path>', 'Path to charset file (optional)')
52
52
  .option('-o, --output <path>', 'Output file for results (optional)')
53
53
  .action(async (directory, options) => {
54
54
  try {
@@ -89,7 +89,7 @@ program
89
89
  try {
90
90
  const { MonOCR } = require('../src/index');
91
91
  const ocr = new MonOCR();
92
- await ocr.modelManager.downloadModels();
92
+ await ocr.modelManager.downloadModel();
93
93
  } catch (err) {
94
94
  console.error('Error:', err.message);
95
95
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "monocr",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Cross-platform Mon (mnw) language OCR using ONNX Runtime. Supports Node.js.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -0,0 +1 @@
1
+ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ကခဂဃငစဆဇဈဉညဋဌဍဎဏတထဒဓနပဖဗဘမယရလဝသဟဠအဢဣဤဥဦဧဨဩဪါာိီုူေဲဳဴဵံ့း္်ျြွှဿ၀၁၂၃၄၅၆၇၈၉၊။၌၍၎၏ၐၑၓၚၛၜၝၞၟၠၡၢၣၤၥၨၪၰၱၲၳၴၵၷၸၹၺၻၼၾၿႀႄႅႆႇႈႉႊႏ႐႒႓႔႕႘႙ႜႝ႟
@@ -9,12 +9,10 @@ class ModelManager {
9
9
  // Default cache directory in user's home
10
10
  this.cacheDir = path.join(os.homedir(), '.monocr', 'models');
11
11
 
12
- // HuggingFace model URLs
12
+ // HuggingFace model URL
13
13
  this.baseUrl = 'https://huggingface.co/janakhpon/monocr/resolve/main';
14
- this.modelFiles = {
15
- model: 'onnx/monocr.onnx',
16
- charset: 'charset.txt'
17
- };
14
+ this.modelFileName = 'monocr.onnx';
15
+ this.hfModelPath = 'onnx/monocr.onnx';
18
16
  }
19
17
 
20
18
  /**
@@ -27,19 +25,17 @@ class ModelManager {
27
25
  }
28
26
 
29
27
  /**
30
- * Get local path for a model file
28
+ * Get local path for the model
31
29
  */
32
- getLocalPath(fileKey) {
33
- return path.join(this.cacheDir, path.basename(this.modelFiles[fileKey]));
30
+ getModelPath() {
31
+ return path.join(this.cacheDir, this.modelFileName);
34
32
  }
35
33
 
36
34
  /**
37
- * Check if model files exist locally
35
+ * Check if model exists locally
38
36
  */
39
- hasModels() {
40
- const modelPath = this.getLocalPath('model');
41
- const charsetPath = this.getLocalPath('charset');
42
- return fs.existsSync(modelPath) && fs.existsSync(charsetPath);
37
+ hasModel() {
38
+ return fs.existsSync(this.getModelPath());
43
39
  }
44
40
 
45
41
  /**
@@ -49,43 +45,44 @@ class ModelManager {
49
45
  return new Promise((resolve, reject) => {
50
46
  const file = fs.createWriteStream(destPath);
51
47
 
52
- https.get(url, { headers: { 'User-Agent': 'monocr-npm' } }, (response) => {
53
- if (response.statusCode === 302 || response.statusCode === 301) {
54
- // Follow redirect
55
- https.get(response.headers.location, (redirectResponse) => {
56
- const totalSize = parseInt(redirectResponse.headers['content-length'], 10);
48
+ const request = (requestUrl) => {
49
+ https.get(requestUrl, { headers: { 'User-Agent': 'monocr-npm' } }, (response) => {
50
+ if ([301, 302, 307, 308].includes(response.statusCode)) {
51
+ let redirectUrl = response.headers.location;
52
+ if (!redirectUrl.startsWith('http')) {
53
+ const originalUrl = new URL(requestUrl);
54
+ redirectUrl = `${originalUrl.protocol}//${originalUrl.host}${redirectUrl}`;
55
+ }
56
+ request(redirectUrl);
57
+ } else if (response.statusCode === 200) {
58
+ const totalSize = parseInt(response.headers['content-length'], 10);
57
59
  let downloadedSize = 0;
58
60
 
59
- redirectResponse.on('data', (chunk) => {
61
+ response.on('data', (chunk) => {
60
62
  downloadedSize += chunk.length;
61
- const progress = ((downloadedSize / totalSize) * 100).toFixed(1);
62
- process.stdout.write(`\r Progress: ${progress}% (${(downloadedSize / 1024 / 1024).toFixed(2)} MB)`);
63
+ if (totalSize) {
64
+ const progress = ((downloadedSize / totalSize) * 100).toFixed(1);
65
+ process.stdout.write(`\r Downloading model: ${progress}% (${(downloadedSize / 1024 / 1024).toFixed(2)} MB)`);
66
+ }
63
67
  });
64
-
65
- redirectResponse.pipe(file);
68
+
69
+ response.pipe(file);
66
70
 
67
71
  file.on('finish', () => {
68
72
  file.close();
69
73
  process.stdout.write('\n');
70
74
  resolve();
71
75
  });
72
- }).on('error', (err) => {
73
- fs.unlink(destPath, () => {});
74
- reject(err);
75
- });
76
- } else if (response.statusCode === 200) {
77
- response.pipe(file);
78
- file.on('finish', () => {
79
- file.close();
80
- resolve();
81
- });
82
- } else {
83
- reject(new Error(`Failed to download: ${response.statusCode}`));
84
- }
85
- }).on('error', (err) => {
86
- fs.unlink(destPath, () => {});
87
- reject(err);
88
- });
76
+ } else {
77
+ reject(new Error(`Failed to download: ${response.statusCode}`));
78
+ }
79
+ }).on('error', (err) => {
80
+ fs.unlink(destPath, () => {});
81
+ reject(err);
82
+ });
83
+ };
84
+
85
+ request(url);
89
86
 
90
87
  file.on('error', (err) => {
91
88
  fs.unlink(destPath, () => {});
@@ -95,44 +92,32 @@ class ModelManager {
95
92
  }
96
93
 
97
94
  /**
98
- * Download all model files
95
+ * Download model file
99
96
  */
100
- async downloadModels() {
97
+ async downloadModel() {
101
98
  this.ensureCacheDir();
102
99
 
103
- console.log('Downloading monocr models from HuggingFace...');
104
- console.log(`Cache directory: ${this.cacheDir}\n`);
105
-
106
- // Download model
107
- const modelUrl = `${this.baseUrl}/${this.modelFiles.model}`;
108
- const modelPath = this.getLocalPath('model');
109
- console.log('Downloading monocr.onnx...');
110
- await this.downloadFile(modelUrl, modelPath);
111
- console.log('✓ Model downloaded\n');
100
+ console.log('Downloading monocr model from HuggingFace...');
101
+ console.log(`Cache directory: ${this.cacheDir}`);
112
102
 
113
- // Download charset
114
- const charsetUrl = `${this.baseUrl}/${this.modelFiles.charset}`;
115
- const charsetPath = this.getLocalPath('charset');
116
- console.log('Downloading charset.txt...');
117
- await this.downloadFile(charsetUrl, charsetPath);
118
- console.log('✓ Charset downloaded\n');
103
+ const modelUrl = `${this.baseUrl}/${this.hfModelPath}`;
104
+ const destPath = this.getModelPath();
119
105
 
120
- console.log('All models downloaded successfully!');
106
+ await this.downloadFile(modelUrl, destPath);
107
+ console.log('✓ Model downloaded successfully!');
121
108
  }
122
109
 
123
110
  /**
124
- * Get model paths, downloading if needed
111
+ * Get model path, downloading if needed
125
112
  */
126
- async getModelPaths() {
127
- if (!this.hasModels()) {
128
- await this.downloadModels();
113
+ async ensureModel() {
114
+ if (!this.hasModel()) {
115
+ await this.downloadModel();
129
116
  }
130
-
131
- return {
132
- modelPath: this.getLocalPath('model'),
133
- charsetPath: this.getLocalPath('charset')
134
- };
117
+ return this.getModelPath();
135
118
  }
136
119
  }
137
120
 
138
121
  module.exports = ModelManager;
122
+
123
+ module.exports = ModelManager;
package/src/monocr.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const ort = require('onnxruntime-node');
2
2
  const sharp = require('sharp');
3
3
  const fs = require('fs');
4
+ const path = require('path');
4
5
  const LineSegmenter = require('./segmenter');
5
6
  const ModelManager = require('./model-manager');
6
7
 
@@ -21,17 +22,18 @@ class MonOCR {
21
22
  async init() {
22
23
  if (this.session) return;
23
24
 
24
- // If paths not provided, use auto-download
25
- if (!this.modelPath || !this.charsetPath) {
26
- const paths = await this.modelManager.getModelPaths();
27
- this.modelPath = this.modelPath || paths.modelPath;
28
- this.charsetPath = this.charsetPath || paths.charsetPath;
25
+ // Ensure model exists
26
+ if (!this.modelPath) {
27
+ this.modelPath = await this.modelManager.ensureModel();
29
28
  }
30
29
 
31
- this.session = await ort.InferenceSession.create(this.modelPath);
32
- if (this.charsetPath) {
33
- this.charset = fs.readFileSync(this.charsetPath, 'utf-8').trim();
30
+ // Use bundled charset if not provided
31
+ if (!this.charsetPath) {
32
+ this.charsetPath = path.join(__dirname, 'charset.txt');
34
33
  }
34
+
35
+ this.session = await ort.InferenceSession.create(this.modelPath);
36
+ this.charset = fs.readFileSync(this.charsetPath, 'utf-8').trim();
35
37
  }
36
38
 
37
39
  /**