сделал модуль LSB
добавил уточнения в gitignore
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -17,3 +17,9 @@ output.txt
|
|||||||
# OS
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
# Тестовые файлы
|
||||||
|
test_image.png
|
||||||
|
encoded_lsb.png
|
||||||
|
test_*.png
|
||||||
|
*_lsb.png
|
||||||
134
core/lsb.py
134
core/lsb.py
@@ -0,0 +1,134 @@
|
|||||||
|
"""
|
||||||
|
Модуль LSB (Least Significant Bit) стеганографии.
|
||||||
|
|
||||||
|
Скрывает текст в изображении путем замены последних битов пикселей.
|
||||||
|
Каждый пиксель RGB хранит 3 бита информации (по одному в каждом канале).
|
||||||
|
"""
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Алгоритм:
|
||||||
|
1. Преобразует текст в битовую строку
|
||||||
|
2. Добавляет стоп-маркер (8 нулевых битов) для обозначения конца
|
||||||
|
3. Проходит по всем пикселям изображения
|
||||||
|
4. В каждом канале (R,G,B) заменяет последний бит на бит сообщения
|
||||||
|
|
||||||
|
Аргументы:
|
||||||
|
image_path (str): Путь к исходному изображению
|
||||||
|
message (str): Текст для сокрытия
|
||||||
|
output_path (str): Путь для сохранения результата
|
||||||
|
|
||||||
|
Возвращает:
|
||||||
|
bool: True если успешно, False если сообщение слишком длинное
|
||||||
|
"""
|
||||||
|
# Загружаем изображение
|
||||||
|
img = Image.open(image_path).convert('RGB')
|
||||||
|
pixels = np.array(img, dtype=np.uint8)
|
||||||
|
height, width, channels = pixels.shape
|
||||||
|
|
||||||
|
# Преобразуем сообщение в биты и добавляем стоп-маркер
|
||||||
|
bits = text_to_bits(message) + '00000000'
|
||||||
|
|
||||||
|
# Проверяем, поместится ли сообщение
|
||||||
|
max_bits = height * width * channels
|
||||||
|
if len(bits) > max_bits:
|
||||||
|
print(f"Ошибка: сообщение слишком большое.")
|
||||||
|
print(f"Доступно битов: {max_bits}, требуется: {len(bits)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Прячем биты в изображение
|
||||||
|
bit_index = 0
|
||||||
|
total_bits = len(bits)
|
||||||
|
|
||||||
|
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 = int(bits[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.
|
||||||
|
|
||||||
|
Алгоритм:
|
||||||
|
1. Проходит по всем пикселям изображения
|
||||||
|
2. Из каждого канала извлекает последний бит
|
||||||
|
3. Собирает биты в байты
|
||||||
|
4. Останавливается при обнаружении стоп-маркера (8 нулевых битов)
|
||||||
|
|
||||||
|
Аргументы:
|
||||||
|
image_path (str): Путь к изображению со скрытым сообщением
|
||||||
|
|
||||||
|
Возвращает:
|
||||||
|
str: Извлеченное сообщение
|
||||||
|
"""
|
||||||
|
# Загружаем изображение
|
||||||
|
img = Image.open(image_path).convert('RGB')
|
||||||
|
pixels = np.array(img, dtype=np.uint8)
|
||||||
|
height, width, channels = pixels.shape
|
||||||
|
|
||||||
|
# Извлекаем биты
|
||||||
|
extracted_bits = []
|
||||||
|
stop_counter = 0
|
||||||
|
stop_marker_length = 8
|
||||||
|
|
||||||
|
for i in range(height):
|
||||||
|
for j in range(width):
|
||||||
|
for c in range(channels):
|
||||||
|
# Извлекаем последний бит
|
||||||
|
bit = pixels[i, j, c] & 1
|
||||||
|
extracted_bits.append(bit)
|
||||||
|
|
||||||
|
# Проверяем стоп-маркер
|
||||||
|
if bit == 0:
|
||||||
|
stop_counter += 1
|
||||||
|
if stop_counter == stop_marker_length:
|
||||||
|
# Убираем стоп-маркер из результата
|
||||||
|
extracted_bits = extracted_bits[:-stop_marker_length]
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
stop_counter = 0
|
||||||
|
|
||||||
|
if stop_counter == stop_marker_length:
|
||||||
|
break
|
||||||
|
if stop_counter == stop_marker_length:
|
||||||
|
break
|
||||||
|
|
||||||
|
if len(extracted_bits) == 0:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# Преобразуем биты в строку и декодируем
|
||||||
|
bits_string = ''.join(str(bit) for bit in extracted_bits)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return bits_to_text(bits_string)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Ошибка при декодировании: {e}")
|
||||||
|
return ""
|
||||||
Reference in New Issue
Block a user