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

Как видите, на скриншотах
- шрифт Times New Roman
- постоянный размер шрифта
- постоянный форматирование (без курсива, без жирного шрифта/подчеркивания)
Для этой цели я сначала генерирую набор обучающих данных с помощью 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
Мобильная версия