проект по улучшению качества цифровых копий
ScanMakeUp
Европейский Университет | ПАНДАН | 2026

Разработка и тестирование приложения для автоматизированной коррекции сканированных документов
проект по улучшению качества
цифровых копий
ScanMakeUp
Европейский Университет | ПАНДАН | 2026

Разработка и тестирование приложения
для автоматизированной коррекции
сканированных документов

01 – ИСТОРИЯ ОЦИФРОВКИ

Оцифровка книг и документов в РФ

На сегодняшний день в Интернете можно найти множество непригодных или слабо пригодных для чтения оцифрованных документов. Какие-то из них создавались частными лицами, как правило, при помощи телефона или домашнего сканера: неудивительно, что качество таких «кустарных» сканов далеко от идеала.

В то же время многие проблемные цифровые копии возникали на базе архивов и библиотек. Причина плохого качества многих библиотечных и архивных сканов заключается в том, что до 2010-х гг. отсутствовали единые методические руководства по оцифровке фондов, а само сканирование часто осуществлялось на некачественном или непрофессиональном оборудовании.

Установить последний факт помогло небольшое историческое исследование, проведенное в рамках анализа предметной области для проекта ScanMakeUp. Результатом стала хронология оцифровки в архивах и библиотеках Российской Федерации с конца 1980-х гг. до наших дней, приведенная ниже.
Конец 1980-х — 1990-е гг.: первые опыты цифровизации и
начало формирования стратегий
1989
Первый опыт цифровизации: в Российской государственной библиотеке (РГБ) запущена пилотная версия электронного каталога, оцифрованного из карточного
1991
В Российском государственном архиве кинофотофонодокументов (РГАКФД) началось создание первой базы фотодокументов
1993
В Государственном архиве Российской Федерации (ГАРФ) стартовало использование цифровых инструментов — сначала для ведения списков, с середины 1990-х — для ввода и печати архивных описей
1995
Наметился переход от хаотичных попыток цифровизации к централизованному и целенаправленному введению цифровых технологий в архивной и библиотечной сферах: принята «Концепция автоматизации архивного дела в России», обозначившая курс на централизованную информатизацию. Единых правил оцифровки документов не вводилось.
1997
На основе «Концепции» создана «Программа информатизации архивного дела России на 1997–2000 гг.», подразумевавшая компьютеризацию архивов, разработку ПК «Архивный фонд», запуск портала «Архивы России»
1998
Состоялось обсуждение программы «Российские электронные библиотеки» (не реализована); открыта Научная электронная библиотека для учёных
2000-е гг.: запуск масштабных проектов по оцифровке и начало внедрения специализированного оборудования
2001
Продолжение цифровизации библиотек: Российская национальная библиотека (РНБ) начала выборочную оцифровку изданий, РГБ опубликовала «Электронную библиотеку диссертаций»
2002
Запущена федеральная программа «Электронная Россия (2002–2010 гг.)» с акцентом на создание электронных архивов и управление электронным документооборотом; в рамках этой программы была сформирована «Система электронного Архива Президента Российской Федерации с обеспечением доступа к нему граждан и организаций», включавшая электронный архив объёмом 500 000 листов
2004
По инициативе Министерства культуры на базе РГБ запущена Национальная электронная библиотека (НЭБ) — главный национальный проект по оцифровке
2006
Российская академия наук положила начало электронной библиотеке «Научное наследие России»
2008
Владимирская областная универсальная научная библиотека им. М. Горького (ВОУНБ) приобрела планетарный сканер ЭларПланСкан — пример постепенного оснащения библиотек специализированным оборудованием
2010-е гг.: разработка единых норм оцифровки
для архивов и библиотек
2011
В рамках «Программы информатизации Росархива на 2011–2020 гг.» Всероссийский научно-исследовательский институт документоведения и архивного дела (ВНИИДАД) проводит масштабные исследования по следующим темам: оказание государственных услуг в электронном виде, внедрение МЭДО, СМЭВ и СЭД, информатизация основной деятельности архивов
2012
ВНИИДАД разработал «Методические рекомендации по электронному копированию архивных документов» — первый детальный документ, описывающий оборудование, параметры и процедуры оцифровки (рекомендованы профессиональные бесконтактные планетарные сканеры)
2013
Экспертный совет по цифровым копиям РНБ утвердил «Рекомендации по оцифровке материалов из фондов библиотек» с чёткими параметрами допустимого оборудования и постобработки
2014
ВНИИДАД опубликовал «Концепцию информатизации деятельности государственного архива» — создана единая модель управления цифровизацией
2015
ВНИИДАД разработал первое описание правил работы архивов с электронными документами: для текстовых электронных копий предписан формат PDF/A
2020-е гг.: детализация законодательства и
применение искусственного интеллекта
2020
Приняты новые «Правила организации хранения и использования документов Архивного фонда РФ» (приказ Росархива №24, с изменением 2022 г.)
2021
Принята «Стратегия развития библиотечного дела в России» до 2030 г.; стартовал проект транскрибирования тибетских рукописей с применением ИИ (Сибирское отделение РАН, Новосибирский государственный университет (НГУ), Центр искусственного интеллекта МТС)
2022
Стартовал проект «Digital Пётр» от РИО и Сбера: автоматическое транскрибирование рукописей Петра I
2023
Всероссийская научно-практическая конференция «Приоритизация оцифровки архивных документов» на базе Российского государственного социального университета: подчёркивалось, что современная оцифровка неотделима от задач искусственного интеллекта
2025
В Минобрнауки состоялось обсуждение федерального проекта по оцифровке фондов научно-технических библиотек в вузах и научных организациях

02 – ПРОБЛЕМЫ

Недостатки исходных сканов

Результаты опроса

Мы спросили пользователей о том, что больше всего раздражает в сканах документов и в каких форматах они обычно работают с цифровыми материалами.

53 респондента

Проблемы сканов документов

Средняя оценка по шкале 1–5, где 5 — очень беспокоит

1Файлы отсканированы под углом, кривые поля 3.667
2Огромный «тяжёлый» файл 3.154
3Сканы низкого разрешения, текст нечитаем 2.923
4Тени, разводы, пятна, посторонние объекты 2.897
5Проблемы с цветопередачей 2.692
6Отсутствие важных фрагментов 2.462
7Слабый или отсутствующий OCR-слой 2.447
8«Файл-мусорка» (несвязанные документы) 1.744

Форматы цифровых документов

 

PDF (многостраничный) 46 (97,9%)
Текстовые файлы (TXT, DOC, EPUB) 27 (57,4%)
JPG / PNG (изображения страниц) 25 (53,2%)
DJVU 3 (6,4%)
Только онлайн-просмотр в браузере 3 (6,4%)
Наклон и искажения
Геометрия страницы нарушена: текст неудобно читать, вид страницы далёк от прекрасного. Часто встречается при сканировании книг разворотами.
Просвечивающий текст
На открытой странице виден текст с обратной стороны. Главная причина – тонкая бумага.
Широкие поля и две страницы
Отсканированные разворотами книги часто очень не удобны в использовании, особенно если текст написан/напечатан мелким шрифтом.
Посторонние элементы
Пятна, линии, рукописные комментарии и/или исправления на полях и в печатном тексте, тени, пальцы – всё это мешает чтению и портит внешний вид скана.
«Тяжёлые» файлы
Иногда файлы с оцифрованными книгами настолько велики, что их даже нельзя открыть, не говоря о том, чтобы читать.
Отсутствие OCR-слоя
Нет возможности поиска и копирования текста

03 – О ПРОЕКТЕ

Преимущества проекта ScanMakeUp

Наш пользователь — любой человек (как исследователь, так и нет), который хочет поработать со старым плохо читаемым сканом, не заработав головную боль и не потратив целое состояние на приведение скана в читаемый вид.
  • Открытый исходный код
    Весь код проекта находится в открытом доступе. Любой исследователь или разработчик может адаптировать, расширить или интегрировать компоненты в собственную инфраструктуру.
  • Конвейерная архитектура
    Система построена как конвейер независимых, взаимозаменяемых модулей. Каждый компонент решает строго одну задачу и может быть заменён или дополнен без перестройки всей системы.
  • Бесплатное использование
    Инструментарий распространяется без лицензионных ограничений и не требует подписок. Доступность является ключевым принципом проекта.
  • Множественность инструментов
    Для каждой задачи предусмотрено несколько алгоритмических решений — от классических методов компьютерного зрения до нейросетевых подходов — что позволяет выбирать стратегию под конкретный тип дефекта и решать сразу несколько проблем, комбинируя инструменты из «палитры».
  • Ориентация на архивные и библиотечные документы
    Датасет и тестовые сценарии сформированы на основе реальных оцифрованных фондов из открытых российских архивных и библиотечных ресурсов.
  • Опора на реальные пользовательские проблемы
    Набор инструментов разработан на основе проблем, выявленных в ходе опроса пользователей, — постобработка без ресканирования и без повторного доступа к оригиналу.

04 – ДАТАСЕТ

Датасет

Раздел I
Статистика коллекции PDF — мягкий тёмный стиль
39.81 GB
Общий объём
667
PDF
613
Из них имеет OCR
15
Широкие* PDF
* ширина > высоты

Размер файла

Средний размер 61.11 MB
Медианный размер 20.30 MB

Размеры страниц (px)

Средний размер (573, 854)
Медианный размер (422, 619)
Типичный диапазон (~50%)
Ширина 370538
Высота 573737

Личный опыт работы со сканированием — особенности формирования и обработки датасета

Структура каталога

Вместо специализированных библиоменеджеров — интуитивная файловая иерархия. Для каждого исследователя — отдельная папка, внутри — текстовый файл с биографической справкой (институциональная принадлежность, научные векторы). Сборники статей группируются по тематическим папкам.

Быстрое восстановление контекста при каждом обращении к работам.

OCR‑инструменты и чтение

ABBYY FineReader — первичная обработка фото разворотов: разделение, выравнивание строк, ч/б конвертация, распознавание с произвольным набором языков. Adobe Acrobat — быстрое чтение больших PDF, менее требователен к ресурсам.

  • Transkribus — для сложной исторической типографики
  • Tesseract (Fraktur) — распознавание готического шрифта

Сжатие и цифровые коллекции

Исходные фото разворотов (2–3 МБ) проходили сжатие и ч/б преобразование — радикальное сокращение объёма без потери читаемости OCR.

Обращение к онлайн-архивам: РГИА (описи дел), ЦГИА, РНБ ОР, зарубежные проекты (Баварская государственная библиотека, Тюбинген).

* Ширина страницы больше высоты — то есть, скорее всего, это сканы разворотов.
Раздел II
Аналитика датасета — мягкая тёмная тема
30.4
ГБ
435
PDF-файла
54 791
Страниц
39.8
ГБ раздел I (667 PDF)
Алгоритм парсинга НЭБ: формирование URL → проверка наличия → фильтрация → скачивание PDF
Категории файлов
Цветные файлы252
57.9% от всех файлов
Файлы с OCR296
68.0% от всех файлов
Высокое разрешение (>500 КБ/стр)198
45.5% от всех файлов
Топ-3 года
2023 — 109 файлов
2024 — 53 файла
2009 — 39 файлов
Топ-3 месяца
Декабрь — 69 файлов
31 декабря: 37 файлов
Октябрь — 56 файлов
Апрель — 43 файла
Файлов по годам
Корреляции между признаками (уровень страниц)
−1.0
+1.0
jpeg_ratio ↔ compression_ratio+1.000
approx_colors ↔ page_size+0.918
jpeg_ratio ↔ entropy+0.912
mean_brightness ↔ entropy−0.782
compression ↔ mean_brightness−0.743
jpeg_ratio ↔ approx_colors+0.578
Ключевые гипотезы
1OCR-слой не зависит от года сканированияНе подтверждается
Корреляция год ↔ OCR = −0.006. До 2000 г. — 100% OCR, после 2010 г. — лишь 78.0%. Линейного тренда нет.
2Качество скана частично растёт со временемЧастично
По энтропии слабая положительная корреляция (r = +0.108). По цветовому разнообразию — слабая положительная (r = +0.094).
3Инструмент создания определяет наличие OCRПодтверждается
ABBYY FineReader — 100% OCR. ImageMagick — 81.7%. Adobe Acrobat — лишь 27.3%.
4Сильнее сжатие → ниже контраст и энтропияПодтверждается
r(сжатие ↔ энтропия) = +0.912 · r(сжатие ↔ std_brightness) = −0.131.
5Изменённые после создания файлы хуже по качествуПодтверждается
OCR у изменённых — 32.1%, у неизменённых — 84.1%. Средняя энтропия: 4.16 против 5.41.
Эволюция цветовых типов страниц по годам

05 – АРХИТЕКТУРА СИСТЕМЫ

Конвейер обработки

Модульный конвейер с независимо заменяемыми компонентами

SourceBreaker
Декомпозиция источников на атомарные единицы
Палитра предобработки
Применение выбранных инструментов к каждой странице
Компилятор
Сборка результата в финальный документ
Результат
Улучшенная копия, оптимизированный размер

06 – ИНСТРУМЕНТЫ

Алгоритмы обработки

Rotate — наклон
Split — разворот
Traces — просвечивание
Margins — поля
Fonts — шрифты
Artifacts — пятна и линии
Проблема: наклон полей страницы при сканировании — документ уложен под углом, горизонтали текста не параллельны краям кадра.
K-Means
Геометрический (geom)
Линейная алгебра (linalg)
Нейросеть (AngleNet)

Изображение поворачивается в диапазоне углов; на каждом шаге строкам назначаются кластеры (текст / фон) методом K-Means. Угол, при котором в «белом» кластере максимальное число строк, считается углом коррекции.

def doit(img): original = img.copy() stat = [] gray = original.convert('L') for angle in np.arange(-7, 7, 0.1): img_rot = gray.rotate(angle, expand=False, fillcolor='white') h = np.array(img_rot).sum(1) km = KMeans(n_clusters=2, n_init="auto").fit(h.reshape(-1, 1)) lb = km.labels_ white = 1 if np.mean(h[np.argwhere(lb == 1)]) > np.mean(h[np.argwhere(lb == 0)]) else 0 stat.append((angle, np.bincount(lb)[white])) stat = np.array(stat) rotate_angle = stat[np.argmax(stat[:, 1]), 0] return original.rotate(rotate_angle, expand=False, fillcolor='white')

Бинаризация методом Оцу → поиск ненулевых координат → минимальный описывающий прямоугольник (minAreaRect). Угол наклона извлекается из параметров прямоугольника.

def __detect_angle_geom(self, page): gray = cv.cvtColor(page, cv.COLOR_BGR2GRAY) blurred = cv.GaussianBlur(gray, (5, 5), 0) _, thresh = cv.threshold(blurred, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU) kernel = cv.getStructuringElement(cv.MORPH_RECT, (4, 4)) thresh_filtered = cv.morphologyEx(thresh, cv.MORPH_OPEN, kernel) nonZeroCoordinates = cv.findNonZero(thresh_filtered) if nonZeroCoordinates is None: return None box = cv.minAreaRect(nonZeroCoordinates) if box[1][0] * box[1][1] > 0.90 * page.shape[0] * page.shape[1]: return 0 angle = box[2] width, height = box[1] angle = 90 - angle if width < height else -angle if angle > 45: angle -= 90 elif angle < -45: angle += 90 return -angle

Детектор границ Canny → PCA по координатам граничных пикселей. Главный собственный вектор ковариационной матрицы даёт направление основного текстового содержимого.

def __detect_angle_linalg(self, page): gray = cv.cvtColor(page, cv.COLOR_BGR2GRAY) edges = cv.Canny(cv.GaussianBlur(gray, (5, 5), 0), 50, 150) y, x = np.where(edges > 0) if len(x) < 2: return None points = np.stack((x, y), axis=1) cov_matrix = np.cov(points, rowvar=False) eig_vals, eig_vecs = np.linalg.eig(cov_matrix) main_vec = eig_vecs[:, np.argmax(eig_vals)] angle = np.degrees(np.arctan2(main_vec[1], main_vec[0])) if angle > 45: angle -= 90 elif angle < -45: angle += 90 return angle

Лёгкая свёрточная сеть AngleNet на синтетических данных. Напрямую предсказывает нормализованный угол поворота. Обучение на синтетических страницах с известным углом в диапазоне ±15°.

class AngleNet(nn.Module): def __init__(self, in_channels=1, base=16): super().__init__() self.inc = DoubleConv(in_channels, base, drop=False) self.down1 = Down(base, base * 2, drop=False) self.down2 = Down(base * 2, base * 4, drop=True) self.head = nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Linear(base * 4, 64), nn.ReLU(inplace=True), nn.Dropout(0.1), nn.Linear(64, 1), ) def forward(self, x): x = self.inc(x) x = self.down1(x) x = self.down2(x) return self.head(x) criterion = nn.SmoothL1Loss() pred_angle = model(img_tensor) * CONFIG["max_rotation"]
Проблема: при сканировании книг или журналов два листа попадают в один кадр как единый разворот — страницы необходимо разделить.
simple
pixel
var
kmeans
нейросеть (SpineNet)

Разделение ровно посередине изображения или по явно переданным координатам линии разреза. Быстрый метод для случаев, когда геометрия разворота известна заранее.

def _simple_split(self, page, p1=None, p2=None): h, w = page.shape[:2] if None in (p1, p2): left = page[:, :w // 2] right = page[:, w // 2:] else: mid_x = int((p1[0] + p2[0]) / 2) left = page[:, :mid_x] right = page[:, mid_x:] cv.imwrite('left_part.png', left) cv.imwrite('right_part.png', right)

Вертикальная проекция суммы тёмных пикселей: в центральной зоне разворота текст отсутствует, создавая характерный минимум. Нахождение этого минимума определяет линию разреза.

def __pixel_values_split(self, page): gray = cv.cvtColor(page, cv.COLOR_BGR2GRAY) h, w = gray.shape _, binary = cv.threshold(gray, 200, 255, cv.THRESH_BINARY_INV) proj = np.sum(binary[int(h*0.1):int(h*0.9), :], axis=0) smoothed = uniform_filter1d(proj.astype(float), size=int(w * 0.02)) center_region = smoothed[int(w*0.3):int(w*0.7)] above = np.where(center_region >= center_region.max() * 0.85)[0] best_x = int(w*0.3) + (above[0] + above[-1]) // 2 return (float(best_x), 0.0), (float(best_x), float(h))

Локальная дисперсия яркости: области с текстом имеют высокую дисперсию, поля и желоб разворота — низкую. Минимум суммарной дисперсии по столбцам указывает на линию разреза.

def __var_split(self, page): h, w = page.shape[:2] scale = min(1.0, 250.0 / max(h, w)) small = cv.resize(page, (int(w*scale), int(h*scale))) gray = cv.cvtColor(small, cv.COLOR_BGR2GRAY).astype(np.float32) mu = cv.blur(gray, (7, 7)) var = cv.blur(gray**2, (7, 7)) - mu**2 text_mask = (var > np.percentile(var, 65)).astype(np.uint8) * 255 proj = uniform_filter1d(np.sum(text_mask, axis=0).astype(float), ...) split_x = int((cx0 + np.argmin(center)) / scale) return (float(split_x), 0.0), (float(split_x), float(h))

K-Means кластеризует пиксели в два класса (текст/фон). По дисперсии текстового кластера в центральной зоне определяется желоб разворота. Изображение масштабируется до 250 пкс без потери структурной информации — для ускорения расчётов.

def __kmeans_split(self, page): h, w = page.shape[:2] scale = 250 / max(h, w) small = cv.resize(page, (int(w*scale), int(h*scale))) if scale < 1 else page.copy() gray = cv.cvtColor(small, cv.COLOR_BGR2GRAY) km = KMeans(n_clusters=2, n_init='auto') labels = km.fit_predict(gray.reshape(-1, 1)).reshape(gray.shape) text_cluster = np.argmin(km.cluster_centers_) mask = cv.resize((labels == text_cluster).astype(np.uint8) * 255, (w, h), interpolation=cv.INTER_NEAREST) col_variance = uniform_filter1d(np.var(mask, axis=0), size=int(w*0.02)) split_x = left + (text_cols[0] + text_cols[-1]) // 2 return (float(split_x), 0.0), (float(split_x), float(h))

Для решения задачи детекции и точного позиционирования линии книжного разворота был разработан пайплайн, включающий синтетический датасет, двухголовую нейронную сеть и специализированную функцию потерь.

Архитектура модели, реализованная в классе SpineNet, построена по принципу условного выполнения и состоит из общего свёрточного энкодера и двух независимых «голов». Энкодер извлекает глобальные признаки изображения, после чего они передаются на классификатор и регрессор. Классификационная голова определяет факт наличия разворота, выдавая единственный логит. Регрессионная голова, применяющая сигмоиду на выходе, предсказывает те самые две нормализованные координаты линии разворота. Важной особенностью реализации является то, что регрессионная часть вычисляется только в том случае, если классификатор уверен в наличии разворота. Если разворот не обнаружен, модель сразу возвращает нулевые координаты, что позволяет избежать вычислительных затрат и паразитных предсказаний на фоне.

Обучение модели оптимизируется с помощью составной функции потерь SpineLoss, которая агрегирует ошибки классификации и регрессии с заданными весами. Для блока классификации используется бинарная кросс-энтропия с логитами, а для блока регрессии применяется SmoothL1Loss, менее чувствительный к грубым выбросам в координатах. Особенность этой функции потерь заключается в маскировании: ошибка регрессии вычисляется исключительно для тех изображений, на которых разворот присутствует.

import torch import torch.nn as nn class ConvBlock(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.block = nn.Sequential( nn.Conv2d(in_ch, out_ch, 3, padding=1, bias=False), nn.BatchNorm2d(out_ch), nn.ReLU(inplace=True), nn.Conv2d(out_ch, out_ch, 3, padding=1, bias=False), nn.BatchNorm2d(out_ch), nn.ReLU(inplace=True), ) def forward(self, x): return self.block(x) class SpineNet(nn.Module): def __init__(self, base: int = 32): super().__init__() self.encoder = nn.Sequential( ConvBlock(1, base), nn.MaxPool2d(2), ConvBlock(base, base * 2), nn.MaxPool2d(2), ConvBlock(base * 2, base * 4), nn.MaxPool2d(2), ConvBlock(base * 4, base * 8), nn.AdaptiveAvgPool2d(1), nn.Flatten(), ) feat = base * 8 self.cls_head = nn.Sequential( nn.Linear(feat, 64), nn.ReLU(inplace=True), nn.Dropout(0.1), nn.Linear(64, 1), ) self.reg_head = nn.Sequential( nn.Linear(feat, 128), nn.ReLU(inplace=True), nn.Dropout(0.1), nn.Linear(128, 2), nn.Sigmoid(), ) def forward(self, x): feat = self.encoder(x) cls = self.cls_head(feat) mask = (cls > 0).squeeze(-1) reg = torch.zeros(cls.size(0), 2, device=cls.device, dtype=cls.dtype) if mask.any(): reg[mask] = self.reg_head(feat[mask]) return torch.cat([cls, reg], dim=1)
Проблема: на скане проступает текст с оборотной стороны листа (просвечивание), затрудняющий чтение основного содержимого.

Жёсткая бинаризация по порогу 170: пиксели разделяются строго на чёрный (текст) и белый (фон). Полутона, которыми проявляется просвечивающий текст, уходят в белый класс и исчезают из результата.

def doit(img): img = img.convert('L') img_array = np.array(img) binary_array = np.where(img_array <= 170, 0, 255).astype(np.uint8) img = Image.fromarray(binary_array).convert('RGB') return img
Проблема: широкие пустые поля занимают значительную часть кадра, уменьшая видимую площадь содержательной части скана.

Горизонтальная и вертикальная проекции бинаризованного изображения: строки/столбцы с достаточным количеством тёмных пикселей считаются содержательными. Изображение обрезается по крайним содержательным строкам/столбцам с небольшим отступом.

def doit(img): binary = (np.array(img.convert('L')) <= 128) h = binary.sum(1) v = binary.sum(0) rows = np.where(h > 0.1 * h.max())[0] cols = np.where(v > 0.1 * v.max())[0] l, r = cols[0] - 10, cols[-1] + 10 t, b = rows[0] - 10, rows[-1] + 10 return img.crop((l, t, r, b))
Проблема: разрушение растровой структуры символов при низком разрешении сканирования или компрессии — «битые» шрифты, потеря читаемости.

Для восстановления качества шрифтов применяется нейросетевой подход на основе свёрточной архитектуры BalancedTextSR с обучением на парах «низкое разрешение → высокое разрешение».

План реализации:
  1. Датасет: формирование пар изображений из директорий bad_tiles_all и good_tiles_all с приведением к градациям серого и бинаризацией.
  2. Модель: свёрточная сеть BalancedTextSR с энкодером и декодером на основе пропускающих соединений. Количество параметров — 152,833. Выходные логиты преобразуются через сигмоиду.
  3. Функция потерь: взвешенная бинарная кросс-энтропия с логитами, где положительный класс (текст) получает больший вес для компенсации дисбаланса.
  4. Оптимизация: AdamW с начальной скоростью обучения 1e-3 и динамическим снижением при затухании улучшений.
  5. Метрики: оценка качества по PSNR и SSIM на бинаризованных предсказаниях.
  6. Обучение: разделение на обучающую (80%) и валидационную (20%) выборки с сохранением лучшей модели.
Данный инструмент находится в активной стадии разработки и тестирования.
Проблема: чёрные пятна, линии и другие артефакты сканирования, не пересекающиеся с текстом, — грязь на стекле сканера, повреждения носителя, пометки карандашом. Их необходимо удалить, не затронув содержательные символы.

Алгоритм работает в два этапа: сначала находит кандидатов на текстовые символы по геометрическим признакам (get_text_candidates), затем маскирует всё, что находится вне текстовых зон (text_mask), заменяя фоном — тем самым «стирая» артефакты, изолированные от текста.

Шаг 1 — поиск кандидатов на текст
def get_text_candidates(binary, min_area, max_area, min_aspect=0.1, max_aspect=7.0, solidity_thresh=0.25, fill_ratio_thresh=0.1): contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) candidates = [] for cnt in contours: area = cv2.contourArea(cnt) if area < min_area or area > max_area: continue x, y, rw, rh = cv2.boundingRect(cnt) bbox_area = rw * rh if bbox_area == 0: continue aspect = rw / rh if rh > 0 else 0 if aspect < min_aspect or aspect > max_aspect: continue if aspect > 10.0 or aspect < 0.1: continue hull = cv2.convexHull(cnt) hull_area = cv2.contourArea(hull) solidity = area / hull_area if hull_area > 0 else 0 if solidity < solidity_thresh: continue fill_ratio = area / bbox_area if fill_ratio < fill_ratio_thresh: continue candidates.append({ 'contour': cnt, 'bbox': (x, y, rw, rh), 'center': (x + rw//2, y + rh//2), 'area': area, 'aspect': aspect, 'solidity': solidity, }) return candidates
Контуры отфильтровываются по пяти критериям: площадь (min_area / max_area), соотношение сторон (0.1–7.0 — характерно для букв), solidity — заполненность выпуклой оболочки (≥ 0.25 исключает нитевидные линии), fill_ratio — заполненность bbox (≥ 0.1 исключает рамки без содержимого). Оставшиеся контуры — вероятные символы.
Шаг 2 — маскирование и удаление артефактов
text_mask = cv2.bitwise_and(binary_clean, text_zone_mask) if margin > 0: kernel = cv2.getStructuringElement( cv2.MORPH_ELLIPSE, (margin*2+1, margin*2+1)) text_mask = cv2.dilate(text_mask, kernel, iterations=dilate_iterations) result = np.array(img_rgb) result[text_mask == 0] = (255, 255, 255) return Image.fromarray(result)
Пересечение binary_clean и text_zone_mask оставляет тёмные пиксели только внутри текстовых зон. Морфологическое расширение (dilation) эллиптическим ядром добавляет «буфер» вокруг символов — чтобы не срезать штрихи на краях. Всё, что осталось за пределами маски, закрашивается белым: пятна и линии исчезают, текст остаётся нетронутым.
Синтетический датасет для детекции линии разворота
  1. Изображения создаются синтетически с применением широкого спектра аугментаций, моделирующих реальные условия сканирования.
  2. В набор аугментаций включены: неравномерное освещение, аддитивный и мультипликативный шум, артефакты сжатия, геометрические искажения текстовых строк, а также случайные повороты страницы.
Примеры
Примеры использования кода
Rotate
Traces
Margins
Artifacts

07 – РЕЗУЛЬТАТЫ

Демонстрация полученных результатов

Кейс — тестовый документ
«Историческая хронология» Ермолаева И.П.
Разделение страниц Удаление пятен Поворот Уменьшение полей

ДО → ПОСЛЕ

Возможности ScanMakeUp среди бесплатных аналогов

Возможности ScanMakeUp среди бесплатных аналогов

Анализ на примере базового инструмента Split
нажмите, чтобы раскрыть
ScanMakeUp — конвейер из шести независимых модулей постобработки сканов. Система обработки архивных сканов, предполагающая последовательное применение ряда инструментов к одному файлу, не имеет прямых аналогов среди бесплатных онлайн-инструментов — это узкоспециализированная операция улучшения перцептивного качества архивных сканов.
В то же время ряд инструментов ScanMakeUp имеет аналоги среди веб-утилит. К примеру — разделение разворота на две страницы (модуль Split). В основе сравнения лежат четыре популярных онлайн-сервиса для работы с PDF, в числе прочих инструментов предоставляющих разделение страниц.
* Эти сервисы для работы с PDF в числе прочих инструментов представляют разделение страниц. Они не претендуют на реставрацию сканов как таковую — это узкие, однофункциональные единицы, и сравнивать их с ScanMakeUp как с целостной системой было бы некорректно.

Четыре бесплатных аналога модуля Split

Рассматриваемые сервисы представляют собой узкие, однофункциональные веб-утилиты. Сопоставление выполнено по функциональности в задаче разделения разворота.

01
PDF24 Tools
Halve PDF Pages
Разделение строго по центру, без возможности корректировки линии или выборочной обработки страниц. Проект немецкой компании geek software GmbH.
Бесплатно
02
i2PDF
Split PDF Pages in Half
Выбор вертикальной или горизонтальной ориентации разреза, ручная настройка положения не предусмотрена. Файлы удаляются через 30 минут.
Бесплатно
03
DeftPDF
Split PDF Down the Middle
Наибольшая гибкость: ручное позиционирование линии, исключение страниц, поддержка RTL-документов. Экспорт в облако, по почте, печать.
Freemium
04
FlipHTML5
Split PDF Page
Автоматическое определение либо ручной выбор направления. Хранение — 6 часов.
Бесплатно
Примечание: Все сервисы доступны без регистрации. Анализ выполнен на основе открытых данных по состоянию на 2026 год.
Критерий
ScanMakeUp
PDF24
i2PDF
DeftPDF
FlipHTML5
Алгоритм разделения
4 стратегии
Фиксированный центр
Выбор ориентации
Ручная линия
Авто / ручной
Положение линии
Автоматическое определение
Центр страницы
Центр страницы
Ручная настройка
Авто / ручной
Хранение файлов
Локальная обработка
Не указано
30 минут
5 часов
6 часов
Ограничения по объёму
Не заявлены
Не заявлены
Не заявлены
1 файл за раз
Не заявлены
Заключение. Качество разделения PDF-разворотов сопоставимо у всех пяти инструментов (ScanMakeUp, PDF24, i2PDF, DeftPDF, FlipHTML5). Все решения корректно разделяют страницы без значимых потерь качества. Различия — в гибкости настроек, политике хранения и ограничениях бесплатного использования.

08 – ПРОГРАММА

Как протестировать нашу программу на своём файле?


Html code will be here

Скачать приложение можно здесь
Get started now

09 – КОМАНДА

Участники проекта

  • Иван Бибилов
    Куратор проекта
  • Мария Филиппова
    Студентка ДПО программы ПАНДАН
  • Анастасия Борискина
    Студентка магистратуры ПАНДАН
  • Дмитрий Местковский
    Студент магистратуры ПАНДАН
Made on
Tilda