GameSentenceMiner 2.10.17__py3-none-any.whl → 2.11.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.
@@ -195,10 +195,10 @@ all_cords = None
195
195
  rectangles = None
196
196
  last_ocr2_result = []
197
197
 
198
- def do_second_ocr(ocr1_text, time, img, filtering, ignore_furigana_filter=False):
198
+ def do_second_ocr(ocr1_text, time, img, filtering, ignore_furigana_filter=False, ignore_previous_result=False):
199
199
  global twopassocr, ocr2, last_ocr2_result
200
200
  try:
201
- orig_text, text = run.process_and_write_results(img, None, last_ocr2_result, filtering, None,
201
+ orig_text, text = run.process_and_write_results(img, None, last_ocr2_result if not ignore_previous_result else None, filtering, None,
202
202
  engine=ocr2, furigana_filter_sensitivity=furigana_filter_sensitivity if not ignore_furigana_filter else 0)
203
203
 
204
204
  if compare_ocr_results(last_ocr2_result, orig_text):
@@ -344,7 +344,8 @@ def run_oneocr(ocr_config: OCRConfig, rectangles):
344
344
  gsm_ocr_config=ocr_config,
345
345
  screen_capture_areas=screen_areas,
346
346
  furigana_filter_sensitivity=furigana_filter_sensitivity,
347
- screen_capture_combo=manual_ocr_hotkey if manual_ocr_hotkey and manual else None)
347
+ screen_capture_combo=manual_ocr_hotkey if manual_ocr_hotkey and manual else None,
348
+ keep_line_breaks=keep_newline)
348
349
  except Exception as e:
349
350
  logger.exception(f"Error running OneOCR: {e}")
350
351
  done = True
@@ -359,14 +360,14 @@ def add_ss_hotkey(ss_hotkey="ctrl+shift+g"):
359
360
  def capture():
360
361
  print("Taking screenshot...")
361
362
  img = cropper.run()
362
- do_second_ocr("", datetime.now(), img, filtering, ignore_furigana_filter=True)
363
+ do_second_ocr("", datetime.now(), img, filtering, ignore_furigana_filter=True, ignore_previous_result=True)
363
364
  def capture_main_monitor():
364
365
  print("Taking screenshot of main monitor...")
365
366
  with mss.mss() as sct:
366
367
  main_monitor = sct.monitors[1] if len(sct.monitors) > 1 else sct.monitors[0]
367
368
  img = sct.grab(main_monitor)
368
369
  img_bytes = mss.tools.to_png(img.rgb, img.size)
369
- do_second_ocr("", datetime.now(), img_bytes, filtering, ignore_furigana_filter=True)
370
+ do_second_ocr("", datetime.now(), img_bytes, filtering, ignore_furigana_filter=True, ignore_previous_result=True)
370
371
  hotkey_reg = None
371
372
  try:
372
373
  hotkey_reg = keyboard.add_hotkey(ss_hotkey, capture)
@@ -404,7 +405,7 @@ def set_force_stable_hotkey():
404
405
 
405
406
  if __name__ == "__main__":
406
407
  try:
407
- global ocr1, ocr2, twopassocr, language, ss_clipboard, ss, ocr_config, furigana_filter_sensitivity, area_select_ocr_hotkey, window, optimize_second_scan, use_window_for_config
408
+ global ocr1, ocr2, twopassocr, language, ss_clipboard, ss, ocr_config, furigana_filter_sensitivity, area_select_ocr_hotkey, window, optimize_second_scan, use_window_for_config, keep_newline
408
409
  import sys
409
410
 
410
411
  import argparse
@@ -428,6 +429,7 @@ if __name__ == "__main__":
428
429
  help="Optimize second scan by cropping based on first scan results")
429
430
  parser.add_argument("--use_window_for_config", action="store_true",
430
431
  help="Use the specified window for loading OCR configuration")
432
+ parser.add_argument("--keep_newline", action="store_true", help="Keep new lines in OCR output")
431
433
 
432
434
  args = parser.parse_args()
433
435
 
@@ -446,6 +448,7 @@ if __name__ == "__main__":
446
448
  clipboard_output = args.clipboard_output
447
449
  optimize_second_scan = args.optimize_second_scan
448
450
  use_window_for_config = args.use_window_for_config
451
+ keep_newline = args.keep_newline
449
452
 
450
453
  window = None
451
454
  logger.info(f"Received arguments: {vars(args)}")
@@ -91,8 +91,11 @@ def empty_post_process(text):
91
91
  return text
92
92
 
93
93
 
94
- def post_process(text):
95
- text = ' '.join([''.join(i.split()) for i in text.splitlines()])
94
+ def post_process(text, keep_blank_lines=False):
95
+ if keep_blank_lines:
96
+ text = '\n'.join([''.join(i.split()) for i in text.splitlines()])
97
+ else:
98
+ text = ''.join([''.join(i.split()) for i in text.splitlines()])
96
99
  text = text.replace('…', '...')
97
100
  text = re.sub('[・.]{2,}', lambda x: (x.end() - x.start()) * '.', text)
98
101
  text = jaconv.h2z(text, ascii=True, digit=True)
@@ -304,22 +307,42 @@ class GoogleLens:
304
307
  response_proto = LensOverlayServerResponse().FromString(res.content)
305
308
  response_dict = response_proto.to_dict(betterproto.Casing.SNAKE)
306
309
 
307
- # with open(os.path.join(get_temporary_directory(), 'glens_response.json'), 'w', encoding='utf-8') as f:
308
- # json.dump(response_dict, f, indent=4, ensure_ascii=False)
310
+ with open(os.path.join(r"C:\Users\Beangate\GSM\Electron App\test", 'glens_response.json'), 'w', encoding='utf-8') as f:
311
+ json.dump(response_dict, f, indent=4, ensure_ascii=False)
309
312
  res = ''
310
313
  text = response_dict['objects_response']['text']
311
314
  skipped = []
312
- if furigana_filter_sensitivity > 0:
313
- if 'text_layout' in text:
314
- for paragraph in text['text_layout']['paragraphs']:
315
- for line in paragraph['lines']:
315
+ previous_line = None
316
+ if 'text_layout' in text:
317
+ for paragraph in text['text_layout']['paragraphs']:
318
+ if previous_line:
319
+ prev_bbox = previous_line['geometry']['bounding_box']
320
+ curr_bbox = paragraph['geometry']['bounding_box']
321
+ vertical_space = abs(curr_bbox['center_y'] - prev_bbox['center_y']) * img.height
322
+ prev_height = prev_bbox['height'] * img.height
323
+ current_height = curr_bbox['height'] * img.height
324
+ avg_height = (prev_height + current_height) / 2
325
+ # If vertical space is close to previous line's height, add a blank line
326
+ # logger.info(f"Vertical space: {vertical_space}, Average height: {avg_height}")
327
+ # logger.info(avg_height * 2)
328
+ if vertical_space > avg_height * 2:
329
+ logger.info('Adding blank line')
330
+ res += 'BLANK_LINE'
331
+ for line in paragraph['lines']:
332
+ if furigana_filter_sensitivity:
316
333
  if furigana_filter_sensitivity < line['geometry']['bounding_box']['width'] * img.width and furigana_filter_sensitivity < line['geometry']['bounding_box']['height'] * img.height:
317
334
  for word in line['words']:
318
335
  res += word['plain_text'] + word['text_separator']
319
336
  else:
320
337
  skipped.append(word['plain_text'] for word in line['words'])
321
338
  continue
322
- res += '\n'
339
+ else:
340
+ for word in line['words']:
341
+ res += word['plain_text'] + word['text_separator']
342
+ else:
343
+ continue
344
+ previous_line = paragraph
345
+ res += '\n'
323
346
  # logger.info(
324
347
  # f"Skipped {len(skipped)} chars due to furigana filter sensitivity: {furigana_filter_sensitivity}")
325
348
  # widths = []
@@ -350,16 +373,16 @@ class GoogleLens:
350
373
  # else:
351
374
  # continue
352
375
  # res += '\n'
353
- else:
354
- if 'text_layout' in text:
355
- paragraphs = text['text_layout']['paragraphs']
356
- for paragraph in paragraphs:
357
- for line in paragraph['lines']:
358
- for word in line['words']:
359
- res += word['plain_text'] + word['text_separator']
360
- else:
361
- continue
362
- res += '\n'
376
+ # else:
377
+ # if 'text_layout' in text:
378
+ # paragraphs = text['text_layout']['paragraphs']
379
+ # for paragraph in paragraphs:
380
+ # for line in paragraph['lines']:
381
+ # for word in line['words']:
382
+ # res += word['plain_text'] + word['text_separator']
383
+ # else:
384
+ # continue
385
+ # res += '\n'
363
386
 
364
387
  x = (True, res)
365
388
 
@@ -353,7 +353,9 @@ class TextFiltering:
353
353
 
354
354
  orig_text_filtered = []
355
355
  for block in orig_text:
356
- if lang == "ja":
356
+ if "BLANK_LINE" in block:
357
+ block_filtered = ["\n"]
358
+ elif lang == "ja":
357
359
  block_filtered = self.kana_kanji_regex.findall(block)
358
360
  elif lang == "zh":
359
361
  block_filtered = self.chinese_common_regex.findall(block)
@@ -394,7 +396,8 @@ class TextFiltering:
394
396
  new_blocks = []
395
397
  for idx, block in enumerate(orig_text):
396
398
  if orig_text_filtered[idx] and (orig_text_filtered[idx] not in last_text):
397
- new_blocks.append(block)
399
+ new_blocks.append(str(block).strip().replace("BLANK_LINE", "\n"))
400
+
398
401
 
399
402
  final_blocks = []
400
403
  if self.accurate_filtering:
@@ -407,9 +410,10 @@ class TextFiltering:
407
410
  else:
408
411
  for block in new_blocks:
409
412
  # This only filters out NON JA/ZH from text when lang is JA/ZH
410
- if lang not in ["ja", "zh"] or self.classify(block)[0] in ['ja', 'zh']:
413
+ if lang not in ["ja", "zh"] or self.classify(block)[0] in ['ja', 'zh'] or block == "\n":
411
414
  final_blocks.append(block)
412
415
 
416
+
413
417
  text = '\n'.join(final_blocks)
414
418
  return text, orig_text_filtered
415
419
 
@@ -937,7 +941,7 @@ def process_and_write_results(img_or_path, write_to=None, last_result=None, filt
937
941
  if filtering:
938
942
  text, orig_text = filtering(text, last_result)
939
943
  if lang == "ja" or lang == "zh":
940
- text = post_process(text)
944
+ text = post_process(text, keep_blank_lines=keep_new_lines)
941
945
  logger.opt(ansi=True).info(f'Text recognized in {end_time - start_time:0.03f}s using <{engine_color}>{engine_instance.readable_name}</{engine_color}>: {text}')
942
946
  if notify and config.get_general('notifications'):
943
947
  notifier.send(title='owocr', message='Text recognized: ' + text)
@@ -999,6 +1003,7 @@ def run(read_from=None,
999
1003
  ocr2=None,
1000
1004
  gsm_ocr_config=None,
1001
1005
  furigana_filter_sensitivity=None,
1006
+ keep_line_breaks=False,
1002
1007
  ):
1003
1008
  """
1004
1009
  Japanese OCR client
@@ -1075,11 +1080,13 @@ def run(read_from=None,
1075
1080
  global engine_instances
1076
1081
  global engine_keys
1077
1082
  global lang
1083
+ global keep_new_lines
1078
1084
  lang = language
1079
1085
  engine_instances = []
1080
1086
  config_engines = []
1081
1087
  engine_keys = []
1082
1088
  default_engine = ''
1089
+ keep_new_lines = keep_line_breaks
1083
1090
 
1084
1091
  if len(config.get_general('engines')) > 0:
1085
1092
  for config_engine in config.get_general('engines').split(','):
@@ -16,13 +16,13 @@ import toml
16
16
  from dataclasses_json import dataclass_json
17
17
 
18
18
  OFF = 'OFF'
19
- VOSK = 'VOSK'
19
+ # VOSK = 'VOSK'
20
20
  SILERO = 'SILERO'
21
21
  WHISPER = 'WHISPER'
22
- GROQ = 'GROQ'
22
+ # GROQ = 'GROQ'
23
23
 
24
- VOSK_BASE = 'BASE'
25
- VOSK_SMALL = 'SMALL'
24
+ # VOSK_BASE = 'BASE'
25
+ # VOSK_SMALL = 'SMALL'
26
26
 
27
27
  WHISPER_TINY = 'tiny'
28
28
  WHISPER_BASE = 'base'
@@ -33,6 +33,7 @@ WHISPER_TURBO = 'turbo'
33
33
 
34
34
  AI_GEMINI = 'Gemini'
35
35
  AI_GROQ = 'Groq'
36
+ AI_LOCAL = 'Local'
36
37
 
37
38
  INFO = 'INFO'
38
39
  DEBUG = 'DEBUG'
@@ -219,7 +220,7 @@ class VAD:
219
220
  whisper_model: str = WHISPER_BASE
220
221
  do_vad_postprocessing: bool = True
221
222
  language: str = 'ja'
222
- vosk_url: str = VOSK_BASE
223
+ # vosk_url: str = VOSK_BASE
223
224
  selected_vad_model: str = WHISPER
224
225
  backup_vad_model: str = SILERO
225
226
  trim_beginning: bool = False
@@ -234,11 +235,11 @@ class VAD:
234
235
  def is_whisper(self):
235
236
  return self.selected_vad_model == WHISPER or self.backup_vad_model == WHISPER
236
237
 
237
- def is_vosk(self):
238
- return self.selected_vad_model == VOSK or self.backup_vad_model == VOSK
238
+ # def is_vosk(self):
239
+ # return self.selected_vad_model == VOSK or self.backup_vad_model == VOSK
239
240
 
240
- def is_groq(self):
241
- return self.selected_vad_model == GROQ or self.backup_vad_model == GROQ
241
+ # def is_groq(self):
242
+ # return self.selected_vad_model == GROQ or self.backup_vad_model == GROQ
242
243
 
243
244
 
244
245
  @dataclass_json
@@ -266,6 +267,7 @@ class Ai:
266
267
  anki_field: str = ''
267
268
  provider: str = AI_GEMINI
268
269
  gemini_model: str = 'gemini-2.5-flash'
270
+ local_model: str = OFF
269
271
  groq_model: str = 'meta-llama/llama-4-scout-17b-16e-instruct'
270
272
  api_key: str = '' # Deprecated
271
273
  gemini_api_key: str = ''
@@ -20,6 +20,7 @@ class GameLine:
20
20
  next: 'GameLine | None'
21
21
  index: int = 0
22
22
  scene: str = ""
23
+ TL: str = ""
23
24
 
24
25
  def get_previous_time(self):
25
26
  if self.prev:
@@ -31,6 +32,9 @@ class GameLine:
31
32
  return self.next.time
32
33
  return 0
33
34
 
35
+ def set_TL(self, tl: str):
36
+ self.TL = tl
37
+
34
38
  def __str__(self):
35
39
  return str({"text": self.text, "time": self.time})
36
40