95 lines
3.3 KiB
Python
95 lines
3.3 KiB
Python
"""
|
|
Модуль LSB (Least Significant Bit) стеганографии.
|
|
Поддержка цветных изображений и русских текстов.
|
|
"""
|
|
|
|
import numpy as np
|
|
from PIL import Image
|
|
from .utils import text_to_bits, bits_to_text
|
|
|
|
def encode_lsb(image_path: str, message: str, output_path: str) -> bool:
|
|
"""Скрывает сообщение в изображении методом LSB."""
|
|
img = Image.open(image_path).convert('RGB')
|
|
pixels = np.array(img, dtype=np.uint8)
|
|
height, width, channels = pixels.shape
|
|
|
|
# Формат: [длина сообщения: 32 бита] + [сообщение]
|
|
msg_bytes = message.encode('utf-8')
|
|
msg_length = len(msg_bytes)
|
|
length_bits = format(msg_length, '032b')
|
|
message_bits = text_to_bits(message)
|
|
all_bits = length_bits + message_bits
|
|
bit_list = [int(b) for b in all_bits]
|
|
total_bits = len(bit_list)
|
|
|
|
# Проверка вместимости
|
|
max_bits = height * width * channels
|
|
if total_bits > max_bits:
|
|
print(f"Ошибка: сообщение слишком большое.")
|
|
print(f"Доступно: {max_bits} бит, требуется: {total_bits}")
|
|
return False
|
|
|
|
# Внедрение битов
|
|
bit_index = 0
|
|
for i in range(height):
|
|
for j in range(width):
|
|
for c in range(channels):
|
|
if bit_index >= total_bits:
|
|
break
|
|
pixel_value = pixels[i, j, c]
|
|
bit = bit_list[bit_index]
|
|
pixels[i, j, c] = (pixel_value & 0xFE) | bit
|
|
bit_index += 1
|
|
if bit_index >= total_bits:
|
|
break
|
|
if bit_index >= total_bits:
|
|
break
|
|
|
|
result_img = Image.fromarray(pixels, mode='RGB')
|
|
result_img.save(output_path)
|
|
print(f"Успешно! Спрятано {total_bits} бит ({total_bits // 8} байт)")
|
|
return True
|
|
|
|
|
|
def decode_lsb(image_path: str) -> str:
|
|
"""Извлекает сообщение из изображения методом LSB."""
|
|
img = Image.open(image_path).convert('RGB')
|
|
pixels = np.array(img, dtype=np.uint8)
|
|
height, width, channels = pixels.shape
|
|
|
|
# Извлекаем все биты
|
|
all_bits = []
|
|
for i in range(height):
|
|
for j in range(width):
|
|
for c in range(channels):
|
|
all_bits.append(pixels[i, j, c] & 1)
|
|
|
|
# Читаем длину сообщения (первые 32 бита)
|
|
if len(all_bits) < 32:
|
|
return ""
|
|
|
|
length_bits = all_bits[:32]
|
|
length_str = ''.join(str(b) for b in length_bits)
|
|
msg_length = int(length_str, 2)
|
|
|
|
# Проверяем, что длина разумная
|
|
if msg_length > (len(all_bits) - 32) // 8:
|
|
return ""
|
|
|
|
# Читаем сообщение
|
|
message_bits = all_bits[32:32 + msg_length * 8]
|
|
|
|
# Дополняем до кратности 8 (на всякий случай)
|
|
remainder = len(message_bits) % 8
|
|
if remainder != 0:
|
|
message_bits.extend([0] * (8 - remainder))
|
|
|
|
if len(message_bits) == 0:
|
|
return ""
|
|
|
|
bits_string = ''.join(str(b) for b in message_bits)
|
|
try:
|
|
return bits_to_text(bits_string)
|
|
except Exception as e:
|
|
print(f"Ошибка при декодировании: {e}")
|
|
return "" |