py steganography

Школьный проект.

Код из файлика для кодировки:

from PIL import Image

def encode_image(image_path, message, output_path):
    img = Image.open(image_path)
    img = img.convert('RGB')
    binary_message = ''.join(format(ord(i), '08b') for i in message)
    binary_message += '1111111111111110'
    message_index = 0
    pixels = img.load()

    for i in range(img.width):
        for j in range(img.height):
            pixel = list(pixels[i, j])
            for n in range(3):
                if message_index < len(binary_message):
                    pixel[n] = pixel[n] & 0xFE | int(binary_message[message_index])
                    message_index += 1
            pixels[i, j] = tuple(pixel)

            if message_index >= len(binary_message):
                break
        else:
            continue
        break

    img.save(output_path)

s = input('Введите сообщение, которое хотите скрыть в иозбражении (используйте латиницу или цифры) \n')
encode_image('input_image.png', s, 'encoded_image.png')

код файлик из для получения смски из изображения:

from PIL import Image

def decode_image(image_path):
    img = Image.open(image_path)
    img = img.convert('RGB')
    pixels = img.load()

    binary_message = ''

    for i in range(img.width):
        for j in range(img.height):
            pixel = pixels[i, j]
            for n in range(3):
                binary_message += str(pixel[n] & 1)
            if binary_message[-16:] == '1111111111111110':
                break
        else:
            continue
        break

    message = ''
    for i in range(0, len(binary_message) - 16, 8):
        byte = binary_message[i:i + 8]
        message += chr(int(byte, 2))

    return message

hidden_message = decode_image('encoded_image.png')
print("Извлеченное сообщение:", hidden_message)

по итогу извлеченное сообщение я получаю, но с огромным количеством рандомных символов

(9ü ?UUoV¥R©[jz¤¿©¸ÜI$­©%U Î?9½’8íŽÇlq$ìq8’N ð?à ?ü~ ÿ и тп) .

вероятно, дело либо в кодировках, либо с фоткой че-т происходит. может вы чего подскажите?


Ответы (2 шт):

Автор решения: CrazyElf

Современные кодировки русского языка 2-байтовые. Одного байта вам не хватит чтобы закодировать код русского символа, получающийся через ord(i). Проверим это утверждение:

x = format(ord('а'), '08b')
print(x, len(x))
# 10000110000 11

Получилось 11 битов, а не 8, на которые вы рассчитывали при обратной расшифровке. Поэтому всё и "поехало" у вас.

Чиним это так. Переделываем однобайтовое кодирование в двухбайтовое. И стоп-слово тоже увеличиваем с 2-х байт, до 4-х.

#    binary_message = ''.join(format(ord(i), '08b') for i in message)
#    binary_message += '1111111111111110'
    binary_message = ''.join(format(ord(i), '016b') for i in message)
    binary_message += '11111111111111111111111111111110'
...
#            if binary_message[-16:] == '1111111111111110':
            if binary_message[-32:] == '11111111111111111111111111111110':
...
#    for i in range(0, len(binary_message) - 16, 8):
#        byte = binary_message[i:i + 8]
    for i in range(0, len(binary_message) - 32, 16):
        byte = binary_message[i:i + 16]

После этих изменений вроде бы нормально работает и с русскими буквами.

→ Ссылка
Автор решения: eri

я бы не использовал ord

import bitarray

binary_message = bitarray.bitarray()
binary_message.frombytes(message.encode('utf-8'))
binary_message += '1111111111111110'

обратно

binary_message.tobytes()[:-2].decode('utf-8')
→ Ссылка