Визуальная криптография для цветных изображений

Недавно наткнулся на хабре на пару постов о сокрытии данных в BMP-файлах (один и два) и решил поделиться своим опытом в этой области. В этот раз будем прятать цветную картинку, а в качестве контейнеров будем использовать еще три изображения.


Пару слов о BMP


BMP-файл состоит из заголовка — структуры размером в несколько десятков байт, содержащей основные параметры изображения (размеры, глубину цвета и т.д.), палитры — массива, описывающего цвета, используемые в изображении, и области данных, содержащей последовательность кодов пикселов изображения. Палитра не является обязательной — в большинстве BMP-файлах палитра отсутствует, а область данных содержит описание всех цветовых компонент каждого пикселя изображения в формате RGB (то есть количественные значения красного, зеленого и синего цветов).

Описание метода


В качестве входных изображений используются 24-разрядные bitmap-рисунки, в которых на каждый цвет приходится по 8 бит информации. Прятать данные будем по методу LSB.
Суть алгоритма заключается в том, что секретное изображение разбивается на три цветовых примитива (то есть на оттенки красного, зеленого и синего), а затем каждый примитив записывается в младшие биты одного из изображений-контейнеров. Таким образом после зашифровки каждый контейнер будет содержать в себе одну цветовую составляющую секретного изображения.

Зашифровка изображения


Чтобы определить, в какое из изображений прятать R-составляющую, в какое — G, а в какое — B, перед запуском алгоритма определяется количественное значение каждого из трех оттенков в каждом изображении-контейнере. Далее контейнеры выбираются таким образом, чтобы разница в цветах контейнера и цветового примитива секретного изображения была минимальна.
Далее из каждого цветового примитива берется два старших бита и записывается в младшие биты соответствующего цвета у соответствующего контейнера. Два младших бита в двух оставшихся цветах обнуляются. Операция повторяется для каждого пикселя.

Код функции, записывающей цветовой примитив в контейнер
private Color CombineColorsRGB(Color secretColor, Color containerColor, HidingColor hidingColor)
{
	byte r1 = secretColor.R;
	byte g1 = secretColor.G;
	byte b1 = secretColor.B;
	byte r2 = containerColor.R;
	byte g2 = containerColor.G;
	byte b2 = containerColor.B;

	int secretBits = m_secretBits;
	int originalBits = 8 - m_secretBits;
	
	switch (hidingColor)
	{
		case HidingColor.R:
			r1 = (byte)(r1 >> originalBits);
			r2 = (byte)((r2 >> secretBits) << secretBits);
			r2 = (byte)(r1 | r2);
			g2 = (byte)((g2 >> secretBits) << secretBits);
			b2 = (byte)((b2 >> secretBits) << secretBits);
			break;
		case HidingColor.G:
			g1 = (byte)(g1 >> originalBits);
			g2 = (byte)((g2 >> secretBits) << secretBits);
			g2 = (byte)(g1 | g2);
			r2 = (byte)((r2 >> secretBits) << secretBits);
			b2 = (byte)((b2 >> secretBits) << secretBits);
			break;
		case HidingColor.B:
			b1 = (byte)(b1 >> originalBits);
			b2 = (byte)((b2 >> secretBits) << secretBits);
			b2 = (byte)(b1 | b2);
			r2 = (byte)((r2 >> secretBits) << secretBits);
			g2 = (byte)((g2 >> secretBits) << secretBits);
			break;
	}
	
	Color col = Color.FromArgb(r2, g2, b2);

	return col;
}

Эта функция выполняется для каждого цвета каждого пикселя секретного изображения.
В нашем случае m_secretBits = 2.

Секретное изображение:


Набор исходных изображений для зашифровки:






Те же изображения, но уже с зашифрованным в них секретным изображением:






Как видно на изображениях, визуально искажения не заметны. Прошу прощения, что изображения маленькие — пожалел трафик. На больших изображениях искажения также не заметны — уж поверьте.

Восстановление изображения


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

Код функции, восстанавливающей секретное изображение из контейнеров
private Color GetSecretColorRGB(Color color1, Color color2, Color color3)
{
	byte r1 = color1.R;
	byte g1 = color1.G;
	byte b1 = color1.B;
	byte r2 = color2.R;
	byte g2 = color2.G;
	byte b2 = color2.B;
	byte r3 = color3.R;
	byte g3 = color3.G;
	byte b3 = color3.B;

	int secretBitsDigit = (int)(Math.Pow(2.0, (double)m_secretBits)) - 1;
	int originalBits = 8 - m_secretBits;

	byte r = (byte)(((r1 & secretBitsDigit) << originalBits) + ((r2 & secretBitsDigit) << originalBits) + ((r3 & secretBitsDigit) << originalBits));
	byte g = (byte)(((g1 & secretBitsDigit) << originalBits) + ((g2 & secretBitsDigit) << originalBits) + ((g3 & secretBitsDigit) << originalBits));
	byte b = (byte)(((b1 & secretBitsDigit) << originalBits) + ((b2 & secretBitsDigit) << originalBits) + ((b3 & secretBitsDigit) << originalBits));

	Color col = Color.FromArgb(r, g, b);

	return col;
}

Эта функция выполняется для каждой тройки соответствующих пикселей изображений-контейнеров.

Секретное изображение после восстановления:


А вот что получится, если хотя бы одно из изображений не будет содержать нужных зашифрованных данных:

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

А вот если пересохранить изображения в JPEG со средним качеством, а потом снова в BMP, то дополнительных искажений это практически не вызовет:


Итоги


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

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 10

    0
    Как то очень заметно ухудшилось качество изображения после восстановления. И оно ещё полегчало более чем на 50Кб.
      0
      Оно и понятно — исходное изображение было 24-битным, а после восстановления стало 6-битным.
      0
      Попробуйте сделать сохранение информации в нескольких изображениях, так можно повысить ёмкость, а также, наверное «криптостойкость».
        +1
        Согласен. В принципе, еще можно брать контейнеры с шириной и высотой в 2 раза большими, чем у секретного изображения. Тогда искажений после восстановления совсем не будет.
          +1
          Ещё можно сделать типа изображение ключ, в котором хранится некоторая маска, которая по определённому алгоритму расшифровывает закриптованное изображение.
        +3
        это называется «стеганография». не путайте термины
          +3
          ошибся, да, не обратил внимание на то, что используется разделение секрета
          0
          Для работы с JPEG стоит использовать помехоустойчивое кодирование, например Reed-solomon.
          При его использовании скорее всего можно будет жать зашифрованное изображение в JPEG с еще большей компрессией.
          Да, конечно это уменьшит размер полезных зашифрованных данных, но повысит вероятность безошибочного восстановления зашифрованного сообщения. также стоит произвести операцию interleve'нга, «размазав» весь кодовый блок по всему изображению, тем самым это должно дать лучшую помехоустойчивую защиту от всяческого прореживания. (выигрыш (Code Gain) от такой схемы кодирования может быть до 6 децибелл )
            +1
            Спасибо за советы — принял к сведению.
              0
              спасибо Вам за статью. рад буду прочитать продолжение!

          Only users with full accounts can post comments. Log in, please.