128 lines
4.5 KiB
Python
128 lines
4.5 KiB
Python
"""
|
||
Вспомогательные функции для стеганографии
|
||
Модуль содержит утилиты для работы с текстом, битами и метриками качества
|
||
"""
|
||
|
||
import numpy as np
|
||
from math import log10, sqrt
|
||
|
||
|
||
def text_to_bits(text: str) -> str:
|
||
"""
|
||
Преобразует текст в строку битов.
|
||
|
||
Аргументы:
|
||
text (str): Входной текст (например, "Привет")
|
||
|
||
Возвращает:
|
||
str: Строка из '0' и '1' (например, "11010001...")
|
||
|
||
Пример:
|
||
>>> text_to_bits("A")
|
||
'01000001'
|
||
"""
|
||
# Кодируем текст в байты (UTF-8 поддерживает русский язык)
|
||
bytes_data = text.encode('utf-8')
|
||
|
||
# Преобразуем каждый байт в 8-битную строку и объединяем
|
||
bits = ''.join(format(byte, '08b') for byte in bytes_data)
|
||
|
||
return bits
|
||
|
||
|
||
def bits_to_text(bits: str) -> str:
|
||
"""
|
||
Преобразует строку битов обратно в текст.
|
||
|
||
Аргументы:
|
||
bits (str): Строка из '0' и '1' (длина должна быть кратна 8)
|
||
|
||
Возвращает:
|
||
str: Декодированный текст
|
||
|
||
Пример:
|
||
>>> bits_to_text("01000001")
|
||
'A'
|
||
"""
|
||
# Проверяем, что длина строки кратна 8
|
||
if len(bits) % 8 != 0:
|
||
raise ValueError("Длина битовой строки должна быть кратна 8")
|
||
|
||
# Разбиваем строку на блоки по 8 бит и преобразуем в байты
|
||
bytes_data = bytearray()
|
||
for i in range(0, len(bits), 8):
|
||
byte = bits[i:i + 8] # берем 8 бит
|
||
bytes_data.append(int(byte, 2)) # преобразуем из двоичной в число
|
||
|
||
# Декодируем байты в строку (UTF-8)
|
||
return bytes_data.decode('utf-8')
|
||
|
||
|
||
def calculate_psnr(original_path: str, modified_path: str) -> float:
|
||
"""
|
||
Вычисляет PSNR (Peak Signal-to-Noise Ratio) между двумя изображениями.
|
||
|
||
PSNR показывает, насколько сильно изменилось изображение после внедрения.
|
||
Чем выше значение, тем меньше изменений.
|
||
|
||
- PSNR > 40 dB: отличное качество (изменения практически незаметны)
|
||
- PSNR 30-40 dB: хорошее качество
|
||
- PSNR 20-30 dB: заметные искажения
|
||
- PSNR < 20 dB: сильные искажения
|
||
|
||
Аргументы:
|
||
original_path (str): Путь к исходному изображению
|
||
modified_path (str): Путь к измененному изображению
|
||
|
||
Возвращает:
|
||
float: Значение PSNR в децибелах (dB)
|
||
"""
|
||
from PIL import Image
|
||
|
||
# Загружаем изображения
|
||
img1 = Image.open(original_path).convert('RGB')
|
||
img2 = Image.open(modified_path).convert('RGB')
|
||
|
||
# Преобразуем в массивы numpy
|
||
img1_array = np.array(img1, dtype=np.float64)
|
||
img2_array = np.array(img2, dtype=np.float64)
|
||
|
||
# Вычисляем среднеквадратичную ошибку (MSE)
|
||
mse = np.mean((img1_array - img2_array) ** 2)
|
||
|
||
# Если MSE = 0, изображения идентичны
|
||
if mse == 0:
|
||
return float('inf')
|
||
|
||
# Максимальное значение пикселя (для RGB это 255)
|
||
max_pixel = 255.0
|
||
|
||
# Формула PSNR: 10 * log10(MAX^2 / MSE)
|
||
psnr = 10 * log10((max_pixel ** 2) / mse)
|
||
|
||
return psnr
|
||
|
||
|
||
def calculate_capacity(image_path: str) -> int:
|
||
"""
|
||
Рассчитывает максимальную вместимость изображения в байтах для LSB метода.
|
||
|
||
Для LSB: каждый пиксель (RGB) = 3 бита = 0.375 байта.
|
||
|
||
Аргументы:
|
||
image_path (str): Путь к изображению
|
||
|
||
Возвращает:
|
||
int: Максимальное количество байт, которое можно спрятать
|
||
"""
|
||
from PIL import Image
|
||
|
||
img = Image.open(image_path)
|
||
width, height = img.size
|
||
total_pixels = width * height
|
||
|
||
# 3 бита на пиксель, переводим в байты
|
||
max_bits = total_pixels * 3
|
||
max_bytes = max_bits // 8
|
||
|
||
return max_bytes |