├── .gitignore
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── assets
└── banner.jpg
├── examples
├── advanced.nim
├── simple.nim
└── test.png
├── nimtesseract.nimble
├── src
└── nimtesseract.nim
└── tests
├── config.nims
├── test.png
└── test_recognize.nim
/.gitignore:
--------------------------------------------------------------------------------
1 | **/*.traineddata
2 | **/*.py
3 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "nim.buildOnSave": false,
3 | "nim.buildCommand": "c",
4 | "nim.lintOnSave": true,
5 | "nim.project": ["./src/nimtesseract.nim"],
6 | "nim.licenseString": ""
7 | }
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Nim Tesseract 👑👁
2 |
3 | 
4 | Nim Tesseract is a Nim wrapper for the [Tesseract](https://github.com/tesseract-ocr/tesseract/) OCR library, via its dynamic library.
5 |
6 | ## Installation 👇
7 | ```bash
8 | $ nimble install nimtesseract
9 | ```
10 |
11 | ## Usage 🌷
12 | 1. Install (lib)tesseract via your package manager or put the tesseract so/dll/dylib file in the project directory
13 | E.g. for Arch Linux:
14 | ```bash
15 | $ pacman -Sy tesseract
16 | ```
17 |
18 | for macOS:
19 | ```bash
20 | $ brew install tesseract
21 | ```
22 |
23 | 2. Download trained data from https://github.com/tesseract-ocr/tessdata or https://github.com/tesseract-ocr/tessdata_fast
24 | 3. Done ✅
25 |
26 | ## Example 🤔
27 | ```nim
28 | import nimtesseract
29 |
30 | echo imageToText("file.png")
31 | ```
32 | More examples in the [examples folder](/examples)
33 |
34 | ## Development 🔩
35 | Download trained data and put it into src folder
36 |
37 | > ⚠️ Outdated, but still useful. Don't refer to this.
38 | > ```bash
39 | > $ cd src
40 | > $ TESSDATA_PREFIX=$(pwd) nim r -d:pixieUseStb nimtesseract.nim
41 | > ```
42 |
43 | Run tests with nimble:
44 | ```bash
45 | $ nimble test
46 | ```
47 |
48 | `capi.h` reference: https://github.com/tesseract-ocr/tesseract/blob/main/include/tesseract/capi.h
49 |
50 | ## Credits 👻
51 | Inspired from https://github.com/Altabeh/tesseract-ocr-wrapper
52 |
53 | ## License 📕
54 | This project is under the `Unlicense` license.
55 | This is free and unencumbered software released into the public domain.
56 |
--------------------------------------------------------------------------------
/assets/banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DavideGalilei/nimtesseract/a315d45e3ebb58214fbf07c49f58ea603de93e59/assets/banner.jpg
--------------------------------------------------------------------------------
/examples/advanced.nim:
--------------------------------------------------------------------------------
1 | import pixie
2 |
3 | import std/[os, json]
4 | import ../src/nimtesseract
5 |
6 | # We get a handle to the created Tesseract instance
7 | let handle = TessBaseAPICreate()
8 |
9 | # Initialize it
10 | let status = TessBaseAPIInit3(
11 | handle = handle,
12 | language = cstring("eng"),
13 | datapath = cstring(currentSourcePath() / "../../tests"),
14 | )
15 |
16 | # Check if things went wrong
17 | if int(status) == -1:
18 | raise newException(TesseractError, "Couldn't initialize tesseract")
19 |
20 | # Open the image with Pixie
21 | let imagePath = currentSourcePath.parentDir / "test.png"
22 | var image = readImage(imagePath)
23 |
24 | # Pixie stores images as a list of RGBA tuples, hence 4 bytes each
25 | const bytesPerPixel = 4
26 |
27 | # Set the image to recognize the text from
28 | TessBaseAPISetImage(
29 | handle = handle,
30 | imagedata = addr(image.data[0]), # Note the [0], from where the actual pixels start
31 | width = cint(image.width),
32 | height = cint(image.height),
33 | bytes_per_pixel = cint(bytesPerPixel),
34 | bytes_per_line = cint(image.width * bytesPerPixel),
35 | )
36 |
37 | # Set the PPI
38 | TessBaseAPISetSourceResolution(
39 | handle = handle,
40 | ppi = cint(70),
41 | )
42 |
43 | # Get the text. Note: since it's a cstring,
44 | # we need to convert it back to Nim's string
45 | # using cstring's `$` procedure.
46 | let text = $TessBaseAPIGetUTF8Text(handle = handle)
47 |
48 | # Don't forget to clean up to avoid memory leaks!
49 | TessBaseAPIDelete(handle = handle)
50 |
51 | echo "Recognized text: ", escapeJson(text)
52 | # Should look like "Noisy image\nto test\nTesseract OCR\n"
53 |
--------------------------------------------------------------------------------
/examples/simple.nim:
--------------------------------------------------------------------------------
1 | import std/[os, json]
2 | import ../src/nimtesseract
3 |
4 | # Make sure you downloaded eng.traineddata file
5 | let recognized = imageToText(
6 | "test.png",
7 | language = "eng",
8 | datapath = currentSourcePath() / "../../tests"
9 | )
10 | echo "Recognized text: ", escapeJson(recognized)
11 | # Should look like "Noisy image\nto test\nTesseract OCR\n"
12 |
--------------------------------------------------------------------------------
/examples/test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DavideGalilei/nimtesseract/a315d45e3ebb58214fbf07c49f58ea603de93e59/examples/test.png
--------------------------------------------------------------------------------
/nimtesseract.nimble:
--------------------------------------------------------------------------------
1 | # Package
2 | version = "0.2.0"
3 | author = "Davide Galilei"
4 | description = "A wrapper to Tesseract OCR library for Nim"
5 | license = "Unlicense"
6 | srcDir = "src"
7 |
8 | requires "pixie"
9 |
--------------------------------------------------------------------------------
/src/nimtesseract.nim:
--------------------------------------------------------------------------------
1 | import pixie
2 | import std/[os, json]
3 |
4 |
5 | const pattern* = "(|lib)tesseract(|.so|.dll|.dylib)"
6 |
7 | type
8 | TesseractError* = object of CatchableError
9 | TessBaseAPI* = pointer
10 |
11 | Tesseract* = object
12 | handle*: TessBaseAPI
13 |
14 |
15 | proc TessBaseAPICreate*(): TessBaseAPI {.importc, dynlib: pattern.}
16 | proc TessBaseAPIInit3*(handle: TessBaseAPI, datapath, language: cstring): cint {.importc, dynlib: pattern.}
17 | proc TessBaseAPIDelete*(handle: TessBaseAPI) {.importc, dynlib: pattern.}
18 | proc TessBaseAPISetVariable*(handle: TessBaseAPI, name, value: cstring): bool {.importc, dynlib: pattern.}
19 | proc TessBaseAPISetImage*(handle: TessBaseAPI, imagedata: pointer, width, height, bytes_per_pixel, bytes_per_line: cint) {.importc, dynlib: pattern.}
20 | proc TessBaseAPISetSourceResolution*(handle: TessBaseAPI, ppi: cint) {.importc, dynlib: pattern.}
21 | proc TessBaseAPIGetUTF8Text*(handle: TessBaseAPI): cstring {.importc, dynlib: pattern.}
22 | # proc TessBaseAPI*() {.importc, dynlib: pattern.}
23 |
24 |
25 | proc initTesseract*(language: string = "eng", datapath: string = ""): Tesseract =
26 | result.handle = TessBaseAPICreate()
27 |
28 | var
29 | language: cstring = cstring(language)
30 | datapath: cstring = cstring(datapath)
31 |
32 | let status = TessBaseAPIInit3(
33 | handle = result.handle,
34 | datapath = datapath,
35 | language = language,
36 | )
37 | if int(status) == -1:
38 | raise newException(TesseractError, "Couldn't initialize tesseract")
39 |
40 |
41 | # https://nim-lang.org/docs/destructors.html
42 | proc `=destroy`*(self: var Tesseract) =
43 | if not isNil(self.handle):
44 | TessBaseAPIDelete(self.handle)
45 | self.handle = nil
46 |
47 |
48 | proc delete*(self: Tesseract) =
49 | TessBaseAPIDelete(self.handle)
50 |
51 |
52 | proc setVariable*(self: Tesseract, name, value: string): bool =
53 | return TessBaseAPISetVariable(
54 | handle = self.handle,
55 | name = cstring(name),
56 | value = cstring(value),
57 | )
58 |
59 |
60 | proc setImage*(self: Tesseract, imagedata: pointer, width, height, bytesPerPixel: int, bytesPerLine: int = 0) =
61 | var
62 | # imagedata: cstring = cstring(imagedata)
63 | width: cint = cint(width)
64 | height: cint = cint(height)
65 | bytesPerPixel: cint = cint(bytesPerPixel)
66 |
67 | bytesPerLine: cint = if bytesPerLine != 0: cint(bytesPerLine)
68 | else: width * bytesPerPixel
69 |
70 | TessBaseAPISetImage(
71 | self.handle,
72 | imagedata,
73 | width,
74 | height,
75 | bytesPerPixel,
76 | bytesPerLine,
77 | )
78 |
79 |
80 | proc setSourceResolution(self: Tesseract, ppi: int) =
81 | TessBaseAPISetSourceResolution(self.handle, cint(ppi))
82 |
83 |
84 | proc getText*(self: Tesseract): string =
85 | return $TessBaseAPIGetUTF8Text(handle = self.handle)
86 |
87 |
88 | proc imageToText*(path: string, language: string = "eng", datapath: string = "", ppi: int = 70): string =
89 | if not fileExists(path):
90 | raise newException(TesseractError, "File not found: " & escapeJson(path))
91 |
92 | let
93 | tesseract = initTesseract(datapath = datapath, language = language)
94 | image = readImage(path)
95 |
96 | tesseract.setImage(
97 | imagedata = addr(image.data[0]), # https://github.com/Altabeh/tesseract-ocr-wrapper/blob/main/utils.py#L52
98 | image.width,
99 | image.height,
100 | 4 # Pixie uses RGBA every time, 4 bytes each
101 | )
102 | tesseract.setSourceResolution(ppi)
103 | # Fixes annoying warning: "Warning: Invalid resolution 0 dpi. Using 70 instead."
104 | # https://stackoverflow.com/a/58296472
105 |
106 | result = tesseract.getText()
107 | # tesseract.delete()
108 | # Automatically freed by destructor
109 | # Leaving delete() proc for convenience, just in case
110 |
--------------------------------------------------------------------------------
/tests/config.nims:
--------------------------------------------------------------------------------
1 | switch("path", "$projectDir/../src")
2 |
--------------------------------------------------------------------------------
/tests/test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DavideGalilei/nimtesseract/a315d45e3ebb58214fbf07c49f58ea603de93e59/tests/test.png
--------------------------------------------------------------------------------
/tests/test_recognize.nim:
--------------------------------------------------------------------------------
1 | import std/[os, unittest, strformat]
2 |
3 | import nimtesseract
4 |
5 | test "Recognize text":
6 | let datapath = currentSourcePath().parentDir / "eng.traineddata"
7 |
8 | if not fileExists(datapath):
9 | echo "Downloading eng data..."
10 | discard execShellCmd(fmt"wget -q -O {quoteShell(datapath)} https://github.com/tesseract-ocr/tessdata/raw/4767ea922bcc460e70b87b1d303ebdfed0897da8/eng.traineddata")
11 | echo "Finished downloading data"
12 |
13 | putEnv("TESSDATA_PREFIX", currentSourcePath.parentDir)
14 |
15 | var tess = initTesseract(
16 | language = "eng",
17 | # datapath = "",
18 | )
19 |
20 | # https://commons.wikimedia.org/wiki/File:Example_01.png
21 | check imageToText(currentSourcePath.parentDir / "test.png") == "Noisy image\nto test\nTesseract OCR\n"
22 |
--------------------------------------------------------------------------------