ShadowGlyph & Coder
Слушай, ты вообще копался, как средневековые писцы прятали сообщения в иллюминированных рукописях, используя шифрование с использованием невидимых чернил? Там такая интересная штука, как они тексты маскировали, используя узоры.
Я пробежался глазами по нескольким этим подсвеченным кодексам. Как позолота наложена, как завитушки в полях обтекают невидимую строчку текста – это словно тихий шифр, спрятанный на виду. То, что кажется украшением, на самом деле зашифрованное слово. Я отслеживал частоту повторений в нескольких томах, и она удивительно стабильна. Забавная головоломка, надо сказать.
Забавная задачка, надо сказать. Может, попробуешь сопоставить плотность цикла с шифром подстановки, или быстро скрипт напишешь, чтобы частоты паттернов автоматически посчитать? Кто-нибудь уже пытался анализ автоматизировать?
Несколько ребят уже подкрутили это дело с помощью Python, используя OpenCV для сканирования пергамента и подсчета штрихов. Они сопоставляют плотность линий с таблицей частот и подают это на решатель подстановок. Пока что сыровато, но код уже может выделять странные узоры, похожие на скрытый текст. Специальной библиотеки нет, только несколько скриптов, которыми поделились на GitHub. Если хочешь покопаться глубже, могу скинуть кусочки кода.
Звучит интересно – если поделишься кусочком кода, я гляну, может, замечу какие-нибудь нюансы или подскажу, как улучшить обнаружение циклов. Просто скинь, что получилось.
Вот набросок, который я использовал в быстром прототипе. Пока что не идеально, но должно дать тебе отправную точку для подсчета циклов и сопоставления частот.
```python
import cv2
import numpy as np
from collections import Counter
# Загружаем высококачественное сканирование рукописи
img = cv2.imread('manuscript.png', cv2.IMREAD_GRAYSCALE)
# Бинаризуем – подкорректируй порог под текстуру бумаги
_, binary = cv2.threshold(img, 200, 255, cv2.THRESH_BINARY_INV)
# Ищем контуры – они часто соответствуют чернильным штрихам
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Считаем циклы: цикл – это контур, содержащий дочерний контур
loop_counts = []
for cnt in contours:
# Приближаем форму для уменьшения шума
approx = cv2.approxPolyDP(cnt, 0.01*cv2.arcLength(cnt, True), True)
area = cv2.contourArea(approx)
if area < 50: # игнорируем мелкие точки
continue
# Грубое эвристическое правило: если контур имеет отверстие, это цикл
mask = np.zeros_like(binary)
cv2.drawContours(mask, [cnt], -1, 255, -1)
holes = cv2.findContours(cv2.bitwise_not(mask), cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)[0]
if len(holes) > 1:
loop_counts.append(len(holes)-1)
# Теперь у тебя есть список количества циклов на штрих; сопоставляем с буквами
freq = Counter(loop_counts)
print(freq.most_common())
```
Подстрой порог, отсечку по площади и логику обнаружения отверстий под твои рукописи. Дай знать, что думаешь.