Адаптивная Стеганография: Искусство прятаться в шуме
Классический LSB-метод встраивает данные последовательно или случайно, но он не учитывает контент изображения. Это делает его уязвимым: изменение гладкого голубого неба (где все пиксели одинаковы) заметно статистически. Ответом на это стала Адаптивная Стеганография.
Идея: Прячем там, где не видно
Адаптивные алгоритмы (такие как HUGO, WOW, S-UNIWARD) работают по принципу «минимума искажений». Прежде чем спрятать бит, алгоритм анализирует изображение и строит карту сложности.
- Гладкие области (Smooth): Низкая сложность. Изменения здесь сильно искажают статистику. Алгоритм их избегает.
- Текстуры и границы (Edges/Noise): Высокая сложность. Здесь пиксели и так сильно отличаются друг от друга. Изменение LSB в листве деревьев или на границе объектов статистически неотличимо от естественного шума камеры.
Таким образом, адаптивная стеганография прячет данные только в «шумных» частях картинки, делая их невидимыми для RS-анализа и других простых атак.
Пример кода: Адаптивное встраивание по границам (Python)
Мы напишем упрощенный пример, который вычисляет карту границ (градиент) и прячет данные только в пикселях с высокой вариацией.
import cv2
import numpy as np
def to_bin(data):
return ''.join(format(ord(i), '08b') for i in data)
def hide_adaptive(image_path, secret, output_path, threshold=30):
"""
Встраивает данные только в области границ (высокая дисперсия).
threshold: порог 'шумности', выше которого можно прятать.
"""
img = cv2.imread(image_path)
if img is None:
return "Ошибка открытия"
# 1. Вычисляем карту границ (используем градиент Собеля)
# Преобразуем в оттенки серого для анализа сложности
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# dx и dy - изменения яркости по X и Y
grad_x = cv2.Sobel(gray, _64F, 1, 0, ksize=3)
grad_y = cv2.Sobel(gray, _64F, 0, 1, ksize=3)
# Общая величина градиента в каждой точке
magnitude = cv2.magnitude(grad_x, grad_y)
# 2. Подготовка данных
binary_secret = to_bin(secret + "[END]")
data_idx = 0
data_len = len(binary_secret)
height, width, channels = img.shape
capacity = 0
# 3. Встраивание
for y in range(height):
for x in range(width):
# Проверяем, достаточно ли 'шумный' этот пиксель
# magnitude[y, x] показывает силу границы в этой точке
if magnitude[y, x] > threshold:
capacity += 1
if data_idx < data_len:
# Прячем в синий канал (0)
current_bit = int(binary_secret[data_idx])
img[y, x, 0] = (img[y, x, 0] & 254) | current_bit
data_idx += 1
cv2.imwrite(output_path, img)
if data_idx < data_len:
return f"Ошибка: Не хватило места. Спрятано {data_idx} из {data_len} бит. Попробуйте уменьшить порог."
return f"Успешно. Использовано пикселей-границ: {capacity}. Файл: {output_path}"
# # Пример использования
# # Данные будут спрятаны только в контурах объектов, оставляя небо/фоны чистыми.
# print(hide_adaptive('landscape.jpg', 'Секрет в лесу', 'adaptive_stego.png'))
Извлечение данных из такого контейнера требует повторения процесса: получатель должен вычислить ту же самую карту границ (градиент), чтобы понять, какие именно пиксели читать. Это создает проблему синхронизации, так как само встраивание слегка меняет пиксели и может изменить карту границ. Профессиональные алгоритмы (WOW) используют специальные функции «стоимости», устойчивые к изменениям LSB.







