Kevin Kwok
8 years ago
31 changed files with 13339 additions and 711 deletions
@ -1,26 +0,0 @@ |
|||||||
var path = require('path'); |
|
||||||
var express = require('express'); |
|
||||||
var webpack = require('webpack'); |
|
||||||
var config = require('./webpack.config.dev'); |
|
||||||
|
|
||||||
var app = express(); |
|
||||||
var compiler = webpack(config); |
|
||||||
|
|
||||||
app.use(require('webpack-dev-middleware')(compiler, { |
|
||||||
noInfo: true, |
|
||||||
publicPath: config[0].output.publicPath |
|
||||||
})); |
|
||||||
|
|
||||||
// app.use(require('webpack-hot-middleware')(compiler));
|
|
||||||
|
|
||||||
app.use('/', express.static('./')); |
|
||||||
|
|
||||||
var port = 7355 |
|
||||||
app.listen(port, 'localhost', function(err) { |
|
||||||
if (err) { |
|
||||||
console.log(err); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
console.log('Listening at http://localhost:' + port); |
|
||||||
}); |
|
@ -1 +1,251 @@ |
|||||||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Tesseract=t():e.Tesseract=t()}(this,function(){return function(e){function t(n){if(r[n])return r[n].exports;var o=r[n]={exports:{},id:n,loaded:!1};return e[n].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var r={};return t.m=e,t.c=r,t.p="",t(0)}([function(e,t){"use strict";function r(e,t){return a||(a=o(g.coreUrl,g.workerUrl,g.langUrl)),a.recognize(e,t)}function n(e){return a||(a=o(g.coreUrl,g.workerUrl,g.langUrl)),a.detect(e)}function o(){function e(e,t){var r=s++;u[r]={};var n=0;return Object.getOwnPropertyNames(t).filter(function(e){return"function"==typeof t[e]}).forEach(function(o){n++,t[o](function(a){t[o]=a,0==--n&&i.postMessage({jobId:r,action:e,args:t})})}),0==n&&i.postMessage({jobId:r,action:e,args:t}),{then:function(e){return u[r].result=e,this},error:function(e){return u[r].error=e,this},progress:function(e){return u[r].progress=e,this}}}function t(e){if(e.match&&e.match(/^https?:\/\//))return function(r){var n=new Image;n.src=e,n.onload=function(){return r(t(n))}};if("string"==typeof e&&(e=document.querySelector(e)),e.getContext)e=e.getContext("2d");else if("IMG"==e.tagName||"VIDEO"==e.tagName){var r=document.createElement("canvas");r.width=e.naturalWidth||e.videoWidth,r.height=e.naturalHeight||e.videoHeight;var n=r.getContext("2d");n.drawImage(e,0,0),e=n}return e.getImageData&&(e=e.getImageData(0,0,e.canvas.width,e.canvas.height)),e}var r=arguments.length>0&&void 0!==arguments[0]?arguments[0]:g.coreUrl,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:g.workerUrl,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:g.langUrl,a=new Blob(["importScripts('"+r+"');\n\t\t importScripts('"+n+"');"]),i=new Worker(window.URL.createObjectURL(a)),c=!1,s=0,u={};return i.onmessage=function(e){var t=e.data,r=t.jobId,n=t.progress,o=t.error,a=t.result,i=u[r];n&&i.progress&&i.progress(n),o&&i.error&&i.error(o),a&&i.result&&i.result(a)},e("init",{mem:100663296,langUrl:o}),{detect:function(r){return e("detect",{image:t(r)})},recognize:function(r){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"eng";return"string"==typeof n?n={lang:n}:n.lang=n.lang||"eng",c||["chi_sim","chi_tra","jpn"].indexOf(n.lang)==-1||(e("init",{mem:167772160,langUrl:o}),c=!0),e("recognize",{options:n,image:t(r)})}}}var a,i="https://cdn.rawgit.com/naptha/tesseract.js-core/master/index.js",c="https://cdn.rawgit.com/naptha/tesseract.js/8b915dc/dist/tesseract.worker.js",s="https://cdn.rawgit.com/naptha/tessdata/gh-pages/3.02/",g={coreUrl:i,workerUrl:c,langUrl:s,recognize:r,detect:n,createWorker:o};e.exports=g}])}); |
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Tesseract = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ |
||||||
|
'use strict'; |
||||||
|
|
||||||
|
exports.defaultOptions = { |
||||||
|
langPath: 'https://cdn.rawgit.com/naptha/tessdata/gh-pages/3.02/', |
||||||
|
workerPath: 'dist/worker.js', |
||||||
|
tesseractPath: 'https://cdn.rawgit.com/naptha/tesseract.js-core/0.1.0/index.js' |
||||||
|
}; |
||||||
|
|
||||||
|
exports.spawnWorker = function spawnWorker(instance, workerOptions) { |
||||||
|
var worker = new Worker(workerOptions.workerPath); |
||||||
|
worker.onmessage = function (e) { |
||||||
|
instance._recv(e.data); |
||||||
|
}; |
||||||
|
return worker; |
||||||
|
}; |
||||||
|
|
||||||
|
exports.terminateWorker = function (instance) { |
||||||
|
instance.worker.terminate(); |
||||||
|
}; |
||||||
|
|
||||||
|
exports.sendPacket = function sendPacket(instance, packet) { |
||||||
|
loadImage(packet.payload.image, function (img) { |
||||||
|
packet.payload.image = img; |
||||||
|
instance.worker.postMessage(packet); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
function loadImage(image, cb) { |
||||||
|
if (typeof image === 'string') { |
||||||
|
if (/^\#/.test(image)) { |
||||||
|
// element css selector
|
||||||
|
return loadImage(document.querySelector(image), cb); |
||||||
|
} else { |
||||||
|
// url or path
|
||||||
|
var im = new Image(); |
||||||
|
im.src = image; |
||||||
|
im.onload = function (e) { |
||||||
|
return loadImage(im, cb); |
||||||
|
}; |
||||||
|
return; |
||||||
|
} |
||||||
|
} else if (image instanceof File) { |
||||||
|
// files
|
||||||
|
var fr = new FileReader(); |
||||||
|
fr.onload = function (e) { |
||||||
|
return loadImage(fr.result, cb); |
||||||
|
}; |
||||||
|
fr.readAsDataURL(image); |
||||||
|
return; |
||||||
|
} else if (image instanceof Blob) { |
||||||
|
return loadImage(URL.createObjectURL(image), cb); |
||||||
|
} else if (image.getContext) { |
||||||
|
// canvas element
|
||||||
|
return loadImage(image.getContext('2d'), cb); |
||||||
|
} else if (image.tagName == "IMG" || image.tagName == "VIDEO") { |
||||||
|
// image element or video element
|
||||||
|
var c = document.createElement('canvas'); |
||||||
|
c.width = image.naturalWidth || image.videoWidth; |
||||||
|
c.height = image.naturalHeight || image.videoHeight; |
||||||
|
var ctx = c.getContext('2d'); |
||||||
|
ctx.drawImage(image, 0, 0); |
||||||
|
return loadImage(ctx, cb); |
||||||
|
} else if (image.getImageData) { |
||||||
|
// canvas context
|
||||||
|
var data = image.getImageData(0, 0, image.canvas.width, image.canvas.height); |
||||||
|
return loadImage(data, cb); |
||||||
|
} |
||||||
|
cb(image); |
||||||
|
} |
||||||
|
|
||||||
|
},{}],2:[function(require,module,exports){ |
||||||
|
"use strict"; |
||||||
|
|
||||||
|
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); |
||||||
|
|
||||||
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } |
||||||
|
|
||||||
|
var adapter = require('./node/index.js'); |
||||||
|
|
||||||
|
function createWorker(workerOptions) { |
||||||
|
return new TesseractWorker(workerOptions); |
||||||
|
} |
||||||
|
|
||||||
|
var TesseractWorker = function () { |
||||||
|
function TesseractWorker(workerOptions) { |
||||||
|
_classCallCheck(this, TesseractWorker); |
||||||
|
|
||||||
|
this.worker = null; |
||||||
|
this.workerOptions = workerOptions; |
||||||
|
this._currentJob = null; |
||||||
|
this._queue = []; |
||||||
|
} |
||||||
|
|
||||||
|
_createClass(TesseractWorker, [{ |
||||||
|
key: 'recognize', |
||||||
|
value: function recognize(image, options) { |
||||||
|
var _this = this; |
||||||
|
|
||||||
|
return this._delay(function (job) { |
||||||
|
options = options || {}; |
||||||
|
options.lang = options.lang || 'eng'; |
||||||
|
job._send('recognize', { image: image, options: options, workerOptions: _this.workerOptions }); |
||||||
|
}); |
||||||
|
} |
||||||
|
}, { |
||||||
|
key: 'detect', |
||||||
|
value: function detect(image, options) { |
||||||
|
var _this2 = this; |
||||||
|
|
||||||
|
options = options || {}; |
||||||
|
return this._delay(function (job) { |
||||||
|
job._send('detect', { image: image, options: options, workerOptions: _this2.workerOptions }); |
||||||
|
}); |
||||||
|
} |
||||||
|
}, { |
||||||
|
key: 'terminate', |
||||||
|
value: function terminate() { |
||||||
|
if (this.worker) adapter.terminateWorker(this); |
||||||
|
this.worker = null; |
||||||
|
} |
||||||
|
}, { |
||||||
|
key: '_delay', |
||||||
|
value: function _delay(fn) { |
||||||
|
var _this3 = this; |
||||||
|
|
||||||
|
if (!this.worker) this.worker = adapter.spawnWorker(this, this.workerOptions); |
||||||
|
|
||||||
|
var job = new TesseractJob(this); |
||||||
|
this._queue.push(function (e) { |
||||||
|
_this3._queue.shift(); |
||||||
|
_this3._currentJob = job; |
||||||
|
fn(job); |
||||||
|
}); |
||||||
|
if (!this._currentJob) this._dequeue(); |
||||||
|
return job; |
||||||
|
} |
||||||
|
}, { |
||||||
|
key: '_dequeue', |
||||||
|
value: function _dequeue() { |
||||||
|
this._currentJob = null; |
||||||
|
if (this._queue.length > 0) { |
||||||
|
this._queue[0](); |
||||||
|
} |
||||||
|
} |
||||||
|
}, { |
||||||
|
key: '_recv', |
||||||
|
value: function _recv(packet) { |
||||||
|
if (this._currentJob.id === packet.jobId) { |
||||||
|
this._currentJob._handle(packet); |
||||||
|
} else { |
||||||
|
console.warn('Job ID ' + packet.jobId + ' not known.'); |
||||||
|
} |
||||||
|
} |
||||||
|
}]); |
||||||
|
|
||||||
|
return TesseractWorker; |
||||||
|
}(); |
||||||
|
|
||||||
|
var jobCounter = 0; |
||||||
|
|
||||||
|
var TesseractJob = function () { |
||||||
|
function TesseractJob(instance) { |
||||||
|
_classCallCheck(this, TesseractJob); |
||||||
|
|
||||||
|
this.id = 'Job-' + ++jobCounter + '-' + Math.random().toString(16).slice(3, 8); |
||||||
|
|
||||||
|
this._instance = instance; |
||||||
|
this._resolve = []; |
||||||
|
this._reject = []; |
||||||
|
this._progress = []; |
||||||
|
} |
||||||
|
|
||||||
|
_createClass(TesseractJob, [{ |
||||||
|
key: 'then', |
||||||
|
value: function then(resolve, reject) { |
||||||
|
if (this._resolve.push) { |
||||||
|
this._resolve.push(resolve); |
||||||
|
} else { |
||||||
|
resolve(this._resolve); |
||||||
|
} |
||||||
|
|
||||||
|
if (reject) this.catch(reject); |
||||||
|
return this; |
||||||
|
} |
||||||
|
}, { |
||||||
|
key: 'catch', |
||||||
|
value: function _catch(reject) { |
||||||
|
if (this._reject.push) { |
||||||
|
this._reject.push(reject); |
||||||
|
} else { |
||||||
|
reject(this._reject); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
}, { |
||||||
|
key: 'progress', |
||||||
|
value: function progress(fn) { |
||||||
|
this._progress.push(fn); |
||||||
|
return this; |
||||||
|
} |
||||||
|
}, { |
||||||
|
key: '_send', |
||||||
|
value: function _send(action, payload) { |
||||||
|
adapter.sendPacket(this._instance, { |
||||||
|
jobId: this.id, |
||||||
|
action: action, |
||||||
|
payload: payload |
||||||
|
}); |
||||||
|
} |
||||||
|
}, { |
||||||
|
key: '_handle', |
||||||
|
value: function _handle(packet) { |
||||||
|
var data = packet.data; |
||||||
|
if (packet.status === 'resolve') { |
||||||
|
if (this._resolve.length === 0) console.debug(data); |
||||||
|
this._resolve.forEach(function (fn) { |
||||||
|
var ret = fn(data); |
||||||
|
if (ret && typeof ret.then == 'function') { |
||||||
|
console.warn('TesseractJob instances do not chain like ES6 Promises. To convert it into a real promise, use Promise.resolve.'); |
||||||
|
} |
||||||
|
}); |
||||||
|
this._resolve = data; |
||||||
|
this._instance._dequeue(); |
||||||
|
} else if (packet.status === 'reject') { |
||||||
|
if (this._reject.length === 0) console.error(data); |
||||||
|
this._reject.forEach(function (fn) { |
||||||
|
return fn(data); |
||||||
|
}); |
||||||
|
this._reject = data; |
||||||
|
this._instance._dequeue(); |
||||||
|
} else if (packet.status === 'progress') { |
||||||
|
this._progress.forEach(function (fn) { |
||||||
|
return fn(data); |
||||||
|
}); |
||||||
|
} else { |
||||||
|
console.warn('Message type unknown', packet.status); |
||||||
|
} |
||||||
|
} |
||||||
|
}]); |
||||||
|
|
||||||
|
return TesseractJob; |
||||||
|
}(); |
||||||
|
|
||||||
|
var DefaultTesseract = createWorker(adapter.defaultOptions); |
||||||
|
DefaultTesseract.createWorker = createWorker; |
||||||
|
|
||||||
|
module.exports = DefaultTesseract; |
||||||
|
|
||||||
|
},{"./node/index.js":1}]},{},[2])(2) |
||||||
|
}); |
@ -0,0 +1,5 @@ |
|||||||
|
<script src="dist/tesseract.js"></script> |
||||||
|
<input type="file" onchange="Tesseract.recognize(this.files[0]).progress(console.log)"> |
||||||
|
|
||||||
|
|
||||||
|
<input type="file" onchange="Tesseract.recognize(this.files[0], {lang: 'chi_sim'}).progress(console.log)"> |
@ -0,0 +1,23 @@ |
|||||||
|
var Tesseract = require('./src/index.js') |
||||||
|
global.Tesseract = Tesseract; |
||||||
|
|
||||||
|
// Tesseract.recognize('yolop.png', {
|
||||||
|
// lang: 'eng'
|
||||||
|
// }).progress(function(info){
|
||||||
|
// console.log('--', info)
|
||||||
|
// })
|
||||||
|
// .then(function(data){
|
||||||
|
// console.log('--', data)
|
||||||
|
// })
|
||||||
|
|
||||||
|
|
||||||
|
// Tesseract.recognize('cosmic.jpg', {
|
||||||
|
// lang: 'eng'
|
||||||
|
// })
|
||||||
|
Tesseract.detect('cosmic.jpg') |
||||||
|
.progress(function(info){ |
||||||
|
console.log(info) |
||||||
|
}) |
||||||
|
.then(function(data){ |
||||||
|
console.log('done', data) |
||||||
|
}) |
@ -1,30 +0,0 @@ |
|||||||
<canvas id="c"></canvas> |
|
||||||
<script type="text/javascript" src="./tesseract/tesseract.js"></script> |
|
||||||
<script type="text/javascript"> |
|
||||||
var canvas = document.getElementById('c') |
|
||||||
canvas.width = 400 |
|
||||||
canvas.height = 400 |
|
||||||
var ctx = canvas.getContext('2d'); |
|
||||||
ctx.font = '30px "Arial Black"' |
|
||||||
ctx.fillText('Hell0 World', 100, 40) |
|
||||||
// ctx.fillText("囚犯離奇掙脫囚犯離奇掙脫", 100, 40) |
|
||||||
ctx.font = '30px "Times New Roman"' |
|
||||||
ctx.fillText('from beyond', 100, 80) |
|
||||||
// ctx.fillText('2小時可換乘2次2小時可換乘2次', 100, 80) |
|
||||||
ctx.font = '30px sans-serif' |
|
||||||
ctx.fillText('the Cosmic Void', 100, 120) |
|
||||||
|
|
||||||
|
|
||||||
Tesseract.workerUrl = location.protocol+'//'+location.host+'/tesseract/tesseract.worker.js' |
|
||||||
|
|
||||||
// Tesseract.detect(canvas) |
|
||||||
// Tesseract.recognize('http://localhost:7355/westmorland.jpg') |
|
||||||
// Tesseract.recognize(canvas, { tessedit_char_blacklist: 'e' }) |
|
||||||
Tesseract.recognize(canvas) |
|
||||||
.progress(function(e){ |
|
||||||
console.log('progress', e) |
|
||||||
}) |
|
||||||
.then(function(e){ |
|
||||||
console.log('result', e) |
|
||||||
}) |
|
||||||
</script> |
|
@ -1,106 +1,64 @@ |
|||||||
var coreUrl = 'https://cdn.rawgit.com/naptha/tesseract.js-core/master/index.js', |
exports.defaultOptions = { |
||||||
workerUrl = 'https://cdn.rawgit.com/naptha/tesseract.js/8b915dc/dist/tesseract.worker.js', |
langPath: 'https://cdn.rawgit.com/naptha/tessdata/gh-pages/3.02/', |
||||||
langUrl = 'https://cdn.rawgit.com/naptha/tessdata/gh-pages/3.02/', |
workerPath: 'dist/worker.js', |
||||||
worker; |
tesseractPath: 'https://cdn.rawgit.com/naptha/tesseract.js-core/0.1.0/index.js', |
||||||
|
|
||||||
function recognize(image, options){ |
|
||||||
if(!worker) worker = createWorker( Tesseract.coreUrl, Tesseract.workerUrl, Tesseract.langUrl ) |
|
||||||
return worker.recognize(image, options) |
|
||||||
} |
} |
||||||
|
|
||||||
function detect(image){ |
exports.spawnWorker = function spawnWorker(instance, workerOptions){ |
||||||
if(!worker) worker = createWorker( Tesseract.coreUrl, Tesseract.workerUrl, Tesseract.langUrl ) |
var worker = new Worker(workerOptions.workerPath) |
||||||
return worker.detect(image) |
worker.onmessage = function(e){ |
||||||
|
instance._recv(e.data) |
||||||
|
} |
||||||
|
return worker |
||||||
} |
} |
||||||
|
|
||||||
function createWorker(coreUrl=Tesseract.coreUrl, workerUrl=Tesseract.workerUrl, langUrl=Tesseract.langUrl){ |
exports.terminateWorker = function(instance){ |
||||||
|
instance.worker.terminate() |
||||||
var blob = new Blob([`importScripts('${coreUrl}');
|
} |
||||||
importScripts('${workerUrl}');`])
|
|
||||||
|
|
||||||
var worker = new Worker(window.URL.createObjectURL(blob)); |
|
||||||
|
|
||||||
var bigworker = false |
|
||||||
var jobCounter = 0 |
|
||||||
var handlers = {} |
|
||||||
|
|
||||||
function runAsync(action, args){ |
|
||||||
|
|
||||||
var jobId = jobCounter++ |
|
||||||
handlers[jobId] = {} |
|
||||||
|
|
||||||
var waitingCount = 0 |
exports.sendPacket = function sendPacket(instance, packet){ |
||||||
Object.getOwnPropertyNames(args) |
loadImage(packet.payload.image, function(img){ |
||||||
.filter(name => typeof args[name] === 'function') |
packet.payload.image = img |
||||||
.forEach(name => { |
instance.worker.postMessage(packet) |
||||||
waitingCount++ |
|
||||||
args[name](value => { |
|
||||||
args[name] = value |
|
||||||
if(--waitingCount == 0) worker.postMessage({jobId, action, args}) |
|
||||||
}) |
|
||||||
}) |
}) |
||||||
|
} |
||||||
|
|
||||||
if(waitingCount == 0) worker.postMessage({jobId, action, args}) |
|
||||||
return { |
|
||||||
then (f){ handlers[jobId].result = f; return this}, |
|
||||||
error (f){ handlers[jobId].error = f; return this}, |
|
||||||
progress(f){ handlers[jobId].progress = f; return this} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
worker.onmessage = function(e){ |
|
||||||
var {jobId, progress, error, result} = e.data |
|
||||||
var handler = handlers[jobId] |
|
||||||
if(progress && handler.progress) handler.progress(progress); |
|
||||||
if(error && handler.error) handler.error(error); |
|
||||||
if(result && handler.result) handler.result(result); |
|
||||||
} |
|
||||||
|
|
||||||
function convertToImageData(image){ |
function loadImage(image, cb){ |
||||||
if(image.match && image.match(/^https?:\/\//)) { |
if(typeof image === 'string'){ |
||||||
return function thunk(cb){ |
if(/^\#/.test(image)){ |
||||||
var img = new Image() |
// element css selector
|
||||||
img.src = image |
return loadImage(document.querySelector(image), cb) |
||||||
img.onload = () => cb(convertToImageData(img)) |
}else{ |
||||||
} |
// url or path
|
||||||
|
var im = new Image |
||||||
|
im.src = image; |
||||||
|
im.onload = e => loadImage(im, cb); |
||||||
|
return |
||||||
} |
} |
||||||
|
}else if(image instanceof File){ |
||||||
if(typeof image === 'string') image = document.querySelector(image) |
// files
|
||||||
if(image.getContext) image = image.getContext('2d'); |
var fr = new FileReader() |
||||||
else if(image.tagName == "IMG" || image.tagName == "VIDEO"){ |
fr.onload = e => loadImage(fr.result, cb); |
||||||
|
fr.readAsDataURL(image) |
||||||
|
return |
||||||
|
}else if(image instanceof Blob){ |
||||||
|
return loadImage(URL.createObjectURL(image), cb) |
||||||
|
}else if(image.getContext){ |
||||||
|
// canvas element
|
||||||
|
return loadImage(image.getContext('2d'), cb) |
||||||
|
}else if(image.tagName == "IMG" || image.tagName == "VIDEO"){ |
||||||
|
// image element or video element
|
||||||
var c = document.createElement('canvas'); |
var c = document.createElement('canvas'); |
||||||
c.width = image.naturalWidth || image.videoWidth; |
c.width = image.naturalWidth || image.videoWidth; |
||||||
c.height = image.naturalHeight || image.videoHeight; |
c.height = image.naturalHeight || image.videoHeight; |
||||||
var ctx = c.getContext('2d'); |
var ctx = c.getContext('2d'); |
||||||
ctx.drawImage(image, 0, 0); |
ctx.drawImage(image, 0, 0); |
||||||
image = ctx; |
return loadImage(ctx, cb) |
||||||
} |
}else if(image.getImageData){ |
||||||
if(image.getImageData) image = image.getImageData(0, 0, image.canvas.width, image.canvas.height); |
// canvas context
|
||||||
return image |
var data = image.getImageData(0, 0, image.canvas.width, image.canvas.height); |
||||||
} |
return loadImage(data, cb) |
||||||
|
|
||||||
runAsync('init', {mem: (1<<24) * 6, langUrl}) |
|
||||||
|
|
||||||
return { |
|
||||||
detect(image){ |
|
||||||
return runAsync('detect', {image: convertToImageData(image)}) |
|
||||||
}, |
|
||||||
|
|
||||||
recognize(image, options='eng'){ |
|
||||||
|
|
||||||
if (typeof options === 'string') options = {lang: options}; |
|
||||||
else options.lang = options.lang || 'eng'; |
|
||||||
|
|
||||||
if (!bigworker && ['chi_sim', 'chi_tra', 'jpn'].indexOf(options.lang) != -1){ |
|
||||||
runAsync('init', {mem: (1<<24) * 10, langUrl}) |
|
||||||
bigworker = true |
|
||||||
} |
|
||||||
|
|
||||||
return runAsync('recognize', {options, image: convertToImageData(image)}) |
|
||||||
} |
|
||||||
} |
} |
||||||
|
cb(image) |
||||||
} |
} |
||||||
|
|
||||||
var Tesseract = {coreUrl, workerUrl, langUrl, recognize, detect, createWorker} |
|
||||||
|
|
||||||
module.exports = Tesseract |
|
@ -0,0 +1,61 @@ |
|||||||
|
var leveljs = require('level-js') |
||||||
|
var db = typeof indexedDB === 'undefined' ? { open: (_, cb) => cb(true) } : leveljs('./tessdata2') |
||||||
|
|
||||||
|
var langdata = require('../common/langdata.json') |
||||||
|
|
||||||
|
module.exports = function getLanguageData(req, res, cb){ |
||||||
|
var lang = req.options.lang; |
||||||
|
|
||||||
|
function saveDataFile(data){ |
||||||
|
db.put(lang, data, err => console.log('cached', lang, err)) |
||||||
|
cb(data) |
||||||
|
} |
||||||
|
|
||||||
|
db.open({ compression: false }, err => { |
||||||
|
if (err) return fetchLanguageData(req, res, cb); |
||||||
|
db.get(lang, (err, data) => { |
||||||
|
if (err) return fetchLanguageData(req, res, saveDataFile); |
||||||
|
res.progress({ status: 'found in cache ' + lang + '.traineddata' }) |
||||||
|
cb(data) |
||||||
|
}) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
var ungzip = require('pako').ungzip; |
||||||
|
|
||||||
|
function fetchLanguageData(req, res, cb){ |
||||||
|
var lang = req.options.lang; |
||||||
|
var langfile = lang + '.traineddata.gz'; |
||||||
|
var url = req.workerOptions.langPath + langfile; |
||||||
|
|
||||||
|
var xhr = new XMLHttpRequest(); |
||||||
|
xhr.responseType = 'arraybuffer'; |
||||||
|
xhr.open('GET', url, true); |
||||||
|
xhr.onerror = e => { |
||||||
|
xhr.onprogress = xhr.onload = null |
||||||
|
cb(xhr, null) |
||||||
|
} |
||||||
|
xhr.onprogress = e => |
||||||
|
res.progress({ |
||||||
|
status: 'downloading ' + langfile, |
||||||
|
loaded: e.loaded, |
||||||
|
progress: Math.min(1, e.loaded / langdata[lang]) |
||||||
|
}); |
||||||
|
|
||||||
|
xhr.onload = e => { |
||||||
|
if (!(xhr.status == 200 || (xhr.status == 0 && xhr.response))) return res.reject('Error downloading language ' + url); |
||||||
|
res.progress({ status: 'unzipping ' + langfile }) |
||||||
|
|
||||||
|
// in case the gzips are already ungzipped or extra gzipped
|
||||||
|
var response = new Uint8Array(xhr.response) |
||||||
|
try { |
||||||
|
while(response[0] == 0x1f && response[1] == 0x8b) response = ungzip(response); |
||||||
|
} catch (err) { |
||||||
|
return res.reject('Error unzipping language file ' + langfile + '\n' + err.message) |
||||||
|
} |
||||||
|
|
||||||
|
cb(response) |
||||||
|
} |
||||||
|
xhr.send() |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
"use strict"; |
||||||
|
|
||||||
|
var workerUtils = require('../common/worker.js') |
||||||
|
|
||||||
|
global.addEventListener('message', function(e){ |
||||||
|
var packet = e.data; |
||||||
|
workerUtils.dispatchHandlers(packet, obj => postMessage(obj)) |
||||||
|
}) |
||||||
|
|
||||||
|
exports.getLanguageData = require('./lang.js') |
||||||
|
|
||||||
|
exports.getCore = function(req, res){ |
||||||
|
if(!global.TesseractCore){ |
||||||
|
res.progress({ status: 'loading tesseract core' }) |
||||||
|
importScripts(req.workerOptions.tesseractPath) |
||||||
|
res.progress({ status: 'loaded tesseract core' }) |
||||||
|
} |
||||||
|
return TesseractCore |
||||||
|
} |
||||||
|
|
||||||
|
workerUtils.setAdapter(module.exports); |
@ -1,4 +1,4 @@ |
|||||||
export default function circularize(page){ |
module.exports = function circularize(page){ |
||||||
page.paragraphs = [] |
page.paragraphs = [] |
||||||
page.lines = [] |
page.lines = [] |
||||||
page.words = [] |
page.words = [] |
@ -0,0 +1,23 @@ |
|||||||
|
module.exports = function desaturate(image){ |
||||||
|
var width, height; |
||||||
|
if(image.data){ |
||||||
|
var src = image.data; |
||||||
|
width = image.width, height = image.height; |
||||||
|
var dst = new Uint8Array(width * height); |
||||||
|
var srcLength = src.length | 0, srcLength_16 = (srcLength - 16) | 0; |
||||||
|
|
||||||
|
for (var i = 0, j = 0; i <= srcLength_16; i += 16, j += 4) { |
||||||
|
// convert to grayscale 4 pixels at a time; eveything with alpha gets put in front of 50% gray
|
||||||
|
dst[j] = (((src[i] * 77 + src[i+1] * 151 + src[i+2] * 28) * src[i+3]) + ((255-src[i+3]) << 15) + 32768) >> 16 |
||||||
|
dst[j+1] = (((src[i+4] * 77 + src[i+5] * 151 + src[i+6] * 28) * src[i+7]) + ((255-src[i+7]) << 15) + 32768) >> 16 |
||||||
|
dst[j+2] = (((src[i+8] * 77 + src[i+9] * 151 + src[i+10] * 28) * src[i+11]) + ((255-src[i+11]) << 15) + 32768) >> 16 |
||||||
|
dst[j+3] = (((src[i+12] * 77 + src[i+13] * 151 + src[i+14] * 28) * src[i+15]) + ((255-src[i+15]) << 15) + 32768) >> 16 |
||||||
|
} |
||||||
|
for (; i < srcLength; i += 4, ++j) //finish up
|
||||||
|
dst[j] = (((src[i] * 77 + src[i+1] * 151 + src[i+2] * 28) * src[i+3]) + ((255-src[i+3]) << 15) + 32768) >> 16 |
||||||
|
image = dst; |
||||||
|
} else { |
||||||
|
throw 'Expected ImageData' |
||||||
|
} |
||||||
|
return image |
||||||
|
} |
@ -0,0 +1,161 @@ |
|||||||
|
function deindent(html){ |
||||||
|
var lines = html.split('\n') |
||||||
|
if(lines[0].substring(0,2) === " "){ |
||||||
|
for (var i = 0; i < lines.length; i++) { |
||||||
|
if (lines[i].substring(0,2) === " ") { |
||||||
|
lines[i] = lines[i].slice(2) |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
return lines.join('\n') |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = function DumpLiterallyEverything(Module, base){ |
||||||
|
var ri = base.GetIterator(); |
||||||
|
var blocks = []; |
||||||
|
var block, para, textline, word, symbol; |
||||||
|
|
||||||
|
function enumToString(value, prefix){ |
||||||
|
return (Object.keys(Module) |
||||||
|
.filter(function(e){ return e.substr(0, prefix.length + 1) == prefix + '_' }) |
||||||
|
.filter(function(e){ return Module[e] === value }) |
||||||
|
.map(function(e){ return e.slice(prefix.length + 1) })[0]) |
||||||
|
} |
||||||
|
|
||||||
|
ri.Begin() |
||||||
|
do { |
||||||
|
if(ri.IsAtBeginningOf(Module.RIL_BLOCK)){ |
||||||
|
var poly = ri.BlockPolygon(); |
||||||
|
var polygon = null; |
||||||
|
// BlockPolygon() returns null when automatic page segmentation is off
|
||||||
|
if(Module.getPointer(poly) > 0){ |
||||||
|
var n = poly.get_n(), |
||||||
|
px = poly.get_x(), |
||||||
|
py = poly.get_y(), |
||||||
|
polygon = []; |
||||||
|
for(var i = 0; i < n; i++){ |
||||||
|
polygon.push([px.getValue(i), py.getValue(i)]); |
||||||
|
} |
||||||
|
Module._ptaDestroy(Module.getPointer(poly)); |
||||||
|
} |
||||||
|
|
||||||
|
block = { |
||||||
|
paragraphs: [], |
||||||
|
|
||||||
|
text: ri.GetUTF8Text(Module.RIL_BLOCK), |
||||||
|
confidence: ri.Confidence(Module.RIL_BLOCK), |
||||||
|
baseline: ri.getBaseline(Module.RIL_BLOCK), |
||||||
|
bbox: ri.getBoundingBox(Module.RIL_BLOCK), |
||||||
|
|
||||||
|
blocktype: enumToString(ri.BlockType(), 'PT'), |
||||||
|
polygon: polygon |
||||||
|
} |
||||||
|
blocks.push(block) |
||||||
|
} |
||||||
|
if(ri.IsAtBeginningOf(Module.RIL_PARA)){ |
||||||
|
para = { |
||||||
|
lines: [], |
||||||
|
|
||||||
|
text: ri.GetUTF8Text(Module.RIL_PARA), |
||||||
|
confidence: ri.Confidence(Module.RIL_PARA), |
||||||
|
baseline: ri.getBaseline(Module.RIL_PARA), |
||||||
|
bbox: ri.getBoundingBox(Module.RIL_PARA), |
||||||
|
|
||||||
|
is_ltr: !!ri.ParagraphIsLtr() |
||||||
|
} |
||||||
|
block.paragraphs.push(para) |
||||||
|
} |
||||||
|
if(ri.IsAtBeginningOf(Module.RIL_TEXTLINE)){ |
||||||
|
textline = { |
||||||
|
words: [], |
||||||
|
|
||||||
|
text: ri.GetUTF8Text(Module.RIL_TEXTLINE), |
||||||
|
confidence: ri.Confidence(Module.RIL_TEXTLINE), |
||||||
|
baseline: ri.getBaseline(Module.RIL_TEXTLINE), |
||||||
|
bbox: ri.getBoundingBox(Module.RIL_TEXTLINE) |
||||||
|
} |
||||||
|
para.lines.push(textline) |
||||||
|
} |
||||||
|
if(ri.IsAtBeginningOf(Module.RIL_WORD)){ |
||||||
|
var fontInfo = ri.getWordFontAttributes(), |
||||||
|
wordDir = ri.WordDirection(); |
||||||
|
word = { |
||||||
|
symbols: [], |
||||||
|
choices: [], |
||||||
|
|
||||||
|
text: ri.GetUTF8Text(Module.RIL_WORD), |
||||||
|
confidence: ri.Confidence(Module.RIL_WORD), |
||||||
|
baseline: ri.getBaseline(Module.RIL_WORD), |
||||||
|
bbox: ri.getBoundingBox(Module.RIL_WORD), |
||||||
|
|
||||||
|
is_numeric: !!ri.WordIsNumeric(), |
||||||
|
in_dictionary: !!ri.WordIsFromDictionary(), |
||||||
|
direction: enumToString(wordDir, 'DIR'), |
||||||
|
language: ri.WordRecognitionLanguage(), |
||||||
|
|
||||||
|
is_bold: fontInfo.is_bold, |
||||||
|
is_italic: fontInfo.is_italic, |
||||||
|
is_underlined: fontInfo.is_underlined, |
||||||
|
is_monospace: fontInfo.is_monospace, |
||||||
|
is_serif: fontInfo.is_serif, |
||||||
|
is_smallcaps: fontInfo.is_smallcaps, |
||||||
|
font_size: fontInfo.pointsize, |
||||||
|
font_id: fontInfo.font_id, |
||||||
|
font_name: fontInfo.font_name, |
||||||
|
} |
||||||
|
var wc = new Module.WordChoiceIterator(ri); |
||||||
|
do { |
||||||
|
word.choices.push({ |
||||||
|
text: wc.GetUTF8Text(), |
||||||
|
confidence: wc.Confidence() |
||||||
|
}) |
||||||
|
} while (wc.Next()); |
||||||
|
Module.destroy(wc) |
||||||
|
textline.words.push(word) |
||||||
|
} |
||||||
|
|
||||||
|
var image = null; |
||||||
|
// var pix = ri.GetBinaryImage(Module.RIL_SYMBOL)
|
||||||
|
// var image = pix2array(pix);
|
||||||
|
// // for some reason it seems that things stop working if you destroy pics
|
||||||
|
// Module._pixDestroy(Module.getPointer(pix));
|
||||||
|
if(ri.IsAtBeginningOf(Module.RIL_SYMBOL)){ |
||||||
|
symbol = { |
||||||
|
choices: [], |
||||||
|
image: image, |
||||||
|
|
||||||
|
text: ri.GetUTF8Text(Module.RIL_SYMBOL), |
||||||
|
confidence: ri.Confidence(Module.RIL_SYMBOL), |
||||||
|
baseline: ri.getBaseline(Module.RIL_SYMBOL), |
||||||
|
bbox: ri.getBoundingBox(Module.RIL_SYMBOL), |
||||||
|
|
||||||
|
is_superscript: !!ri.SymbolIsSuperscript(), |
||||||
|
is_subscript: !!ri.SymbolIsSubscript(), |
||||||
|
is_dropcap: !!ri.SymbolIsDropcap(), |
||||||
|
} |
||||||
|
word.symbols.push(symbol) |
||||||
|
var ci = new Module.ChoiceIterator(ri); |
||||||
|
do { |
||||||
|
symbol.choices.push({ |
||||||
|
text: ci.GetUTF8Text(), |
||||||
|
confidence: ci.Confidence() |
||||||
|
}) |
||||||
|
} while (ci.Next()); |
||||||
|
Module.destroy(ci) |
||||||
|
} |
||||||
|
} while (ri.Next(Module.RIL_SYMBOL)); |
||||||
|
Module.destroy(ri) |
||||||
|
|
||||||
|
return { |
||||||
|
text: base.GetUTF8Text(), |
||||||
|
html: deindent(base.GetHOCRText()), |
||||||
|
|
||||||
|
confidence: base.MeanTextConf(), |
||||||
|
|
||||||
|
blocks: blocks, |
||||||
|
|
||||||
|
psm: enumToString(base.GetPageSegMode(), 'PSM'), |
||||||
|
oem: enumToString(base.oem(), 'OEM'), |
||||||
|
version: base.Version(), |
||||||
|
} |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
{"afr": 1079573, "ara": 1701536, "aze": 1420865, "bel": 1276820, "ben": 6772012, "bul": 1605615, "cat": 1652368, "ces": 1035441, "chi_sim": 17710414, "chi_tra": 24717749, "chr": 320649, "dan-frak": 677656, "dan": 1972936, "deu-frak": 822644, "deu": 991656, "ell": 859719, "eng": 9453554, "enm": 619254, "epo": 1241212, "equ": 821130, "est": 1905040, "eus": 1641190, "fin": 979418, "fra": 1376221, "frk": 5912963, "frm": 5147082, "glg": 1674938, "grc": 3012615, "heb": 1051501, "hin": 6590065, "hrv": 1926995, "hun": 3074473, "ind": 1874776, "isl": 1634041, "ita": 948593, "ita_old": 3436571, "jpn": 13507168, "kan": 4390317, "kor": 5353098, "lav": 1843944, "lit": 1779240, "mal": 5966263, "meme": 88453, "mkd": 1163087, "mlt": 1463001, "msa": 1665427, "nld": 1134708, "nor": 2191610, "osd": 4274649, "pol": 7024662, "por": 909359, "ron": 915680, "rus": 5969957, "slk-frak": 289885, "slk": 2217342, "slv": 1611338, "spa": 883170, "spa_old": 5647453, "sqi": 1667041, "srp": 1770244, "swa": 757916, "swe": 2451917, "tam": 3498763, "tel": 5795246, "tgl": 1496256, "tha": 3811136, "tur": 3563264, "ukr": 937566, "vie": 2195922} |
@ -0,0 +1,143 @@ |
|||||||
|
var latestJob; |
||||||
|
var Module; |
||||||
|
var base; |
||||||
|
var adapter = {}; |
||||||
|
|
||||||
|
function dispatchHandlers(packet, send){ |
||||||
|
function respond(status, data){ |
||||||
|
send({ |
||||||
|
jobId: packet.jobId, |
||||||
|
status: status, |
||||||
|
data: data |
||||||
|
}) |
||||||
|
} |
||||||
|
respond.resolve = respond.bind(this, 'resolve') |
||||||
|
respond.reject = respond.bind(this, 'reject') |
||||||
|
respond.progress = respond.bind(this, 'progress') |
||||||
|
|
||||||
|
latestJob = respond; |
||||||
|
|
||||||
|
if(packet.action === 'recognize'){ |
||||||
|
handleRecognize(packet.payload, respond) |
||||||
|
}else if(packet.action === 'detect'){ |
||||||
|
handleDetect(packet.payload, respond) |
||||||
|
} |
||||||
|
} |
||||||
|
exports.dispatchHandlers = dispatchHandlers; |
||||||
|
|
||||||
|
exports.setAdapter = function setAdapter(impl){ |
||||||
|
adapter = impl; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function handleInit(req, res){ |
||||||
|
if(!Module){ |
||||||
|
var Core = adapter.getCore(req, res); |
||||||
|
|
||||||
|
res.progress({ status: 'initializing tesseract api' }) |
||||||
|
Module = Core({ |
||||||
|
TOTAL_MEMORY: req.memory, |
||||||
|
TesseractProgress(percent){ |
||||||
|
latestJob.progress({ status: 'recognizing text', progress: Math.max(0, (percent-30)/70) }) |
||||||
|
}, |
||||||
|
onRuntimeInitialized() {} |
||||||
|
}) |
||||||
|
Module.FS_createPath("/", "tessdata", true, true) |
||||||
|
base = new Module.TessBaseAPI() |
||||||
|
res.progress({ status: 'initialized tesseract api' }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var dump = require('./dump.js') |
||||||
|
var desaturate = require('./desaturate.js') |
||||||
|
|
||||||
|
|
||||||
|
function setImage(Module, base, image){ |
||||||
|
var imgbin = desaturate(image), |
||||||
|
width = image.width, |
||||||
|
height = image.height; |
||||||
|
|
||||||
|
var ptr = Module.allocate(imgbin, 'i8', Module.ALLOC_NORMAL); |
||||||
|
base.SetImage(Module.wrapPointer(ptr), width, height, 1, width); |
||||||
|
base.SetRectangle(0, 0, width, height) |
||||||
|
return ptr; |
||||||
|
} |
||||||
|
|
||||||
|
function loadLanguage(req, res, cb){ |
||||||
|
var lang = req.options.lang; |
||||||
|
|
||||||
|
if(!Module._loadedLanguages) Module._loadedLanguages = {}; |
||||||
|
if(lang in Module._loadedLanguages) return cb(); |
||||||
|
|
||||||
|
adapter.getLanguageData(req, res, function(data){ |
||||||
|
Module.FS_createDataFile('tessdata', lang + ".traineddata", data, true, false); |
||||||
|
res.progress({ status: 'loaded ' + lang + '.traineddata' }) |
||||||
|
Module._loadedLanguages[lang] = true; |
||||||
|
cb() |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function handleRecognize(req, res){ |
||||||
|
handleInit(req, res) |
||||||
|
|
||||||
|
loadLanguage(req, res, function(){ |
||||||
|
var lang = req.options.lang; |
||||||
|
|
||||||
|
base.Init(null, lang) |
||||||
|
res.progress({ status: 'initialized with language' }) |
||||||
|
|
||||||
|
var ptr = setImage(Module, base, req.image); |
||||||
|
base.Recognize(null) |
||||||
|
|
||||||
|
var result = dump(Module, base) |
||||||
|
|
||||||
|
base.End(); |
||||||
|
Module._free(ptr); |
||||||
|
|
||||||
|
res.resolve(result); |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function handleDetect(req, res){ |
||||||
|
handleInit(req, res) |
||||||
|
req.options.lang = 'osd'; |
||||||
|
loadLanguage(req, res, function(){ |
||||||
|
|
||||||
|
base.Init(null, 'osd') |
||||||
|
base.SetPageSegMode(Module.PSM_OSD_ONLY) |
||||||
|
|
||||||
|
var ptr = setImage(Module, base, req.image); |
||||||
|
|
||||||
|
var results = new Module.OSResults(); |
||||||
|
var success = base.DetectOS(results); |
||||||
|
if(!success){ |
||||||
|
base.End(); |
||||||
|
Module._free(ptr); |
||||||
|
res.reject("failed to detect os") |
||||||
|
} else { |
||||||
|
var charset = results.get_unicharset() |
||||||
|
|
||||||
|
var best = results.get_best_result() |
||||||
|
var oid = best.get_orientation_id(), |
||||||
|
sid = best.get_script_id(); |
||||||
|
|
||||||
|
var result = { |
||||||
|
tesseract_script_id: sid, |
||||||
|
script: charset.get_script_from_script_id(sid), |
||||||
|
script_confidence: best.get_sconfidence(), |
||||||
|
orientation_degrees: [0, 270, 180, 90][oid], |
||||||
|
orientation_confidence: best.get_oconfidence() |
||||||
|
} |
||||||
|
|
||||||
|
base.End(); |
||||||
|
Module._free(ptr); |
||||||
|
|
||||||
|
res.resolve(result) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
@ -0,0 +1,137 @@ |
|||||||
|
"use strict"; |
||||||
|
|
||||||
|
var adapter = require('./node/index.js') |
||||||
|
|
||||||
|
function createWorker(workerOptions){ |
||||||
|
return new TesseractWorker(workerOptions) |
||||||
|
} |
||||||
|
|
||||||
|
class TesseractWorker { |
||||||
|
constructor(workerOptions){ |
||||||
|
this.worker = null; |
||||||
|
this.workerOptions = workerOptions; |
||||||
|
this._currentJob = null; |
||||||
|
this._queue = [] |
||||||
|
} |
||||||
|
|
||||||
|
recognize(image, options){ |
||||||
|
return this._delay(job => { |
||||||
|
options = options || {} |
||||||
|
options.lang = options.lang || 'eng'; |
||||||
|
job._send('recognize', { image: image, options: options, workerOptions: this.workerOptions }) |
||||||
|
}) |
||||||
|
} |
||||||
|
detect(image, options){ |
||||||
|
options = options || {} |
||||||
|
return this._delay(job => { |
||||||
|
job._send('detect', { image: image, options: options, workerOptions: this.workerOptions }) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
terminate(){ |
||||||
|
if(this.worker) adapter.terminateWorker(this); |
||||||
|
this.worker = null; |
||||||
|
} |
||||||
|
|
||||||
|
_delay(fn){ |
||||||
|
if(!this.worker) this.worker = adapter.spawnWorker(this, this.workerOptions); |
||||||
|
|
||||||
|
var job = new TesseractJob(this); |
||||||
|
this._queue.push(e => { |
||||||
|
this._queue.shift() |
||||||
|
this._currentJob = job; |
||||||
|
fn(job) |
||||||
|
}) |
||||||
|
if(!this._currentJob) this._dequeue(); |
||||||
|
return job |
||||||
|
} |
||||||
|
|
||||||
|
_dequeue(){ |
||||||
|
this._currentJob = null; |
||||||
|
if(this._queue.length > 0){ |
||||||
|
this._queue[0]() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
_recv(packet){ |
||||||
|
if(this._currentJob.id === packet.jobId){ |
||||||
|
this._currentJob._handle(packet) |
||||||
|
}else{ |
||||||
|
console.warn('Job ID ' + packet.jobId + ' not known.') |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var jobCounter = 0; |
||||||
|
|
||||||
|
class TesseractJob { |
||||||
|
constructor(instance){ |
||||||
|
this.id = 'Job-' + (++jobCounter) + '-' + Math.random().toString(16).slice(3, 8) |
||||||
|
|
||||||
|
this._instance = instance; |
||||||
|
this._resolve = [] |
||||||
|
this._reject = [] |
||||||
|
this._progress = [] |
||||||
|
} |
||||||
|
|
||||||
|
then(resolve, reject){ |
||||||
|
if(this._resolve.push){ |
||||||
|
this._resolve.push(resolve) |
||||||
|
}else{ |
||||||
|
resolve(this._resolve) |
||||||
|
} |
||||||
|
|
||||||
|
if(reject) this.catch(reject); |
||||||
|
return this; |
||||||
|
} |
||||||
|
catch(reject){ |
||||||
|
if(this._reject.push){ |
||||||
|
this._reject.push(reject) |
||||||
|
}else{ |
||||||
|
reject(this._reject) |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
progress(fn){ |
||||||
|
this._progress.push(fn) |
||||||
|
return this; |
||||||
|
} |
||||||
|
_send(action, payload){ |
||||||
|
adapter.sendPacket(this._instance, { |
||||||
|
jobId: this.id, |
||||||
|
action: action, |
||||||
|
payload: payload |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
_handle(packet){ |
||||||
|
var data = packet.data; |
||||||
|
if(packet.status === 'resolve'){ |
||||||
|
if(this._resolve.length === 0) console.debug(data); |
||||||
|
this._resolve.forEach(fn => { |
||||||
|
var ret = fn(data); |
||||||
|
if(ret && typeof ret.then == 'function'){ |
||||||
|
console.warn('TesseractJob instances do not chain like ES6 Promises. To convert it into a real promise, use Promise.resolve.') |
||||||
|
} |
||||||
|
}) |
||||||
|
this._resolve = data; |
||||||
|
this._instance._dequeue() |
||||||
|
}else if(packet.status === 'reject'){ |
||||||
|
if(this._reject.length === 0) console.error(data); |
||||||
|
this._reject.forEach(fn => fn(data)) |
||||||
|
this._reject = data; |
||||||
|
this._instance._dequeue() |
||||||
|
}else if(packet.status === 'progress'){ |
||||||
|
this._progress.forEach(fn => fn(data)) |
||||||
|
}else{ |
||||||
|
console.warn('Message type unknown', packet.status) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
var DefaultTesseract = createWorker(adapter.defaultOptions) |
||||||
|
DefaultTesseract.createWorker = createWorker; |
||||||
|
|
||||||
|
module.exports = DefaultTesseract |
||||||
|
|
@ -0,0 +1,83 @@ |
|||||||
|
var path = require('path') |
||||||
|
exports.defaultOptions = { |
||||||
|
workerPath: path.join(__dirname, 'worker.js'), |
||||||
|
langPath: 'http://cdn.rawgit.com/naptha/tessdata/gh-pages/3.02/', |
||||||
|
} |
||||||
|
|
||||||
|
var fork = require('child_process').fork; |
||||||
|
var fs = require('fs') |
||||||
|
|
||||||
|
exports.spawnWorker = function spawnWorker(instance, workerOptions){ |
||||||
|
var cp = fork(workerOptions.workerPath); |
||||||
|
cp.on('message', function(packet){ |
||||||
|
instance._recv(packet) |
||||||
|
}) |
||||||
|
return cp; |
||||||
|
} |
||||||
|
|
||||||
|
exports.terminateWorker = function(instance){ |
||||||
|
instance.worker.kill() |
||||||
|
} |
||||||
|
|
||||||
|
exports.sendPacket = function sendPacket(instance, packet){ |
||||||
|
loadImage(packet.payload.image, function(img){ |
||||||
|
packet.payload.image = img |
||||||
|
instance.worker.send(packet) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function loadImage(image, cb){ |
||||||
|
if(typeof image === 'string'){ |
||||||
|
fs.readFile(image, function(err, buffer){ |
||||||
|
loadImage(buffer, cb) |
||||||
|
}) |
||||||
|
return |
||||||
|
}else if(image instanceof Buffer){ |
||||||
|
var fileType = require('file-type'); |
||||||
|
var mime = fileType(image).mime |
||||||
|
|
||||||
|
|
||||||
|
if(mime === 'image/png'){ |
||||||
|
var PNGReader = require('png.js'); |
||||||
|
var reader = new PNGReader(image); |
||||||
|
reader.parse(function(err, png){ |
||||||
|
if (err) throw err; |
||||||
|
|
||||||
|
var image = { |
||||||
|
width: png.getWidth(), |
||||||
|
height: png.getHeight() |
||||||
|
} |
||||||
|
image.data = new Uint8Array(image.width * image.height * 4) |
||||||
|
for(var j = 0; j < image.height; j++){ |
||||||
|
for(var i = 0; i < image.width; i++){ |
||||||
|
var offset = 4 * (i + j * image.width), |
||||||
|
pix = png.getPixel(i, j); |
||||||
|
|
||||||
|
image.data[offset] = pix[0] |
||||||
|
image.data[offset + 1] = pix[1] |
||||||
|
image.data[offset + 2] = pix[2] |
||||||
|
image.data[offset + 3] = pix[3]; |
||||||
|
} |
||||||
|
} |
||||||
|
// console.log(image)
|
||||||
|
loadImage(image, cb) |
||||||
|
}); |
||||||
|
return |
||||||
|
}else if(mime === 'image/jpeg'){ |
||||||
|
var jpeg = require('jpeg-js'); |
||||||
|
loadImage(jpeg.decode(image), cb) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: support for TIFF, NetPBM, BMP, etc.
|
||||||
|
} |
||||||
|
|
||||||
|
// node uses json.stringify for ipc which means we need to turn
|
||||||
|
// fancy arrays into raw arrays
|
||||||
|
if(image && image.data && image.data.length && !Array.isArray(image.data)){ |
||||||
|
image.data = Array.from(image.data) |
||||||
|
return loadImage(image, cb) |
||||||
|
} |
||||||
|
cb(image) |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
var http = require("http"), |
||||||
|
zlib = require("zlib"), |
||||||
|
fs = require("fs"), |
||||||
|
path = require("path"); |
||||||
|
|
||||||
|
var langdata = require('../common/langdata.json') |
||||||
|
|
||||||
|
function getLanguageData(req, res, cb){ |
||||||
|
var lang = req.options.lang; |
||||||
|
var langfile = lang + '.traineddata.gz'; |
||||||
|
var url = req.workerOptions.langPath + langfile; |
||||||
|
|
||||||
|
fs.readFile(lang + '.traineddata', function (err, data) { |
||||||
|
if(!err) return cb(new Uint8Array(data)); |
||||||
|
|
||||||
|
http.get(url, function(stream){ |
||||||
|
var received_bytes = 0; |
||||||
|
stream.on('data', function(chunk) { |
||||||
|
received_bytes += chunk.length; |
||||||
|
res.progress({ |
||||||
|
status: 'downloading ' + langfile, |
||||||
|
loaded: received_bytes, |
||||||
|
progress: Math.min(1, received_bytes / langdata[lang]) |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
var gunzip = zlib.createGunzip(); |
||||||
|
stream.pipe(gunzip).pipe(fs.createWriteStream(lang + '.traineddata')) |
||||||
|
gunzip.on('end', function(){ getLanguageData(req, stream, cb) }) |
||||||
|
}) |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
module.exports = getLanguageData; |
@ -0,0 +1,22 @@ |
|||||||
|
"use strict"; |
||||||
|
|
||||||
|
var workerUtils = require('../common/worker.js') |
||||||
|
|
||||||
|
process.on('message', function(packet){ |
||||||
|
workerUtils.dispatchHandlers(packet, obj => process.send(obj)) |
||||||
|
}) |
||||||
|
|
||||||
|
exports.getLanguageData = require('./lang.js') |
||||||
|
|
||||||
|
|
||||||
|
var TesseractCore; |
||||||
|
exports.getCore = function(req, res){ |
||||||
|
if(!TesseractCore){ |
||||||
|
res.progress({ status: 'loading tesseract core' }) |
||||||
|
TesseractCore = require('tesseract.js-core') |
||||||
|
res.progress({ status: 'loaded tesseract core' }) |
||||||
|
} |
||||||
|
return TesseractCore |
||||||
|
} |
||||||
|
|
||||||
|
workerUtils.setAdapter(module.exports); |
@ -1,26 +0,0 @@ |
|||||||
export default function desaturate(image){ |
|
||||||
var width, height; |
|
||||||
if(image.data){ |
|
||||||
var src = image.data; |
|
||||||
width = image.width, height = image.height; |
|
||||||
var dst = new Uint8Array(width * height); |
|
||||||
var srcLength = src.length | 0, srcLength_16 = (srcLength - 16) | 0; |
|
||||||
|
|
||||||
for (var i = 0, j = 0; i <= srcLength_16; i += 16, j += 4) { |
|
||||||
// convert to grayscale 4 pixels at a time; eveything with alpha gets put in front of 50% gray
|
|
||||||
dst[j] = (((src[i] * 77 + src[i+1] * 151 + src[i+2] * 28) * src[i+3]) + ((255-src[i+3]) << 15) + 32768) >> 16 |
|
||||||
dst[j+1] = (((src[i+4] * 77 + src[i+5] * 151 + src[i+6] * 28) * src[i+7]) + ((255-src[i+7]) << 15) + 32768) >> 16 |
|
||||||
dst[j+2] = (((src[i+8] * 77 + src[i+9] * 151 + src[i+10] * 28) * src[i+11]) + ((255-src[i+11]) << 15) + 32768) >> 16 |
|
||||||
dst[j+3] = (((src[i+12] * 77 + src[i+13] * 151 + src[i+14] * 28) * src[i+15]) + ((255-src[i+15]) << 15) + 32768) >> 16 |
|
||||||
|
|
||||||
} |
|
||||||
for (; i < srcLength; i += 4, ++j) //finish up
|
|
||||||
dst[j] = (((src[i] * 77 + src[i+1] * 151 + src[i+2] * 28) * src[i+3]) + ((255-src[i+3]) << 15) + 32768) >> 16 |
|
||||||
|
|
||||||
image = dst; |
|
||||||
} |
|
||||||
else { |
|
||||||
throw 'Expected ImageData' |
|
||||||
} |
|
||||||
return image |
|
||||||
} |
|
@ -1,166 +0,0 @@ |
|||||||
function deindent(html){ |
|
||||||
var lines = html.split('\n') |
|
||||||
if(lines[0].substring(0,2) === " "){ |
|
||||||
for (var i = 0; i < lines.length; i++) { |
|
||||||
if (lines[i].substring(0,2) === " ") { |
|
||||||
lines[i] = lines[i].slice(2) |
|
||||||
} |
|
||||||
}; |
|
||||||
} |
|
||||||
return lines.join('\n') |
|
||||||
} |
|
||||||
|
|
||||||
export default function DumpLiterallyEverything(){ |
|
||||||
|
|
||||||
var {module, base} = self |
|
||||||
|
|
||||||
var ri = base.GetIterator(); |
|
||||||
var blocks = []; |
|
||||||
var block, para, textline, word, symbol; |
|
||||||
|
|
||||||
function enumToString(value, prefix){ |
|
||||||
return (Object.keys(module) |
|
||||||
.filter(function(e){ return e.substr(0, prefix.length + 1) == prefix + '_' }) |
|
||||||
.filter(function(e){ return module[e] === value }) |
|
||||||
.map(function(e){ return e.slice(prefix.length + 1) })[0]) |
|
||||||
} |
|
||||||
|
|
||||||
const {RIL_BLOCK, RIL_PARA, RIL_TEXTLINE, RIL_WORD, RIL_SYMBOL} = module |
|
||||||
|
|
||||||
ri.Begin() |
|
||||||
do { |
|
||||||
if(ri.IsAtBeginningOf(RIL_BLOCK)){ |
|
||||||
var poly = ri.BlockPolygon(); |
|
||||||
var polygon = null; |
|
||||||
// BlockPolygon() returns null when automatic page segmentation is off
|
|
||||||
if(module.getPointer(poly) > 0){ |
|
||||||
var n = poly.get_n(), |
|
||||||
px = poly.get_x(), |
|
||||||
py = poly.get_y(), |
|
||||||
polygon = []; |
|
||||||
for(var i = 0; i < n; i++){ |
|
||||||
polygon.push([px.getValue(i), py.getValue(i)]); |
|
||||||
} |
|
||||||
module._ptaDestroy(module.getPointer(poly)); |
|
||||||
} |
|
||||||
|
|
||||||
block = { |
|
||||||
paragraphs: [], |
|
||||||
|
|
||||||
text: ri.GetUTF8Text(RIL_BLOCK), |
|
||||||
confidence: ri.Confidence(RIL_BLOCK), |
|
||||||
baseline: ri.getBaseline(RIL_BLOCK), |
|
||||||
bbox: ri.getBoundingBox(RIL_BLOCK), |
|
||||||
|
|
||||||
blocktype: enumToString(ri.BlockType(), 'PT'), |
|
||||||
polygon: polygon |
|
||||||
} |
|
||||||
blocks.push(block) |
|
||||||
} |
|
||||||
if(ri.IsAtBeginningOf(RIL_PARA)){ |
|
||||||
para = { |
|
||||||
lines: [], |
|
||||||
|
|
||||||
text: ri.GetUTF8Text(RIL_PARA), |
|
||||||
confidence: ri.Confidence(RIL_PARA), |
|
||||||
baseline: ri.getBaseline(RIL_PARA), |
|
||||||
bbox: ri.getBoundingBox(RIL_PARA), |
|
||||||
|
|
||||||
is_ltr: !!ri.ParagraphIsLtr() |
|
||||||
} |
|
||||||
block.paragraphs.push(para) |
|
||||||
} |
|
||||||
if(ri.IsAtBeginningOf(RIL_TEXTLINE)){ |
|
||||||
textline = { |
|
||||||
words: [], |
|
||||||
|
|
||||||
text: ri.GetUTF8Text(RIL_TEXTLINE), |
|
||||||
confidence: ri.Confidence(RIL_TEXTLINE), |
|
||||||
baseline: ri.getBaseline(RIL_TEXTLINE), |
|
||||||
bbox: ri.getBoundingBox(RIL_TEXTLINE) |
|
||||||
} |
|
||||||
para.lines.push(textline) |
|
||||||
} |
|
||||||
if(ri.IsAtBeginningOf(RIL_WORD)){ |
|
||||||
var fontInfo = ri.getWordFontAttributes(), |
|
||||||
wordDir = ri.WordDirection(); |
|
||||||
word = { |
|
||||||
symbols: [], |
|
||||||
choices: [], |
|
||||||
|
|
||||||
text: ri.GetUTF8Text(RIL_WORD), |
|
||||||
confidence: ri.Confidence(RIL_WORD), |
|
||||||
baseline: ri.getBaseline(RIL_WORD), |
|
||||||
bbox: ri.getBoundingBox(RIL_WORD), |
|
||||||
|
|
||||||
is_numeric: !!ri.WordIsNumeric(), |
|
||||||
in_dictionary: !!ri.WordIsFromDictionary(), |
|
||||||
direction: enumToString(wordDir, 'DIR'), |
|
||||||
language: ri.WordRecognitionLanguage(), |
|
||||||
|
|
||||||
is_bold: fontInfo.is_bold, |
|
||||||
is_italic: fontInfo.is_italic, |
|
||||||
is_underlined: fontInfo.is_underlined, |
|
||||||
is_monospace: fontInfo.is_monospace, |
|
||||||
is_serif: fontInfo.is_serif, |
|
||||||
is_smallcaps: fontInfo.is_smallcaps, |
|
||||||
font_size: fontInfo.pointsize, |
|
||||||
font_id: fontInfo.font_id, |
|
||||||
font_name: fontInfo.font_name, |
|
||||||
} |
|
||||||
var wc = new module.WordChoiceIterator(ri); |
|
||||||
do { |
|
||||||
word.choices.push({ |
|
||||||
text: wc.GetUTF8Text(), |
|
||||||
confidence: wc.Confidence() |
|
||||||
}) |
|
||||||
} while (wc.Next()); |
|
||||||
module.destroy(wc) |
|
||||||
textline.words.push(word) |
|
||||||
} |
|
||||||
|
|
||||||
var image = null; |
|
||||||
// var pix = ri.GetBinaryImage(RIL_SYMBOL)
|
|
||||||
// var image = pix2array(pix);
|
|
||||||
// // for some reason it seems that things stop working if you destroy pics
|
|
||||||
// module._pixDestroy(module.getPointer(pix));
|
|
||||||
if(ri.IsAtBeginningOf(RIL_SYMBOL)){ |
|
||||||
symbol = { |
|
||||||
choices: [], |
|
||||||
image: image, |
|
||||||
|
|
||||||
text: ri.GetUTF8Text(RIL_SYMBOL), |
|
||||||
confidence: ri.Confidence(RIL_SYMBOL), |
|
||||||
baseline: ri.getBaseline(RIL_SYMBOL), |
|
||||||
bbox: ri.getBoundingBox(RIL_SYMBOL), |
|
||||||
|
|
||||||
is_superscript: !!ri.SymbolIsSuperscript(), |
|
||||||
is_subscript: !!ri.SymbolIsSubscript(), |
|
||||||
is_dropcap: !!ri.SymbolIsDropcap(), |
|
||||||
} |
|
||||||
word.symbols.push(symbol) |
|
||||||
var ci = new module.ChoiceIterator(ri); |
|
||||||
do { |
|
||||||
symbol.choices.push({ |
|
||||||
text: ci.GetUTF8Text(), |
|
||||||
confidence: ci.Confidence() |
|
||||||
}) |
|
||||||
} while (ci.Next()); |
|
||||||
module.destroy(ci) |
|
||||||
} |
|
||||||
} while (ri.Next(RIL_SYMBOL)); |
|
||||||
module.destroy(ri) |
|
||||||
|
|
||||||
return { |
|
||||||
text: base.GetUTF8Text(), |
|
||||||
html: deindent(base.GetHOCRText()), |
|
||||||
|
|
||||||
confidence: base.MeanTextConf(), |
|
||||||
|
|
||||||
blocks: blocks, |
|
||||||
|
|
||||||
psm: enumToString(base.GetPageSegMode(), 'PSM'), |
|
||||||
oem: enumToString(base.oem(), 'OEM'), |
|
||||||
version: base.Version(), |
|
||||||
} |
|
||||||
} |
|
@ -1,2 +0,0 @@ |
|||||||
const fileSizes = {"afr": 1079573, "ara": 1701536, "aze": 1420865, "bel": 1276820, "ben": 6772012, "bul": 1605615, "cat": 1652368, "ces": 1035441, "chi_sim": 17710414, "chi_tra": 24717749, "chr": 320649, "dan-frak": 677656, "dan": 1972936, "deu-frak": 822644, "deu": 991656, "ell": 859719, "eng": 9453554, "enm": 619254, "epo": 1241212, "equ": 821130, "est": 1905040, "eus": 1641190, "fin": 979418, "fra": 1376221, "frk": 5912963, "frm": 5147082, "glg": 1674938, "grc": 3012615, "heb": 1051501, "hin": 6590065, "hrv": 1926995, "hun": 3074473, "ind": 1874776, "isl": 1634041, "ita": 948593, "ita_old": 3436571, "jpn": 13507168, "kan": 4390317, "kor": 5353098, "lav": 1843944, "lit": 1779240, "mal": 5966263, "meme": 88453, "mkd": 1163087, "mlt": 1463001, "msa": 1665427, "nld": 1134708, "nor": 2191610, "osd": 4274649, "pol": 7024662, "por": 909359, "ron": 915680, "rus": 5969957, "slk-frak": 289885, "slk": 2217342, "slv": 1611338, "spa": 883170, "spa_old": 5647453, "sqi": 1667041, "srp": 1770244, "swa": 757916, "swe": 2451917, "tam": 3498763, "tel": 5795246, "tgl": 1496256, "tha": 3811136, "tur": 3563264, "ukr": 937566, "vie": 2195922} |
|
||||||
export default fileSizes; |
|
@ -1,3 +0,0 @@ |
|||||||
import leveljs from 'level-js' |
|
||||||
var db = typeof indexedDB === 'undefined' ? { open: (_, cb) => cb(true) } : leveljs('./tessdata') |
|
||||||
export default db |
|
@ -1,53 +0,0 @@ |
|||||||
import desaturate from '../shared/desaturate' |
|
||||||
import loadLanguage from './loadLanguage' |
|
||||||
|
|
||||||
export default function detect(jobId, image, cb){ |
|
||||||
var width = image.width, height = image.height; |
|
||||||
image = desaturate(image) |
|
||||||
|
|
||||||
var ptr = self.module.allocate(image, 'i8', self.module.ALLOC_NORMAL); |
|
||||||
// console.log('allocated image')
|
|
||||||
|
|
||||||
loadLanguage(jobId, 'osd', err => { |
|
||||||
self.module._free(ptr); |
|
||||||
cb(err) |
|
||||||
}, success => { |
|
||||||
self.base.Init(null, 'osd') |
|
||||||
self.base.SetPageSegMode(self.module.PSM_OSD_ONLY) |
|
||||||
// console.log('loaded language')
|
|
||||||
|
|
||||||
self.base.SetImage(self.module.wrapPointer(ptr), width, height, 1, width) |
|
||||||
self.base.SetRectangle(0, 0, width, height) |
|
||||||
|
|
||||||
var results = new self.module.OSResults(); |
|
||||||
var success = self.base.DetectOS(results); |
|
||||||
if(!success){ |
|
||||||
self.base.End(); |
|
||||||
self.module._free(ptr); |
|
||||||
cb("failed to detect os") |
|
||||||
} |
|
||||||
else { |
|
||||||
var charset = results.get_unicharset() |
|
||||||
// console.log(charset)
|
|
||||||
// results.print_scores()
|
|
||||||
|
|
||||||
var best = results.get_best_result() |
|
||||||
var oid = best.get_orientation_id(), |
|
||||||
sid = best.get_script_id(); |
|
||||||
// console.log('orientation id', oid, [0, 270, 180, 90][oid], best.get_oconfidence())
|
|
||||||
// console.log('script id', sid, charset.get_script_from_script_id(sid), best.get_sconfidence())
|
|
||||||
// console.log(best)
|
|
||||||
|
|
||||||
cb(null, { |
|
||||||
tesseract_script_id: sid, |
|
||||||
script: charset.get_script_from_script_id(sid), |
|
||||||
script_confidence: best.get_sconfidence(), |
|
||||||
orientation_degrees: [0, 270, 180, 90][oid], |
|
||||||
orientation_confidence: best.get_oconfidence() |
|
||||||
}) |
|
||||||
|
|
||||||
self.base.End(); |
|
||||||
self.module._free(ptr); |
|
||||||
} |
|
||||||
}) |
|
||||||
} |
|
@ -1,37 +0,0 @@ |
|||||||
import recognize from './recognize' |
|
||||||
import detect from './detect' |
|
||||||
|
|
||||||
var module, base, jobId |
|
||||||
|
|
||||||
onmessage = function(e) { |
|
||||||
var {action, args} = e.data; |
|
||||||
jobId = e.data.jobId |
|
||||||
|
|
||||||
console.log('worker got action', action) |
|
||||||
|
|
||||||
if(action == 'init'){ |
|
||||||
|
|
||||||
self.langUrl = args.langUrl |
|
||||||
self.module = TesseractCore({ |
|
||||||
TOTAL_MEMORY: args.mem, //must be a multiple of 10 megabytes
|
|
||||||
TesseractProgress(percent){ |
|
||||||
postMessage({ jobId, |
|
||||||
'progress': { |
|
||||||
'recognized': Math.max(0,(percent-30)/70) |
|
||||||
} |
|
||||||
}) |
|
||||||
}, |
|
||||||
onRuntimeInitialized() {} |
|
||||||
}) |
|
||||||
self.module.FS_createPath("/","tessdata",true,true) |
|
||||||
self.base = new self.module.TessBaseAPI() |
|
||||||
|
|
||||||
} else if(action === 'recognize'){ |
|
||||||
var {image, options} = args |
|
||||||
recognize(jobId, image, options, |
|
||||||
(error, result) => postMessage({jobId, error: error.message, result})) |
|
||||||
} else if(action === 'detect'){ |
|
||||||
detect(jobId, args.image, |
|
||||||
(error, result) => postMessage({jobId, error: error.message, result})) |
|
||||||
} |
|
||||||
} |
|
@ -1,99 +0,0 @@ |
|||||||
import {ungzip} from 'pako' |
|
||||||
import db from './db' |
|
||||||
import fileSizes from '../shared/fileSizes' |
|
||||||
|
|
||||||
function getLanguageData(lang, progress, cb){ |
|
||||||
var xhr = new XMLHttpRequest(); |
|
||||||
xhr.responseType = 'arraybuffer'; |
|
||||||
xhr.open('GET', self.langUrl + lang + '.traineddata.gz', true); |
|
||||||
xhr.onerror = e => { |
|
||||||
xhr.onprogress = xhr.onload = null |
|
||||||
cb(xhr, null) |
|
||||||
} |
|
||||||
xhr.onprogress = e => progress({ |
|
||||||
'loaded_lang_model': e.loaded/fileSizes[lang], //this is kinda wrong on safari
|
|
||||||
cached: false |
|
||||||
}) |
|
||||||
xhr.onload = e => { |
|
||||||
if (!(xhr.status == 200 || (xhr.status == 0 && xhr.response))) return cb(xhr, null); |
|
||||||
|
|
||||||
progress({'unzipping_lang_model': true}) |
|
||||||
|
|
||||||
var response = new Uint8Array(xhr.response) |
|
||||||
while(response[0] == 0x1f && response[1] == 0x8b) response = ungzip(response); |
|
||||||
|
|
||||||
progress({ |
|
||||||
'unzipped_lang_model': true, |
|
||||||
'lang_model_size': response.length |
|
||||||
}) |
|
||||||
|
|
||||||
cb(null, response) |
|
||||||
} |
|
||||||
|
|
||||||
progress({ |
|
||||||
'loaded_lang_model': 0, |
|
||||||
cached: false, |
|
||||||
requesting: true |
|
||||||
}) |
|
||||||
|
|
||||||
xhr.send() |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
function load(lang, jobId, cb){ |
|
||||||
|
|
||||||
console.log('loadLanguage jobId', jobId) |
|
||||||
|
|
||||||
function progressMessage(progress){ |
|
||||||
postMessage({ jobId, progress }) |
|
||||||
} |
|
||||||
|
|
||||||
function finish(err, data) { |
|
||||||
if(err) return cb(err); |
|
||||||
// loaded_langs.push(lang)
|
|
||||||
cb(null, data) |
|
||||||
} |
|
||||||
|
|
||||||
function createDataFile(err, data){ |
|
||||||
progressMessage({ created_virtual_datafile: true}) |
|
||||||
finish(err, data) |
|
||||||
} |
|
||||||
|
|
||||||
function createDataFileCached(err, data) { |
|
||||||
if(err) return createDataFile(err); |
|
||||||
|
|
||||||
db.put(lang, data, err => console.log('cached', lang, err)) |
|
||||||
progressMessage({cached_lang: lang}) |
|
||||||
createDataFile(null, data) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
db.open({compression: false}, err => { |
|
||||||
if (err) return getLanguageData(lang, progressMessage, createDataFile); |
|
||||||
|
|
||||||
db.get(lang, (err, data) => { |
|
||||||
|
|
||||||
if (err) return getLanguageData(lang, progressMessage, createDataFileCached) |
|
||||||
|
|
||||||
while(data[0] == 0x1f && data[1] == 0x8b) data = ungzip(data); |
|
||||||
|
|
||||||
progressMessage({ loaded_lang_model: lang, from_cache: true }) |
|
||||||
|
|
||||||
cb(null, data) |
|
||||||
}) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
var loaded_langs = [] |
|
||||||
|
|
||||||
export default function loadLanguage(jobId, lang, error, success){ |
|
||||||
if(loaded_langs.indexOf(lang) == -1) load(lang, jobId, function(err, result){ |
|
||||||
if(err) return error(err) |
|
||||||
|
|
||||||
loaded_langs.push(lang) |
|
||||||
self.module.FS_createDataFile('tessdata', lang +".traineddata", result, true, false); |
|
||||||
|
|
||||||
success() |
|
||||||
}) |
|
||||||
else success(); |
|
||||||
} |
|
@ -1,56 +0,0 @@ |
|||||||
import desaturate from '../shared/desaturate' |
|
||||||
import loadLanguage from './loadLanguage' |
|
||||||
import circularize from '../shared/circularize' |
|
||||||
import dump from '../shared/dump' |
|
||||||
|
|
||||||
var loaded_langs = [] |
|
||||||
|
|
||||||
export default function recognize(jobId, image, options, cb){ |
|
||||||
|
|
||||||
console.log('recognize id', jobId) |
|
||||||
var {lang} = options |
|
||||||
var width = image.width, height = image.height; |
|
||||||
|
|
||||||
image = desaturate(image) |
|
||||||
|
|
||||||
var ptr = self.module.allocate(image, 'i8', self.module.ALLOC_NORMAL); |
|
||||||
|
|
||||||
loadLanguage(jobId, lang, err => { |
|
||||||
self.module._free(ptr) |
|
||||||
cb(err) |
|
||||||
}, success => { |
|
||||||
self.base.Init(null, lang) |
|
||||||
|
|
||||||
postMessage({ |
|
||||||
jobId, |
|
||||||
'progress': { |
|
||||||
'initialized_with_lang': lang |
|
||||||
} |
|
||||||
}) |
|
||||||
|
|
||||||
for (var option in options) { |
|
||||||
if (options.hasOwnProperty(option)) { |
|
||||||
self.base.SetVariable(option, options[option]); |
|
||||||
postMessage({ |
|
||||||
jobId: jobId, |
|
||||||
'progress': { |
|
||||||
'set_variable': { |
|
||||||
variable: option, |
|
||||||
value: options[option] |
|
||||||
} |
|
||||||
} |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
self.base.SetImage(self.module.wrapPointer(ptr), width, height, 1, width) |
|
||||||
self.base.SetRectangle(0, 0, width, height) |
|
||||||
// self.base.GetUTF8Text()
|
|
||||||
self.base.Recognize(null) |
|
||||||
var everything = circularize(dump()) |
|
||||||
self.base.End(); |
|
||||||
self.module._free(ptr); |
|
||||||
cb(null, everything) |
|
||||||
}) |
|
||||||
} |
|
@ -1,42 +0,0 @@ |
|||||||
var path = require('path'); |
|
||||||
var webpack = require('webpack'); |
|
||||||
|
|
||||||
function config(opt) { |
|
||||||
return { |
|
||||||
devtool: 'cheap-module-eval-source-map', |
|
||||||
entry: opt.entry, |
|
||||||
output: Object.assign({}, opt.output, { |
|
||||||
path: path.join(__dirname, 'build'), |
|
||||||
publicPath: '/tesseract/', |
|
||||||
}), |
|
||||||
plugins: [ |
|
||||||
new webpack.NoErrorsPlugin() |
|
||||||
], |
|
||||||
module: { |
|
||||||
loaders: [{ |
|
||||||
test: /\.js$/, |
|
||||||
loaders: ['babel'], |
|
||||||
include: opt.include |
|
||||||
}] |
|
||||||
}, |
|
||||||
node: { |
|
||||||
fs: "empty" |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
module.exports = [{ |
|
||||||
entry: './src/browser/index.js', |
|
||||||
output: { |
|
||||||
filename: 'tesseract.js', |
|
||||||
library: "Tesseract", |
|
||||||
libraryTarget: "umd" |
|
||||||
}, |
|
||||||
include: [path.join(__dirname, 'src/browser'), path.join(__dirname, 'src/shared')] |
|
||||||
}, { |
|
||||||
entry: './src/worker/index.js', |
|
||||||
output: { |
|
||||||
filename: 'tesseract.worker.js', |
|
||||||
}, |
|
||||||
include: [path.join(__dirname, 'src/worker'), path.join(__dirname, 'src/shared')] |
|
||||||
}].map(config); |
|
@ -1,46 +0,0 @@ |
|||||||
var path = require('path'); |
|
||||||
var webpack = require('webpack'); |
|
||||||
|
|
||||||
function config({entry, output, include}) { |
|
||||||
return { |
|
||||||
entry, |
|
||||||
output: Object.assign({}, output, { |
|
||||||
path: path.join(__dirname, 'dist') |
|
||||||
}), |
|
||||||
plugins: [ |
|
||||||
new webpack.optimize.OccurenceOrderPlugin(), |
|
||||||
new webpack.optimize.DedupePlugin(), |
|
||||||
new webpack.optimize.UglifyJsPlugin({ |
|
||||||
compressor: { |
|
||||||
warnings: false |
|
||||||
} |
|
||||||
}) |
|
||||||
], |
|
||||||
module: { |
|
||||||
loaders: [{ |
|
||||||
test: /\.js$/, |
|
||||||
loaders: ['babel'], |
|
||||||
include |
|
||||||
}] |
|
||||||
}, |
|
||||||
node: { |
|
||||||
fs: "empty" |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
module.exports = [{ |
|
||||||
entry: './src/browser/index.js', |
|
||||||
output: { |
|
||||||
filename: 'tesseract.js', |
|
||||||
library: "Tesseract", |
|
||||||
libraryTarget: "umd" |
|
||||||
}, |
|
||||||
include: [path.join(__dirname, 'src/browser')] |
|
||||||
}, { |
|
||||||
entry: './src/worker/index.js', |
|
||||||
output: { |
|
||||||
filename: 'tesseract.worker.js', |
|
||||||
}, |
|
||||||
include: [path.join(__dirname, 'src/worker')] |
|
||||||
}].map(config); |
|
Loading…
Reference in new issue