[Перевод] Как спрятать фото в другом фото

vbzst9kbyntpdzvf31taxduv0tg.png


Сокрытие сообщения в другом сообщении относится к области стеганографии. В этой статье мы будем прятать одну фотографию внутри другой. В результате при передаче такое фото будет выглядеть как обычный снимок, но по факту содержать два. Второй при этом можно будет извлечь при помощи внешнего инструмента.

Используя этот метод, можно прятать в своей художественной работе подпись или сохранять несколько снимков как один в целях экономии памяти на миниатюрных устройствах. Есть ещё один аналогичный подход, с помощью которого игровые ресурсы картриджей Pico-8 включаются в небольшой PNG самой игры*.

*Подробнее в статье «Steganography: decoding Pico-8 cartridges».


▍ Фотографии как матрицы


Чтобы иметь возможность управлять фотографиями и встраивать в них что-либо, сначала нужно разобраться с их возможным представлением и сохранением. Один из вариантов — это представить снимки в виде математических матриц, в которых каждая ячейка выражает пиксель, а её значение — цвет этого пикселя/ячейки.

bxtyr3d7iauygdjyp80vryoowak.png


Например, при такой матрице:

ut-fbfb5uv3qluhd7q5yh0p9mgk.png


Мы получим такое фото:

twrk8vy9ofbondtp96n30oqpmlq.png


Если фотографию и матрицу сопоставить, то можно заметить, чем ближе значение к 255, тем белее пиксель. При этом 0 означает чёрный, а всё между 0 и 255 оттенки серого.

▍ Фотографии как битовые матрицы


Итак, снимки в виде матриц мы представили. Теперь давайте немного поботаним и выразим значение каждой ячейки не в десятичном, а в двоичном виде (обещаю, дальше это пригодится).

Та же фотография с помощью этой матрицы теперь будет выражаться так:

w0pehx1trzmeanxmdonkh-yikaq.png


Далее нам нужно выяснить, что произойдёт с фотографией, если изменить младший бит. Для этого я их всё реверсирую и посмотрю, как в итоге будут выглядеть матрица и новое фото:

wrn-rpp2ci_bd_h-b0k4gzjz8mc.png


fye0c4spne0118rtaefdtl0b58o.png


Можете разглядеть какие-либо изменения? Предполагаю, что нет. Полученный вариант определённо выглядит так же, как оригинал (если только вы не обладатель сверхзрения), поскольку мы изменили лишь младшие биты, имеющие наименьшую значимость. С помощью этого элементарного приёма можно встраивать фотографии одну в другую, не вызывая подозрений в отношении оригинального снимка.

▍ Встраивание фото


Для встраивания фотографии нужно заменить каждый младший бит основного снимка на каждый бит встраиваемого, то есть размер фото, которое мы прячем, должен быть меньше основного.

isoju3typi07ivm5ncilybsthss.png


Но насколько меньше? В видимой фотографии размером n * m есть nm/8 младших бит, то есть внутри неё можно разместить фото размером √nm/8 * √nm/8. Например, при размере видимого снимка 1000×1000 px мы получаем:

i9ippqwdy7snidm4dwdukbxpqdk.png


Это означает, что можно скрыть в нём фотографию размером 353×353 px.

▍ Цветные фото


В цветных снимках каждый пиксель представлен тремя матрицами: красной, зелёной и синей. Вместо этого можно рассматривать такие снимки как одну матрицу с кортежами из трёх значений в диапазоне от 0 до 255.

qp53cqon4vspqz75ifijvom7cru.png


В этом случае наш подход по встраиванию фотографии также сработает — нужно лишь заменить все младшие биты в каждой матрице каждой ячейки.

▍ PNG, JPEG и сжатие


Вы наверняка знаете, что некоторые форматы фото (например, JPEG) сильно сжимают исходное изображение, используя различные математические техники (например, вейвлет Хаара). Что же происходит со встраиваемым фото в таком случае? Оно с наибольшей вероятностью уничтожается, и при попытке извлечения мы увидим лишь шум. Но есть и другие форматы, например, PNG, которые сохраняют фотографию. Так что можно использовать рассмотренный трюк с ними.

▍ Реализация


Я реализовал этот метод давно, когда ещё учился на бакалавра, и соответствующий код лежит здесь. Это десктопное приложение, в котором для работы с битами фотографий используется Emgu CV.

oug5kh6sjydt9llengsiebnp40w.png

© Habrahabr.ru