Обучение Тессеракта декодированию файлов ЭпштейнаPython

Программы на Python
Ответить
Anonymous
 Обучение Тессеракта декодированию файлов Эпштейна

Сообщение Anonymous »

Министерство юстиции недавно опубликовало 9 и 10 тома дела Эпштейна. Среди них есть PDF-файл: https://www.justice.gov/epstein/files/D ... 012650.pdf
Этот PDF-файл содержит скриншоты электронного письма, в котором показаны версии двух изображений JPEG в кодировке Base64 (ссылка на https://pdfcrowd.com/inspect-pdf/ для отображения объектов PDF, второй JPEG начинается на странице 237; все изображения имеют размер 816 x 1056 пикселей). В PDF-файле есть текст, допускающий копирование и вставку, но похоже, что этот текст получен в результате плохо выполненного оптического распознавания символов и не является исходным текстом. При копировании текста встречаются даже случайные китайские символы.
Изображение

Как видите, на скриншотах
  • шрифт Times New Roman
  • постоянный размер шрифта
  • постоянный форматирование (без курсива, без жирного шрифта/подчеркивания)
Сейчас я пытаюсь использовать Tesseract (и Tesstrain) с его нейронной сетью «долговременной краткосрочной памяти» (LSTM), чтобы иметь возможность извлекать исходный текст из этого PDF-файла, на 100% без ошибок, чтобы иметь возможность снова декодировать данные в Base64 в исходные JPEG. Даже один пропущенный символ и весь битовый поток смещаются настолько сильно (на 6 бит), что остальная часть файла становится нечитаемой.
Для этой цели я сначала генерирую набор обучающих данных с помощью PIL, загружая исходный шрифт C:/Windows/Fonts/times.ttf в Windows как известный мне наиболее близкий на данный момент вариант. В результате получается настраиваемое количество файлов .png (одна визуализируемая строка) и один .gt.txt (основной текст), на котором Tesseract может очень удобно обучаться.
Пример сгенерированных тестовых данных: base64_size16_line1971.png
Изображение

С base64_size16_line1971.gt.txt как
2EjUA2oxeuwKF+uG+tJakM1kVnN3cEao11IFTEOMlDR8X

Используя, например, подсистему Windows для Linux (при условии, что все зависимости установлены и сгенерированные данные обучения помещены в data/base64-ground-truth и, начиная с английской модели, всего 9000 изображений для 6 размеров шрифта)
make training MODEL_NAME=base64 START_MODEL=eng MAX_ITERATIONS=100000 RANDOM_SEED=43234

Создает сеть с погрешностью символов 0,011%.
2 Percent improvement time=2781, best error was 2.214 @ 580
At iteration 3361/97500/97500, Mean rms=0.168%, delta=0.005%, char train=0.011%, word train=0.6%, skip ratio=0%, New best char error = 0.011 wrote best model:data/base64/checkpoints/base640.011_3361.checkpoint wrote checkpoint.
..
Finished! Error rate = 0.011

Если эта сеть действительно что-то классифицирует, это также приведет к получению результата
sudo cp data/base64.traineddata /usr/share/tesseract-ocr/4.00/tessdata/

tesseract part2.png output.txt -l base64 --psm 6 -c load_system_dawg=0 -c load_freq_dawg=0 -c load_number_dawg=0 -c "tessedit_char_whitelist=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/+-="

$ cat output2.txt
/9j/4AAQSkZJRgABAQAASAB1AAD/4QdURXhpZgANTU0AKgAAAAgACQEPAAlAAAAGAAAAegfQA
AIA
AAAJAAAAgAESAAMAAAABAAYAAAEaAAUAAAABAAAAigEbAAUAAAABAAAAkgEoAAMAAAA
BAAlAAAEx
AAlAAAAFAAAAmgEyAA1AAAAUAAAAoldpAAQAAAABAAAAtAAAAABBcFlBsZQBpUGhvbmUgW
AAAAAAA
SAAAAAEAAABlAAAAATEyLjEAADlwMTg6MTl6MTggMTg6NTQ6MzUAACCCmgAFAAAAAQAAAj
qCnQAF

Это и так выглядит очень хорошо, однако, при ближайшем рассмотрении, есть некоторые ошибки. Например, в 4-й строке, Ground Truth (как я прочитал), четвертая буква - заглавная "i"
BAAI

но OCR пишет строчную букву «l»
BAAl

Кажется, действительно возникают проблемы со сходством «1», «l» и «I». Когда я смотрю на это в Word с тем же шрифтом, даже я едва могу заметить разницу. В данном контексте это «заглавная I», «строчная L» и «цифра «1».
Изображение

Из исходного PDF-файла: Это то, что я думаю, заглавная "I" (две из них), увеличенная цифра "1" и строчная "L". Это не так. Помогите, что исходный источник имеет плохое разрешение.
Изображение

Конкретный вопрос: как я могу успешно обучить тессеракт или другую нейронную сеть для извлечения этого текста без ошибок. Конечно, здесь также могут помочь некоторые исходные данные с «рукописными/распознанными» достоверными данными или более синтетически сгенерированные? Не лучше ли создать этап постобработки с более «классической» оценкой распознавания/сравнения изображений, чтобы отсеять все возможные путаницы с I/l/1?
Также открытые вопросы:
  • изображение, полученное в формате PIL, не на 100% похоже на экспортированный текст Microsoft Word в формате PDF: размер шрифта Word 12 очень близко соответствует исходному PDF, тогда как в PIL размер шрифта 16, возможно, должен быть наиболее близким; сделайте это с настройками DPI или ClearType. Исходный шрифт исходного снимка экрана неизвестен. Возможно, это устройство Apple или другое устройство Windows.
Скрипт для создания тестовых данных для справки.
from PIL import Image, ImageDraw, ImageFont
import random
import os
FONT_PATH = "C:/Windows/Fonts/times.ttf" # Windows

# Font sizes to train on (cover the range in your images)
FONT_SIZES = [14, 15, 16, 17, 18, 20]

# Base64 character set
CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="

# Output directory
OUTPUT_DIR = "tessdata/base64-ground-truth"

# Number of training lines to generate per font size
LINES_PER_FONT_SIZE = 750

def ensure_directory():
os.makedirs(OUTPUT_DIR, exist_ok=True)
print(f"Output directory: {OUTPUT_DIR}")

def generate_base64_line(min_length=40, max_length=80):
length = random.randint(min_length, max_length)
text = ''.join(random.choices(CHARSET[:-1], k=length))
# Add padding (=) at end sometimes
if random.random() < 0.3:
padding = random.choice(['', '=', '=='])
text = text[:-len(padding)] if padding else text
text += padding
return text

def create_line_image(text, font_size, line_number):
try:
font = ImageFont.truetype(FONT_PATH, font_size)
except Exception as e:
print(f"Error loading font: {e}")
print(f"Please check FONT_PATH: {FONT_PATH}")
return False
# Use textbbox for accurate sizing
bbox = font.getbbox(text)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]

# Add padding
padding_x = 20
padding_y = 10
img_width = text_width + padding_x * 2
img_height = text_height + padding_y * 2

# Create white background image
img = Image.new('L', (img_width, img_height), color=255)
draw = ImageDraw.Draw(img)

# Draw black text
draw.text((padding_x, padding_y), text, fill=0, font=font)

# Save as .png and ground truth text (.gt.txt)
base_filename = f"base64_size{font_size:02d}_line{line_number:04d}"
img_path = os.path.join(OUTPUT_DIR, f"{base_filename}.png")
txt_path = os.path.join(OUTPUT_DIR, f"{base_filename}.gt.txt")
img.save(img_path)
with open(txt_path, 'w', encoding='utf-8') as f:
f.write(text)

return True

def generate_training_set():
ensure_directory()
print("=" * 60)
print("GENERATING TESSERACT TRAINING DATA")
print("=" * 60)
print(f"Font: {FONT_PATH}")
print(f"Font sizes: {FONT_SIZES}")
print(f"Lines per size: {LINES_PER_FONT_SIZE}")
print(f"Total lines: {len(FONT_SIZES) * LINES_PER_FONT_SIZE}")
print("=" * 60)

total_generated = 0
for font_size in FONT_SIZES:
print(f"\nGenerating {LINES_PER_FONT_SIZE} lines at {font_size}pt...")
for i in range(LINES_PER_FONT_SIZE):
text = generate_base64_line()
success = create_line_image(text, font_size, total_generated)
if success:
total_generated += 1
if (i + 1) % 50 == 0:
print(f" Progress: {i + 1}/{LINES_PER_FONT_SIZE}")
else:
print(f" Failed to create image {i}")
return

print("\n" + "=" * 60)
print(f"✓ Successfully generated {total_generated} training images")
print(f"✓ Saved to: {OUTPUT_DIR}")
print("=" * 60)

def verify_font():
print("Verifying font...")
try:
font = ImageFont.truetype(FONT_PATH, 12)
print(f"✓ Font loaded successfully: {FONT_PATH}")

# Test rendering
test_img = Image.new('L', (200, 50), color=255)
draw = ImageDraw.Draw(test_img)
draw.text((10, 10), "Test123/+", fill=0, font=font)

print("✓ Font can render text")
return True
except Exception as e:
print(f"✗ Error with font: {e}")
print("\nPlease update FONT_PATH in the script:")
print(" Windows: C:/Windows/Fonts/times.ttf")
print(" Linux: /usr/share/fonts/truetype/liberation/LiberationSerif-Regular.ttf")
print(" Mac: /System/Library/Fonts/Supplemental/Times New Roman.ttf")
return False

if __name__ == "__main__":
if verify_font():
print()
generate_training_set()
else:
print("\nPlease fix the font path and try again.")


Подробнее здесь: https://stackoverflow.com/questions/798 ... tein-files
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»