Сокрытие сообщения в другом сообщении относится к области стеганографии. В этой статье мы будем прятать одну фотографию внутри другой. В результате при передаче такое фото будет выглядеть как обычный снимок, но по факту содержать два. Второй при этом можно будет извлечь при помощи внешнего инструмента.
Используя этот метод, можно прятать в своей художественной работе подпись или сохранять несколько снимков как один в целях экономии памяти на миниатюрных устройствах. Есть ещё один аналогичный подход, с помощью которого игровые ресурсы картриджей Pico-8 включаются в небольшой PNG самой игры*.
*Подробнее в статье «Steganography: decoding Pico-8 cartridges».
▍ Фотографии как матрицы
Чтобы иметь возможность управлять фотографиями и встраивать в них что-либо, сначала нужно разобраться с их возможным представлением и сохранением. Один из вариантов – это представить снимки в виде математических матриц, в которых каждая ячейка выражает пиксель, а её значение – цвет этого пикселя/ячейки.
Например, при такой матрице:
Мы получим такое фото:
Если фотографию и матрицу сопоставить, то можно заметить, чем ближе значение к
255
, тем белее пиксель. При этом 0
означает чёрный, а всё между 0
и 255
оттенки серого.▍ Фотографии как битовые матрицы
Итак, снимки в виде матриц мы представили. Теперь давайте немного поботаним и выразим значение каждой ячейки не в десятичном, а в двоичном виде (обещаю, дальше это пригодится).
Та же фотография с помощью этой матрицы теперь будет выражаться так:
Далее нам нужно выяснить, что произойдёт с фотографией, если изменить младший бит. Для этого я их всё реверсирую и посмотрю, как в итоге будут выглядеть матрица и новое фото:
Можете разглядеть какие-либо изменения? Предполагаю, что нет. Полученный вариант определённо выглядит так же, как оригинал (если только вы не обладатель сверхзрения), поскольку мы изменили лишь младшие биты, имеющие наименьшую значимость. С помощью этого элементарного приёма можно встраивать фотографии одну в другую, не вызывая подозрений в отношении оригинального снимка.
▍ Встраивание фото
Для встраивания фотографии нужно заменить каждый младший бит основного снимка на каждый бит встраиваемого, то есть размер фото, которое мы прячем, должен быть меньше основного.
Но насколько меньше? В видимой фотографии размером n * m есть nm/8 младших бит, то есть внутри неё можно разместить фото размером √nm/8 * √nm/8. Например, при размере видимого снимка 1000 * 1000px мы получаем:
Это означает, что можно скрыть в нём фотографию размером 353 * 353px.
▍ Цветные фото
В цветных снимках каждый пиксель представлен тремя матрицами: красной, зелёной и синей. Вместо этого можно рассматривать такие снимки как одну матрицу с кортежами из трёх значений в диапазоне от
0
до 255
.В этом случае наш подход по встраиванию фотографии также сработает – нужно лишь заменить все младшие биты в каждой матрице каждой ячейки.
▍ PNG, JPEG и сжатие
Вы наверняка знаете, что некоторые форматы фото (например, JPEG) сильно сжимают исходное изображение, используя различные математические техники (например, вейвлет Хаара). Что же происходит со встраиваемым фото в таком случае? Оно с наибольшей вероятностью уничтожается, и при попытке извлечения мы увидим лишь шум. Но есть и другие форматы, например, PNG, которые сохраняют фотографию. Так что можно использовать рассмотренный трюк с ними.
▍ Реализация
Я реализовал этот метод давно, когда ещё учился на бакалавра, и соответствующий код лежит здесь. Это десктопное приложение, в котором для работы с битами фотографий используется Emgu CV.