commit
19743356f1
10 changed files with 3627 additions and 0 deletions
@ -0,0 +1,59 @@ |
|||||
|
# Logs |
||||
|
logs |
||||
|
*.log |
||||
|
npm-debug.log* |
||||
|
|
||||
|
# Runtime data |
||||
|
pids |
||||
|
*.pid |
||||
|
*.seed |
||||
|
|
||||
|
# Directory for instrumented libs generated by jscoverage/JSCover |
||||
|
lib-cov |
||||
|
|
||||
|
# Coverage directory used by tools like istanbul |
||||
|
coverage |
||||
|
|
||||
|
# nyc test coverage |
||||
|
.nyc_output |
||||
|
|
||||
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) |
||||
|
.grunt |
||||
|
|
||||
|
# node-waf configuration |
||||
|
.lock-wscript |
||||
|
|
||||
|
# Compiled binary addons (http://nodejs.org/api/addons.html) |
||||
|
build/Release |
||||
|
|
||||
|
# Dependency directories |
||||
|
node_modules |
||||
|
jspm_packages |
||||
|
|
||||
|
# Optional npm cache directory |
||||
|
.npm |
||||
|
|
||||
|
# Optional REPL history |
||||
|
.node_repl_history |
||||
|
|
||||
|
# 0x |
||||
|
profile-* |
||||
|
|
||||
|
# mac files |
||||
|
.DS_Store |
||||
|
|
||||
|
# vim swap files |
||||
|
*.swp |
||||
|
|
||||
|
# webstorm |
||||
|
.idea |
||||
|
|
||||
|
# vscode |
||||
|
.vscode |
||||
|
*code-workspace |
||||
|
|
||||
|
# clinic |
||||
|
profile* |
||||
|
*clinic* |
||||
|
*flamegraph* |
||||
|
.bash_history |
@ -0,0 +1,24 @@ |
|||||
|
'use strict' |
||||
|
|
||||
|
const path = require('path') |
||||
|
const AutoLoad = require('fastify-autoload') |
||||
|
|
||||
|
|
||||
|
module.exports = async function (fastify, opts) { |
||||
|
|
||||
|
fastify.register(require('fastify-static'), { |
||||
|
root: path.join(__dirname, 'public'), |
||||
|
prefix: '/', // optional: default '/'
|
||||
|
}) |
||||
|
// Place here your custom code!
|
||||
|
|
||||
|
// Do not touch the following lines
|
||||
|
|
||||
|
|
||||
|
// This loads all plugins defined in routes
|
||||
|
// define your routes in one of these
|
||||
|
fastify.register(AutoLoad, { |
||||
|
dir: path.join(__dirname, 'routes'), |
||||
|
options: Object.assign({}, opts) |
||||
|
}) |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
{ |
||||
|
"name": "ocrapp", |
||||
|
"version": "1.0.0", |
||||
|
"description": "", |
||||
|
"main": "app.js", |
||||
|
"directories": { |
||||
|
"test": "test" |
||||
|
}, |
||||
|
"scripts": { |
||||
|
"test": "tap \"test/**/*.test.js\"", |
||||
|
"start": "fastify start -l info app.js", |
||||
|
"dev": "fastify start -w -l info -P app.js" |
||||
|
}, |
||||
|
"keywords": [], |
||||
|
"author": "", |
||||
|
"license": "ISC", |
||||
|
"dependencies": { |
||||
|
"fastify": "^3.0.0", |
||||
|
"fastify-autoload": "^3.3.1", |
||||
|
"fastify-cli": "^2.13.0", |
||||
|
"fastify-multipart": "^5.0.0", |
||||
|
"fastify-plugin": "^3.0.0", |
||||
|
"fastify-sensible": "^3.1.0", |
||||
|
"fastify-static": "^4.2.3", |
||||
|
"mime-types": "^2.1.32" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"tap": "^15.0.9" |
||||
|
} |
||||
|
} |
@ -0,0 +1,90 @@ |
|||||
|
/* |
||||
|
* ഓം ബ്രഹ്മാർപ്പണം |
||||
|
* app.js |
||||
|
* Created: Mon Mar 09 2020 02:16:15 GMT+0530 (GMT+05:30) |
||||
|
* Copyright 2020 Harish.K<harish2704@gmail.com> |
||||
|
*/ |
||||
|
|
||||
|
function ocrByServer(image) { |
||||
|
var data = new FormData(); |
||||
|
data.append("image", image); |
||||
|
return fetch( |
||||
|
"/ocr", |
||||
|
{ |
||||
|
method: "POST", |
||||
|
body: data, |
||||
|
} |
||||
|
).then((res) => res.json()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
new Vue({ |
||||
|
el: "#main", |
||||
|
components: { |
||||
|
}, |
||||
|
data: { |
||||
|
scalingFactor: 1, |
||||
|
selectedFileName: "", |
||||
|
currentFileBlob: null, |
||||
|
isLoading: false, |
||||
|
ocrOutput: "", |
||||
|
}, |
||||
|
methods: { |
||||
|
doOcr: function () { |
||||
|
this.isLoading = true; |
||||
|
ocrByServer(this.currentFileBlob) |
||||
|
.then((data) => { |
||||
|
console.log("success", data); |
||||
|
this.ocrOutput = data.text; |
||||
|
this.isLoading = false; |
||||
|
}) |
||||
|
.catch((error) => { |
||||
|
console.log("error", error); |
||||
|
alert("Some error occurred while Calling OCR API"); |
||||
|
this.isLoading = false; |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
setImage: function (file) { |
||||
|
var self = this; |
||||
|
var canvas = this.$refs.canvas; |
||||
|
var ctx = canvas.getContext("2d"); |
||||
|
var img = new Image(); |
||||
|
this.currentFileBlob = file; |
||||
|
img.onload = function () { |
||||
|
canvas.width = img.width; |
||||
|
canvas.height = img.height; |
||||
|
ctx.drawImage(img, 0, 0); |
||||
|
self.scalingFactor = canvas.clientWidth / canvas.width; |
||||
|
}; |
||||
|
img.src = URL.createObjectURL(file); |
||||
|
}, |
||||
|
|
||||
|
onChangeFile: function File(e) { |
||||
|
this.setImage(e.target.files[0]); |
||||
|
this.selectedFileName = e.target.files[0].name; |
||||
|
}, |
||||
|
}, |
||||
|
mounted: function () { |
||||
|
var self = this; |
||||
|
fetch("./sample.png") |
||||
|
.then((v) => v.blob()) |
||||
|
.then((blob) => this.setImage(blob)); |
||||
|
document.onpaste = function (event) { |
||||
|
var items = (event.clipboardData || event.originalEvent.clipboardData) |
||||
|
.items; |
||||
|
for (var index in items) { |
||||
|
var item = items[index]; |
||||
|
if (item.kind === "file") { |
||||
|
var blob = item.getAsFile(); |
||||
|
self.setImage(blob); |
||||
|
self.ocrOutput = ""; |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
window.addEventListener("resize", function () { |
||||
|
var canvas = self.$refs.canvas; |
||||
|
self.scalingFactor = canvas.clientWidth / canvas.width; |
||||
|
}); |
||||
|
}, |
||||
|
}); |
@ -0,0 +1,114 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html> |
||||
|
<head> |
||||
|
<meta charset="utf-8" /> |
||||
|
<title>Tesseract OCR Malayalam demo</title> |
||||
|
<link |
||||
|
rel="stylesheet" |
||||
|
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" |
||||
|
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" |
||||
|
crossorigin="anonymous" |
||||
|
/> |
||||
|
<style type="text/css" media="all"> |
||||
|
.btn .spinner-border, |
||||
|
.btn .txt-loading, |
||||
|
.btn[disabled] .txt-button { |
||||
|
display: none; |
||||
|
} |
||||
|
.btn[disabled] .spinner-border, |
||||
|
.btn[disabled] .txt-loading, |
||||
|
.btn .txt-button { |
||||
|
display: inline-block; |
||||
|
} |
||||
|
</style> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div class="container-fluid" id="main"> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-12"> |
||||
|
<h2>Tesseract OCR Malayalam</h2> |
||||
|
<h4 class="text-danger">Please note</h4> |
||||
|
<ol> |
||||
|
<li>It can detect both Malayalam & English script</li> |
||||
|
<li>Pasting image from clip board is supported</li> |
||||
|
</ol> |
||||
|
</div> |
||||
|
|
||||
|
<div class="col-md-12"> |
||||
|
<div class="col-md-6 col-sm-12"> |
||||
|
<div class="form-group"> |
||||
|
<input |
||||
|
type="file" |
||||
|
accept="image/*" |
||||
|
style="visibility: hidden; height: 0" |
||||
|
@change="onChangeFile($event)" |
||||
|
ref="file_input" |
||||
|
/> |
||||
|
<div class="input-group input-file"> |
||||
|
<input |
||||
|
type="text" |
||||
|
readonly |
||||
|
v-model="selectedFileName" |
||||
|
class="form-control" |
||||
|
placeholder="Choose a file..." |
||||
|
@click="$refs.file_input.click()" |
||||
|
/> |
||||
|
<div class="input-group-append"> |
||||
|
<button |
||||
|
class="btn btn-primary btn-choose" |
||||
|
type="button" |
||||
|
@click="$refs.file_input.click()" |
||||
|
> |
||||
|
Choose another image |
||||
|
</button> |
||||
|
<button |
||||
|
class="btn btn-success pull-right" |
||||
|
@click="doOcr()" |
||||
|
:disabled="isLoading" |
||||
|
> |
||||
|
<span |
||||
|
class="spinner-border spinner-border-sm" |
||||
|
role="status" |
||||
|
aria-hidden="true" |
||||
|
></span> |
||||
|
<span class="txt-loading">Loading...</span> |
||||
|
<span class="txt-button">Run OCR</span> |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="row w-100"> |
||||
|
<div class="col-md-6 col-sm-12"> |
||||
|
<div> |
||||
|
<canvas |
||||
|
ref="canvas" |
||||
|
class="w-100" |
||||
|
alt="Selected image" |
||||
|
@resize="onCanvasResize($event)" |
||||
|
></canvas> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-md-6 col-sm-12"> |
||||
|
<div class="card"> |
||||
|
<div class="card-body"> |
||||
|
<h5 class="card-title text-primary">OCR Output</h5> |
||||
|
<div id="output"> |
||||
|
<textarea |
||||
|
class="form-control" |
||||
|
rows="15" |
||||
|
v-model="ocrOutput" |
||||
|
></textarea> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script> |
||||
|
<script src="./app.js" type="text/javascript" charset="utf-8"></script> |
||||
|
</body> |
||||
|
</html> |
After Width: | Height: | Size: 108 KiB |
@ -0,0 +1,27 @@ |
|||||
|
# Routes Folder |
||||
|
|
||||
|
Routes define routes within your application. Fastify provides an |
||||
|
easy path to a microservice architecture, in the future you might want |
||||
|
to independently deploy some of those. |
||||
|
|
||||
|
In this folder you should define all the routes that define the endpoints |
||||
|
of your web application. |
||||
|
Each service is a [Fastify |
||||
|
plugin](https://www.fastify.io/docs/latest/Plugins/), it is |
||||
|
encapsulated (it can have its own independent plugins) and it is |
||||
|
typically stored in a file; be careful to group your routes logically, |
||||
|
e.g. all `/users` routes in a `users.js` file. We have added |
||||
|
a `root.js` file for you with a '/' root added. |
||||
|
|
||||
|
If a single file become too large, create a folder and add a `index.js` file there: |
||||
|
this file must be a Fastify plugin, and it will be loaded automatically |
||||
|
by the application. You can now add as many files as you want inside that folder. |
||||
|
In this way you can create complex routes within a single monolith, |
||||
|
and eventually extract them. |
||||
|
|
||||
|
If you need to share functionality between routes, place that |
||||
|
functionality into the `plugins` folder, and share it via |
||||
|
[decorators](https://www.fastify.io/docs/latest/Decorators/). |
||||
|
|
||||
|
If you're a bit confused about using `async/await` to write routes, you would |
||||
|
better take a look at [Promise resolution](https://www.fastify.io/docs/latest/Routes/#promise-resolution) for more details. |
@ -0,0 +1,30 @@ |
|||||
|
"use strict"; |
||||
|
|
||||
|
const fs = require("fs"); |
||||
|
const util = require("util"); |
||||
|
const path = require("path"); |
||||
|
const { pipeline } = require("stream"); |
||||
|
const pump = util.promisify(pipeline); |
||||
|
const crypto = require("crypto"); |
||||
|
const mime = require("mime-types"); |
||||
|
const exec = util.promisify(require("child_process").execFile); |
||||
|
|
||||
|
const UPLOAD_PATH = "./uploads"; |
||||
|
|
||||
|
module.exports = async function (fastify, opts) { |
||||
|
fastify.register(require("fastify-multipart")); |
||||
|
fastify.post("/ocr", async function (req, reply) { |
||||
|
const data = await req.file(); |
||||
|
const uid = crypto.randomBytes(16).toString("hex"); |
||||
|
const filename = `${UPLOAD_PATH}/${uid}.${mime.extension(data.mimetype)}`; |
||||
|
const lang = "mal+eng"; |
||||
|
await pump(data.file, fs.createWriteStream(filename)); |
||||
|
const args = [filename, "stdout", "-l", lang]; |
||||
|
const { stdout, stderr } = await exec("tesseract", args); |
||||
|
exec("rm", ["-f", filename]); |
||||
|
return { |
||||
|
text: stdout, |
||||
|
error: stderr, |
||||
|
}; |
||||
|
}); |
||||
|
}; |
@ -0,0 +1 @@ |
|||||
|
../uploads |
File diff suppressed because it is too large
Loading…
Reference in new issue