Pull to refresh

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

Reading time 4 min
Views 13K
Недавно наткнулся на хабре на пару постов о сокрытии данных в 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, то дополнительных искажений это практически не вызовет:


Итоги


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

Articles