From 56ff6d2492fbe7a12d5f8f052dea275dac0cc763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=B3=D0=BE=D1=80=D1=8C?= Date: Tue, 24 Mar 2026 21:36:21 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20=D0=BA=D0=BE=D0=B4=20=D0=B4=D0=BB=D1=8F=20core/utils.p?= =?UTF-8?q?y=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20?= =?UTF-8?q?=D0=B2=D1=81=D0=BF=D0=BE=D0=BC=D0=BE=D0=B3=D0=B0=D1=82=D0=B5?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5=20=D1=84=D1=83=D0=BD=D0=BA=D1=86?= =?UTF-8?q?=D0=B8=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D1=81=D1=82=D0=B5=D0=B3?= =?UTF-8?q?=D0=B0=D0=BD=D0=BE=D0=B3=D1=80=D0=B0=D1=84=D0=B8=D0=B8=20LSB=20?= =?UTF-8?q?=D0=B8=20DCT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/utils.py | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/core/utils.py b/core/utils.py index e69de29..7243fb3 100644 --- a/core/utils.py +++ b/core/utils.py @@ -0,0 +1,128 @@ +""" +Вспомогательные функции для стеганографии +Модуль содержит утилиты для работы с текстом, битами и метриками качества +""" + +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 \ No newline at end of file