From 85ad9cf1bb34f51af38cb303ed2bd6932c1620db Mon Sep 17 00:00:00 2001 From: Charles Poitras Date: Fri, 21 May 2021 00:22:54 -0400 Subject: [PATCH] Simpler install (#16) * Cleanup tesseract files, no more need to install tess * Update readme+add ico * correction creation * icon tweaking --- README.md | 39 ++-- media/icon.ico | Bin 0 -> 4286 bytes scripts/build_release.bat | 7 - skill_corrections.csv | 11 ++ src/charm_extraction.py | 34 ++-- .../Tesseract.py} | 91 +--------- src/tesseract/TesseractError.py | 3 + src/tesseract/tesseract_utils.py | 167 ++++++++++++++++++ src/utils.py | 5 +- utsushis-charm.spec | 4 +- 10 files changed, 243 insertions(+), 118 deletions(-) create mode 100644 media/icon.ico rename src/{manual_tesseract_bindings.py => tesseract/Tesseract.py} (64%) create mode 100644 src/tesseract/TesseractError.py create mode 100644 src/tesseract/tesseract_utils.py diff --git a/README.md b/README.md index 22dc57e..423ccb1 100644 --- a/README.md +++ b/README.md @@ -14,20 +14,13 @@ It's called Utsushi's charm because I thought it would be funny to make a comple ## Requirements - A computer (Windows) - - Linux and Mac might work too, you wont be able to run the EXE and will have to run from source in a terminal window. + - Linux and Mac might work too, you wont be able to run the EXE and will have to run from source in a terminal window. Refer to [Running from source](#Running-from-source) - A USB cable to connect your switch to transfer files - This latest version of this downloaded to your computer (Utsushis-Charm_**vx_x**.zip) - You can find it [here](https://github.com/chpoit/utsushis-charm/releases/latest) -- **Google Tesseract** installed and in path - - A copy of the version 4 is bundled with the release. Just run it, no extra packages needed - - Built by UB-Mannheim [License (Apache 2.0)](https://github.com/tesseract-ocr/tesseract/blob/master/LICENSE) - - Alternatively, download the same version here: [Installer here](https://digi.bib.uni-mannheim.de/tesseract/tesseract-ocr-w64-setup-v4.1.0.20190314.exe) - - Other Versions available on the [UB-Mannheim Github](https://github.com/UB-Mannheim/tesseract/wiki) page - Some knowledge of how to type things in the terminal - - AKA: Knowing how to type in a hacker box - Being able to read - ## Steps 0. Unequip all jewels. You will create "fake" charms otherwise. @@ -156,19 +149,41 @@ In all seriousness, the work is done in a few broad steps: Sometimes windows will lock some files for a while and there is nothing you can do about it other than wait. -# Running from source on a mac +# Running from source +Common requirements: +- Python3 installed and in path +- Set up a virtual environment (optional) +- Install pip packages (in virtualenv if you use it.) + +Normal instructions apply once the application starts. +## MacOS - Requirements: - have python3 and tesseract installed via brew (or some other way) - - setup a virtual environment - - `pip3 install virtualenv` to install virtualenv - - create a virtual env at the root of the repository `virtualenv -p python3 env` + - Virtual env on mac (optional): `virtualenv -p python3 env` - Running: - switch to the virtualenv `source env/bin/activate` (run at the root of the repository) - set TESSDATA_PREFIX: `export TESSDATA_PREFIX=/usr/local/Cellar/tesseract//share/tessdata` - install the project dependencies: `pip3 install .` - run with `python3 main.py` +## Linux +- Requirements + - You will need to install Google tesseract with your package manager of choice. +- Running + - `python3 main.py` + - + +## Windows +- Requirements + - **Google Tesseract** installed and in path + - A copy of the version 4 is bundled with the release. Just run it, no extra packages needed + - You can download the installer here version here: [Installer here](https://digi.bib.uni-mannheim.de/tesseract/tesseract-ocr-w64-setup-v4.1.0.20190314.exe) + - Make sure it's in path + - Python3 installed and in path +- Running: + - `source env/bin/activate` (if you use a virtual env) + # Extra command line options If you run from source, or call the executable from the terminal you can make use of the following flags/arguments to achieve different functionality diff --git a/media/icon.ico b/media/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..1e9f6e5e5d4920ee50fe38b1bbdfcbdb216fb6d6 GIT binary patch literal 4286 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 DYPji@ literal 0 HcmV?d00001 diff --git a/scripts/build_release.bat b/scripts/build_release.bat index ac82048..1bf6ce2 100644 --- a/scripts/build_release.bat +++ b/scripts/build_release.bat @@ -17,11 +17,4 @@ cmd /c ".\env\scripts\activate & python -m PyInstaller .\utsushis-charm.spec --o copy %skill_corrections% "dist\%skill_corrections%" -if not exist %tesseract_name% ( - echo "Tesseract installer missing, downloading..." - curl https://digi.bib.uni-mannheim.de/tesseract/tesseract-ocr-w64-setup-v4.1.0.20190314.exe -o %tesseract_name% -) - -copy %tesseract_name% "dist\%tesseract_name%" - 7z a -tzip %archive_name% ".\dist\*" diff --git a/skill_corrections.csv b/skill_corrections.csv index 9f44003..d5b1976 100644 --- a/skill_corrections.csv +++ b/skill_corrections.csv @@ -901,3 +901,14 @@ slugs",slugger SDSIS,spare Free:,Free Free.,Free +Aftack,Attack +Affnity,Affinity +Siding,Sliding +Wide—Range,Wide-Range +Artilery,Artillery +Ballstics,Ballistics +Fortity,Fortify +Eyo,Eye +Sholls,Shells +Recoll,Recoil +Consttution,Constitution diff --git a/src/charm_extraction.py b/src/charm_extraction.py index 97f056e..cd32aea 100644 --- a/src/charm_extraction.py +++ b/src/charm_extraction.py @@ -20,6 +20,7 @@ import json import cv2 import os +from pathlib import Path DEBUG = False @@ -32,19 +33,30 @@ spell.load_dictionary(get_resource_path("skill_dict"), 0, 1) -known_corrections = {} -with open(get_resource_path('skill_corrections'), encoding='utf-8') as scf: - for line in scf.readlines(): - line = line.strip() - w, r = line.split(',') - known_corrections[w] = r +def load_corrections(known_corrections=None): + known_corrections = known_corrections or {} + corrections_path = get_resource_path('skill_corrections') + Path(corrections_path).touch() # if not exists + with open(corrections_path, encoding='utf-8') as scf: + for line in scf.readlines(): + line = line.strip() + w, r = line.split(',') + known_corrections[w] = r + return known_corrections -all_skills = {} -with open(get_resource_path('skill_list')) as slf: - for line in slf.readlines(): - skill_name = line.strip() - all_skills[skill_name.lower()] = skill_name + +def load_all_skills(all_skills=None): + all_skills = all_skills or {} + with open(get_resource_path('skill_list')) as slf: + for line in slf.readlines(): + skill_name = line.strip() + all_skills[skill_name.lower()] = skill_name + return all_skills + + +known_corrections = load_corrections() +all_skills = load_all_skills() def is_skill(skill_dict, skill_name): diff --git a/src/manual_tesseract_bindings.py b/src/tesseract/Tesseract.py similarity index 64% rename from src/manual_tesseract_bindings.py rename to src/tesseract/Tesseract.py index b16797f..ce21d39 100644 --- a/src/manual_tesseract_bindings.py +++ b/src/tesseract/Tesseract.py @@ -9,69 +9,11 @@ import shutil import platform import logging +from pathlib import Path +from .tesseract_utils import * logger = logging.getLogger(__name__) - -class TesseractError(Exception): - pass - - -def find_tesseract(): - # TODO: Make this resilient to "change" (tesseract version), probably not necessary - locations = [ - ctypes.util.find_library("libtesseract-4"), # win32 - ctypes.util.find_library("libtesseract302"), # win32 version 3.2 - ctypes.util.find_library("tesseract"), # others - ] - - if platform.system() == "Windows": - locations += [ - os.path.join(os.getenv("ProgramW6432"), - "Tesseract-OCR", "libtesseract-4.dll"), - os.path.join(os.getenv('LOCALAPPDATA'), - "Tesseract-OCR", "libtesseract-4.dll"), - os.path.join(os.getenv("ProgramFiles"), - "Tesseract-OCR", "libtesseract-4.dll"), - os.path.join(os.getenv("programfiles(x86)"), - "Tesseract-OCR", "libtesseract-4.dll"), - ] - elif platform.system() == "Darwin": # MacOS - locations += [ - # add potential environment paths here: - # Example: - # os.path.join(os.getenv("MACOS_ENV_NAME"), "Tesseract-OCR", "libtesseract-4.dll"), - ] - elif platform.system() == "Linux": - locations += [ - # add potential environment paths here: - # Example: - # os.path.join(os.getenv("LINUX_ENV_NAME"), "Tesseract-OCR", "libtesseract-4.dll"), - ] - - for potential in filter(lambda x: x, locations): - if os.path.isfile(potential): - logger.debug(f"Using tesseract at {potential}") - return potential - - raise TesseractError( - 'Tesseract library was not found on your system. Please install it') - - -def set_tessdata(): - if 'TESSDATA_PREFIX' in os.environ: - return - path = find_tesseract() - path = os.path.dirname(path) - TESSDATA_PREFIX = os.path.join(path, 'tessdata') - os.environ['TESSDATA_PREFIX'] = TESSDATA_PREFIX - logger.debug(f"Set 'TESSDATA_PREFIX' to {TESSDATA_PREFIX}") - - -def get_datapath(): - if 'TESSDATA_PREFIX' not in os.environ: - set_tessdata() - return os.environ['TESSDATA_PREFIX'] - +from .TesseractError import TesseractError class Tesseract(object): _lib = None @@ -130,6 +72,8 @@ def __init__(self, language='eng', datapath=None, lib_path=None): self.setup_lib(lib_path) self._api = self._lib.TessBaseAPICreate() + download_language_data(language) + # required windows nonsense encoded_lang = language.encode("utf-8") datapath = get_datapath() if datapath is None else datapath @@ -182,36 +126,13 @@ def set_variable(self, key, val): self._lib.TessBaseAPISetVariable(self._api, key, val) -def convert_to_grayscale(image_data): - return cv2.cvtColor(image_data, cv2.COLOR_BGR2GRAY) - - -def process_image_with_tesseract(tesseract, image): - whitelist = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890\'/-" - - height, width = image.shape[:2] - if len(image.shape) == 2: - depth = 1 - else: - depth = image.shape[2] - - # Forcing obnoxious type conversion, probably some windows BS - image = image.astype(np.uint8) - - tesseract.set_image(image.ctypes, width, height, depth) - tesseract.set_variable("whitelist", whitelist) - tesseract.set_resolution() - text = tesseract.get_text() - return text.strip() - - if __name__ == '__main__': set_tessdata() PACKAGE_PARENT = '..' SCRIPT_DIR = os.path.dirname(os.path.realpath( os.path.join(os.getcwd(), os.path.expanduser(__file__)))) sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT))) - from src.utils import remove_non_skill_info, apply_trunc_threshold, get_skills, _trim_image_past_skill_name + from ..utils import remove_non_skill_info, apply_trunc_threshold, get_skills, _trim_image_past_skill_name test_img = [ "frames/frame0.png", diff --git a/src/tesseract/TesseractError.py b/src/tesseract/TesseractError.py new file mode 100644 index 0000000..c94ce53 --- /dev/null +++ b/src/tesseract/TesseractError.py @@ -0,0 +1,3 @@ + +class TesseractError(Exception): + pass \ No newline at end of file diff --git a/src/tesseract/tesseract_utils.py b/src/tesseract/tesseract_utils.py new file mode 100644 index 0000000..86fc1f9 --- /dev/null +++ b/src/tesseract/tesseract_utils.py @@ -0,0 +1,167 @@ +from .TesseractError import TesseractError +import numpy as np +import os +import sys +import ctypes +import ctypes.util +import platform +from urllib import request +from tqdm import tqdm + +import logging +from pathlib import Path +logger = logging.getLogger(__name__) +HOME = str(Path.home()) + + +def _is_pyinstaller(): + return hasattr(sys, '_MEIPASS') + + +def _get_pyinstaller_tesseract_path(): + base_path = sys._MEIPASS + bundled_path = os.path.join(base_path, + "Tesseract-OCR", "libtesseract-4.dll") + return bundled_path + + +def find_tesseract(): + if _is_pyinstaller(): + print("Using bundled tesseract") + return _get_pyinstaller_tesseract_path() + + # TODO: Make this resilient to "change" (tesseract version), probably not necessary + locations = [ + ctypes.util.find_library("libtesseract-4"), # win32 + ctypes.util.find_library("libtesseract302"), # win32 version 3.2 + ctypes.util.find_library("tesseract"), # others + ] + + if platform.system() == "Windows": + locations += [ + os.path.join(os.getenv("ProgramW6432"), + "Tesseract-OCR", "libtesseract-4.dll"), + os.path.join(os.getenv('LOCALAPPDATA'), + "Tesseract-OCR", "libtesseract-4.dll"), + os.path.join(os.getenv("ProgramFiles"), + "Tesseract-OCR", "libtesseract-4.dll"), + os.path.join(os.getenv("programfiles(x86)"), + "Tesseract-OCR", "libtesseract-4.dll"), + ] + elif platform.system() == "Darwin": # MacOS + locations += [ + # add potential environment paths here: + # Example: + # os.path.join(os.getenv("MACOS_ENV_NAME"), "Tesseract-OCR", "libtesseract-4.dll"), + ] + elif platform.system() == "Linux": + locations += [ + # add potential environment paths here: + # Example: + # os.path.join(os.getenv("LINUX_ENV_NAME"), "Tesseract-OCR", "libtesseract-4.dll"), + ] + + for potential in filter(lambda x: x, locations): + if os.path.isfile(potential): + logger.debug(f"Using tesseract at {potential}") + return potential + + raise TesseractError( + 'Tesseract library was not found on your system. Please install it') + + +def set_tessdata(): + if _is_pyinstaller() or True: + base_path = HOME + if platform.system() == "Windows": + base_path = os.getenv('LOCALAPPDATA') or HOME + tessdata = os.path.join(base_path, "utsushis-charm", 'tessdata') + os.environ['TESSDATA_PREFIX'] = tessdata + os.makedirs(tessdata, exist_ok=True) + + if 'TESSDATA_PREFIX' in os.environ: + return + + path = find_tesseract() + path = os.path.dirname(path) + TESSDATA_PREFIX = os.path.join(path, 'tessdata') + os.environ['TESSDATA_PREFIX'] = TESSDATA_PREFIX + logger.debug(f"Set 'TESSDATA_PREFIX' to {TESSDATA_PREFIX}") + + +def get_datapath(): + if 'TESSDATA_PREFIX' not in os.environ: + set_tessdata() + + return os.environ['TESSDATA_PREFIX'] + + +def download_language_data(lang="eng"): + target_dir = get_datapath() + full_name = os.path.join(target_dir, f"{lang}.traineddata") + if os.path.isfile(full_name): + return + + name_map = {"eng": "English"} + pack_name = name_map[lang] + + url = f"https://github.com/tesseract-ocr/tessdata_best/raw/master/{lang}.traineddata" + + print(f"Downloading {pack_name} language pack to: {target_dir}") + with tqdm(unit='B', unit_scale=True, unit_divisor=1024, miniters=1, + desc=f"Downloading {pack_name} pack...") as pbar: + + request.urlretrieve(url, filename=full_name, + reporthook=_tqdm_dl_hook(pbar), data=None) + + +def process_image_with_tesseract(tesseract, image): + whitelist = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890\'/-" + + height, width = image.shape[:2] + if len(image.shape) == 2: + depth = 1 + else: + depth = image.shape[2] + + # Forcing obnoxious type conversion, probably some windows BS + image = image.astype(np.uint8) + + tesseract.set_image(image.ctypes, width, height, depth) + tesseract.set_variable("whitelist", whitelist) + tesseract.set_resolution() + text = tesseract.get_text() + return text.strip() + + +def _tqdm_dl_hook(tqdm_instance): + # Source: https://github.com/tqdm/tqdm/blob/master/examples/tqdm_wget.py + # Licence: LICENSES/tqdm (MPLv2.0 and MIT) + """Wraps tqdm instance. + Don't forget to close() or __exit__() + the tqdm instance once you're done with it (easiest using `with` syntax). + Example + ------- + >>> with tqdm(...) as t: + ... reporthook = my_hook(t) + ... urllib.urlretrieve(..., reporthook=reporthook) + """ + last_b = [0] + + def update_to(b=1, bsize=1, tsize=None): + """ + b : int, optional + Number of blocks transferred so far [default: 1]. + bsize : int, optional + Size of each block (in tqdm units) [default: 1]. + tsize : int, optional + Total size (in tqdm units). If [default: None] or -1, + remains unchanged. + """ + if tsize not in (None, -1): + tqdm_instance.total = tsize + displayed = tqdm_instance.update((b - last_b[0]) * bsize) + last_b[0] = b + return displayed + + return update_to diff --git a/src/utils.py b/src/utils.py index 0a1abf8..63b739d 100644 --- a/src/utils.py +++ b/src/utils.py @@ -3,9 +3,10 @@ import numpy as np from skimage.metrics import structural_similarity from math import floor -from .manual_tesseract_bindings import Tesseract, process_image_with_tesseract -tess = Tesseract() +from .tesseract.Tesseract import Tesseract +from .tesseract.tesseract_utils import process_image_with_tesseract +tess = Tesseract() def _load_potentially_transparent(filename): pot_transparent = cv2.imread(filename, cv2.IMREAD_UNCHANGED) diff --git a/utsushis-charm.spec b/utsushis-charm.spec index dcf9285..6d9b3e5 100644 --- a/utsushis-charm.spec +++ b/utsushis-charm.spec @@ -8,6 +8,7 @@ a = Analysis(['main.py'], pathex=['.'], binaries=[], datas=[ + ("media/icon.ico", 'media/icon.ico' ), ("images", 'images' ), ("data", 'data' ), ("LICENSES", 'LICENSES' ), @@ -37,4 +38,5 @@ exe = EXE(pyz, upx=True, upx_exclude=[], runtime_tmpdir=None, - console=True) + console=True, + icon='media/icon.ico')