GameSentenceMiner 2.8.6__py3-none-any.whl → 2.8.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- GameSentenceMiner/ai/ai_prompting.py +201 -0
- GameSentenceMiner/anki.py +4 -3
- GameSentenceMiner/config_gui.py +42 -12
- GameSentenceMiner/configuration.py +39 -15
- GameSentenceMiner/gametext.py +26 -34
- GameSentenceMiner/gsm.py +58 -42
- GameSentenceMiner/obs.py +47 -24
- GameSentenceMiner/ocr/owocr_area_selector.py +4 -2
- GameSentenceMiner/ocr/owocr_helper.py +32 -3
- GameSentenceMiner/owocr/owocr/config.py +3 -1
- GameSentenceMiner/owocr/owocr/run.py +78 -6
- GameSentenceMiner/web/texthooking_page.py +172 -15
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.7.dist-info}/METADATA +2 -1
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.7.dist-info}/RECORD +18 -20
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.7.dist-info}/WHEEL +1 -1
- GameSentenceMiner/ai/gemini.py +0 -143
- GameSentenceMiner/web/static/text_replacements.html +0 -238
- GameSentenceMiner/web/static/utility.html +0 -316
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.7.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.7.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.8.6.dist-info → gamesentenceminer-2.8.7.dist-info}/top_level.txt +0 -0
GameSentenceMiner/gsm.py
CHANGED
@@ -1,36 +1,45 @@
|
|
1
|
-
import
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
import
|
6
|
-
import
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
from
|
12
|
-
|
13
|
-
|
14
|
-
from
|
15
|
-
|
16
|
-
|
17
|
-
from GameSentenceMiner import
|
18
|
-
from GameSentenceMiner import
|
19
|
-
from GameSentenceMiner import
|
20
|
-
from GameSentenceMiner import
|
21
|
-
from GameSentenceMiner import
|
22
|
-
from GameSentenceMiner
|
23
|
-
from GameSentenceMiner
|
24
|
-
from GameSentenceMiner
|
25
|
-
|
26
|
-
from GameSentenceMiner.
|
27
|
-
from GameSentenceMiner.
|
28
|
-
|
29
|
-
from GameSentenceMiner.
|
30
|
-
from GameSentenceMiner.
|
31
|
-
from GameSentenceMiner.
|
32
|
-
from GameSentenceMiner.
|
33
|
-
from GameSentenceMiner.
|
1
|
+
import asyncio
|
2
|
+
|
3
|
+
try:
|
4
|
+
import os.path
|
5
|
+
import signal
|
6
|
+
from subprocess import Popen
|
7
|
+
|
8
|
+
import keyboard
|
9
|
+
import psutil
|
10
|
+
import ttkbootstrap as ttk
|
11
|
+
from PIL import Image, ImageDraw
|
12
|
+
from pystray import Icon, Menu, MenuItem
|
13
|
+
from watchdog.events import FileSystemEventHandler
|
14
|
+
from watchdog.observers import Observer
|
15
|
+
|
16
|
+
|
17
|
+
from GameSentenceMiner import anki
|
18
|
+
from GameSentenceMiner import config_gui
|
19
|
+
from GameSentenceMiner import configuration
|
20
|
+
from GameSentenceMiner import ffmpeg
|
21
|
+
from GameSentenceMiner import gametext
|
22
|
+
from GameSentenceMiner import notification
|
23
|
+
from GameSentenceMiner import obs
|
24
|
+
from GameSentenceMiner import util
|
25
|
+
from GameSentenceMiner.communication import Message
|
26
|
+
from GameSentenceMiner.communication.send import send_restart_signal
|
27
|
+
from GameSentenceMiner.communication.websocket import connect_websocket, register_websocket_message_handler, \
|
28
|
+
FunctionName
|
29
|
+
from GameSentenceMiner.configuration import *
|
30
|
+
from GameSentenceMiner.downloader.download_tools import download_obs_if_needed, download_ffmpeg_if_needed
|
31
|
+
from GameSentenceMiner.ffmpeg import get_audio_and_trim, get_video_timings
|
32
|
+
from GameSentenceMiner.obs import check_obs_folder_is_correct
|
33
|
+
from GameSentenceMiner.text_log import GameLine, get_text_event, get_mined_line, get_all_lines
|
34
|
+
from GameSentenceMiner.util import *
|
35
|
+
from GameSentenceMiner.web import texthooking_page
|
36
|
+
from GameSentenceMiner.web.texthooking_page import run_text_hooker_page
|
37
|
+
except Exception as e:
|
38
|
+
from GameSentenceMiner.configuration import logger
|
39
|
+
import time
|
40
|
+
logger.info("Something bad happened during import/initialization, closing in 5 seconds")
|
41
|
+
logger.exception(e)
|
42
|
+
time.sleep(5)
|
34
43
|
|
35
44
|
if is_windows():
|
36
45
|
import win32api
|
@@ -449,10 +458,12 @@ def run_tray():
|
|
449
458
|
|
450
459
|
def close_obs():
|
451
460
|
obs.disconnect_from_obs()
|
452
|
-
if obs.
|
461
|
+
if obs.obs_process_pid:
|
453
462
|
try:
|
454
|
-
subprocess.run(["taskkill", "/PID", str(obs.
|
455
|
-
print(f"OBS (PID {obs.
|
463
|
+
subprocess.run(["taskkill", "/PID", str(obs.obs_process_pid), "/F"], check=True, capture_output=True, text=True)
|
464
|
+
print(f"OBS (PID {obs.obs_process_pid}) has been terminated.")
|
465
|
+
if os.path.exists(obs.OBS_PID_FILE):
|
466
|
+
os.remove(obs.OBS_PID_FILE)
|
456
467
|
except subprocess.CalledProcessError as e:
|
457
468
|
print(f"Error terminating OBS: {e.stderr}")
|
458
469
|
else:
|
@@ -460,9 +471,9 @@ def close_obs():
|
|
460
471
|
|
461
472
|
|
462
473
|
def restart_obs():
|
463
|
-
if obs.
|
474
|
+
if obs.obs_process_pid:
|
464
475
|
close_obs()
|
465
|
-
time.sleep(
|
476
|
+
time.sleep(1)
|
466
477
|
obs.start_obs()
|
467
478
|
obs.connect_to_obs()
|
468
479
|
|
@@ -532,7 +543,7 @@ def initialize(reloading=False):
|
|
532
543
|
# whisper_helper.initialize_whisper_model()
|
533
544
|
|
534
545
|
def initialize_async():
|
535
|
-
tasks = [
|
546
|
+
tasks = [connect_websocket, run_tray]
|
536
547
|
threads = []
|
537
548
|
tasks.append(anki.start_monitoring_anki)
|
538
549
|
for task in tasks:
|
@@ -555,7 +566,6 @@ def post_init():
|
|
555
566
|
whisper_helper.initialize_whisper_model()
|
556
567
|
if get_config().vad.is_silero():
|
557
568
|
from GameSentenceMiner.vad import silero_trim
|
558
|
-
start_web_server()
|
559
569
|
|
560
570
|
util.run_new_thread(do_post_init)
|
561
571
|
|
@@ -572,8 +582,10 @@ def handle_websocket_message(message: Message):
|
|
572
582
|
case _:
|
573
583
|
logger.debug(f"unknown message from electron websocket: {message.to_json()}")
|
574
584
|
|
585
|
+
def post_init2():
|
586
|
+
asyncio.run(gametext.start_text_monitor())
|
575
587
|
|
576
|
-
def main(reloading=False):
|
588
|
+
async def main(reloading=False):
|
577
589
|
global root, settings_window
|
578
590
|
logger.info("Script started.")
|
579
591
|
root = ttk.Window(themename='darkly')
|
@@ -586,12 +598,16 @@ def main(reloading=False):
|
|
586
598
|
if not is_linux():
|
587
599
|
register_hotkeys()
|
588
600
|
|
601
|
+
util.run_new_thread(post_init2)
|
602
|
+
util.run_new_thread(run_text_hooker_page)
|
603
|
+
|
589
604
|
# Register signal handlers for graceful shutdown
|
590
605
|
signal.signal(signal.SIGTERM, handle_exit()) # Handle `kill` commands
|
591
606
|
signal.signal(signal.SIGINT, handle_exit()) # Handle Ctrl+C
|
592
607
|
if is_windows():
|
593
608
|
win32api.SetConsoleCtrlHandler(handle_exit())
|
594
609
|
|
610
|
+
|
595
611
|
try:
|
596
612
|
# if get_config().general.open_config_on_startup:
|
597
613
|
# root.after(0, settings_window.show)
|
@@ -612,7 +628,7 @@ def main(reloading=False):
|
|
612
628
|
if __name__ == "__main__":
|
613
629
|
logger.info("Starting GSM")
|
614
630
|
try:
|
615
|
-
main()
|
631
|
+
asyncio.run(main())
|
616
632
|
except Exception as e:
|
617
633
|
logger.exception(e)
|
618
634
|
time.sleep(5)
|
GameSentenceMiner/obs.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import os.path
|
2
2
|
import subprocess
|
3
3
|
import time
|
4
|
+
import psutil
|
4
5
|
|
5
6
|
from obswebsocket import obsws, requests
|
6
7
|
|
@@ -9,8 +10,9 @@ from GameSentenceMiner.configuration import *
|
|
9
10
|
from GameSentenceMiner.model import *
|
10
11
|
|
11
12
|
client: obsws = None
|
12
|
-
|
13
|
+
obs_process_pid = None
|
13
14
|
logging.getLogger('obswebsocket').setLevel(logging.CRITICAL)
|
15
|
+
OBS_PID_FILE = os.path.join(configuration.get_app_directory(), 'obs-studio', 'obs_pid.txt')
|
14
16
|
|
15
17
|
# REFERENCE: https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md
|
16
18
|
|
@@ -18,20 +20,43 @@ logging.getLogger('obswebsocket').setLevel(logging.CRITICAL)
|
|
18
20
|
def get_obs_path():
|
19
21
|
return os.path.join(configuration.get_app_directory(), 'obs-studio/bin/64bit/obs64.exe')
|
20
22
|
|
23
|
+
def is_process_running(pid):
|
24
|
+
try:
|
25
|
+
process = psutil.Process(pid)
|
26
|
+
return 'obs' in process.exe()
|
27
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied, OSError):
|
28
|
+
if os.path.exists(OBS_PID_FILE):
|
29
|
+
os.remove(OBS_PID_FILE)
|
30
|
+
return False
|
31
|
+
|
21
32
|
def start_obs():
|
22
|
-
global
|
33
|
+
global obs_process_pid
|
34
|
+
if os.path.exists(OBS_PID_FILE):
|
35
|
+
with open(OBS_PID_FILE, "r") as f:
|
36
|
+
try:
|
37
|
+
obs_process_pid = int(f.read().strip())
|
38
|
+
if is_process_running(obs_process_pid):
|
39
|
+
print(f"OBS is already running with PID: {obs_process_pid}")
|
40
|
+
connect_to_obs()
|
41
|
+
return obs_process_pid
|
42
|
+
except ValueError:
|
43
|
+
print("Invalid PID found in file. Launching new OBS instance.")
|
44
|
+
except OSError:
|
45
|
+
print("No process found with the stored PID. Launching new OBS instance.")
|
46
|
+
|
23
47
|
obs_path = get_obs_path()
|
24
48
|
if not os.path.exists(obs_path):
|
25
|
-
|
49
|
+
print(f"OBS not found at {obs_path}. Please install OBS.")
|
26
50
|
return None
|
27
|
-
|
28
51
|
try:
|
29
|
-
obs_process = subprocess.Popen([obs_path, '--disable-shutdown-check', '--portable', '--startreplaybuffer'], cwd=os.path.dirname(obs_path))
|
30
|
-
|
31
|
-
|
32
|
-
|
52
|
+
obs_process = subprocess.Popen([obs_path, '--disable-shutdown-check', '--portable', '--startreplaybuffer', ], cwd=os.path.dirname(obs_path))
|
53
|
+
obs_process_pid = obs_process.pid
|
54
|
+
with open(OBS_PID_FILE, "w") as f:
|
55
|
+
f.write(str(obs_process_pid))
|
56
|
+
print(f"OBS launched with PID: {obs_process_pid}")
|
57
|
+
return obs_process_pid
|
33
58
|
except Exception as e:
|
34
|
-
|
59
|
+
print(f"Error launching OBS: {e}")
|
35
60
|
return None
|
36
61
|
|
37
62
|
def check_obs_folder_is_correct():
|
@@ -69,33 +94,32 @@ def get_obs_websocket_config_values():
|
|
69
94
|
|
70
95
|
if get_config().obs.password == 'your_password':
|
71
96
|
logger.info("OBS WebSocket password is not set. Setting it now...")
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
97
|
+
full_config = get_master_config()
|
98
|
+
full_config.get_config().obs.port = server_port
|
99
|
+
full_config.get_config().obs.password = server_password
|
100
|
+
full_config.sync_shared_fields()
|
101
|
+
full_config.save()
|
77
102
|
reload_config()
|
78
103
|
|
79
104
|
|
80
|
-
|
105
|
+
connected = False
|
81
106
|
|
82
107
|
def on_connect(obs):
|
83
|
-
global
|
108
|
+
global connected
|
84
109
|
logger.info("Reconnected to OBS WebSocket.")
|
85
|
-
|
86
|
-
|
87
|
-
reconnecting = False
|
110
|
+
start_replay_buffer()
|
111
|
+
connected = True
|
88
112
|
|
89
113
|
|
90
114
|
def on_disconnect(obs):
|
91
|
-
global
|
115
|
+
global connected
|
92
116
|
logger.error("OBS Connection Lost!")
|
93
|
-
|
117
|
+
connected = False
|
94
118
|
|
95
119
|
|
96
120
|
def connect_to_obs():
|
97
121
|
global client
|
98
|
-
if get_config().obs.enabled:
|
122
|
+
if get_config().obs.enabled and not client:
|
99
123
|
if util.is_windows():
|
100
124
|
get_obs_websocket_config_values()
|
101
125
|
client = obsws(host=get_config().obs.host, port=get_config().obs.port,
|
@@ -133,7 +157,6 @@ def do_obs_call(request, from_dict = None, retry=10):
|
|
133
157
|
time.sleep(1)
|
134
158
|
return do_obs_call(request, from_dict, retry - 1)
|
135
159
|
else:
|
136
|
-
logger.error(f"Error doing obs call: {e}")
|
137
160
|
raise e
|
138
161
|
return None
|
139
162
|
|
@@ -187,7 +210,7 @@ def get_current_scene():
|
|
187
210
|
try:
|
188
211
|
return do_obs_call(requests.GetCurrentProgramScene(), SceneInfo.from_dict, retry=0).sceneName
|
189
212
|
except Exception as e:
|
190
|
-
logger.
|
213
|
+
logger.debug(f"Couldn't get scene: {e}")
|
191
214
|
return ''
|
192
215
|
|
193
216
|
|
@@ -88,7 +88,6 @@ class ScreenSelector:
|
|
88
88
|
try:
|
89
89
|
windows = gw.getWindowsWithTitle(self.window_name)
|
90
90
|
if windows:
|
91
|
-
# TODO: Handle multiple matches if necessary (e.g., let user choose?)
|
92
91
|
if len(windows) > 1:
|
93
92
|
print(f"Warning: Multiple windows found with title '{self.window_name}'. Using the first one.")
|
94
93
|
return windows[0]
|
@@ -235,7 +234,7 @@ class ScreenSelector:
|
|
235
234
|
# --- End Conversion ---
|
236
235
|
|
237
236
|
# Validate size using the final absolute pixel coordinates
|
238
|
-
if abs_coords and abs_coords[2] >= MIN_RECT_WIDTH and abs_coords[3] >= MIN_RECT_HEIGHT:
|
237
|
+
if coordinate_system == COORD_SYSTEM_PERCENTAGE or (abs_coords and abs_coords[2] >= MIN_RECT_WIDTH and abs_coords[3] >= MIN_RECT_HEIGHT):
|
239
238
|
# Find the correct monitor dict from self.monitors based on index
|
240
239
|
monitor_index = monitor_data['index']
|
241
240
|
target_monitor = next((m for m in self.monitors if m['index'] == monitor_index), None)
|
@@ -863,6 +862,9 @@ if __name__ == "__main__":
|
|
863
862
|
# if not target_window_title:
|
864
863
|
# target_window_title = get_ocr_config().window
|
865
864
|
|
865
|
+
if not target_window_title:
|
866
|
+
target_window_title = "Windowed Projector (Preview)"
|
867
|
+
|
866
868
|
# Get the selection result
|
867
869
|
selection_result = get_screen_selection(target_window_title)
|
868
870
|
|
@@ -254,8 +254,13 @@ def text_callback(text, orig_text, rectangle_index, time, img=None):
|
|
254
254
|
# if orig_text:
|
255
255
|
# print(orig_text_string)
|
256
256
|
if not twopassocr:
|
257
|
+
if previous_orig_text and fuzz.ratio(orig_text_string, previous_orig_text) >= 80:
|
258
|
+
logger.info("Seems like Text we already sent, not doing anything.")
|
259
|
+
return
|
257
260
|
img.save(os.path.join(get_temporary_directory(), "last_successful_ocr.png"))
|
258
261
|
send_result(text, time)
|
262
|
+
orig_text_results[rectangle_index] = orig_text_string
|
263
|
+
last_ocr1_results[rectangle_index] = previous_text
|
259
264
|
if not text:
|
260
265
|
if previous_text:
|
261
266
|
if rectangle_index in text_stable_start_times:
|
@@ -269,8 +274,8 @@ def text_callback(text, orig_text, rectangle_index, time, img=None):
|
|
269
274
|
logger.info("Seems like Text we already sent, not doing anything.")
|
270
275
|
return
|
271
276
|
orig_text_results[rectangle_index] = orig_text_string
|
272
|
-
do_second_ocr(previous_text, rectangle_index, stable_time, previous_img)
|
273
277
|
last_ocr1_results[rectangle_index] = previous_text
|
278
|
+
do_second_ocr(previous_text, rectangle_index, stable_time, previous_img)
|
274
279
|
return
|
275
280
|
return
|
276
281
|
|
@@ -315,10 +320,24 @@ def run_oneocr(ocr_config: OCRConfig, i, area=False):
|
|
315
320
|
text_callback=text_callback,
|
316
321
|
screen_capture_exclusions=exclusions,
|
317
322
|
rectangle=i,
|
318
|
-
|
323
|
+
language="ja")
|
319
324
|
done = True
|
320
325
|
|
321
326
|
|
327
|
+
def get_window(window_name):
|
328
|
+
import pygetwindow as gw
|
329
|
+
try:
|
330
|
+
windows = gw.getWindowsWithTitle(window_name)
|
331
|
+
if windows:
|
332
|
+
if len(windows) > 1:
|
333
|
+
print(f"Warning: Multiple windows found with title '{window_name}'. Using the first one.")
|
334
|
+
return windows[0]
|
335
|
+
else:
|
336
|
+
return None
|
337
|
+
except Exception as e:
|
338
|
+
print(f"Error finding window '{self.window_name}': {e}")
|
339
|
+
return None
|
340
|
+
|
322
341
|
if __name__ == "__main__":
|
323
342
|
global ocr1, ocr2, twopassocr
|
324
343
|
import sys
|
@@ -343,7 +362,17 @@ if __name__ == "__main__":
|
|
343
362
|
logger.info(f"Received arguments: ocr1={ocr1}, ocr2={ocr2}, twopassocr={twopassocr}")
|
344
363
|
global ocr_config
|
345
364
|
ocr_config: OCRConfig = get_ocr_config()
|
346
|
-
|
365
|
+
if ocr_config:
|
366
|
+
if ocr_config.window:
|
367
|
+
start_time = time.time()
|
368
|
+
while time.time() - start_time < 30:
|
369
|
+
if get_window(ocr_config.window):
|
370
|
+
break
|
371
|
+
logger.info(f"Window: {ocr_config.window} Could not be found, retrying in 1 second...")
|
372
|
+
time.sleep(1)
|
373
|
+
else:
|
374
|
+
logger.error(f"Window '{ocr_config.window}' not found within 30 seconds.")
|
375
|
+
sys.exit(1)
|
347
376
|
logger.info(f"Starting OCR with configuration: Window: {ocr_config.window}, Rectangles: {len(ocr_config.rectangles)}, Engine 1: {ocr1}, Engine 2: {ocr2}, Two-pass OCR: {twopassocr}")
|
348
377
|
if ocr_config:
|
349
378
|
rectangles = list(filter(lambda rect: not rect.is_excluded, ocr_config.rectangles))
|
@@ -114,12 +114,14 @@ class Config:
|
|
114
114
|
for sub_key in config[key]:
|
115
115
|
self.__engine_config[key.lower()][sub_key.lower()] = self.__parse(config[key][sub_key])
|
116
116
|
|
117
|
-
def get_general(self, value):
|
117
|
+
def get_general(self, value, default_value=None):
|
118
118
|
if self.__provided_cli_args.get(value, None) is not None:
|
119
119
|
return self.__provided_cli_args[value]
|
120
120
|
try:
|
121
121
|
return self.__general_config[value]
|
122
122
|
except KeyError:
|
123
|
+
if default_value:
|
124
|
+
return default_value
|
123
125
|
if value in self.__default_config:
|
124
126
|
return self.__default_config[value]
|
125
127
|
else:
|
@@ -305,8 +305,21 @@ class TextFiltering:
|
|
305
305
|
|
306
306
|
def __init__(self):
|
307
307
|
from pysbd import Segmenter
|
308
|
-
self.segmenter = Segmenter(language=
|
308
|
+
self.segmenter = Segmenter(language=lang, clean=True)
|
309
309
|
self.kana_kanji_regex = re.compile(r'[\u3041-\u3096\u30A1-\u30FA\u4E00-\u9FFF]')
|
310
|
+
self.chinese_common_regex = re.compile(r'[\u4E00-\u9FFF]')
|
311
|
+
self.english_regex = re.compile(r'[a-zA-Z0-9.,!?;:"\'()\[\]{}]')
|
312
|
+
self.kana_kanji_regex = re.compile(r'[\u3041-\u3096\u30A1-\u30FA\u4E00-\u9FFF]')
|
313
|
+
self.chinese_common_regex = re.compile(r'[\u4E00-\u9FFF]')
|
314
|
+
self.english_regex = re.compile(r'[a-zA-Z0-9.,!?;:"\'()\[\]{}]')
|
315
|
+
self.korean_regex = re.compile(r'[\uAC00-\uD7AF]')
|
316
|
+
self.arabic_regex = re.compile(r'[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]')
|
317
|
+
self.russian_regex = re.compile(r'[\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F\u1C80-\u1C8F]')
|
318
|
+
self.greek_regex = re.compile(r'[\u0370-\u03FF\u1F00-\u1FFF]')
|
319
|
+
self.hebrew_regex = re.compile(r'[\u0590-\u05FF\uFB1D-\uFB4F]')
|
320
|
+
self.thai_regex = re.compile(r'[\u0E00-\u0E7F]')
|
321
|
+
self.latin_extended_regex = re.compile(
|
322
|
+
r'[a-zA-Z\u00C0-\u00FF\u0100-\u017F\u0180-\u024F\u0250-\u02AF\u1D00-\u1D7F\u1D80-\u1DBF\u1E00-\u1EFF\u2C60-\u2C7F\uA720-\uA7FF\uAB30-\uAB6F]')
|
310
323
|
try:
|
311
324
|
from transformers import pipeline, AutoTokenizer
|
312
325
|
import torch
|
@@ -334,7 +347,28 @@ class TextFiltering:
|
|
334
347
|
|
335
348
|
orig_text_filtered = []
|
336
349
|
for block in orig_text:
|
337
|
-
|
350
|
+
if lang == "ja":
|
351
|
+
block_filtered = self.kana_kanji_regex.findall(block)
|
352
|
+
elif lang == "zh":
|
353
|
+
block_filtered = self.chinese_common_regex.findall(block)
|
354
|
+
elif lang == "ko":
|
355
|
+
block_filtered = self.korean_regex.findall(block)
|
356
|
+
elif lang == "ar":
|
357
|
+
block_filtered = self.arabic_regex.findall(block)
|
358
|
+
elif lang == "ru":
|
359
|
+
block_filtered = self.russian_regex.findall(block)
|
360
|
+
elif lang == "el":
|
361
|
+
block_filtered = self.greek_regex.findall(block)
|
362
|
+
elif lang == "he":
|
363
|
+
block_filtered = self.hebrew_regex.findall(block)
|
364
|
+
elif lang == "th":
|
365
|
+
block_filtered = self.thai_regex.findall(block)
|
366
|
+
elif lang in ["en", "fr", "de", "es", "it", "pt", "nl", "sv", "da", "no",
|
367
|
+
"fi"]: # Many European languages use extended Latin
|
368
|
+
block_filtered = self.latin_extended_regex.findall(block)
|
369
|
+
else:
|
370
|
+
block_filtered = self.english_regex.findall(block)
|
371
|
+
|
338
372
|
if block_filtered:
|
339
373
|
orig_text_filtered.append(''.join(block_filtered))
|
340
374
|
else:
|
@@ -355,12 +389,12 @@ class TextFiltering:
|
|
355
389
|
detection_results = self.pipe(new_blocks, top_k=3, truncation=True)
|
356
390
|
for idx, block in enumerate(new_blocks):
|
357
391
|
for result in detection_results[idx]:
|
358
|
-
if result['label'] ==
|
392
|
+
if result['label'] == lang:
|
359
393
|
final_blocks.append(block)
|
360
394
|
break
|
361
395
|
else:
|
362
396
|
for block in new_blocks:
|
363
|
-
if self.classify(block)[0] ==
|
397
|
+
if self.classify(block)[0] == lang:
|
364
398
|
final_blocks.append(block)
|
365
399
|
|
366
400
|
text = '\n'.join(final_blocks)
|
@@ -562,10 +596,16 @@ def process_and_write_results(img_or_path, write_to, notifications, last_result,
|
|
562
596
|
|
563
597
|
orig_text = []
|
564
598
|
engine_color = config.get_general('engine_color')
|
599
|
+
# print(filtering)
|
600
|
+
#
|
601
|
+
#
|
602
|
+
# print(lang)
|
603
|
+
|
565
604
|
if res:
|
566
605
|
if filtering:
|
567
606
|
text, orig_text = filtering(text, last_result)
|
568
|
-
|
607
|
+
if lang == "ja" or lang == "zh":
|
608
|
+
text = post_process(text)
|
569
609
|
logger.opt(ansi=True).info(f'Text recognized in {t1 - t0:0.03f}s using <{engine_color}>{engine_instance.readable_name}</{engine_color}>: {text}')
|
570
610
|
if notifications:
|
571
611
|
notifier.send(title='owocr', message='Text recognized: ' + text)
|
@@ -585,6 +625,9 @@ def process_and_write_results(img_or_path, write_to, notifications, last_result,
|
|
585
625
|
else:
|
586
626
|
logger.opt(ansi=True).info(f'<{engine_color}>{engine_instance.readable_name}</{engine_color}> reported an error after {t1 - t0:0.03f}s: {text}')
|
587
627
|
|
628
|
+
# print(orig_text)
|
629
|
+
# print(text)
|
630
|
+
|
588
631
|
return orig_text, text
|
589
632
|
|
590
633
|
|
@@ -617,7 +660,7 @@ def run(read_from=None,
|
|
617
660
|
screen_capture_event_bus=None,
|
618
661
|
rectangle=None,
|
619
662
|
text_callback=None,
|
620
|
-
|
663
|
+
language=None,
|
621
664
|
):
|
622
665
|
"""
|
623
666
|
Japanese OCR client
|
@@ -652,9 +695,36 @@ def run(read_from=None,
|
|
652
695
|
if screen_capture_only_active_windows is None:
|
653
696
|
screen_capture_only_active_windows = config.get_general('screen_capture_only_active_windows')
|
654
697
|
|
698
|
+
if screen_capture_exclusions is None:
|
699
|
+
screen_capture_exclusions = config.get_general('screen_capture_exclusions')
|
700
|
+
|
701
|
+
if screen_capture_window is None:
|
702
|
+
screen_capture_window = config.get_general('screen_capture_window')
|
703
|
+
|
704
|
+
if screen_capture_delay_secs is None:
|
705
|
+
screen_capture_delay_secs = config.get_general('screen_capture_delay_secs')
|
706
|
+
|
707
|
+
if screen_capture_combo is None:
|
708
|
+
screen_capture_combo = config.get_general('screen_capture_combo')
|
709
|
+
|
710
|
+
if stop_running_flag is None:
|
711
|
+
stop_running_flag = config.get_general('stop_running_flag')
|
712
|
+
|
713
|
+
if screen_capture_event_bus is None:
|
714
|
+
screen_capture_event_bus = config.get_general('screen_capture_event_bus')
|
715
|
+
|
716
|
+
if rectangle is None:
|
717
|
+
rectangle = config.get_general('rectangle')
|
718
|
+
|
719
|
+
if text_callback is None:
|
720
|
+
text_callback = config.get_general('text_callback')
|
721
|
+
|
655
722
|
if write_to is None:
|
656
723
|
write_to = config.get_general('write_to')
|
657
724
|
|
725
|
+
if language is None:
|
726
|
+
language = config.get_general('language', "ja")
|
727
|
+
|
658
728
|
logger.configure(handlers=[{'sink': sys.stderr, 'format': config.get_general('logger_format')}])
|
659
729
|
|
660
730
|
if config.has_config:
|
@@ -666,6 +736,8 @@ def run(read_from=None,
|
|
666
736
|
|
667
737
|
global engine_instances
|
668
738
|
global engine_keys
|
739
|
+
global lang
|
740
|
+
lang = language
|
669
741
|
engine_instances = []
|
670
742
|
config_engines = []
|
671
743
|
engine_keys = []
|