GameSentenceMiner 2.7.8__py3-none-any.whl → 2.7.10__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/ffmpeg.py +2 -2
- GameSentenceMiner/gsm.py +1 -1
- GameSentenceMiner/obs.py +1 -1
- GameSentenceMiner/ocr/gsm_ocr_config.py +26 -0
- GameSentenceMiner/ocr/owocr_area_selector.py +260 -241
- GameSentenceMiner/ocr/owocr_helper.py +200 -155
- GameSentenceMiner/owocr/owocr/run.py +12 -5
- GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py +16 -8
- GameSentenceMiner/utility_gui.py +53 -59
- {gamesentenceminer-2.7.8.dist-info → gamesentenceminer-2.7.10.dist-info}/METADATA +1 -1
- {gamesentenceminer-2.7.8.dist-info → gamesentenceminer-2.7.10.dist-info}/RECORD +15 -14
- {gamesentenceminer-2.7.8.dist-info → gamesentenceminer-2.7.10.dist-info}/WHEEL +1 -1
- {gamesentenceminer-2.7.8.dist-info → gamesentenceminer-2.7.10.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.7.8.dist-info → gamesentenceminer-2.7.10.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.7.8.dist-info → gamesentenceminer-2.7.10.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,6 @@
|
|
1
|
-
# area_selector.py
|
2
1
|
import asyncio
|
3
|
-
import base64
|
4
2
|
import difflib
|
3
|
+
import json
|
5
4
|
import logging
|
6
5
|
import os
|
7
6
|
import queue
|
@@ -9,52 +8,62 @@ import threading
|
|
9
8
|
import time
|
10
9
|
from datetime import datetime
|
11
10
|
from logging.handlers import RotatingFileHandler
|
11
|
+
from pathlib import Path
|
12
12
|
from tkinter import messagebox
|
13
13
|
|
14
14
|
import mss
|
15
15
|
import websockets
|
16
|
+
from rapidfuzz import fuzz
|
16
17
|
from PIL import Image, ImageDraw
|
17
|
-
import json
|
18
|
-
from pathlib import Path
|
19
18
|
|
20
19
|
from GameSentenceMiner import obs, util
|
21
20
|
from GameSentenceMiner.configuration import get_config, get_app_directory
|
22
|
-
from GameSentenceMiner.
|
21
|
+
from GameSentenceMiner.electron_config import get_ocr_scan_rate, get_requires_open_window
|
22
|
+
from GameSentenceMiner.ocr.gsm_ocr_config import OCRConfig, Rectangle
|
23
23
|
from GameSentenceMiner.owocr.owocr import screen_coordinate_picker, run
|
24
24
|
from GameSentenceMiner.owocr.owocr.run import TextFiltering
|
25
|
-
|
25
|
+
|
26
|
+
from dataclasses import dataclass
|
27
|
+
from typing import List, Optional
|
26
28
|
|
27
29
|
CONFIG_FILE = Path("ocr_config.json")
|
28
30
|
DEFAULT_IMAGE_PATH = r"C:\Users\Beangate\Pictures\msedge_acbl8GL7Ax.jpg" # CHANGE THIS
|
29
|
-
|
30
31
|
logger = logging.getLogger("GSM_OCR")
|
31
32
|
logger.setLevel(logging.DEBUG)
|
32
|
-
|
33
33
|
# Create a file handler for logging
|
34
34
|
log_file = os.path.join(get_app_directory(), "logs", "ocr_log.txt")
|
35
35
|
os.makedirs(os.path.join(get_app_directory(), "logs"), exist_ok=True)
|
36
|
-
|
37
36
|
file_handler = RotatingFileHandler(log_file, maxBytes=1024 * 1024, backupCount=5, encoding='utf-8')
|
38
37
|
file_handler.setLevel(logging.DEBUG)
|
39
|
-
|
40
38
|
# Create a formatter and set it for the handler
|
41
39
|
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
42
40
|
file_handler.setFormatter(formatter)
|
43
|
-
|
44
41
|
# Add the handler to the logger
|
45
42
|
logger.addHandler(file_handler)
|
46
43
|
|
44
|
+
console_handler = logging.StreamHandler()
|
45
|
+
console_handler.setLevel(logging.INFO)
|
46
|
+
console_handler.setFormatter(formatter)
|
47
|
+
logger.addHandler(console_handler)
|
48
|
+
|
49
|
+
|
47
50
|
def get_new_game_cords():
|
48
51
|
"""Allows multiple coordinate selections."""
|
49
52
|
coords_list = []
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
53
|
+
with mss.mss() as sct:
|
54
|
+
monitors = sct.monitors
|
55
|
+
monitor_map = {i: mon for i, mon in enumerate(monitors)}
|
56
|
+
while True:
|
57
|
+
selected_monitor_index, cords = screen_coordinate_picker.get_screen_selection_with_monitor(monitor_map)
|
58
|
+
selected_monitor = monitor_map[selected_monitor_index]
|
59
|
+
coords_list.append({"monitor": {"left": selected_monitor["left"], "top": selected_monitor["top"],
|
60
|
+
"width": selected_monitor["width"], "height": selected_monitor["height"],
|
61
|
+
"index": selected_monitor_index}, "coordinates": cords,
|
62
|
+
"is_excluded": False})
|
63
|
+
if messagebox.askyesno("Add Another Region", "Do you want to add another region?"):
|
64
|
+
continue
|
65
|
+
else:
|
66
|
+
break
|
58
67
|
app_dir = Path.home() / "AppData" / "Roaming" / "GameSentenceMiner"
|
59
68
|
ocr_config_dir = app_dir / "ocr_config"
|
60
69
|
ocr_config_dir.mkdir(parents=True, exist_ok=True)
|
@@ -62,13 +71,13 @@ def get_new_game_cords():
|
|
62
71
|
scene = util.sanitize_filename(obs.get_current_scene())
|
63
72
|
config_path = ocr_config_dir / f"{scene}.json"
|
64
73
|
with open(config_path, 'w') as f:
|
65
|
-
json.dump(coords_list, f, indent=4)
|
74
|
+
json.dump({"scene": scene, "window": None, "rectangles": coords_list}, f, indent=4)
|
66
75
|
print(f"Saved OCR config to {config_path}")
|
67
76
|
return coords_list
|
68
77
|
|
69
78
|
|
70
|
-
def get_ocr_config():
|
71
|
-
"""Loads
|
79
|
+
def get_ocr_config() -> OCRConfig:
|
80
|
+
"""Loads and updates screen capture areas from the corresponding JSON file."""
|
72
81
|
app_dir = Path.home() / "AppData" / "Roaming" / "GameSentenceMiner"
|
73
82
|
ocr_config_dir = app_dir / "ocr_config"
|
74
83
|
obs.connect_to_obs()
|
@@ -76,14 +85,58 @@ def get_ocr_config():
|
|
76
85
|
config_path = ocr_config_dir / f"{scene}.json"
|
77
86
|
if not config_path.exists():
|
78
87
|
raise Exception(f"No config file found at {config_path}.")
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
88
|
+
try:
|
89
|
+
with open(config_path, 'r', encoding="utf-8") as f:
|
90
|
+
config_data = json.load(f)
|
91
|
+
if "rectangles" in config_data and isinstance(config_data["rectangles"], list) and all(
|
92
|
+
isinstance(item, list) and len(item) == 4 for item in config_data["rectangles"]):
|
93
|
+
# Old config format, convert to new
|
94
|
+
new_rectangles = []
|
95
|
+
with mss.mss() as sct:
|
96
|
+
monitors = sct.monitors
|
97
|
+
default_monitor = monitors[1] if len(monitors) > 1 else monitors[0]
|
98
|
+
for rect in config_data["rectangles"]:
|
99
|
+
new_rectangles.append({
|
100
|
+
"monitor": {
|
101
|
+
"left": default_monitor["left"],
|
102
|
+
"top": default_monitor["top"],
|
103
|
+
"width": default_monitor["width"],
|
104
|
+
"height": default_monitor["height"],
|
105
|
+
"index": 0 # Assuming single monitor for old config
|
106
|
+
},
|
107
|
+
"coordinates": rect,
|
108
|
+
"is_excluded": False
|
109
|
+
})
|
110
|
+
for rect in config_data['excluded_rectangles']:
|
111
|
+
new_rectangles.append({
|
112
|
+
"monitor": {
|
113
|
+
"left": default_monitor["left"],
|
114
|
+
"top": default_monitor["top"],
|
115
|
+
"width": default_monitor["width"],
|
116
|
+
"height": default_monitor["height"],
|
117
|
+
"index": 0 # Assuming single monitor for old config
|
118
|
+
},
|
119
|
+
"coordinates": rect,
|
120
|
+
"is_excluded": True
|
121
|
+
})
|
122
|
+
new_config_data = {"scene": config_data.get("scene", scene), "window": config_data.get("window", None),
|
123
|
+
"rectangles": new_rectangles}
|
124
|
+
with open(config_path, 'w', encoding="utf-8") as f:
|
125
|
+
json.dump(new_config_data, f, indent=4)
|
126
|
+
return OCRConfig.from_dict(new_config_data)
|
127
|
+
elif "rectangles" in config_data and isinstance(config_data["rectangles"], list) and all(
|
128
|
+
isinstance(item, dict) and "coordinates" in item for item in config_data["rectangles"]):
|
129
|
+
logger.info("Loading new OCR config format.")
|
130
|
+
logger.info(config_data)
|
131
|
+
return OCRConfig.from_dict(config_data)
|
132
|
+
else:
|
133
|
+
raise Exception(f"Invalid config format in {config_path}.")
|
134
|
+
except json.JSONDecodeError:
|
135
|
+
print("Error decoding JSON. Please check your config file.")
|
136
|
+
return None
|
137
|
+
except Exception as e:
|
138
|
+
print(f"Error loading config: {e}")
|
139
|
+
return None
|
87
140
|
|
88
141
|
|
89
142
|
websocket_server_thread = None
|
@@ -130,7 +183,8 @@ class WebsocketServerThread(threading.Thread):
|
|
130
183
|
|
131
184
|
def send_text(self, text, line_time: datetime):
|
132
185
|
if text:
|
133
|
-
return asyncio.run_coroutine_threadsafe(
|
186
|
+
return asyncio.run_coroutine_threadsafe(
|
187
|
+
self.send_text_coroutine(json.dumps({"sentence": text, "time": line_time.isoformat()})), self.loop)
|
134
188
|
|
135
189
|
def stop_server(self):
|
136
190
|
self.loop.call_soon_threadsafe(self._stop_event.set)
|
@@ -140,129 +194,119 @@ class WebsocketServerThread(threading.Thread):
|
|
140
194
|
self._loop = asyncio.get_running_loop()
|
141
195
|
self._stop_event = stop_event = asyncio.Event()
|
142
196
|
self._event.set()
|
143
|
-
self.server = start_server = websockets.serve(self.server_handler,
|
197
|
+
self.server = start_server = websockets.serve(self.server_handler,
|
198
|
+
get_config().general.websocket_uri.split(":")[0],
|
199
|
+
get_config().general.websocket_uri.split(":")[1],
|
200
|
+
max_size=1000000000)
|
144
201
|
async with start_server:
|
145
202
|
await stop_event.wait()
|
203
|
+
|
146
204
|
asyncio.run(main())
|
147
205
|
|
206
|
+
|
148
207
|
all_cords = None
|
149
208
|
rectangles = None
|
150
209
|
|
151
|
-
def
|
152
|
-
global twopassocr, ocr2,
|
210
|
+
def do_second_ocr(ocr1_text, rectangle_index, time, img):
|
211
|
+
global twopassocr, ocr2, last_ocr1_results, last_ocr2_results
|
212
|
+
last_result = ([], -1)
|
213
|
+
|
214
|
+
previous_ocr1_text = last_ocr1_results[rectangle_index]
|
215
|
+
if fuzz.ratio(previous_ocr1_text, ocr1_text) >= 80:
|
216
|
+
logger.info("Seems like the same text, not doing 2nd pass")
|
217
|
+
last_ocr1_results[rectangle_index] = ocr1_text
|
218
|
+
try:
|
219
|
+
orig_text, text = run.process_and_write_results(img, None, None, last_result, TextFiltering(),
|
220
|
+
engine=ocr2)
|
221
|
+
previous_ocr2_text = last_ocr2_results[rectangle_index]
|
222
|
+
if fuzz.ratio(previous_ocr2_text, text) >= 80:
|
223
|
+
logger.info("Seems like the same text from previous ocr2 result, not sending")
|
224
|
+
return
|
225
|
+
last_ocr2_results[rectangle_index] = text
|
226
|
+
if get_config().advanced.ocr_sends_to_clipboard:
|
227
|
+
import pyperclip
|
228
|
+
pyperclip.copy(text)
|
229
|
+
websocket_server_thread.send_text(text, time)
|
230
|
+
except json.JSONDecodeError:
|
231
|
+
print("Invalid JSON received.")
|
232
|
+
except Exception as e:
|
233
|
+
logger.exception(e)
|
234
|
+
print(f"Error processing message: {e}")
|
235
|
+
|
236
|
+
|
237
|
+
last_oneocr_results_to_check = {} # Store last OCR result for each rectangle
|
238
|
+
last_oneocr_times = {} # Store last OCR time for each rectangle
|
239
|
+
text_stable_start_times = {} # Store the start time when text becomes stable for each rectangle
|
240
|
+
TEXT_APPEARENCE_DELAY = get_ocr_scan_rate() * 1000 + 500 # Adjust as needed
|
241
|
+
|
242
|
+
def text_callback(text, rectangle_index, time, img=None):
|
243
|
+
global twopassocr, ocr2, last_oneocr_results_to_check, last_oneocr_times, text_stable_start_times
|
244
|
+
|
245
|
+
current_time = time if time else datetime.now()
|
246
|
+
|
247
|
+
previous_text = last_oneocr_results_to_check.get(rectangle_index, "")
|
248
|
+
|
153
249
|
if not text:
|
250
|
+
if previous_text:
|
251
|
+
if rectangle_index in text_stable_start_times:
|
252
|
+
stable_time = text_stable_start_times[rectangle_index]
|
253
|
+
if twopassocr:
|
254
|
+
do_second_ocr(previous_text, rectangle_index, time, img)
|
255
|
+
else:
|
256
|
+
previous_ocr1_text = last_ocr1_results[rectangle_index]
|
257
|
+
if fuzz.ratio(previous_ocr1_text, text) >= 80:
|
258
|
+
logger.info("Seems like the same text, not sending")
|
259
|
+
last_ocr1_results[rectangle_index] = text
|
260
|
+
websocket_server_thread.send_text(previous_text, stable_time)
|
261
|
+
del text_stable_start_times[rectangle_index]
|
262
|
+
del last_oneocr_results_to_check[rectangle_index]
|
154
263
|
return
|
155
|
-
|
156
|
-
|
264
|
+
|
265
|
+
if rectangle_index not in last_oneocr_results_to_check:
|
266
|
+
last_oneocr_results_to_check[rectangle_index] = text
|
267
|
+
last_oneocr_times[rectangle_index] = current_time
|
268
|
+
text_stable_start_times[rectangle_index] = current_time
|
157
269
|
return
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
return
|
171
|
-
logger.debug(f"Similarity for region {i}: {similarity}")
|
172
|
-
last_oneocr_results[i] = text
|
173
|
-
last_result = ([], -1)
|
174
|
-
try:
|
175
|
-
sct_params = {'left': cords[0], 'top': cords[1], 'width': cords[2], 'height': cords[3]}
|
176
|
-
sct_img = sct.grab(sct_params)
|
177
|
-
img = Image.frombytes('RGB', sct_img.size, sct_img.bgra, 'raw', 'BGRX')
|
178
|
-
img = img.convert("RGBA")
|
179
|
-
draw = ImageDraw.Draw(img)
|
180
|
-
for exclusion in ocr_config.get("excluded_rectangles", []):
|
181
|
-
left, top, right, bottom = exclusion
|
182
|
-
draw.rectangle((left, top, right, bottom), fill=(0, 0, 0, 0))
|
183
|
-
# draw.rectangle((left, top, right, bottom), fill=(0,0,0))
|
184
|
-
orig_text, text = run.process_and_write_results(img, None, None, last_result, TextFiltering(),
|
185
|
-
engine=ocr2)
|
186
|
-
if ":gsm_prefix:" in text:
|
187
|
-
text = text.split(":gsm_prefix:")[1]
|
188
|
-
if get_config().advanced.ocr_sends_to_clipboard:
|
189
|
-
import pyperclip
|
190
|
-
pyperclip.copy(text)
|
191
|
-
websocket_server_thread.send_text(text, line_time)
|
192
|
-
except json.JSONDecodeError:
|
193
|
-
print("Invalid JSON received.")
|
194
|
-
except Exception as e:
|
195
|
-
logger.exception(e)
|
196
|
-
print(f"Error processing message: {e}")
|
270
|
+
|
271
|
+
stable = text_stable_start_times.get(rectangle_index)
|
272
|
+
|
273
|
+
if stable:
|
274
|
+
time_since_stable_ms = int((current_time - stable).total_seconds() * 1000)
|
275
|
+
|
276
|
+
if time_since_stable_ms >= TEXT_APPEARENCE_DELAY:
|
277
|
+
last_oneocr_results_to_check[rectangle_index] = text
|
278
|
+
last_oneocr_times[rectangle_index] = current_time
|
279
|
+
else:
|
280
|
+
last_oneocr_results_to_check[rectangle_index] = text
|
281
|
+
last_oneocr_times[rectangle_index] = current_time
|
197
282
|
|
198
283
|
done = False
|
199
284
|
|
200
|
-
|
285
|
+
|
286
|
+
def run_oneocr(ocr_config: OCRConfig, i):
|
201
287
|
global done
|
288
|
+
rect_config = ocr_config.rectangles[i]
|
289
|
+
coords = rect_config.coordinates
|
290
|
+
monitor_config = rect_config.monitor
|
291
|
+
exclusions = (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)
|
202
293
|
run.run(read_from="screencapture", write_to="callback",
|
203
|
-
screen_capture_area=
|
204
|
-
|
294
|
+
screen_capture_area=screen_area,
|
295
|
+
# screen_capture_monitor=monitor_config['index'],
|
296
|
+
screen_capture_window=ocr_config.window,
|
205
297
|
screen_capture_only_active_windows=get_requires_open_window(),
|
206
298
|
screen_capture_delay_secs=get_ocr_scan_rate(), engine=ocr1,
|
207
299
|
text_callback=text_callback,
|
208
|
-
screen_capture_exclusions=
|
300
|
+
screen_capture_exclusions=exclusions,
|
209
301
|
rectangle=i)
|
210
302
|
done = True
|
211
303
|
|
212
304
|
|
213
|
-
# async def websocket_client():
|
214
|
-
# uri = "ws://localhost:7331" # Replace with your hosted websocket address
|
215
|
-
# print("Connecting to WebSocket...")
|
216
|
-
# async with websockets.connect(uri) as websocket:
|
217
|
-
# print("Connected to WebSocket.")
|
218
|
-
#
|
219
|
-
# try:
|
220
|
-
# while True:
|
221
|
-
# message = await websocket.recv()
|
222
|
-
# if not message:
|
223
|
-
# continue
|
224
|
-
# line_time = datetime.now()
|
225
|
-
# get_line_history().add_secondary_line(message)
|
226
|
-
# print(f"Received message: {message}, ATTEMPTING LENS OCR")
|
227
|
-
# if ":gsm_prefix:" in message:
|
228
|
-
# i = int(message.split(":gsm_prefix:")[0])
|
229
|
-
# cords = all_cords[i] if i else all_cords[0]
|
230
|
-
# similarity = difflib.SequenceMatcher(None, last_oneocr_results[i], message).ratio()
|
231
|
-
# if similarity > .8:
|
232
|
-
# continue
|
233
|
-
# print(f"Similarity for region {i}: {similarity}")
|
234
|
-
# last_oneocr_results[i] = message
|
235
|
-
# last_result = ([], -1)
|
236
|
-
# try:
|
237
|
-
# sct_params = {'top': cords[1], 'left': cords[0], 'width': cords[2], 'height': cords[3]}
|
238
|
-
# with mss.mss() as sct:
|
239
|
-
# sct_img = sct.grab(sct_params)
|
240
|
-
# img = Image.frombytes('RGB', sct_img.size, sct_img.bgra, 'raw', 'BGRX')
|
241
|
-
# draw = ImageDraw.Draw(img)
|
242
|
-
# for exclusion in ocr_config.get("excluded_rectangles", []):
|
243
|
-
# exclusion = tuple(exclusion)
|
244
|
-
# draw.rectangle(exclusion, fill="black")
|
245
|
-
# orig_text, text = run.process_and_write_results(img, "results.txt", None, last_result, TextFiltering(), engine="glens")
|
246
|
-
# if ":gsm_prefix:" in text:
|
247
|
-
# text = text.split(":gsm_prefix:")[1]
|
248
|
-
# websocket_server_thread.send_text(text, line_time)
|
249
|
-
# except json.JSONDecodeError:
|
250
|
-
# print("Invalid JSON received.")
|
251
|
-
# except Exception as e:
|
252
|
-
# logger.exception(e)
|
253
|
-
# print(f"Error processing message: {e}")
|
254
|
-
# except websockets.exceptions.ConnectionClosed:
|
255
|
-
# print("WebSocket connection closed.")
|
256
|
-
# except Exception as e:
|
257
|
-
# print(f"WebSocket error: {e}")
|
258
|
-
|
259
|
-
|
260
305
|
if __name__ == "__main__":
|
261
306
|
global ocr1, ocr2, twopassocr
|
262
307
|
import sys
|
263
308
|
|
264
309
|
args = sys.argv[1:]
|
265
|
-
|
266
310
|
if len(args) == 3:
|
267
311
|
ocr1 = args[0]
|
268
312
|
ocr2 = args[1]
|
@@ -277,34 +321,35 @@ if __name__ == "__main__":
|
|
277
321
|
twopassocr = False
|
278
322
|
else:
|
279
323
|
ocr1 = "oneocr"
|
280
|
-
|
324
|
+
ocr2 = "glens"
|
325
|
+
twopassocr = True
|
281
326
|
logger.info(f"Received arguments: ocr1={ocr1}, ocr2={ocr2}, twopassocr={twopassocr}")
|
282
327
|
global ocr_config
|
283
|
-
ocr_config = get_ocr_config()
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
328
|
+
ocr_config: OCRConfig = get_ocr_config()
|
329
|
+
if ocr_config and ocr_config.rectangles:
|
330
|
+
rectangles = ocr_config.rectangles
|
331
|
+
last_ocr1_results = [""] * len(rectangles) if rectangles else [""]
|
332
|
+
last_ocr2_results = [""] * len(rectangles) if rectangles else [""]
|
333
|
+
oneocr_threads = []
|
334
|
+
run.init_config(False)
|
335
|
+
if rectangles:
|
336
|
+
for i, rectangle in enumerate(rectangles):
|
337
|
+
thread = threading.Thread(target=run_oneocr, args=(ocr_config, i,), daemon=True)
|
338
|
+
oneocr_threads.append(thread)
|
339
|
+
thread.start()
|
340
|
+
else:
|
341
|
+
single_ocr_thread = threading.Thread(target=run_oneocr, args=(ocr_config, 0,), daemon=True)
|
342
|
+
oneocr_threads.append(single_ocr_thread)
|
343
|
+
single_ocr_thread.start()
|
344
|
+
websocket_server_thread = WebsocketServerThread(read=True)
|
345
|
+
websocket_server_thread.start()
|
346
|
+
try:
|
347
|
+
while not done:
|
348
|
+
time.sleep(1)
|
349
|
+
except KeyboardInterrupt as e:
|
350
|
+
pass
|
351
|
+
for thread in oneocr_threads:
|
352
|
+
thread.join()
|
353
|
+
# asyncio.run(websocket_client())
|
293
354
|
else:
|
294
|
-
|
295
|
-
oneocr_threads.append(single_ocr_thread)
|
296
|
-
single_ocr_thread.start()
|
297
|
-
|
298
|
-
websocket_server_thread = WebsocketServerThread(read=True)
|
299
|
-
websocket_server_thread.start()
|
300
|
-
|
301
|
-
try:
|
302
|
-
while not done:
|
303
|
-
time.sleep(1)
|
304
|
-
except KeyboardInterrupt as e:
|
305
|
-
pass
|
306
|
-
|
307
|
-
for thread in oneocr_threads:
|
308
|
-
thread.join()
|
309
|
-
|
310
|
-
# asyncio.run(websocket_client())
|
355
|
+
print("Failed to load OCR configuration. Please check the logs.")
|
@@ -571,7 +571,7 @@ def process_and_write_results(img_or_path, write_to, notifications, last_result,
|
|
571
571
|
elif write_to == 'clipboard':
|
572
572
|
pyperclipfix.copy(text)
|
573
573
|
elif write_to == "callback":
|
574
|
-
txt_callback(text, rectangle, start_time)
|
574
|
+
txt_callback(text, rectangle, start_time, img_or_path)
|
575
575
|
elif write_to:
|
576
576
|
with Path(write_to).open('a', encoding='utf-8') as f:
|
577
577
|
f.write(text + '\n')
|
@@ -1026,6 +1026,7 @@ def run(read_from=None,
|
|
1026
1026
|
else:
|
1027
1027
|
try:
|
1028
1028
|
coord_left, coord_top, right, bottom = win32gui.GetWindowRect(window_handle)
|
1029
|
+
|
1029
1030
|
coord_width = right - coord_left
|
1030
1031
|
coord_height = bottom - coord_top
|
1031
1032
|
|
@@ -1045,12 +1046,18 @@ def run(read_from=None,
|
|
1045
1046
|
on_window_closed(False)
|
1046
1047
|
break
|
1047
1048
|
|
1048
|
-
rand = random.randint(1, 10)
|
1049
|
+
# rand = random.randint(1, 10)
|
1049
1050
|
|
1050
1051
|
img = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
|
1051
1052
|
|
1053
|
+
# if rand == 1:
|
1054
|
+
# img.show()
|
1055
|
+
|
1052
1056
|
if custom_left:
|
1053
|
-
img = img.crop((custom_left, custom_top, custom_width, custom_height))
|
1057
|
+
img = img.crop((custom_left, custom_top, custom_left + custom_width, custom_top + custom_height))
|
1058
|
+
|
1059
|
+
# if rand == 1:
|
1060
|
+
# img.show()
|
1054
1061
|
|
1055
1062
|
win32gui.DeleteObject(save_bitmap.GetHandle())
|
1056
1063
|
save_dc.DeleteDC()
|
@@ -1064,8 +1071,8 @@ def run(read_from=None,
|
|
1064
1071
|
img = img.convert("RGBA")
|
1065
1072
|
draw = ImageDraw.Draw(img)
|
1066
1073
|
for exclusion in screen_capture_exclusions:
|
1067
|
-
left, top,
|
1068
|
-
draw.rectangle((left, top,
|
1074
|
+
left, top, width, height = exclusion
|
1075
|
+
draw.rectangle((left, top, left + width, top + height), fill=(0, 0, 0, 0))
|
1069
1076
|
# draw.rectangle((left, top, right, bottom), fill=(0, 0, 0))
|
1070
1077
|
# img.save(os.path.join(get_temporary_directory(), 'screencapture.png'), 'png')
|
1071
1078
|
res, _ = process_and_write_results(img, write_to, notifications, last_result, filtering, rectangle=rectangle, start_time=start_time)
|
@@ -55,12 +55,12 @@ class ScreenSelector:
|
|
55
55
|
def on_release(event):
|
56
56
|
nonlocal start_x, start_y
|
57
57
|
end_x, end_y = event.x, event.y
|
58
|
-
|
59
|
-
x1 = min(start_x, end_x)
|
60
|
-
y1 = min(start_y, end_y)
|
61
|
-
x2 = max(start_x, end_x)
|
62
|
-
y2 = max(start_y, end_y)
|
63
|
-
|
58
|
+
|
59
|
+
x1 = min(start_x, end_x)
|
60
|
+
y1 = min(start_y, end_y)
|
61
|
+
x2 = max(start_x, end_x)
|
62
|
+
y2 = max(start_y, end_y)
|
63
|
+
|
64
64
|
self.on_select(monitor, (x1, y1, x2 - x1, y2 - y1))
|
65
65
|
|
66
66
|
canvas.bind('<ButtonPress-1>', on_click)
|
@@ -90,11 +90,19 @@ def get_screen_selection():
|
|
90
90
|
with Manager() as manager:
|
91
91
|
res = manager.dict()
|
92
92
|
process = Process(target=run_screen_selector, args=(res,))
|
93
|
-
|
94
|
-
process.start()
|
93
|
+
|
94
|
+
process.start()
|
95
95
|
process.join()
|
96
96
|
|
97
97
|
if 'monitor' in res and 'coordinates' in res:
|
98
98
|
return res.copy()
|
99
99
|
else:
|
100
100
|
return False
|
101
|
+
|
102
|
+
if __name__ == "__main__":
|
103
|
+
selection = get_screen_selection()
|
104
|
+
if selection:
|
105
|
+
print(f"Selected monitor: {selection['monitor']}")
|
106
|
+
print(f"Selected coordinates: {selection['coordinates']}")
|
107
|
+
else:
|
108
|
+
print("No selection made or process was interrupted.")
|