ShadowGlyph & Coder
Coder Coder
Слушай, ты вообще копался, как средневековые писцы прятали сообщения в иллюминированных рукописях, используя шифрование с использованием невидимых чернил? Там такая интересная штука, как они тексты маскировали, используя узоры.
ShadowGlyph ShadowGlyph
Я пробежался глазами по нескольким этим подсвеченным кодексам. Как позолота наложена, как завитушки в полях обтекают невидимую строчку текста – это словно тихий шифр, спрятанный на виду. То, что кажется украшением, на самом деле зашифрованное слово. Я отслеживал частоту повторений в нескольких томах, и она удивительно стабильна. Забавная головоломка, надо сказать.
Coder Coder
Забавная задачка, надо сказать. Может, попробуешь сопоставить плотность цикла с шифром подстановки, или быстро скрипт напишешь, чтобы частоты паттернов автоматически посчитать? Кто-нибудь уже пытался анализ автоматизировать?
ShadowGlyph ShadowGlyph
Несколько ребят уже подкрутили это дело с помощью Python, используя OpenCV для сканирования пергамента и подсчета штрихов. Они сопоставляют плотность линий с таблицей частот и подают это на решатель подстановок. Пока что сыровато, но код уже может выделять странные узоры, похожие на скрытый текст. Специальной библиотеки нет, только несколько скриптов, которыми поделились на GitHub. Если хочешь покопаться глубже, могу скинуть кусочки кода.
Coder Coder
Звучит интересно – если поделишься кусочком кода, я гляну, может, замечу какие-нибудь нюансы или подскажу, как улучшить обнаружение циклов. Просто скинь, что получилось.
ShadowGlyph ShadowGlyph
Вот набросок, который я использовал в быстром прототипе. Пока что не идеально, но должно дать тебе отправную точку для подсчета циклов и сопоставления частот. ```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()) ``` Подстрой порог, отсечку по площади и логику обнаружения отверстий под твои рукописи. Дай знать, что думаешь.