Fix furigana filter completely removing not Japanese text, make lines not containing Japanese/Chinese text be output as regular half width text

This commit is contained in:
AuroraWright
2025-10-20 11:16:24 +02:00
parent 9834c39cf3
commit d5ebb292d5
3 changed files with 125 additions and 92 deletions

View File

@@ -57,7 +57,7 @@ parser.add_argument('-sc', '--screen_capture_combo', type=str, default=argparse.
parser.add_argument('-scc', '--coordinate_selector_combo', type=str, default=argparse.SUPPRESS,
help='When reading with screen capture, combo to wait on for invoking the coordinate picker to change the screen/window area. Example value: "<ctrl>+<shift>+c". The list of keys can be found here: https://pynput.readthedocs.io/en/latest/keyboard.html#pynput.keyboard.Key')
parser.add_argument('-l', '--language', type=str, default=argparse.SUPPRESS,
help='Two letter language code for filtering screencapture OCR results. Ex. "ja" for Japanese, "zh" for Chinese, "ko" for Korean, "ar" for Arabic, "ru" for Russian, "el" for Greek, "he" for Hebrew, "th" for Thai. Any other value will use Latin Extended (for most European languages and English).')
help='Two letter language code to use for some engines and for filtering screen capture OCR results. Ex. "ja" for Japanese, "zh" for Chinese, "ko" for Korean, "ar" for Arabic, "ru" for Russian, "el" for Greek, "he" for Hebrew, "th" for Thai. Any other value will use Latin Extended (for most European languages and English).')
parser.add_argument('-j', '--join_lines', type=str2bool, nargs='?', const=True, default=argparse.SUPPRESS,
help="Display lines in the text output without a space between them.")
parser.add_argument('-f', '--furigana_filter', type=str2bool, nargs='?', const=True, default=argparse.SUPPRESS,

View File

@@ -507,13 +507,13 @@ class TextFiltering:
frame_stabilization_active = self.frame_stabilization != 0
if (not frame_stabilization_active) or two_pass_processing_active:
changed_lines = self._find_changed_lines_text_impl(current_result, current_result_ocr, None, self.last_frame_text[0], None, True, recovered_lines_count)
changed_lines = self._find_changed_lines_text_impl(current_result, current_result_ocr, self.last_frame_text[0], None, None, recovered_lines_count, True)
if changed_lines == None:
return []
self.last_frame_text = (current_result, current_result_ocr)
return changed_lines
changed_lines_stabilization = self._find_changed_lines_text_impl(current_result, current_result_ocr, None, self.last_frame_text[0], None, False, 0)
changed_lines_stabilization = self._find_changed_lines_text_impl(current_result, current_result_ocr, self.last_frame_text[0], None, None, 0, False)
if changed_lines_stabilization == None:
return []
@@ -528,12 +528,12 @@ class TextFiltering:
return []
if self.line_recovery and self.last_last_frame_text[0]:
logger.debug(f'Checking for missed lines')
recovered_lines = self._find_changed_lines_text_impl(self.last_last_frame_text[0], self.last_last_frame_text[1], None, self.stable_frame_text, current_result, False, 0)
recovered_lines = self._find_changed_lines_text_impl(self.last_last_frame_text[0], self.last_last_frame_text[1], self.stable_frame_text, current_result, None, 0, False)
recovered_lines_count = len(recovered_lines) if recovered_lines else 0
else:
recovered_lines_count = 0
recovered_lines = []
changed_lines = self._find_changed_lines_text_impl(current_result, current_result_ocr, recovered_lines, self.stable_frame_text, None, True, recovered_lines_count)
changed_lines = self._find_changed_lines_text_impl(current_result, current_result_ocr, self.stable_frame_text, None, recovered_lines, recovered_lines_count, True)
self.processed_stable_frame = True
self.stable_frame_text = current_result
return changed_lines
@@ -544,7 +544,7 @@ class TextFiltering:
self.frame_stabilization_timestamp = time.time()
return []
def _find_changed_lines_text_impl(self, current_result, current_result_ocr, recovered_lines, previous_result, next_result, filtering, recovered_lines_count):
def _find_changed_lines_text_impl(self, current_result, current_result_ocr, previous_result, next_result, recovered_lines, recovered_lines_count, regex_filter):
if recovered_lines:
current_result = recovered_lines + current_result
@@ -606,16 +606,33 @@ class TextFiltering:
if current_lines_ocr:
i2 = i if not recovered_lines else i - len(recovered_lines)
if i2 >= 0:
current_line_bbox = current_lines_ocr[i2].bounding_box
is_furigana = self._furigana_filter(current_lines, current_lines_ocr, current_text, i2)
if is_furigana:
continue
# Check if line contains only kana (no kanji)
if first and len(current_text) > 3:
first = False
# For the first line, check if it contains the end of previous text
if regex_filter and all_previous_text:
overlap = self._find_overlap(all_previous_text, current_text)
if overlap and len(current_text) > len(overlap):
logger.opt(colors=True).debug(f"<magenta>Found overlap: '{overlap}'</magenta>")
changed_line = self._cut_at_overlap(changed_line, overlap)
logger.opt(colors=True).debug(f"<magenta>After cutting: '{changed_line}'</magenta>")
changed_lines.append(changed_line)
return changed_lines
def _furigana_filter(self, current_lines, current_lines_ocr, current_text, i):
has_kanji = self.kanji_regex.search(current_text)
if has_kanji:
return False
if not has_kanji:
is_furigana = False
current_line_bbox = current_lines_ocr[i].bounding_box
for j in range(len(current_lines_ocr)):
if i2 == j:
if i == j:
continue
if not current_lines[j]:
continue
@@ -631,11 +648,11 @@ class TextFiltering:
logger.opt(colors=True).debug(f"<magenta>Furigana check against line: '{other_line_text}'</magenta>")
if is_vertical:
width_threshold = other_line_bbox.width * 0.7
width_threshold = other_line_bbox.width * 0.85
is_smaller = current_line_bbox.width < width_threshold
logger.opt(colors=True).debug(f"<magenta>Vertical furigana check width: '{other_line_bbox.width}' '{current_line_bbox.width}'</magenta>")
else:
height_threshold = other_line_bbox.height * 0.7
height_threshold = other_line_bbox.height * 0.85
is_smaller = current_line_bbox.height < height_threshold
logger.opt(colors=True).debug(f"<magenta>Horizontal furigana check height: '{other_line_bbox.height}' '{current_line_bbox.height}'</magenta>")
@@ -655,7 +672,7 @@ class TextFiltering:
logger.opt(colors=True).debug(f"<magenta>Vertical furigana check position: '{horizontal_threshold}' '{horizontal_distance}' '{vertical_overlap}'</magenta>")
# If horizontally close and vertically aligned, it's likely furigana
if (0 < horizontal_distance < horizontal_threshold and vertical_overlap > 0.5):
if (0 < horizontal_distance < horizontal_threshold and vertical_overlap > 0.4):
is_furigana = True
logger.opt(colors=True).debug(f"<magenta>Skipping vertical furigana line: '{current_text}' next to line: '{other_line_text}'</magenta>")
break
@@ -667,32 +684,46 @@ class TextFiltering:
logger.opt(colors=True).debug(f"<magenta>Horizontal furigana check position: '{vertical_threshold}' '{vertical_distance}' '{horizontal_overlap}'</magenta>")
# If vertically close and horizontally aligned, it's likely furigana
if (0 < vertical_distance < vertical_threshold and horizontal_overlap > 0.5):
if (0 < vertical_distance < vertical_threshold and horizontal_overlap > 0.4):
is_furigana = True
logger.opt(colors=True).debug(f"<magenta>Skipping horizontal furigana line: '{current_text}' above line: '{other_line_text}'</magenta>")
break
return is_furigana
def _standalone_furigana_filter(self, result, result_ocr):
if len(result) == 0:
return result
filtered_lines = []
lines = []
lines_ocr = []
for line in result:
text_line = self._normalize_line_for_comparison(line)
lines.append(text_line)
if all(not text_line for text_line in lines):
return result
for p in result_ocr.paragraphs:
lines_ocr.extend(p.lines)
for i, text in enumerate(lines):
filtered_line = result[i]
logger.opt(colors=True).debug(f"<magenta>Line: '{text}'</magenta>")
if not text:
filtered_lines.append(filtered_line)
continue
is_furigana = self._furigana_filter(lines, lines_ocr, text, i)
if is_furigana:
continue
if first and len(current_text) > 3:
first = False
# For the first line, check if it contains the end of previous text
if filtering and all_previous_text:
overlap = self._find_overlap(all_previous_text, current_text)
if overlap and len(current_text) > len(overlap):
logger.opt(colors=True).debug(f"<magenta>Found overlap: '{overlap}'</magenta>")
changed_line = self._cut_at_overlap(changed_line, overlap)
logger.opt(colors=True).debug(f"<magenta>After cutting: '{changed_line}'</magenta>")
changed_lines.append(changed_line)
filtered_lines.append(filtered_line)
return changed_lines
def _standalone_furigana_filter(self, result, result_ocr):
result = self._find_changed_lines_text_impl(result, result_ocr, None, [], None, False, 0)
if result == None:
result = []
return result
return filtered_lines
def _find_overlap(self, previous_text, current_text):
min_overlap_length = 3
@@ -1369,16 +1400,17 @@ class OutputResult:
self.second_pass_thread = SecondPassThread()
def _post_process(self, text, strip_spaces):
is_cj_text = self.filtering.cj_regex.search(''.join(text))
line_separator = '' if strip_spaces else self.line_separator
lines = []
for line in text:
line = line.replace('', '...')
line = re.sub('[・.]{2,}', lambda x: (x.end() - x.start()) * '.', line)
is_cj_text = self.filtering.cj_regex.search(line)
if is_cj_text:
text = line_separator.join([''.join(i.split()) for i in text])
lines.append(jaconv.h2z(''.join(line.split()), ascii=True, digit=True))
else:
text = line_separator.join([re.sub(r'\s+', ' ', i).strip() for i in text])
text = text.replace('', '...')
text = re.sub('[・.]{2,}', lambda x: (x.end() - x.start()) * '.', text)
if is_cj_text:
text = jaconv.h2z(text, ascii=True, digit=True)
lines.append(re.sub(r'\s+', ' ', line).strip())
line_separator = '' if strip_spaces else self.line_separator
text = line_separator.join(lines)
return text
def _extract_lines_from_result(self, result_data):

View File

@@ -111,10 +111,11 @@
;screen_capture_old_macos_api = True
;Two letter language code for filtering screencapture OCR results. Ex. "ja"
;for Japanese, "zh" for Chinese, "ko" for Korean, "ar" for Arabic, "ru" for
;Russian, "el" for Greek, "he" for Hebrew, "th" for Thai. Any other value will
;use Latin Extended (for most European languages and English).
;Two letter language code to use for some engines and for filtering screen
;capture OCR results. Ex. "ja" for Japanese, "zh" for Chinese, "ko" for Korean,
;"ar" for Arabic, "ru" for Russian, "el" for Greek, "he" for Hebrew, "th" for
;Thai. Any other value will use Latin Extended (for most European languages and
;English).
;language = ja
;The output format for OCR results. Can be "text" (default) or "json" (to