Сделай сам: цифровые водяные знаки

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

Рассмотрим ЦВЗ в изображениях. Будем использовать систему встраивания информации (СВИ) в наименее значимые биты изображения (НБЗ).

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

Напишем мини-приложение НБЗ-встраивания черно-белого водяного знака в цветное изображение в среде C++ Builder с использованием языка C++. Оба изображения для простоты берём одинакового размера. Чтобы мы могли увидеть ЦВЗ, встроим его в первые два бита. Чтобы ЦВЗ сделать более невидимой, нужно производить встраивание в 0 бит.

Так как глаз человека наиболее чувствителен к яркости изображения и несколько менее к цветности, для наглядности будем выполнять встраивание в компоненту яркости. Для этого нам необходимо перейти от цветового пространства RGB (Red, Green, Blue) к YUV(Y — яркость, U и V — цветоразностные компоненты). Переход будем выполнять по общеизвестным формулам (смотри в коде под комментарием «преобразование RGB в YUV»). Чтобы ЦВЗ была более незаметна человеку, нужно производить встраивание в одну из цветоразностных компонент.

На главной форме создаём 7 объектов: Image1 — исходное изображение, Image2 — черно-белое изображение водяного знака, Button1 – «Открыть 1», кнопка открытия исходного изображения из файла, Button2 — «Открыть 2», кнопка открытия изображения водяного знака, OpenPictureDialog1 — отвечает за открытие диалогового окна при нажатии на Button1, OpenPictureDialog2 — то же самое, что и OpenPictureDialog1, только для Button2, Button3 — «Встроить», кнопка осуществления встраивания.

Код, который выполнится при нажатии на Button1. Для Button2 меняются индексы у OpenPictureDialog и Image.

int W, H; OpenPictureDialog1->DefaultExt = "BMP"; OpenPictureDialog1->FileName = "*.bmp"; OpenPictureDialog1->InitialDir = ""; if (OpenPictureDialog1->Execute()) Image1->Picture->LoadFromFile(OpenPictureDialog1->FileName); Image1->Stretch=true; Bitmap->Assign(Image1->Picture); //ширина изображения W=Image1->Width; //высота изображения H=Image1->Height;

Код, который выполняется при нажатии на кнопку Button3.

//объявление массивов int R1,G1,B1,R2,G2,B2,R3,G3,B3; int **U1; U1=new int*[H]; for(int j=0; j<H; j++) U1[j]=new int[W]; int **V1; V1=new int*[H]; for(int j=0; j<H; j++) V1[j]=new int[W]; int **Y1; Y1=new int*[H]; for(int j=0; j<H; j++) Y1[j]=new int[W]; int **U2; U2=new int*[H]; for(int j=0; j<H; j++) U2[j]=new int[W]; int **V2; V2=new int*[H]; for(int j=0; j<H; j++) V2[j]=new int[W]; int **Y2; Y2=new int*[H]; for(int j=0; j<H; j++) Y2[j]=new int[W]; int **U3; U3=new int*[H]; for(int j=0; j<H; j++) U3[j]=new int[W]; int **V3; V3=new int*[H]; for(int j=0; j<H; j++) V3[j]=new int[W]; int **Y3; Y3=new int*[H]; for(int j=0; j<H; j++) Y3[j]=new int[W]; BYTE* biImage1; BYTE* biImage2; // сканирование первых пиксельных строк for(int i=0; i<H; i++) { biImage1=(BYTE*)(Form1->Image1)->Picture->Bitmap->ScanLine[i]; biImage2=(BYTE*)(Form1->Image2)->Picture->Bitmap->ScanLine[i]; // сканирование каждого пикселя в строке for(int j=0; j<W; j++) { R1=biImage1[j*4+2]; G1=biImage1[j*4+1]; B1=biImage1[j*4]; R2=biImage2[j*4+2]; G2=biImage2[j*4+1]; B2=biImage2[j*4]; // преобразование RGB в YUV Y1[i][j] = 0.299 * R1 + 0.587 * G1 + 0.114 * B1; Y2[i][j] = 0.299 * R2 + 0.587 * G2 + 0.114 * B2; U1[i][j] = -0.14713 * R1 - 0.28886 * G1 + 0.436 * B1 + 128; V1[i][j] = 0.615 * R1 - 0.51499 * G1 - 0.10001 * B1 + 128; //встраивание ЦВЗ if ((R2==0)and(G2==0)and(B2==0)) { int b1=(Y1[i][j]%4-Y1[i][j]%2)%2; int b2=(Y1[i][j]%8-Y1[i][j]%4)%2; if (b1==1){Y1[i][j]=Y1[i][j]-2;}; if (b2==1){Y1[i][j]=Y1[i][j]-4;} } else { int b1=(Y1[i][j]%4-Y1[i][j]%2)%2; int b2=(Y1[i][j]%8-Y1[i][j]%4)%2; if (b1==0){Y1[i][j]=Y1[i][j]+2;}; if (b2==0){Y1[i][j]=Y1[i][j]+4;} } Y3[i][j]=Y1[i][j]; U3[i][j]=U1[i][j]; V3[i][j]=V1[i][j]; } } //преобразование YUV в RGB for(int i=0; i<H; i++) { biImage1=(BYTE*)(Form1->Image1)->Picture->Bitmap->ScanLine[i]; for(int j=0; j<W; j++) { //красный R R3 = Y3[i][j] + 1.13983 * (V3[i][j] - 128); if(R3>255) R3=255; if(R3<0) R3=0; //зеленый G G3 = Y3[i][j] - 0.39465 * (U3[i][j] - 128) - 0.58060 * (V3[i][j] - 128); if(G3>255) G3=255; if(G3<0) G3=0; //синий B B3 = Y3[i][j] + 2.03211 * (U3[i][j] - 128); if(B3>255) B3=255; if(B3<0) B3=0; //восстановление массива изображения biImage1[j*4+2]= R3; biImage1[j*4+1]= G3; biImage1[j*4] = B3; } } //обновление изображения на экране Form1->Image1->Refresh();

Результат встраивания ЦВЗ представлен на рисунке ниже.

Сделай сам: цифровые водяные знаки
22
11 комментариев

цифрой и не пахнет, имхо, классические водяные знаки которые можно поставить в графическом редакторе - программирование сюда притянуто постольку-поскольку.. хотите автоматизации - можно прикрутить imagemagick ;)

1
Автор

Материал подготовлен в ознакомительных целях. Нацелен на пользователей, которые мало знакомы или вообще не знакомы с понятием "стеганография"

Ну как автоматизировали, так и автоматизируют снятие таких водяных знаков.

Автор

Метод наименее значимого бита не самый стойкий к атакам, но достаточно популярный и один из самых простых.

Комментарий недоступен

Автор

Именно поэтому метод наименее значимого бита используют только в форматах поддерживающих алгоритмов компрессии (BMP, PNG)