Код: Выделить всё
result = client.chat.completions.create(
model=self.image_llm_model,
messages=messages,
max_tokens=max_tokens,
response_format={"type": "json_object"},
)
parsed = json.loads(result.choices[0].message.content)
info_text = parsed.get("info")
return self._normalize_ocr_text(info_text) if info_text else info_text
Чего я хочу
- Надежный, безопасный для производства подход для:
- Автоматического восстановления обычного моджибаке.
- Удаления невидимых/управляющих символов, типичных для Мусор OCR.
- Нормализация символов совместимости/лигатуры (→ fl, варианты валюты).
Код: Выделить всё
fl - Свернуть патологические повторяющиеся прогоны, введенные оптическим распознаванием символов.
В сотнях управляющих символов юникод. Если вы очищаете данные из Интернета или другого источника, которые могут содержать символы, отличные от ASCII, вам понадобится модуль Python unicodedata. Функция unicodedata.category(…) возвращает код категории Юникода (например, управляющий символ, пробел, букву и т. д.) любого символа. Для управляющих символов категория всегда начинается с буквы «C».
Этот фрагмент удаляет все управляющие символы из строки.
Код: Выделить всё
python
import unicodedata
def remove_control_characters(s):
return "".join(ch for ch in s if unicodedata.category(ch)[0]!="C")
Код: Выделить всё
python
>>> from unicodedata import category
>>> category('\r') # carriage return --> Cc : control character
'Cc'
>>> category(u'\u200b') # zero width space -> Cf : control character, formatting
'Cf'
>>> category('A') # letter "A" -------> Lu : letter, uppercase
'Lu'
``
Это демонстрирует надежный способ обнаружения и удаления управляющих символов/символов форматирования (категории Юникода, начинающиеся с «C»), которые часто являются невидимым мусором, который вы видите после специальных глифов в выводе OCR.
Это не прямой ответ на вопрос, но мне повезло использовать ftfy Пакет Python для автоматического обнаружения и исправления mojibake:
Код: Выделить всё
python
import ftfy
print(ftfy.fix_encoding("(ง'⌣')ง")) # prints the repaired result
[2]
ftfy` — это проверенная в боевых условиях библиотека, которая эвристически исправляет многие распространенные ошибки моджибаке и кодирования — безопаснее и полнее, чем длинная таблица замен вручную.
Мой текущий нормализатор (отрывок)
Код: Выделить всё
python
def _normalize_ocr_text(self, text: str) -> str:
if not text or not isinstance(text, str):
return text
mojibake_indicators = ['â€', '´', 'µ', '±', '³', 'º']
has_mojibake = any(indicator in text for indicator in mojibake_indicators)
# repetitive pattern detection...
repetitive_pattern = re.compile(r'(\S)\s*\1{49,}')
has_repetitive = bool(repetitive_pattern.search(text))
if not has_mojibake and not has_repetitive:
return text
# manual replacements
mojibake_fixes = {
'•': '•',
'€': '₹',
'´': '',
# ...
}
# replacements and repeats-handling...
return normalized
- Используйте ftfy.fix_text() (или fix_encoding) заранее для автоматического восстановления mojibake.
- Используйте unicodedata.normalize('NFKC', ...) для свернуть символы совместимости и лигатуры.
- Удалите управляющие символы/символы форматирования по категории Юникода (или диапазону регулярных выражений [\x00-\x1F\x7F-\x9F]), чтобы удалить символы нулевой ширины и другие невидимые символы.
- Сворачивайте патологические повторяющиеся прогоны с помощью консервативного регулярного выражения. (например, замените более 50 повторяющихся символов, не отличных от пробела, компактным маркером).
- Сохраняйте критически важные для домена токены (электронные письма, URL-адреса, суммы в валюте, например 66 499 рупий, временные метки) — для этих полей рассмотрите возможность внесения в белый список или проверки на уровне поля, чтобы избежать непреднамеренного редактирования.
Код: Выделить всё
python
import re
import unicodedata
from ftfy import fix_text
def _remove_control_chars(s: str) -> str:
return re.sub(r'[\x00-\x1F\x7F-\x9F]', '', s)
def _collapse_repetitions(s: str, threshold: int = 50) -> str:
pattern = re.compile(r'(\S)(\s*\1){' + str(threshold-1) + r',}')
def repl(m):
char = m.group(1)
matched = m.group(0)
if ' ' in matched or '\n' in matched:
return f"{char} {char} {char}..."
return char * 3 + "..."
return pattern.sub(repl, s)
def _normalize_ocr_text(self, text: str) -> str:
if not text or not isinstance(text, str):
return text
original_len = len(text)
try:
text = fix_text(text) # fix mojibake
except Exception:
pass
text = unicodedata.normalize("NFKC", text) # compatibility normalization
text = _remove_control_chars(text) # remove invisible/control chars
text = _collapse_repetitions(text, threshold=50)
text = re.sub(r' {10,}', ' ', text)
text = re.sub(r'\n{3,}', '\n\n', text)
text = text.strip(' \t\n')
if len(text) != original_len:
self._stage_print("ocr_text_normalized", f"Text normalized: {original_len} -> {len(text)}")
return text
Достаточно ли хорош текущий подход, я не могу обновить или изменить свою модель.>