GameSentenceMiner 2.7.14__py3-none-any.whl → 2.7.16__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/ocr/owocr_area_selector.py +7 -7
- GameSentenceMiner/ocr/owocr_helper.py +44 -27
- GameSentenceMiner/owocr/owocr/ocr.py +9 -13
- GameSentenceMiner/owocr/owocr/run.py +10 -3
- {gamesentenceminer-2.7.14.dist-info → gamesentenceminer-2.7.16.dist-info}/METADATA +1 -1
- {gamesentenceminer-2.7.14.dist-info → gamesentenceminer-2.7.16.dist-info}/RECORD +10 -10
- {gamesentenceminer-2.7.14.dist-info → gamesentenceminer-2.7.16.dist-info}/WHEEL +0 -0
- {gamesentenceminer-2.7.14.dist-info → gamesentenceminer-2.7.16.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.7.14.dist-info → gamesentenceminer-2.7.16.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.7.14.dist-info → gamesentenceminer-2.7.16.dist-info}/top_level.txt +0 -0
@@ -283,10 +283,10 @@ class ScreenSelector:
|
|
283
283
|
win_w = window_geom_to_save['width']
|
284
284
|
win_h = window_geom_to_save['height']
|
285
285
|
# Basic check for valid dimensions needed for percentage calculation
|
286
|
-
if win_w > 0 and win_h > 0
|
286
|
+
if win_w > 0 and win_h > 0:
|
287
287
|
save_coord_system = COORD_SYSTEM_PERCENTAGE
|
288
|
-
win_l =
|
289
|
-
win_t =
|
288
|
+
win_l = window_geom_to_save['left']
|
289
|
+
win_t = window_geom_to_save['top']
|
290
290
|
print(f"Saving using coordinate system: {save_coord_system} relative to {window_geom_to_save}")
|
291
291
|
else:
|
292
292
|
print(
|
@@ -306,14 +306,14 @@ class ScreenSelector:
|
|
306
306
|
coords_to_save = []
|
307
307
|
|
308
308
|
# --- Convert absolute pixels to the chosen system ---
|
309
|
-
if save_coord_system == COORD_SYSTEM_PERCENTAGE and window_geom_to_save
|
309
|
+
if save_coord_system == COORD_SYSTEM_PERCENTAGE and window_geom_to_save:
|
310
310
|
# Calculate percentages (handle potential float precision issues if necessary)
|
311
311
|
x_pct = (x_abs - win_l) / win_w
|
312
312
|
y_pct = (y_abs - win_t) / win_h
|
313
313
|
w_pct = w_abs / win_w
|
314
314
|
h_pct = h_abs / win_h
|
315
315
|
# Round percentages slightly to avoid overly long floats? Optional.
|
316
|
-
# precision = 6
|
316
|
+
# precision = 6+
|
317
317
|
# coords_to_save = [round(x_pct, precision), round(y_pct, precision), round(w_pct, precision), round(h_pct, precision)]
|
318
318
|
coords_to_save = [x_pct, y_pct, w_pct, h_pct]
|
319
319
|
else:
|
@@ -861,8 +861,8 @@ if __name__ == "__main__":
|
|
861
861
|
# Example: uncomment below to target Calculator on Windows by default if no arg given
|
862
862
|
# if sys.platform == "win32": target_window_title = "Calculator"
|
863
863
|
|
864
|
-
if not target_window_title:
|
865
|
-
|
864
|
+
# if not target_window_title:
|
865
|
+
# target_window_title = get_ocr_config().window
|
866
866
|
|
867
867
|
# Get the selection result
|
868
868
|
selection_result = get_screen_selection(target_window_title)
|
@@ -84,7 +84,8 @@ def get_ocr_config() -> OCRConfig:
|
|
84
84
|
scene = util.sanitize_filename(obs.get_current_scene())
|
85
85
|
config_path = ocr_config_dir / f"{scene}.json"
|
86
86
|
if not config_path.exists():
|
87
|
-
|
87
|
+
config_path.touch()
|
88
|
+
return
|
88
89
|
try:
|
89
90
|
with open(config_path, 'r', encoding="utf-8") as f:
|
90
91
|
config_data = json.load(f)
|
@@ -107,20 +108,21 @@ def get_ocr_config() -> OCRConfig:
|
|
107
108
|
"coordinates": rect,
|
108
109
|
"is_excluded": False
|
109
110
|
})
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
"
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
111
|
+
if 'excluded_rectangles' in config_data:
|
112
|
+
for rect in config_data['excluded_rectangles']:
|
113
|
+
new_rectangles.append({
|
114
|
+
"monitor": {
|
115
|
+
"left": default_monitor["left"],
|
116
|
+
"top": default_monitor["top"],
|
117
|
+
"width": default_monitor["width"],
|
118
|
+
"height": default_monitor["height"],
|
119
|
+
"index": 0 # Assuming single monitor for old config
|
120
|
+
},
|
121
|
+
"coordinates": rect,
|
122
|
+
"is_excluded": True
|
123
|
+
})
|
122
124
|
new_config_data = {"scene": config_data.get("scene", scene), "window": config_data.get("window", None),
|
123
|
-
"rectangles": new_rectangles}
|
125
|
+
"rectangles": new_rectangles, "coordinate_system": "absolute"}
|
124
126
|
with open(config_path, 'w', encoding="utf-8") as f:
|
125
127
|
json.dump(new_config_data, f, indent=4)
|
126
128
|
return OCRConfig.from_dict(new_config_data)
|
@@ -231,32 +233,45 @@ def do_second_ocr(ocr1_text, rectangle_index, time, img):
|
|
231
233
|
last_oneocr_results_to_check = {} # Store last OCR result for each rectangle
|
232
234
|
last_oneocr_times = {} # Store last OCR time for each rectangle
|
233
235
|
text_stable_start_times = {} # Store the start time when text becomes stable for each rectangle
|
236
|
+
orig_text_results = {} # Store original text results for each rectangle
|
234
237
|
TEXT_APPEARENCE_DELAY = get_ocr_scan_rate() * 1000 + 500 # Adjust as needed
|
235
238
|
|
236
|
-
def text_callback(text, rectangle_index, time, img=None):
|
237
|
-
global twopassocr, ocr2, last_oneocr_results_to_check, last_oneocr_times, text_stable_start_times
|
239
|
+
def text_callback(text, orig_text, rectangle_index, time, img=None):
|
240
|
+
global twopassocr, ocr2, last_oneocr_results_to_check, last_oneocr_times, text_stable_start_times, orig_text_results
|
241
|
+
orig_text_string = ''.join([item for item in orig_text if item is not None]) if orig_text else ""
|
238
242
|
|
239
243
|
current_time = time if time else datetime.now()
|
240
244
|
|
241
245
|
previous_text = last_oneocr_results_to_check.get(rectangle_index, "").strip()
|
246
|
+
previous_orig_text = orig_text_results.get(rectangle_index, "").strip()
|
247
|
+
|
248
|
+
# print(previous_orig_text)
|
249
|
+
# if orig_text:
|
250
|
+
# print(orig_text_string)
|
242
251
|
|
243
252
|
if not text:
|
244
253
|
if previous_text:
|
245
254
|
if rectangle_index in text_stable_start_times:
|
246
255
|
stable_time = text_stable_start_times[rectangle_index]
|
256
|
+
previous_result = last_ocr1_results[rectangle_index]
|
257
|
+
if previous_result and fuzz.ratio(previous_result, previous_text) >= 80:
|
258
|
+
logger.info("Seems like the same text, not " + "doing second OCR" if twopassocr else "sending")
|
259
|
+
del last_oneocr_results_to_check[rectangle_index]
|
260
|
+
return
|
261
|
+
if previous_orig_text and fuzz.ratio(orig_text_string, previous_orig_text) >= 80:
|
262
|
+
logger.info("Seems like Text we already sent, not doing anything.")
|
263
|
+
del last_oneocr_results_to_check[rectangle_index]
|
264
|
+
return
|
265
|
+
orig_text_results[rectangle_index] = orig_text_string
|
247
266
|
if twopassocr:
|
248
267
|
do_second_ocr(previous_text, rectangle_index, time, img)
|
249
268
|
else:
|
250
|
-
previous_result = last_ocr1_results[rectangle_index]
|
251
|
-
if previous_result and fuzz.ratio(previous_result, previous_text) >= 80:
|
252
|
-
logger.info("Seems like the same text, not sending")
|
253
|
-
return
|
254
269
|
if get_config().advanced.ocr_sends_to_clipboard:
|
255
270
|
import pyperclip
|
256
271
|
pyperclip.copy(text)
|
257
272
|
websocket_server_thread.send_text(previous_text, stable_time)
|
258
273
|
img.save(os.path.join(get_app_directory(), "temp", "last_successful_ocr.png"))
|
259
|
-
|
274
|
+
last_ocr1_results[rectangle_index] = previous_text
|
260
275
|
del text_stable_start_times[rectangle_index]
|
261
276
|
del last_oneocr_results_to_check[rectangle_index]
|
262
277
|
return
|
@@ -285,11 +300,13 @@ done = False
|
|
285
300
|
|
286
301
|
def run_oneocr(ocr_config: OCRConfig, i, area=False):
|
287
302
|
global done
|
288
|
-
|
289
|
-
|
290
|
-
|
303
|
+
screen_area = None
|
304
|
+
if ocr_config.rectangles:
|
305
|
+
rect_config = ocr_config.rectangles[i]
|
306
|
+
coords = rect_config.coordinates
|
307
|
+
monitor_config = rect_config.monitor
|
308
|
+
screen_area = ",".join(str(c) for c in coords) if area else None
|
291
309
|
exclusions = list(rect.coordinates for rect in list(filter(lambda x: x.is_excluded, ocr_config.rectangles)))
|
292
|
-
screen_area = ",".join(str(c) for c in coords) if area else None
|
293
310
|
run.run(read_from="screencapture", write_to="callback",
|
294
311
|
screen_capture_area=screen_area,
|
295
312
|
# screen_capture_monitor=monitor_config['index'],
|
@@ -326,9 +343,9 @@ if __name__ == "__main__":
|
|
326
343
|
logger.info(f"Received arguments: ocr1={ocr1}, ocr2={ocr2}, twopassocr={twopassocr}")
|
327
344
|
global ocr_config
|
328
345
|
ocr_config: OCRConfig = get_ocr_config()
|
329
|
-
|
346
|
+
print(ocr_config)
|
330
347
|
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}")
|
331
|
-
if ocr_config
|
348
|
+
if ocr_config:
|
332
349
|
rectangles = list(filter(lambda rect: not rect.is_excluded, ocr_config.rectangles))
|
333
350
|
last_ocr1_results = [""] * len(rectangles) if rectangles else [""]
|
334
351
|
last_ocr2_results = [""] * len(rectangles) if rectangles else [""]
|
@@ -759,7 +759,9 @@ class OneOCR:
|
|
759
759
|
|
760
760
|
if sys.platform == 'win32':
|
761
761
|
try:
|
762
|
-
|
762
|
+
ocr_resp = self.model.recognize_pil(img)
|
763
|
+
# print(json.dumps(ocr_resp))
|
764
|
+
res = ocr_resp['text']
|
763
765
|
except RuntimeError as e:
|
764
766
|
return (False, e)
|
765
767
|
else:
|
@@ -773,6 +775,7 @@ class OneOCR:
|
|
773
775
|
if res.status_code != 200:
|
774
776
|
return (False, 'Unknown error!')
|
775
777
|
|
778
|
+
|
776
779
|
res = res.json()['text']
|
777
780
|
|
778
781
|
x = (True, res)
|
@@ -1007,9 +1010,9 @@ class GeminiOCR:
|
|
1007
1010
|
try:
|
1008
1011
|
import google.generativeai as genai
|
1009
1012
|
if isinstance(img_or_path, str) or isinstance(img_or_path, Path):
|
1010
|
-
img = Image.open(img_or_path)
|
1013
|
+
img = Image.open(img_or_path)
|
1011
1014
|
elif isinstance(img_or_path, Image.Image):
|
1012
|
-
img = img_or_path
|
1015
|
+
img = img_or_path
|
1013
1016
|
else:
|
1014
1017
|
raise ValueError(f'img_or_path must be a path or PIL.Image, instead got: {img_or_path}')
|
1015
1018
|
|
@@ -1023,11 +1026,11 @@ class GeminiOCR:
|
|
1023
1026
|
{
|
1024
1027
|
'inline_data': {
|
1025
1028
|
'mime_type': 'image/png',
|
1026
|
-
'data':
|
1029
|
+
'data': img_bytes
|
1027
1030
|
}
|
1028
1031
|
},
|
1029
1032
|
{
|
1030
|
-
'text': '
|
1033
|
+
'text': 'Analyze the image. Extract text *only* from within dialogue boxes (speech bubbles or panels containing character dialogue). From the extracted dialogue text, filter out any furigana. Ignore and do not include any text found outside of dialogue boxes, including character names, speaker labels, or sound effects. Return *only* the filtered dialogue text. If no text is found within dialogue boxes after applying filters, return nothing. Do not include any other output, formatting markers, or commentary.'
|
1031
1034
|
}
|
1032
1035
|
]
|
1033
1036
|
}
|
@@ -1044,11 +1047,4 @@ class GeminiOCR:
|
|
1044
1047
|
return (False, f'Gemini API request failed: {e}')
|
1045
1048
|
|
1046
1049
|
def _preprocess(self, img):
|
1047
|
-
|
1048
|
-
from io import BytesIO
|
1049
|
-
img_io = BytesIO()
|
1050
|
-
img.save(img_io, 'PNG') # Save as PNG
|
1051
|
-
return img_io.getvalue()
|
1052
|
-
except Exception as e:
|
1053
|
-
logger.error(f'Error preprocessing image for Gemini: {e}')
|
1054
|
-
return None
|
1050
|
+
return pil_image_to_bytes(img, png_compression=1)
|
@@ -25,6 +25,10 @@ import psutil
|
|
25
25
|
|
26
26
|
import inspect
|
27
27
|
from .ocr import *
|
28
|
+
try:
|
29
|
+
from .secret import *
|
30
|
+
except ImportError:
|
31
|
+
pass
|
28
32
|
from .config import Config
|
29
33
|
from .screen_coordinate_picker import get_screen_selection
|
30
34
|
from ...configuration import get_temporary_directory
|
@@ -571,7 +575,7 @@ def process_and_write_results(img_or_path, write_to, notifications, last_result,
|
|
571
575
|
elif write_to == 'clipboard':
|
572
576
|
pyperclipfix.copy(text)
|
573
577
|
elif write_to == "callback":
|
574
|
-
txt_callback(text, rectangle, start_time, img_or_path)
|
578
|
+
txt_callback(text, orig_text, rectangle, start_time, img_or_path)
|
575
579
|
elif write_to:
|
576
580
|
with Path(write_to).open('a', encoding='utf-8') as f:
|
577
581
|
f.write(text + '\n')
|
@@ -670,7 +674,10 @@ def run(read_from=None,
|
|
670
674
|
for config_engine in config.get_general('engines').split(','):
|
671
675
|
config_engines.append(config_engine.strip().lower())
|
672
676
|
|
673
|
-
for _,engine_class in sorted(inspect.getmembers(sys.modules[__name__],
|
677
|
+
for _, engine_class in sorted(inspect.getmembers(sys.modules[__name__],
|
678
|
+
lambda x: hasattr(x, '__module__') and x.__module__ and (
|
679
|
+
__package__ + '.ocr' in x.__module__ or __package__ + '.secret' in x.__module__) and inspect.isclass(
|
680
|
+
x))):
|
674
681
|
if len(config_engines) == 0 or engine_class.name in config_engines:
|
675
682
|
if config.get_engine(engine_class.name) == None:
|
676
683
|
engine_instance = engine_class()
|
@@ -921,7 +928,7 @@ def run(read_from=None,
|
|
921
928
|
logger.opt(ansi=True).info(f"Reading from {read_from_readable}, writing to {write_to_readable} using <{engine_color}>{engine_instances[engine_index].readable_name}</{engine_color}>{' (paused)' if paused else ''}")
|
922
929
|
|
923
930
|
while not terminated and not stop_running_flag:
|
924
|
-
start_time = datetime.
|
931
|
+
start_time = datetime.now()
|
925
932
|
if read_from == 'websocket':
|
926
933
|
while True:
|
927
934
|
try:
|
@@ -23,22 +23,22 @@ GameSentenceMiner/downloader/download_tools.py,sha256=mI1u_FGBmBqDIpCH3jOv8DOoZ3
|
|
23
23
|
GameSentenceMiner/ocr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
24
24
|
GameSentenceMiner/ocr/gsm_ocr_config.py,sha256=zagsB4UD9mmZX_r6dFBCXZqdDa0XGk-RvIqbKoPB9lQ,1932
|
25
25
|
GameSentenceMiner/ocr/ocrconfig.py,sha256=hTROOZ3On2HngXKxwQFZvnr5AxlmlMV0mPxv-F3NbMg,6476
|
26
|
-
GameSentenceMiner/ocr/owocr_area_selector.py,sha256=
|
27
|
-
GameSentenceMiner/ocr/owocr_helper.py,sha256=
|
26
|
+
GameSentenceMiner/ocr/owocr_area_selector.py,sha256=bwlvvM_SwRHzwbZ3GSQfxGHT0ASy3rMxB5DQ7RhVZkQ,46742
|
27
|
+
GameSentenceMiner/ocr/owocr_helper.py,sha256=wL6EjjFTU6WJu_1UdY0g1dl0JhLweO54YnAY9fOPjaQ,16117
|
28
28
|
GameSentenceMiner/owocr/owocr/__init__.py,sha256=opjBOyGGyEqZCE6YdZPnyt7nVfiwyELHsXA0jAsjm14,25
|
29
29
|
GameSentenceMiner/owocr/owocr/__main__.py,sha256=r8MI6RAmbkTWqOJ59uvXoDS7CSw5jX5war9ULGWELrA,128
|
30
30
|
GameSentenceMiner/owocr/owocr/config.py,sha256=738QCJHEWpFhMh966plOcXYWwcshSiRsxjjIwldeTtI,7461
|
31
31
|
GameSentenceMiner/owocr/owocr/lens_betterproto.py,sha256=oNoISsPilVVRBBPVDtb4-roJtAhp8ZAuFTci3TGXtMc,39141
|
32
|
-
GameSentenceMiner/owocr/owocr/ocr.py,sha256=
|
33
|
-
GameSentenceMiner/owocr/owocr/run.py,sha256=
|
32
|
+
GameSentenceMiner/owocr/owocr/ocr.py,sha256=n24Xg8Z8dbcgLpq1u4d22z3tLV1evmf0dK3-Xocv3vs,39878
|
33
|
+
GameSentenceMiner/owocr/owocr/run.py,sha256=pEHxot24yRPvbeE3fmgWMcIILBQfUiSQ3sXjw0LJOF0,47791
|
34
34
|
GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py,sha256=fjJ3CSXLti3WboGPpmsa7MWOwIXsfpHC8N4zKahGGY0,3346
|
35
35
|
GameSentenceMiner/vad/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
36
36
|
GameSentenceMiner/vad/silero_trim.py,sha256=ULf3zwS-JMsY82cKF7gZxREHw8L6lgpWF2U1YqgE9Oc,1681
|
37
37
|
GameSentenceMiner/vad/vosk_helper.py,sha256=125X8C9NxFPlWWpoNsbOnEqKx8RCjXN109zNx_QXhyg,6070
|
38
38
|
GameSentenceMiner/vad/whisper_helper.py,sha256=JJ-iltCh813XdjyEw0Wn5DaErf6PDqfH0Efu1Md8cIY,3543
|
39
|
-
gamesentenceminer-2.7.
|
40
|
-
gamesentenceminer-2.7.
|
41
|
-
gamesentenceminer-2.7.
|
42
|
-
gamesentenceminer-2.7.
|
43
|
-
gamesentenceminer-2.7.
|
44
|
-
gamesentenceminer-2.7.
|
39
|
+
gamesentenceminer-2.7.16.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
40
|
+
gamesentenceminer-2.7.16.dist-info/METADATA,sha256=FnLkWirfE9GWkCtHhyegdVXNR9tvpwoIqPoUQBQElAw,5892
|
41
|
+
gamesentenceminer-2.7.16.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
|
42
|
+
gamesentenceminer-2.7.16.dist-info/entry_points.txt,sha256=2APEP25DbfjSxGeHtwBstMH8mulVhLkqF_b9bqzU6vQ,65
|
43
|
+
gamesentenceminer-2.7.16.dist-info/top_level.txt,sha256=V1hUY6xVSyUEohb0uDoN4UIE6rUZ_JYx8yMyPGX4PgQ,18
|
44
|
+
gamesentenceminer-2.7.16.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|