GameSentenceMiner 2.6.5__py3-none-any.whl → 2.7.0__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/anki.py +2 -2
- GameSentenceMiner/configuration.py +3 -1
- GameSentenceMiner/gametext.py +28 -14
- GameSentenceMiner/gsm.py +8 -4
- GameSentenceMiner/obs.py +41 -8
- GameSentenceMiner/ocr/__init__.py +0 -0
- GameSentenceMiner/ocr/ocrconfig.py +130 -0
- GameSentenceMiner/ocr/owocr_area_selector.py +275 -0
- GameSentenceMiner/ocr/owocr_helper.py +306 -0
- GameSentenceMiner/owocr/owocr/__init__.py +1 -0
- GameSentenceMiner/owocr/owocr/__main__.py +8 -0
- GameSentenceMiner/owocr/owocr/config.py +134 -0
- GameSentenceMiner/owocr/owocr/lens_betterproto.py +1238 -0
- GameSentenceMiner/owocr/owocr/ocr.py +975 -0
- GameSentenceMiner/owocr/owocr/run.py +1096 -0
- GameSentenceMiner/owocr/owocr/screen_coordinate_picker.py +100 -0
- {gamesentenceminer-2.6.5.dist-info → gamesentenceminer-2.7.0.dist-info}/METADATA +2 -2
- {gamesentenceminer-2.6.5.dist-info → gamesentenceminer-2.7.0.dist-info}/RECORD +22 -11
- {gamesentenceminer-2.6.5.dist-info → gamesentenceminer-2.7.0.dist-info}/WHEEL +0 -0
- {gamesentenceminer-2.6.5.dist-info → gamesentenceminer-2.7.0.dist-info}/entry_points.txt +0 -0
- {gamesentenceminer-2.6.5.dist-info → gamesentenceminer-2.7.0.dist-info}/licenses/LICENSE +0 -0
- {gamesentenceminer-2.6.5.dist-info → gamesentenceminer-2.7.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,275 @@
|
|
1
|
+
import ctypes
|
2
|
+
import sys
|
3
|
+
import tkinter as tk
|
4
|
+
from tkinter import messagebox, simpledialog
|
5
|
+
import json
|
6
|
+
from pathlib import Path
|
7
|
+
import time # Optional: for debugging timing if needed
|
8
|
+
from PIL import Image, ImageTk
|
9
|
+
import io
|
10
|
+
import keyboard
|
11
|
+
|
12
|
+
from GameSentenceMiner import obs, util
|
13
|
+
|
14
|
+
class DynamicAreaSelector(tk.Tk):
|
15
|
+
def __init__(self, window_title):
|
16
|
+
super().__init__()
|
17
|
+
self.title("Dynamic Area Selector")
|
18
|
+
self.attributes('-fullscreen', True)
|
19
|
+
self.attributes("-topmost", True) # Make the window always on top
|
20
|
+
self.attributes("-alpha", 0.20)
|
21
|
+
|
22
|
+
self.window_title = window_title
|
23
|
+
self.rects = []
|
24
|
+
self.start_x = None
|
25
|
+
self.start_y = None
|
26
|
+
self.current_rect_id = None
|
27
|
+
self.drawn_rect_ids = []
|
28
|
+
self.saved_rect_coords = []
|
29
|
+
self.excluded_rect_coords = [] # New list for excluded rectangles
|
30
|
+
self.drawn_excluded_rect_ids = [] # New list for excluded rect ids
|
31
|
+
self.image_mode = False
|
32
|
+
self.image_item = None
|
33
|
+
self.image_tk = None
|
34
|
+
self.canvas = None
|
35
|
+
|
36
|
+
if not self.initialize_ui():
|
37
|
+
self.after(0, self.destroy)
|
38
|
+
return
|
39
|
+
|
40
|
+
self.canvas.bind("<ButtonPress-1>", self.on_press)
|
41
|
+
self.canvas.bind("<B1-Motion>", self.on_drag)
|
42
|
+
self.canvas.bind("<ButtonRelease-1>", self.on_release)
|
43
|
+
self.canvas.bind("<Button-3>", self.on_right_click) # Bind right-click
|
44
|
+
self.bind("<Control-s>", self.save_rects_event)
|
45
|
+
self.bind("<Control-z>", self.undo_last_rect)
|
46
|
+
self.bind("<Escape>", self.quit_app)
|
47
|
+
self.bind("<Control-i>", self.toggle_image_mode)
|
48
|
+
|
49
|
+
self.protocol("WM_DELETE_WINDOW", self.on_close)
|
50
|
+
|
51
|
+
def initialize_ui(self):
|
52
|
+
try:
|
53
|
+
self.canvas = tk.Canvas(self, highlightthickness=0)
|
54
|
+
self.canvas.pack(fill=tk.BOTH, expand=True)
|
55
|
+
|
56
|
+
self.load_and_draw_existing_rects()
|
57
|
+
return True
|
58
|
+
|
59
|
+
except Exception as e:
|
60
|
+
messagebox.showerror("Initialization Error", f"Failed: {e}")
|
61
|
+
print(f"Initialization error details: {e}")
|
62
|
+
return False
|
63
|
+
|
64
|
+
def load_and_draw_existing_rects(self):
|
65
|
+
if self.get_scene_ocr_config().exists():
|
66
|
+
try:
|
67
|
+
with open(self.get_scene_ocr_config(), 'r') as f:
|
68
|
+
config_data = json.load(f)
|
69
|
+
loaded_rects = []
|
70
|
+
loaded_excluded_rects = []
|
71
|
+
for r in config_data.get("rectangles", []):
|
72
|
+
try:
|
73
|
+
coords = tuple(map(float, r))
|
74
|
+
if len(coords) == 4:
|
75
|
+
loaded_rects.append(coords)
|
76
|
+
else:
|
77
|
+
print(f"Skipping invalid rectangle data: {r}")
|
78
|
+
except (ValueError, TypeError) as coord_err:
|
79
|
+
print(f"Skipping invalid coordinate data in rectangle {r}: {coord_err}")
|
80
|
+
|
81
|
+
for r in config_data.get("excluded_rectangles", []):
|
82
|
+
try:
|
83
|
+
coords = tuple(map(float, r))
|
84
|
+
if len(coords) == 4:
|
85
|
+
loaded_excluded_rects.append(coords)
|
86
|
+
else:
|
87
|
+
print(f"Skipping invalid excluded rectangle data: {r}")
|
88
|
+
except (ValueError, TypeError) as coord_err:
|
89
|
+
print(f"Skipping invalid coordinate data in excluded rectangle {r}: {coord_err}")
|
90
|
+
|
91
|
+
self.saved_rect_coords = loaded_rects
|
92
|
+
self.excluded_rect_coords = loaded_excluded_rects
|
93
|
+
for rect_id in self.drawn_rect_ids:
|
94
|
+
self.canvas.delete(rect_id)
|
95
|
+
for rect_id in self.drawn_excluded_rect_ids:
|
96
|
+
self.canvas.delete(rect_id)
|
97
|
+
self.drawn_rect_ids = []
|
98
|
+
self.drawn_excluded_rect_ids = []
|
99
|
+
|
100
|
+
for rect in self.saved_rect_coords:
|
101
|
+
x1, y1, x2, y2 = rect
|
102
|
+
rect_id = self.canvas.create_rectangle(x1, y1, x2, y2, outline='blue', width=2)
|
103
|
+
self.drawn_rect_ids.append(rect_id)
|
104
|
+
|
105
|
+
for rect in self.excluded_rect_coords:
|
106
|
+
x1, y1, x2, y2 = rect
|
107
|
+
rect_id = self.canvas.create_rectangle(x1, y1, x2, y2, outline='orange', width=2)
|
108
|
+
self.drawn_excluded_rect_ids.append(rect_id)
|
109
|
+
|
110
|
+
except json.JSONDecodeError:
|
111
|
+
messagebox.showwarning("Config Load Warning", f"Could not parse {self.get_scene_ocr_config()}. Starting with no rectangles.")
|
112
|
+
self.saved_rect_coords = []
|
113
|
+
self.excluded_rect_coords = []
|
114
|
+
except FileNotFoundError:
|
115
|
+
self.saved_rect_coords = []
|
116
|
+
self.excluded_rect_coords = []
|
117
|
+
except Exception as e:
|
118
|
+
messagebox.showerror("Config Load Error", f"Error loading rectangles: {e}")
|
119
|
+
self.saved_rect_coords = []
|
120
|
+
self.excluded_rect_coords = []
|
121
|
+
|
122
|
+
def on_press(self, event):
|
123
|
+
if self.current_rect_id is None:
|
124
|
+
self.start_x = self.canvas.canvasx(event.x)
|
125
|
+
self.start_y = self.canvas.canvasy(event.y)
|
126
|
+
outline_color = 'red' if not event.state & 0x0001 else 'purple' # check if shift is pressed
|
127
|
+
self.current_rect_id = self.canvas.create_rectangle(
|
128
|
+
self.start_x, self.start_y, self.start_x, self.start_y,
|
129
|
+
outline=outline_color, width=2
|
130
|
+
)
|
131
|
+
|
132
|
+
def on_drag(self, event):
|
133
|
+
if self.current_rect_id is not None:
|
134
|
+
cur_x = self.canvas.canvasx(event.x)
|
135
|
+
cur_y = self.canvas.canvasy(event.y)
|
136
|
+
self.canvas.coords(self.current_rect_id, self.start_x, self.start_y, cur_x, cur_y)
|
137
|
+
|
138
|
+
def on_release(self, event):
|
139
|
+
if self.current_rect_id is not None:
|
140
|
+
end_x = self.canvas.canvasx(event.x)
|
141
|
+
end_y = self.canvas.canvasy(event.y)
|
142
|
+
x1 = min(self.start_x, end_x)
|
143
|
+
y1 = min(self.start_y, end_y)
|
144
|
+
x2 = max(self.start_x, end_x)
|
145
|
+
y2 = max(self.start_y, end_y)
|
146
|
+
|
147
|
+
self.canvas.coords(self.current_rect_id, x1, y1, x2, y2)
|
148
|
+
if event.state & 0x0001: # shift is pressed
|
149
|
+
self.canvas.itemconfig(self.current_rect_id, outline='orange')
|
150
|
+
self.excluded_rect_coords.append((x1, y1, x2, y2))
|
151
|
+
self.drawn_excluded_rect_ids.append(self.current_rect_id)
|
152
|
+
else:
|
153
|
+
self.canvas.itemconfig(self.current_rect_id, outline='green')
|
154
|
+
self.saved_rect_coords.append((x1, y1, x2, y2))
|
155
|
+
self.drawn_rect_ids.append(self.current_rect_id)
|
156
|
+
|
157
|
+
self.current_rect_id = None
|
158
|
+
self.start_x = None
|
159
|
+
self.start_y = None
|
160
|
+
|
161
|
+
def save_rects(self):
|
162
|
+
try:
|
163
|
+
serializable_rects = [
|
164
|
+
tuple(map(int, rect)) for rect in self.saved_rect_coords
|
165
|
+
]
|
166
|
+
serializable_excluded_rects = [
|
167
|
+
tuple(map(int, rect)) for rect in self.excluded_rect_coords
|
168
|
+
]
|
169
|
+
with open(self.get_scene_ocr_config(), 'w') as f:
|
170
|
+
json.dump({"window": self.window_title if self.window_title else "", "scene": obs.get_current_scene(), "rectangles": serializable_rects, "excluded_rectangles": serializable_excluded_rects}, f, indent=4)
|
171
|
+
for rect_id in self.drawn_rect_ids:
|
172
|
+
if self.canvas.winfo_exists():
|
173
|
+
try:
|
174
|
+
self.canvas.itemconfig(rect_id, outline='blue')
|
175
|
+
except tk.TclError:
|
176
|
+
pass
|
177
|
+
for rect_id in self.drawn_excluded_rect_ids:
|
178
|
+
if self.canvas.winfo_exists():
|
179
|
+
try:
|
180
|
+
self.canvas.itemconfig(rect_id, outline='orange')
|
181
|
+
except tk.TclError:
|
182
|
+
pass
|
183
|
+
print("Rectangles saved.")
|
184
|
+
self.on_close()
|
185
|
+
except Exception as e:
|
186
|
+
messagebox.showerror("Error", f"Failed to save rectangles: {e}")
|
187
|
+
|
188
|
+
def save_rects_event(self, event=None):
|
189
|
+
self.save_rects()
|
190
|
+
|
191
|
+
def undo_last_rect(self, event=None):
|
192
|
+
if self.saved_rect_coords and self.drawn_rect_ids:
|
193
|
+
self.saved_rect_coords.pop()
|
194
|
+
last_rect_id = self.drawn_rect_ids.pop()
|
195
|
+
if self.canvas.winfo_exists():
|
196
|
+
self.canvas.delete(last_rect_id)
|
197
|
+
elif self.excluded_rect_coords and self.drawn_excluded_rect_ids:
|
198
|
+
self.excluded_rect_coords.pop()
|
199
|
+
last_rect_id = self.drawn_excluded_rect_ids.pop()
|
200
|
+
if self.canvas.winfo_exists():
|
201
|
+
self.canvas.delete(last_rect_id)
|
202
|
+
elif self.current_rect_id is not None:
|
203
|
+
if self.canvas.winfo_exists():
|
204
|
+
self.canvas.delete(self.current_rect_id)
|
205
|
+
self.current_rect_id = None
|
206
|
+
self.start_x = None
|
207
|
+
self.start_y = None
|
208
|
+
|
209
|
+
def quit_app(self, event=None):
|
210
|
+
print("Escape pressed, closing application.")
|
211
|
+
self.on_close()
|
212
|
+
|
213
|
+
def on_close(self):
|
214
|
+
self.destroy()
|
215
|
+
|
216
|
+
def get_scene_ocr_config(self):
|
217
|
+
app_dir = Path.home() / "AppData" / "Roaming" / "GameSentenceMiner"
|
218
|
+
ocr_config_dir = app_dir / "ocr_config"
|
219
|
+
ocr_config_dir.mkdir(parents=True, exist_ok=True)
|
220
|
+
scene = util.sanitize_filename(obs.get_current_scene())
|
221
|
+
config_path = ocr_config_dir / f"{scene}.json"
|
222
|
+
return config_path
|
223
|
+
|
224
|
+
def on_right_click(self, event):
|
225
|
+
"""Deletes the rectangle clicked with the right mouse button."""
|
226
|
+
item = self.canvas.find_closest(event.x, event.y)
|
227
|
+
|
228
|
+
if item:
|
229
|
+
if item[0] in self.drawn_rect_ids: # Check if it's a saved rectangle
|
230
|
+
index = self.drawn_rect_ids.index(item[0])
|
231
|
+
self.canvas.delete(item[0])
|
232
|
+
del self.drawn_rect_ids[index]
|
233
|
+
del self.saved_rect_coords[index]
|
234
|
+
elif item[0] in self.drawn_excluded_rect_ids:
|
235
|
+
index = self.drawn_excluded_rect_ids.index(item[0])
|
236
|
+
self.canvas.delete(item[0])
|
237
|
+
del self.drawn_excluded_rect_ids[index]
|
238
|
+
del self.excluded_rect_coords[index]
|
239
|
+
|
240
|
+
def setup_hotkey(self):
|
241
|
+
keyboard.add_hotkey('F13', self.lift_window) # Example hotkey
|
242
|
+
|
243
|
+
def lift_window(self):
|
244
|
+
self.lift() # Bring the window to the front
|
245
|
+
|
246
|
+
def toggle_image_mode(self, event=None):
|
247
|
+
self.image_mode = not self.image_mode
|
248
|
+
if self.image_mode:
|
249
|
+
self.attributes("-alpha", 1.0)
|
250
|
+
self.load_image_from_obs()
|
251
|
+
else:
|
252
|
+
self.attributes("-alpha", 0.20)
|
253
|
+
if self.image_item:
|
254
|
+
self.canvas.delete(self.image_item)
|
255
|
+
self.image_item = None
|
256
|
+
self.image_tk = None
|
257
|
+
|
258
|
+
def load_image_from_obs(self):
|
259
|
+
try:
|
260
|
+
image_path = obs.get_screenshot()
|
261
|
+
image = Image.open(image_path)
|
262
|
+
self.image_tk = ImageTk.PhotoImage(image)
|
263
|
+
self.image_item = self.canvas.create_image(0, 0, anchor=tk.NW, image=self.image_tk)
|
264
|
+
self.canvas.tag_lower(self.image_item)
|
265
|
+
except Exception as e:
|
266
|
+
messagebox.showerror("Image Load Error", f"Failed to load image from OBS: {e}")
|
267
|
+
|
268
|
+
def run_screen_picker(window_title):
|
269
|
+
app = DynamicAreaSelector(window_title)
|
270
|
+
app.mainloop()
|
271
|
+
|
272
|
+
if __name__ == "__main__":
|
273
|
+
args = sys.argv[1:]
|
274
|
+
obs.connect_to_obs()
|
275
|
+
run_screen_picker(args[0] if len(args) > 0 else None)
|
@@ -0,0 +1,306 @@
|
|
1
|
+
# area_selector.py
|
2
|
+
import asyncio
|
3
|
+
import base64
|
4
|
+
import difflib
|
5
|
+
import logging
|
6
|
+
import os
|
7
|
+
import queue
|
8
|
+
import threading
|
9
|
+
import time
|
10
|
+
from datetime import datetime
|
11
|
+
from logging.handlers import RotatingFileHandler
|
12
|
+
from tkinter import messagebox
|
13
|
+
|
14
|
+
import mss
|
15
|
+
import websockets
|
16
|
+
from PIL import Image, ImageDraw
|
17
|
+
import json
|
18
|
+
from pathlib import Path
|
19
|
+
|
20
|
+
from GameSentenceMiner import obs, util
|
21
|
+
from GameSentenceMiner.configuration import get_config, get_app_directory
|
22
|
+
from GameSentenceMiner.gametext import get_line_history
|
23
|
+
from GameSentenceMiner.owocr.owocr import screen_coordinate_picker, run
|
24
|
+
from GameSentenceMiner.owocr.owocr.run import TextFiltering
|
25
|
+
|
26
|
+
CONFIG_FILE = Path("ocr_config.json")
|
27
|
+
DEFAULT_IMAGE_PATH = r"C:\Users\Beangate\Pictures\msedge_acbl8GL7Ax.jpg" # CHANGE THIS
|
28
|
+
|
29
|
+
logger = logging.getLogger("GSM_OCR")
|
30
|
+
logger.setLevel(logging.DEBUG)
|
31
|
+
|
32
|
+
# Create a file handler for logging
|
33
|
+
log_file = os.path.join(get_app_directory(), "logs", "ocr_log.txt")
|
34
|
+
os.makedirs(os.path.join(get_app_directory(), "logs"), exist_ok=True)
|
35
|
+
|
36
|
+
file_handler = RotatingFileHandler(log_file, maxBytes=1024 * 1024, backupCount=5, encoding='utf-8')
|
37
|
+
file_handler.setLevel(logging.DEBUG)
|
38
|
+
|
39
|
+
# Create a formatter and set it for the handler
|
40
|
+
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
41
|
+
file_handler.setFormatter(formatter)
|
42
|
+
|
43
|
+
# Add the handler to the logger
|
44
|
+
logger.addHandler(file_handler)
|
45
|
+
|
46
|
+
def get_new_game_cords():
|
47
|
+
"""Allows multiple coordinate selections."""
|
48
|
+
coords_list = []
|
49
|
+
while True:
|
50
|
+
cords = screen_coordinate_picker.get_screen_selection()
|
51
|
+
coords_list.append({"coordinates": cords})
|
52
|
+
if messagebox.askyesno("Add Another Region", "Do you want to add another region?"):
|
53
|
+
continue
|
54
|
+
else:
|
55
|
+
break
|
56
|
+
|
57
|
+
app_dir = Path.home() / "AppData" / "Roaming" / "GameSentenceMiner"
|
58
|
+
ocr_config_dir = app_dir / "ocr_config"
|
59
|
+
ocr_config_dir.mkdir(parents=True, exist_ok=True)
|
60
|
+
obs.connect_to_obs()
|
61
|
+
scene = util.sanitize_filename(obs.get_current_scene())
|
62
|
+
config_path = ocr_config_dir / f"{scene}.json"
|
63
|
+
with open(config_path, 'w') as f:
|
64
|
+
json.dump(coords_list, f, indent=4)
|
65
|
+
print(f"Saved OCR config to {config_path}")
|
66
|
+
return coords_list
|
67
|
+
|
68
|
+
|
69
|
+
def get_ocr_config():
|
70
|
+
"""Loads multiple screen capture areas from the corresponding JSON file."""
|
71
|
+
app_dir = Path.home() / "AppData" / "Roaming" / "GameSentenceMiner"
|
72
|
+
ocr_config_dir = app_dir / "ocr_config"
|
73
|
+
obs.connect_to_obs()
|
74
|
+
scene = util.sanitize_filename(obs.get_current_scene())
|
75
|
+
config_path = ocr_config_dir / f"{scene}.json"
|
76
|
+
if not config_path.exists():
|
77
|
+
raise Exception(f"No config file found at {config_path}.")
|
78
|
+
|
79
|
+
if not config_path.exists():
|
80
|
+
print("Config Screen picker failed to make file. Please run again.")
|
81
|
+
return
|
82
|
+
|
83
|
+
with open(config_path, 'r') as f:
|
84
|
+
coords_list = json.load(f)
|
85
|
+
return coords_list
|
86
|
+
|
87
|
+
|
88
|
+
websocket_server_thread = None
|
89
|
+
websocket_queue = queue.Queue()
|
90
|
+
paused = False
|
91
|
+
|
92
|
+
|
93
|
+
class WebsocketServerThread(threading.Thread):
|
94
|
+
def __init__(self, read):
|
95
|
+
super().__init__(daemon=True)
|
96
|
+
self._loop = None
|
97
|
+
self.read = read
|
98
|
+
self.clients = set()
|
99
|
+
self._event = threading.Event()
|
100
|
+
|
101
|
+
@property
|
102
|
+
def loop(self):
|
103
|
+
self._event.wait()
|
104
|
+
return self._loop
|
105
|
+
|
106
|
+
async def send_text_coroutine(self, message):
|
107
|
+
for client in self.clients:
|
108
|
+
await client.send(message)
|
109
|
+
|
110
|
+
async def server_handler(self, websocket):
|
111
|
+
self.clients.add(websocket)
|
112
|
+
try:
|
113
|
+
async for message in websocket:
|
114
|
+
if self.read and not paused:
|
115
|
+
websocket_queue.put(message)
|
116
|
+
try:
|
117
|
+
await websocket.send('True')
|
118
|
+
except websockets.exceptions.ConnectionClosedOK:
|
119
|
+
pass
|
120
|
+
else:
|
121
|
+
try:
|
122
|
+
await websocket.send('False')
|
123
|
+
except websockets.exceptions.ConnectionClosedOK:
|
124
|
+
pass
|
125
|
+
except websockets.exceptions.ConnectionClosedError:
|
126
|
+
pass
|
127
|
+
finally:
|
128
|
+
self.clients.remove(websocket)
|
129
|
+
|
130
|
+
def send_text(self, text, line_time: datetime):
|
131
|
+
if text:
|
132
|
+
return asyncio.run_coroutine_threadsafe(self.send_text_coroutine(json.dumps({"sentence": text, "time": line_time.isoformat()})), self.loop)
|
133
|
+
|
134
|
+
def stop_server(self):
|
135
|
+
self.loop.call_soon_threadsafe(self._stop_event.set)
|
136
|
+
|
137
|
+
def run(self):
|
138
|
+
async def main():
|
139
|
+
self._loop = asyncio.get_running_loop()
|
140
|
+
self._stop_event = stop_event = asyncio.Event()
|
141
|
+
self._event.set()
|
142
|
+
self.server = start_server = websockets.serve(self.server_handler, get_config().general.websocket_uri.split(":")[0], get_config().general.websocket_uri.split(":")[1], max_size=1000000000)
|
143
|
+
async with start_server:
|
144
|
+
await stop_event.wait()
|
145
|
+
asyncio.run(main())
|
146
|
+
|
147
|
+
all_cords = None
|
148
|
+
rectangles = None
|
149
|
+
|
150
|
+
def text_callback(text, rectangle):
|
151
|
+
global twopassocr, ocr2, last_oneocr_results
|
152
|
+
if not text:
|
153
|
+
return
|
154
|
+
if not twopassocr or not ocr2:
|
155
|
+
websocket_server_thread.send_text(text, datetime.now())
|
156
|
+
return
|
157
|
+
with mss.mss() as sct:
|
158
|
+
line_time = datetime.now()
|
159
|
+
logger.info(f"Received message: {text}, ATTEMPTING LENS OCR")
|
160
|
+
if rectangles:
|
161
|
+
cords = rectangles[rectangle]
|
162
|
+
i = rectangle
|
163
|
+
else:
|
164
|
+
i = 0
|
165
|
+
mon = sct.monitors
|
166
|
+
cords = [mon[1]['left'], mon[1]['top'], mon[1]['width'], mon[1]['height']]
|
167
|
+
similarity = difflib.SequenceMatcher(None, last_oneocr_results[i], text).ratio()
|
168
|
+
if similarity > .8:
|
169
|
+
return
|
170
|
+
logger.debug(f"Similarity for region {i}: {similarity}")
|
171
|
+
last_oneocr_results[i] = text
|
172
|
+
last_result = ([], -1)
|
173
|
+
try:
|
174
|
+
sct_params = {'left': cords[0], 'top': cords[1], 'width': cords[2], 'height': cords[3]}
|
175
|
+
sct_img = sct.grab(sct_params)
|
176
|
+
img = Image.frombytes('RGB', sct_img.size, sct_img.bgra, 'raw', 'BGRX')
|
177
|
+
img = img.convert("RGBA")
|
178
|
+
draw = ImageDraw.Draw(img)
|
179
|
+
for exclusion in ocr_config.get("excluded_rectangles", []):
|
180
|
+
left, top, right, bottom = exclusion
|
181
|
+
draw.rectangle((left, top, right, bottom), fill=(0, 0, 0, 0))
|
182
|
+
# draw.rectangle((left, top, right, bottom), fill=(0,0,0))
|
183
|
+
orig_text, text = run.process_and_write_results(img, None, None, last_result, TextFiltering(),
|
184
|
+
engine=ocr2)
|
185
|
+
if ":gsm_prefix:" in text:
|
186
|
+
text = text.split(":gsm_prefix:")[1]
|
187
|
+
websocket_server_thread.send_text(text, line_time)
|
188
|
+
except json.JSONDecodeError:
|
189
|
+
print("Invalid JSON received.")
|
190
|
+
except Exception as e:
|
191
|
+
logger.exception(e)
|
192
|
+
print(f"Error processing message: {e}")
|
193
|
+
|
194
|
+
done = False
|
195
|
+
|
196
|
+
def run_oneocr(ocr_config, i):
|
197
|
+
global done
|
198
|
+
run.run(read_from="screencapture", write_to="callback",
|
199
|
+
screen_capture_area=",".join(str(c) for c in ocr_config['rectangles'][i]) if ocr_config['rectangles'] else 'screen_1',
|
200
|
+
screen_capture_window=ocr_config.get("window", None),
|
201
|
+
screen_capture_only_active_windows=True if ocr_config.get("window", None) else False,
|
202
|
+
screen_capture_delay_secs=.25, engine=ocr1,
|
203
|
+
text_callback=text_callback,
|
204
|
+
screen_capture_exclusions=ocr_config.get('excluded_rectangles', None),
|
205
|
+
rectangle=i)
|
206
|
+
done = True
|
207
|
+
|
208
|
+
|
209
|
+
# async def websocket_client():
|
210
|
+
# uri = "ws://localhost:7331" # Replace with your hosted websocket address
|
211
|
+
# print("Connecting to WebSocket...")
|
212
|
+
# async with websockets.connect(uri) as websocket:
|
213
|
+
# print("Connected to WebSocket.")
|
214
|
+
#
|
215
|
+
# try:
|
216
|
+
# while True:
|
217
|
+
# message = await websocket.recv()
|
218
|
+
# if not message:
|
219
|
+
# continue
|
220
|
+
# line_time = datetime.now()
|
221
|
+
# get_line_history().add_secondary_line(message)
|
222
|
+
# print(f"Received message: {message}, ATTEMPTING LENS OCR")
|
223
|
+
# if ":gsm_prefix:" in message:
|
224
|
+
# i = int(message.split(":gsm_prefix:")[0])
|
225
|
+
# cords = all_cords[i] if i else all_cords[0]
|
226
|
+
# similarity = difflib.SequenceMatcher(None, last_oneocr_results[i], message).ratio()
|
227
|
+
# if similarity > .8:
|
228
|
+
# continue
|
229
|
+
# print(f"Similarity for region {i}: {similarity}")
|
230
|
+
# last_oneocr_results[i] = message
|
231
|
+
# last_result = ([], -1)
|
232
|
+
# try:
|
233
|
+
# sct_params = {'top': cords[1], 'left': cords[0], 'width': cords[2], 'height': cords[3]}
|
234
|
+
# with mss.mss() as sct:
|
235
|
+
# sct_img = sct.grab(sct_params)
|
236
|
+
# img = Image.frombytes('RGB', sct_img.size, sct_img.bgra, 'raw', 'BGRX')
|
237
|
+
# draw = ImageDraw.Draw(img)
|
238
|
+
# for exclusion in ocr_config.get("excluded_rectangles", []):
|
239
|
+
# exclusion = tuple(exclusion)
|
240
|
+
# draw.rectangle(exclusion, fill="black")
|
241
|
+
# orig_text, text = run.process_and_write_results(img, "results.txt", None, last_result, TextFiltering(), engine="glens")
|
242
|
+
# if ":gsm_prefix:" in text:
|
243
|
+
# text = text.split(":gsm_prefix:")[1]
|
244
|
+
# websocket_server_thread.send_text(text, line_time)
|
245
|
+
# except json.JSONDecodeError:
|
246
|
+
# print("Invalid JSON received.")
|
247
|
+
# except Exception as e:
|
248
|
+
# logger.exception(e)
|
249
|
+
# print(f"Error processing message: {e}")
|
250
|
+
# except websockets.exceptions.ConnectionClosed:
|
251
|
+
# print("WebSocket connection closed.")
|
252
|
+
# except Exception as e:
|
253
|
+
# print(f"WebSocket error: {e}")
|
254
|
+
|
255
|
+
|
256
|
+
if __name__ == "__main__":
|
257
|
+
global ocr1, ocr2, twopassocr
|
258
|
+
import sys
|
259
|
+
|
260
|
+
args = sys.argv[1:]
|
261
|
+
|
262
|
+
if len(args) == 3:
|
263
|
+
ocr1 = args[0]
|
264
|
+
ocr2 = args[1]
|
265
|
+
twopassocr = bool(int(args[2]))
|
266
|
+
elif len(args) == 2:
|
267
|
+
ocr1 = args[0]
|
268
|
+
ocr2 = args[1]
|
269
|
+
twopassocr = True
|
270
|
+
elif len(args) == 1:
|
271
|
+
ocr1 = args[0]
|
272
|
+
ocr2 = None
|
273
|
+
twopassocr = False
|
274
|
+
else:
|
275
|
+
ocr1 = "oneocr"
|
276
|
+
|
277
|
+
logger.info(f"Received arguments: ocr1={ocr1}, ocr2={ocr2}, twopassocr={twopassocr}")
|
278
|
+
global ocr_config
|
279
|
+
ocr_config = get_ocr_config()
|
280
|
+
rectangles = ocr_config['rectangles']
|
281
|
+
last_oneocr_results = [""] * len(rectangles) if rectangles else [""]
|
282
|
+
oneocr_threads = []
|
283
|
+
run.init_config(False)
|
284
|
+
if rectangles:
|
285
|
+
for i, rectangle in enumerate(rectangles):
|
286
|
+
thread = threading.Thread(target=run_oneocr, args=(ocr_config,i,), daemon=True)
|
287
|
+
oneocr_threads.append(thread)
|
288
|
+
thread.start()
|
289
|
+
else:
|
290
|
+
single_ocr_thread = threading.Thread(target=run_oneocr, args=(ocr_config, 0,), daemon=True)
|
291
|
+
oneocr_threads.append(single_ocr_thread)
|
292
|
+
single_ocr_thread.start()
|
293
|
+
|
294
|
+
websocket_server_thread = WebsocketServerThread(read=True)
|
295
|
+
websocket_server_thread.start()
|
296
|
+
|
297
|
+
try:
|
298
|
+
while not done:
|
299
|
+
time.sleep(1)
|
300
|
+
except KeyboardInterrupt as e:
|
301
|
+
pass
|
302
|
+
|
303
|
+
for thread in oneocr_threads:
|
304
|
+
thread.join()
|
305
|
+
|
306
|
+
# asyncio.run(websocket_client())
|
@@ -0,0 +1 @@
|
|
1
|
+
from owocr.ocr import *
|