From 6d0a9a97e5a5e0af86a8c00d746a59c09be083e8 Mon Sep 17 00:00:00 2001 From: Valentin Marquez Date: Tue, 15 Oct 2024 02:53:10 -0300 Subject: [PATCH] Update build and release workflow, setup-python version, and upload artifact actions --- .github/workflows/build-and-release.yml | 19 +++---- .gitignore | 1 + nikke_ocr.spec | 72 +++++++++++++++++++++++++ scripts/build.bat | 5 ++ src/config.py | 15 ++++-- src/gui/ui.py | 14 +++-- src/utils/image_processor.py | 16 +++++- 7 files changed, 120 insertions(+), 22 deletions(-) create mode 100644 nikke_ocr.spec diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 393ae7b..919fd11 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -1,23 +1,20 @@ name: Build and Release on: + workflow_dispatch: push: - branches: [ "main" ] tags: - 'v*' - pull_request: - branches: [ "main" ] - workflow_dispatch: jobs: build: runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: '3.9' @@ -49,13 +46,13 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./dist/nikke-ocr.exe - asset_name: nikke-ocr.exe + asset_path: ./dist/NIKKE-OCR/NIKKE-OCR.exe + asset_name: NIKKE-OCR.exe asset_content_type: application/octet-stream - name: Upload artifact for debugging if: ${{ !startsWith(github.ref, 'refs/tags/v') }} - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: - name: nikke-ocr - path: ./dist/nikke-ocr.exe + name: NIKKE-OCR + path: ./dist/NIKKE-OCR/NIKKE-OCR.exe diff --git a/.gitignore b/.gitignore index 82f9275..3e77af2 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ MANIFEST # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec +!nikke_ocr.spec # Installer logs pip-log.txt diff --git a/nikke_ocr.spec b/nikke_ocr.spec new file mode 100644 index 0000000..381f5e9 --- /dev/null +++ b/nikke_ocr.spec @@ -0,0 +1,72 @@ +# -*- mode: python ; coding: utf-8 -*- + +import os +from PyInstaller.utils.hooks import collect_all + +block_cipher = None + +# Collect all necessary data for PyQt5 +datas = [] +binaries = [] +hiddenimports = [] +tmp_ret = collect_all('PyQt5') +datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2] + +a = Analysis( + ['src/main.py'], + pathex=[], + binaries=binaries, + datas=[ + ('resources/static', 'resources/static'), + ('src/config.py', 'src'), + ('resources/icon.png', 'resources'), + ] + datas, + hiddenimports=[ + 'PyQt5', 'PyQt5.QtCore', 'PyQt5.QtGui', 'PyQt5.QtWidgets', + 'easyocr', 'numpy', 'cv2', 'skimage', 'pynput', 'pyautogui', + 'torch', 'torchvision' + ] + hiddenimports, + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False, +) + +# Crear las carpetas generated y output al mismo nivel que el ejecutable +a.datas += [('generated/.keep', 'generated/.keep', 'DATA')] +a.datas += [('output/.keep', 'output/.keep', 'DATA')] + +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='NIKKE-OCR', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=False, + disable_windowed_traceback=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon='resources/icon.png' +) + +coll = COLLECT( + exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=True, + upx_exclude=[], + name='NIKKE-OCR' +) diff --git a/scripts/build.bat b/scripts/build.bat index 9ed451c..1ff05a8 100644 --- a/scripts/build.bat +++ b/scripts/build.bat @@ -33,6 +33,11 @@ if errorlevel 1 ( goto :error ) +:: Delete previous build +echo Deleting previous build... +rmdir /s /q "%ROOT_DIR%\dist" >nul 2>&1 +rmdir /s /q "%ROOT_DIR%\build" >nul 2>&1 + :: Install requirements echo Installing requirements... pip install -r "%ROOT_DIR%\requirements\dev.txt" -r "%ROOT_DIR%\requirements\prod.txt" >nul 2>&1 diff --git a/src/config.py b/src/config.py index 8274d3b..2170e16 100644 --- a/src/config.py +++ b/src/config.py @@ -1,13 +1,20 @@ from pathlib import Path +import sys class Config: - BASE_DIR = Path(__file__).resolve().parent.parent - RESOURCES_DIR = BASE_DIR / "resources" + if getattr(sys, "frozen", False): + BASE_DIR = Path(sys.executable).parent + INTERNAL_DIR = BASE_DIR / "_internal" + else: + BASE_DIR = Path(__file__).resolve().parent.parent + INTERNAL_DIR = BASE_DIR + RESOURCES_DIR = INTERNAL_DIR / "resources" STATIC_DIR = RESOURCES_DIR / "static" - GENERATED_DIR = RESOURCES_DIR / "generated" - USER_DATA_DIR = RESOURCES_DIR / "output" + + GENERATED_DIR = BASE_DIR / "generated" + USER_DATA_DIR = BASE_DIR / "output" IMAGES_DIR = STATIC_DIR / "images" LANG_DIR = STATIC_DIR / "lang" diff --git a/src/gui/ui.py b/src/gui/ui.py index e1d8bb9..74e625f 100644 --- a/src/gui/ui.py +++ b/src/gui/ui.py @@ -78,7 +78,7 @@ def _setup_ui(self) -> None: self.setWindowTitle(_("NIKKE OCR")) self.setGeometry(100, 100, 400, 300) - self.setWindowIcon(QIcon("resources/icon.png")) + self.setWindowIcon(QIcon(str(self.config.STATIC_DIR / "icon.png"))) central_widget = QWidget() self.setCentralWidget(central_widget) @@ -443,11 +443,15 @@ def _compare_images( for nikke in nikkes: reference_image: np.ndarray = cv2.imread( - f"{self.config.GENERATED_IMAGES_DIR}/{nikke['images']['big']}" - ) - score: float = self.image_processor.compare_images( - nikke_image, reference_image + f"{self.config.GENERATED_IMAGES_DIR}/{nikke['images']['big']}", + cv2.IMREAD_UNCHANGED, # This will load the image as-is, whether it's color or grayscale ) + + if reference_image is None: + print(f"Warning: Could not load image for {nikke['name']}") + continue + + score: float = self.compare_images(nikke_image, reference_image) if score > best_score: best_score = score best_match = nikke diff --git a/src/utils/image_processor.py b/src/utils/image_processor.py index b4bce3d..0448940 100644 --- a/src/utils/image_processor.py +++ b/src/utils/image_processor.py @@ -44,9 +44,21 @@ def process_roi(self, image: np.ndarray) -> Optional[str]: return self.ocr_processor.process_name_roi(image) def compare_images(self, img1: np.ndarray, img2: np.ndarray) -> float: + # Ensure both images have the same dimensions img2 = cv2.resize(img2, (img1.shape[1], img1.shape[0])) - gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) - gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) + + # Convert images to grayscale if they're not already + if len(img1.shape) == 3: + gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) + else: + gray1 = img1 + + if len(img2.shape) == 3: + gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) + else: + gray2 = img2 + + # Compute SSIM score, _ = ssim(gray1, gray2, full=True) return score