BetterOCR
๐ Better text detection by combining multiple OCR engines with ๐ง LLM.
OCR still sucks! ... Especially when you're from the other side of the world (and face a significant lack of training data in your language) โ or just not thrilled with noisy results.
BetterOCR combines results from multiple OCR engines with an LLM to correct & reconstruct the output.
๐ OCR Engines
Currently supports EasyOCR (JaidedAI), Tesseract (Google), and Pororo (KakaoBrain).
- For Pororo, we're using the code from https://github.com/black7375/korean_ocr_using_pororo
(Pre-processing โก๏ธ Text detection with EasyOCR โก๏ธ Text recognition with BrainOCR (Pororo's OCR module)). - Pororo is used only if the language options (
lang
) specified include either ๐บ๐ธ English (en
) or ๐ฐ๐ท Korean (ko
). Also additional dependencies listed in[tool.poetry.group.pororo.dependencies]
must be available. (If not, it'll automatically be excluded from enabled engines.)
๐ง LLM
Supports Chat models from OpenAI.
๐ Custom Context
Allows users to provide an optional context to use specific keywords such as proper nouns and product names. This assists in spelling correction and noise identification, ensuring accuracy even with rare or unconventional words.
๐ข๏ธ Resources
- Head over to ๐ฏ Examples to view performace by languages (๐บ๐ธ, ๐ฐ๐ท, ๐ฎ๐ณ).
- Coming Soon:
box detection๐งชโ , improved interface ๐ง, async support, and more. Contributions are welcomed.
Warning
This package is under rapid development ๐
Architecture
๐ Usage (WIP)
pip install betterocr
# pip3 install betterocr
import betterocr
# text detection
text = betterocr.detect_text(
"demo.png",
["ko", "en"], # language codes (from EasyOCR)
context="", # (optional) context
tesseract={
# Tesseract options here
"config": "--tessdata-dir ./tessdata"
},
openai={
# OpenAI options here
# `os.environ["OPENAI_API_KEY"]` is used by default
"API_KEY": "sk-xxxxxxx",
# rest are used to pass params to `client.chat.completions.create`
# `{"model": "gpt-4"}` by default
"model": "gpt-3.5-turbo",
},
)
print(text)
๐ฆ Box Detection
Original | Detected |
---|---|
Example Script: https://github.com/junhoyeo/BetterOCR/blob/main/examples/detect_boxes.py (Uses OpenCV and Matplotlib to draw rectangles)
import betterocr
image_path = ".github/images/demo-1.png"
items = betterocr.detect_boxes(
image_path,
["ko", "en"],
context="ํผ๋ฉํ
์ด์
ํฉํ์ธ ์์ด์ผ์ด ํฌ๋ฆผ", # product name
tesseract={
"config": "--psm 6 --tessdata-dir ./tessdata -c tessedit_create_boxfile=1"
},
)
print(items)
View Output
[
{'text': 'JUST FOR YOU', 'box': [[543, 87], [1013, 87], [1013, 151], [543, 151]]},
{'text': '์ด๋ฐ ๋ถ๋ค๊ป ์ถ์ฒ๋๋ฆฌ๋ ํผ๋ฉํ
์ด์
ํฉํ์ธ ์์ด์ผ์ด ํฌ๋ฆผ', 'box': [[240, 171], [1309, 171], [1309, 224], [240, 224]]},
{'text': '๋งค์ผ๋งค์ผ ์งํด์ง๋ ๋คํฌ์ํด์ ๊ฐ์ ํ๊ณ ์ถ๋ค๋ฉด', 'box': [[123, 345], [1166, 345], [1166, 396], [123, 396]]},
{'text': '์ถ์ถ ์ฒ์ง๋ ํผ๋ถ๋ฅผ ํ๋ ฅ ์๊ฒ ๋ฐ๊พธ๊ณ ์ถ๋ค๋ฉด', 'box': [[125, 409], [1242, 409], [1242, 470], [125, 470]]},
{'text': '๋๋ ์ด ๋์ด๊ฐ๋ ๋๊ฐ ์ฃผ๋ฆ์ ์ํํ๊ณ ์ถ๋ค๋ฉด', 'box': [[123, 479], [1112, 479], [1112, 553], [123, 553]]},
{'text': 'FERMENATION', 'box': [[1216, 578], [1326, 578], [1326, 588], [1216, 588]]},
{'text': '๋ฏผ๊ฐ์ฑ ํผ๋ถ์๋ ์ฌ์ฉํ ์ ์๋ ์์ดํฌ๋ฆผ์ ์ฐพ๋๋ค๋ฉด', 'box': [[134, 534], [1071, 534], [1071, 618], [134, 618]]},
{'text': '์๊ณ ์๋ฏผํ ๋๊ฐ ์ฃผ๋ณ ํผ๋ถ๋ฅผ ๊ด๋ฆฌํ๊ณ ์ถ๋ค๋ฉด', 'box': [[173, 634], [1098, 634], [1098, 690], [173, 690]]}
]
๐ฏ Examples
Note
Results may vary due to inherent variability and potential future updates to OCR engines or the OpenAI API.
Example 1 (English with Noise)
Source | Text |
---|---|
EasyOCR | CHAINSAWMANChapter 109:The Easy Way to Stop Bullying~BV-THTSUKIFUUIMUTU ETT |
Tesseract | A\ ira | LT ge a TE ay NS\nye SE F Pa Ce YI AIG 44\nopr See aC\n; a) Ny 7S =u |\n_ F2 SENN\n\ ZR\n3 ~ 1 A \ Ws โโ โs 7 โA\n=) 24 4 = rt fl /1\nยฃ72 7 a NS dA Chapter 109:77/ ยข 4\nZz % = ~ oes os | \STheEasf Way.to Stop Bullying:\nยฉ Wa) ROT\n\n |
Pororo | CHAINSAWNAN\nChapter 109\nThe Easy Way.to Stop Bullying.\nCBY=TATSUKI FUJIMDTO |
LLM | ๐ค GPT-3.5 |
Result | CHAINSAW MAN\n\nChapter 109: The Easy Way to Stop Bullying\n\nBY: TATSUKI FUJIMOTO |
Example 2 (Korean+English)
Source | Text |
---|---|
EasyOCR | JUST FOR YOU์ด๋ฐ ๋ถ๋ค๊ป ์ถ์ฒ๋๋ฆฌ๋ ํผ๋ฉํ
์ด์ ํฌํ์ธ ์์ด์ผ์ด ํฌ๋ฆผ๋งค์ผ๋งค์ผ ์งํด์ง๋
ผ ๋คํฌ์ํด์ฌ ๊ฐ์ ํ๊ณ ์ถ๋ค๋ฉด์ถ์ถ ์ฒ์ง๋
ผ ํผ๋ถ๋ฆ ํ๋ ฅ ์๊ฒ ๋ฐ๊พธ๊ณ ์ถ๋ค๋ฉด๋๋ ์ด ๋์ด๊ฐ๋ ๋๊ฐ ์ฃผ๋ฆ์ฌ ์ํํ๊ณ ์ถ๋ค๋ฉดFERMENATION๋ฏผ๊ฐ์ฑ ํผ๋ถ์๋ ์ฌ์ฉํ ์์๋ ์์ดํฌ๋ฆผ์ฌ ์ฐพ๋๋ค๋ฉด์๊ณ ์๋ฏผํ ๋๊ฐ ์ฃผ๋ณ ํผ๋ถ๋ฆ ๊ด๋ฆฌํ๊ณ ์ถ๋ค๋ฉด |
Tesseract | 9051 508 \ใ
4\n์ด๋ฐ ๋ถ๋ค๊ป ์ถ์ฒ๋๋ฆฌ๋ ํผ๋ฉํ
์ด์
ํ์ธ ์์ด์ผ์ด ํฌ๋ฆผ\n.๋งค์ผ๋งค์ผ ์งํด์ง๋ ๋คํฌ์ํด์ ๊ฐ์ ํ๊ณ ์ถ๋ค๋ฉด "๋\nใ์ถ์ถ ์ฒ์ง๋ ํผ๋ถ๋ฅผ ํ๋ ฅ ์๊ฒ ๋ฐ๊พธ๊ณ ์ถ๋ค๋ฉด 7\nใ๋๋ ์ด ๋์ด๊ฐ๋ ๋๊ฐ ์ฃผ๋ฆ์ ์ํํ๊ณ ์ถ๋ค๋ฉด /\n-๋ฏผ๊ฐ์ฑ ํผ๋ถ์๋ ์ฌ์ฉํ ์ ์๋ ์์ดํฌ๋ฆผ์ ์ฐพ๋๋ค๋ฉด (ํ\nใ์๊ณ ์๋ฏผํ ๋๊ฐ ์ฃผ๋ณ ํผ๋ถ๋ฅผ ๊ด๋ฆฌํ๊ณ ์ถ๋ค๋ฉด ๋ฐธ\n\n |
Pororo | JUST FOR YOU\n์ด๋ฐ ๋ถ๋ค๊ป ์ถ์ฒ๋๋ฆฌ๋ ํผ๋งจํ
์ด์
ํฉํ์ธ ์์ด์ผ์ด ํฌ๋ฆผ\n๋งค์ผ๋งค์ผ ์งํด์ง๋ ๋คํฌ์ํด์ ๊ฐ์ ํ๊ณ ์ถ๋ค๋ฉด\n์ด์ด ์ฒ์ง๋ ํผ๋ถ๋ฅผ ํ๋ ฅ ์๊ฒ ๋ฐ๊พธ๊ณ ์ถ๋ค๋ฉด\n๋๋ ์ด ๋์ด๊ฐ๋ ๋๊ฐ ์ฃผ๋ฆ์ ์ํํ๊ณ ์ถ๋ค๋ฉด\nFERMENTATIOM\n๋ฏผ๊ฐ์ฑ ํผ๋ถ์๋ ์ฌ์ฉํ ์ ์๋ ์์ดํฌ๋ฆผ์ ์ฐพ๋๋ค๋ฉด\n์๊ณ ์๋ฏผํ ๋๊ฐ ์ฃผ๋ณ ํผ๋ถ๋ฅผ ๊ด๋ฆฌํ๊ณ ์ถ๋ค๋ฉด |
LLM | ๐ค GPT-3.5 |
Result | JUST FOR YOU\n์ด๋ฐ ๋ถ๋ค๊ป ์ถ์ฒ๋๋ฆฌ๋ ํผ๋ฉํ
์ด์
ํฉํ์ธ ์์ด์ผ์ด ํฌ๋ฆผ\n๋งค์ผ๋งค์ผ ์งํด์ง๋ ๋คํฌ์ํด์ ๊ฐ์ ํ๊ณ ์ถ๋ค๋ฉด\n์ถ์ถ ์ฒ์ง๋ ํผ๋ถ๋ฅผ ํ๋ ฅ ์๊ฒ ๋ฐ๊พธ๊ณ ์ถ๋ค๋ฉด\n๋๋ ์ด ๋์ด๊ฐ๋ ๋๊ฐ ์ฃผ๋ฆ์ ์ํํ๊ณ ์ถ๋ค๋ฉด\nFERMENTATION\n๋ฏผ๊ฐ์ฑ ํผ๋ถ์๋ ์ฌ์ฉํ ์ ์๋ ์์ดํฌ๋ฆผ์ ์ฐพ๋๋ค๋ฉด\n์๊ณ ์๋ฏผํ ๋๊ฐ ์ฃผ๋ณ ํผ๋ถ๋ฅผ ๊ด๋ฆฌํ๊ณ ์ถ๋ค๋ฉด |
context
)
Example 3 (Korean with custom
Source | Text |
---|---|
EasyOCR | ๋ฐ์ด์คํจ๋ณด#์ธ๋ก๋ชจ๊ณต์กด์กด์ธ๋ผ6๊ธ๋ก์ฐํฝ ์ค๋ฌธ๋จ 100์ธ์ด๊ผผ๊ผผํ๊ฒ ํ๊ฐํ์ด์"#๋์ ํ๋งค์ก 40์ต#์ ํ๋ง์กฑ๋ 1009 |
Tesseract | ๋ฐ์ด์คํ๋ณด\n#์ธ๋ก๋ชจ๊ณตํฐํฐ์ธ๋ผ\nโ๊ธ๋ก ์ผํผ ์๋ฌด๋ค 1 00์ธ์ด\n๊ผผ๊ผผํ๊ฒํ๊ฐํ์ด์โ\n\n |
Pororo | ๋ฐ์ด์คํ๋ณด\n#์ธ๋ก๋ชจ๊ณต์ซ์ซ์ธ๋ผ\n'.\n'๊ธ๋ก์ฐํฝ ์ค๋ฌธ๋จ 100์ธ์ด\n๊ผผ๊ผผํ๊ฒ ํ๊ฐํ์ด์'"\n#๋์ ํ๋งค์ก 40์ต\n# ์ ํ ๋ง์กฑ๋ 100% |
Context | [๋ฐ์ด์คํ๋ณด] ์ธ๋ก๋ชจ๊ณต์ซ์ซ์ธ๋ผ์ผ๋ก ์ฝ๋ผ๊ฒ ํ์ดํธ๋! (6S) |
LLM | ๐ค GPT-4 |
Result | ๋ฐ์ด์คํ๋ณด\n#์ธ๋ก๋ชจ๊ณต์ซ์ซ์ธ๋ผ\n๊ธ๋ก์ฐํฝ ์ค๋ฌธ๋จ 100์ธ์ด ๊ผผ๊ผผํ๊ฒ ํ๊ฐํ์ด์\n#๋์ ํ๋งค์ก 40์ต\n#์ ํ ๋ง์กฑ๋ 100% |
๐ง LLM Reasoning (*Old)
Based on the given OCR results and the context, here is the combined and corrected result:
{
"data": "๋ฐ์ด์คํ๋ณด\n#์ธ๋ก๋ชจ๊ณต์ซ์ซ์ธ๋ผ\n๊ธ๋ก์ฐํฝ ์ค๋ฌธ๋จ 100์ธ์ด ๊ผผ๊ผผํ๊ฒ ํ๊ฐํ์ด์\n#๋์ ํ๋งค์ก 40์ต\n#์ ํ๋ง์กฑ๋ 100%"
}
๋ฐ์ด์คํ๋ณด
is the correct brand name, taken from [1] and the context.#์ธ๋ก๋ชจ๊ณต์ซ์ซ์ธ๋ผ
seems to be the product name and is derived from the context.๊ธ๋ก์ฐํฝ ์ค๋ฌธ๋จ 100์ธ์ด ๊ผผ๊ผผํ๊ฒ ํ๊ฐํ์ด์
is extracted and corrected from both OCR results.#๋์ ํ๋งค์ก 40์ต
is taken from [0].#์ ํ๋ง์กฑ๋ 100%
is corrected from [0].
Example 4 (Hindi)
Source | Text |
---|---|
EasyOCR | `เฅญเคจเคตเคญเคพเคฐเคคเคเคพเคเคฎเฅเคธเคคเฅเคเฅเคฏเฅ เคเคฒเคฟเคเคชเคฟเค เคเฅ เคฒเคฟเค เคญเคพเคฐเคคเฅเคฏ เคฆเคฒเคเคพ เคฅเฅเคฎ เคธเฅเคจเฅเค เคฒเฅเคจเฅเค เคเคฐ เคฆเคฟเคฏเคพ เคเคฏเคพเคฌเฅเคงเคตเคพเคฐ เคเฅ เคเคธ เคธเฅเคจเฅเค เคเฅ เคเคฟเคฏเคพ เคเคฏเคพ เคฒเฅเคจเฅเคเคธเคฟเคเคเคฐ เคฎเฅเคนเคฟเคค เคเฅเคนเคพเคจ เคจเฅ เคฆเฅ เคนเฅ เคเคตเคพเค7เคฒเคเฅเคฒ เคฎเคเคคเฅเคฐเฅ เคเคฟเคฐเคฃ เคฐเคฟเคเคฟเคเฅ เคจเฅ เคเฅเคตเคฟเคเคฐ เคชเคฐ เคถเฅเคฏเคฐเคเคฟเคฏเคพ เคฅเฅเคฎ เคธเฅเคจเฅเค เคเคพ เคตเฅเคกเคฟเคฏเฅ0เคฌเฅช0 เฅจเฅฆเฅจเฅฆเคเฅเคค เคเคพ เคจเคพเคฎ- 'เฅเคฒเคเฅเคทเฅเคฏ เคคเฅเคฐเคพ เคธเคพเคฎเคจเฅ เคนเฅ' , เคเฅเคฒเคฎเคเคคเฅเคฐเฅ เคจเฅ เฅซเฅญ เคธเฅเคเคเคก เคเคพ เคตเฅเคกเคฟเคฏเฅ เคเคฟเคฏเคพ เคถเฅเคฏเคฐ |
Tesseract | '8เคพ.\nเคจเคตเคญเฅเคฐเคค เคเฅเคเคฎเฅเคธ\n\nเคคเฅเคเฅเคฏเฅ เคเคฒเคฟเคเคชเคฟเค เคเฅ เคฒเคฟเคเค เคญเคพเคฐเคคเฅเคฏ เคฆเคฒ\n\nเคเคพ เคฅเฅเคฎ เคธเฅเคจเฅเค เคฒเฅเคจเฅเค เคเคฐ เคฆเคฟเคฏเคพ เคเคฏเคพ\n\nเคฌเฅเคงเคตเคพเคฐ เคเฅ เคนเคธ เคธเฅเคจเฅเค เคเฅ เคเคฟเคฏเคพ เคเคฏเคพ เคฒเฅเคจเฅเค\nเคธเคฟเคเคเคฐ เคฎเฅเคนเคฟเคค เคเฅเคนเคพเคจ เคจเฅ เคฆเฅ เคนเฅ เคเคตเคพเค\n\nเคเฅเคฒ เคฎเคเคคเฅเคฐเฅ เคเคฟเคฐเคฃ เคฐเคฟเคเคฟเคเฅ เคจเฅ เคฆเฅเคตเคฟเคเคฐ เคชเคฐ เคถเฅเคฏเคฐ\nเคเคฟเคฏเคพ เคฅเฅเคฎ เคธเฅเคจเฅเค เคเคพ เคตเฅเคกเคฟเคฏเฅ\n\nเคชเฅ 0 (เฅฏ เคนเฅ 0 2 0 2 0 เคเฅเคค เคเคพ เคจเคพเคฎ- 'เคฒเคเฅเคทเฅเคฏ เคคเฅเคฐเคพ เคธเคพเคฎเคจเฅ เคนเฅ', เคเฅเคฒ\n\n(2 (9เฅฏ) เคฎเคเคคเฅเคฐเฅ เคจเฅ 57 เคธเฅเคเคเคก เคเคพ เคตเฅเคกเคฟเคฏเฅ เคเคฟเคฏเคพ เคถเฅเคฏเคฐ\n\n |
LLM | ๐ค GPT-4 |
Result | เคจเคตเคญเคพเคฐเคค เคเคพเคเคฎเฅเคธ\nเคคเฅเคเฅเคฏเฅ เคเคฒเคฟเคเคชเคฟเค เคเฅ เคฒเคฟเค เคญเคพเคฐเคคเฅเคฏ เคฆเคฒ เคเคพ เคฅเฅเคฎ เคธเฅเคจเฅเค เคฒเฅเคจเฅเค เคเคฐ เคฆเคฟเคฏเคพ เคเคฏเคพ\nเคฌเฅเคงเคตเคพเคฐ เคเฅ เคเคธ เคธเฅเคจเฅเค เคเฅ เคเคฟเคฏเคพ เคเคฏเคพ เคฒเฅเคจเฅเค\nเคธเคฟเคเคเคฐ เคฎเฅเคนเคฟเคค เคเฅเคนเคพเคจ เคจเฅ เคฆเฅ เคนเฅ เคเคตเคพเค\n\nเคเฅเคฒ เคฎเคเคคเฅเคฐเฅ เคเคฟเคฐเคฃ เคฐเคฟเคเคฟเคเฅ เคจเฅ เคเฅเคตเคฟเคเคฐ เคชเคฐ เคถเฅเคฏเคฐ เคเคฟเคฏเคพ เคฅเฅเคฎ เคธเฅเคจเฅเค เคเคพ เคตเฅเคกเคฟเคฏเฅ\n2020 เคเฅเคค เคเคพ เคจเคพเคฎ- 'เคฒเคเฅเคทเฅเคฏ เคคเฅเคฐเคพ เคธเคพเคฎเคจเฅ เคนเฅ', เคเฅเคฒ เคฎเคเคคเฅเคฐเฅ เคจเฅ 57 เคธเฅเคเคเคก เคเคพ เคตเฅเคกเคฟเคฏเฅ เคเคฟเคฏเคพ เคถเฅเคฏเคฐ |
License (Starmie!)
MIT ยฉ Junho Yeo
If you find this project interesting, please consider giving it a star(โญ) and following me on GitHub. I code 24/7 and ship mind-breaking things on a regular basis, so your support definitely won't be in vain!