Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения
KANSoftWare

Вшивание информации в растровые рисунки

Delphi , Синтаксис , Шифрование

Вшивание информации в растровые рисунки

Оформил: DeeCo

Автор: Юрий Писарев

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

Так выглядит программа в "рабочем" состоянии. А текст я взял из какого-то файла, валявшегося у меня под рукой.

Как работать с программой

Для того, чтобы создать "слепок" существующего рисунка, нужно:
  • Загрузить текстовый файл (секция text, кнопка Open)
  • Загрузить понравившиеся рисунки (секция Picture, кнопка Open, можно загружать несколько раз, загруженные ранее картинки не пропадут)
  • Выделить нужный рисунок в компоненте TGraphGrid
  • При желании, отмасштабировать его (кнопка Resize, масштабирует рисунок в соответствии с его пропорциями)
  • Нажать на кнопку Reflect. Через несколько секунд (это если картинка большая, а так - через несколько миллисекунд) в компоненте TGraphGrid появится сгенерированное изображение, созданное на основе выбраной картинки и текстового файла. Новая картинка будет иметь имя "Data".
  • Сохранить при необходимости нужный файл изображения, выбрав его в компоненте TDrawGrid и нажав на кнопку Save.
Для того, чтобы получить текст из двух одинаковых (на первый взгляд) картинок, нужно:
  • Загрузить картинку, на основе которой был сделан "слепок"
  • Нажать на кнопку Assembly. При этом появится диалоговое окно загрузки изображения и на этот раз Вам надо будет выбрать файл "слепка", т.е. измененную картинку.
  • Далее, через некоторое время (в зависимости от количества данных, содержащихся в картинке и размера самой картинки) в компоненте TRichEdit появится этот самый закодированный текст.

Некоторые важные моменты

Выше было сказано, что при больших размерах картинки и при небольшем объеме текстового файла отличить исходную и "слепленную" картинку практически невозможно. Это правильно, но только отчасти. Если взглянуть на изображения, в котором "зашит" большой текстовый файл, то сразу же в глаза бросаются чужеродные пиксели, распределенные по всему изображению (кстати, коэффициент разброса можно менять) а особенно хорошо эти пиксели видны на рисунке, с однородным фоном. Сравните следующие два рисунка:

Исходный рисунок  Рисунок, содержащий информацию

На правом рисунке отчетливо виден шум. Этого отчасти можно было бы избежать, используя неоднороные рисунки с резкими переходами цвета, а также рисунки большего формата. Или можно написать такой хитрый алгоритм кодирования, что второе изображение будет невозможно отличить от первого. В примере вместо увеличения размера рисунка я просто уменьшил количество информации:

Исходный рисунок Рисунок, содержащий информацию

Как работает программа

Программа использует компонент Graph, о котором я рассказывал в прошлой статье.

Примечание:
Материал Компоненты для работы с графикой раздела Сокровищница

Сейчас я несколько улучшил этот компонент. Плюс, в нем появилось две новых функции:
function ReflectData(BaseBitmap, DataBitmap: TBitmap; 
    List: TStrings; 
    Reflection: TReflectionProc; 
    ScatterType: TScatterType; 
    Factor: Integer = 1): TReflectionResult; virtual; 

function AssemblyData(BaseBitmap, DataBitmap: TBitmap; 
    Assembling: TAssemblingProc): Boolean; virtual; 

Первая функция создает изображение, содержащее информацию; вторая - получает информацию из двух изображений, как я рассказывал выше. Параметры функции ReflectData:
  • BaseBitmap - Базовое изображение, на его основе создается изображение, содержащее информацию
  • DataBitmap - Изображение, содержащее информацию
  • Reflection - Процедура, которая изменяет соответствующий байт исходного изображения и формирует DataBitmap Эта процедура отвечает за сохранение информации во второй картинке. Но о ней - чуть позже.
  • ScatterType - Тип распределения информации Может принимать два значения: stGiven и stEvenly По умолчанию в программе установлено значение stEvenly. Это означает, что вся информация будет равномерно распределена по всей картинке. Если так, то задавать значение следующего параметра Factor не нужно. Если ScatterType установить в stGiven, то распределение информации зависит от коэффициента распределения.
  • Factor - Коэффициент распределения. Если параметр ScatterType равен stGiven, то фактически Factor означает через сколько байт то начала картинки (не совсем от начала - первые восемь байт идут на длину записываемого текста и на этот коэффициент) будет вписан следующий код символа текста. В программе этот коэффициент задается в компоненте TEdit.
Теперь несколько слов о используемой функции Reflection. Эту функцию я вынес в библиотеку. Эта библиотека является специфическим ключем, паролем для соединения двух картинок. В нее могут быть заложены самые разные методы шифрования, в данный же момент использзуется самый примитивный: при шифровании код очередного символа просто прибавляется к значению из картинки BaseBitmap, при расшифровке - наооборот. Эта процедура имеет тип:
TReflectionProc = procedure(var Value: Byte; Text: string; Index: Integer); 
Параметры функции Reflection:
  • Value - Величина из BaseBitmap. Именно это значение шифруется.
  • Text - Текст в виде единой строки.
  • Index - Текущий символ в тексте, который должен быть зашифрован. В эой программе код этого символа складывается с параметром Value при зашифровке.
В процедуре AsseblyData происходит обратный процесс, выделение данных на основе двух изображений. Параметры функции AsseblyData:
  • BaseBitmap - Базовое изображение
  • DataBitmap - Изображение, содержащее информацию
  • Assembling - Процедура, формирующая текстовые данные
Процедура Assembling имеет следующий тип:
TAssemblingProc = procedure(BaseValue, DataValue: Byte); 
Ее параметры:
  • BaseValue - Текущая величина из BaseBitmap
  • DataValue - Текущая величина из DestBitmap
В этой программе каждое значение символа высчитывается путем вычитания DataValue из BaseValue.

Заключение

На последок хочу дать один совет. Не стоит сохранять полученное изображение в -jpg файле :) Хотя я один раз попробовал, текст оказался сильно поврежден, хотя такие слова как Microsoft все-таки сохранились... в этом что-то есть... :)

И еще одно, практический, такой пример:

Исходный рисунок Рисунок, содержащий информацию

Попробуйте получить информацию из этих рисунков. Должна получиться статья, (в формате HTML) которую Вы сейчас читаете.

Примечание:
В тексте статьи использовано конвертированное изображение, если Вы хотите получить реальный рисунок в формате BMP, содержащий информацию для вышеописанного конкретного примера, необходимо скачать Image7.zip (162 K)
Скачать :

Статья Вшивание информации в растровые рисунки раздела Синтаксис Шифрование может быть полезна для разработчиков на delphi & pascal.


Комментарии и вопросы


:: 2009-02-02 15:21:07 :: re:Вшивание информации в растровые рисунки

пользователь: Nikolay.

Мне понравилась статейка


:: 2010-11-28 23:55:16 :: re:Вшивание информации в растровые рисунки

пользователь: Владислав.

Здравствуйте!

Похоже, у меня есть некоторые замечания по поводу Вашей статьи "Вшивание информации в растровые рисунки". Они касаются искажения изображений - появления разного рода пикселей других цветов. Просто Вы кое-что не учитываете при кодировании. Если все сделать верно, таких пикселей не будет вообще.

С уважением, Владислав.


:: 2010-11-29 20:56:09 :: re:Вшивание информации в растровые рисунки

пользователь: kan.

Владислав, если Вы оставите пример кода, это будет замечательно.


:: 2010-12-02 09:59:38 :: re:Вшивание информации в растровые рисунки

пользователь: Владислав.

Kan, код выложу сегодня ближе к 21-00 по Москве, пока что я на работе...


:: 2010-12-02 20:53:45 :: Исходный код (текст в изображение)

пользователь: Владислав.

Логика очень проста: сначала нужно подготовить изображение таким образом, чтобы в нем не осталось ни одного оттенка, равного 255 - заменяем его на 254 (это никак не скажется на изображении) с тем расчетом, что при добавлении к нему двоичного кода помещаемого текста он не сбросится в единицу (если к 255 прибавим 1).
После этого первые три байта изображения резервируются для хранения информации о длине помещаемого в изображение текста. Далее - добавляем двоичный код текста к оттенкам точек изображения.
Ниже представлен исходный код всего проекта. Необходимые краткие пояснения также даны. Форма проекта содержит компоненты: Memo1 - для ввода текста, который необходимо поместить в изображение; кнопка SpeedButton1 - для помещения текста в изображение; SpeedButton2 - для извлечения текста из изображения.
Для восстановления текста сравниваются два рисунка по оттенкам, и получаемый таким образом двоичный код преобразуется в текст. Также легко кодировать таким образом с ключом, который, например, будет хранить информацию о порядке расположения бит текста в изображении (на основе приведенного здесь кода это не составит никакого труда). Вот и все.
Кстати, можно также реализовать случай, когда для шифрования и дешифрования используется одно и то же изображение, без необходимости сравнения его с исходным (исходное после шифрования лучше вообще сразу же удалить).

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Buttons, StdCtrls, ExtCtrls, ComCtrls;

type
TForm1 = class(TForm)
OpenDialog1: TOpenDialog;
Memo1: TMemo;
SpeedButton1: TSpeedButton;
SpeedButton2: TSpeedButton;
procedure SpeedButton1Click(Sender: TObject);
procedure SpeedButton2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
Stop_Process: Boolean;

implementation

{Преобразуем текст в бинарный код}
function TextToBinary(S: AnsiString):AnsiString;
Var k,n: LongInt;
A,B: Byte;
St: AnsiString;
Vrem: String;
begin
n:= Length(S);
St:=''; Vrem:='';

// Первые 3 байта в изображении резервируем для хранения
// информации о длине текста - записываем туда длину всего текста
While n>1 do
begin
B:=n mod 2;
n:=n div 2;
If B=1 Then Vrem:='1'+Vrem Else Vrem:='0'+Vrem;
end;
If n=1 Then Vrem:='1'+Vrem;
// Дополняем строку Vrem до 24 символов (3 байта)
While Length(Vrem)1 do // теперь преобразуем его в двоичный код
begin
B:=A mod 2;
A:=A div 2;
If B=1 Then Vrem:='1'+Vrem Else Vrem:='0'+Vrem;
end;
If A=1 Then Vrem:='1'+Vrem;

// Дополняем строку Vrem до 8 символов (1 байт)
While Length(Vrem)


:: 2010-12-02 21:03:24 :: продолжение...

пользователь: Владислав.

продолжение:


:: 2010-12-02 21:05:18 :: продолжение...

пользователь: продолжение....

// Дополняем строку Vrem до 8 символов (1 байт)
While Length(Vrem)


:: 2010-12-02 21:06:29 :: ...

пользователь: Владислав.

...не добавляется что-то больше кода...


:: 2010-12-02 21:09:01 :: продолжение...

пользователь: Владислав.

{здесь знак "меньше"} 8 do Vrem:='0'+Vrem;
St:=St+Vrem; // помещаем результат в исходную строку St
END; // *************************
TextToBinary:=St;
end;

function GetKey_Length(S: String): Integer;
Var i,Vrem: Integer;
begin
Vrem:=0;
For i:=23 DownTo 0 do Vrem:=Vrem+StrToInt(S[24-i]) shl i;
GetKey_Length:=Vrem;
end;

function BinaryToText(S: AnsiString):AnsiString;
Var i,j,k,n: LongInt;
St: AnsiString;
begin
St:='';
i:=1;
n:=Length(S);
While i


:: 2010-12-02 21:13:07 :: продолжение...

пользователь: Владислав.

{здесь знак "меньше"}=n do
begin
k:=0;
For j:=7 DownTo 0 do
begin
k:=k+StrToInt(S[i]) shl j;
i:=i+1;
end;
St:=St+Chr(k);
end;
BinaryToText:=St;
end;

procedure EncodingText; // кодирование текста
Var i,j: Integer;
k,n: LongInt;
S_Line: pByteArray;
File_Key,File_Code: String;
S_Binary: AnsiString;
BitMap: TBitMap;
L,H: Integer;
begin
Form1.OpenDialog1.Title:='Выбор базового файла изображения';
If Form1.OpenDialog1.Execute Then
BEGIN
// Создаем BitMap в оперативной памяти
File_Key:=Form1.OpenDialog1.FileName;
BitMap:= TBitMap.Create;
BitMap.PixelFormat:=pf24bit; // формат изображения
BitMap.LoadFromFile(File_Key);
L:=BitMap.Width; // ширина изображения
H:=BitMap.Height; // высота изображения

// Готовим BitMap к приему текстовой информации:
// заменяем в изображении все оттенки цвета 255 на оттенки 254,
// чтобы можно было прибавить 1 из двоичного кода символов текста
For i:=0 To H-1 do
begin
S_Line:=BitMap.ScanLine[i];
For j:=0 To 3*L-1 do
If S_Line[j]=255 Then S_Line[j]:=254;
end;

// Сохраняем подготовленное изображение-ключ
Delete(File_Key,Length(File_Key)-3,4);
File_Code:=File_Key+'_Code.bmp';
File_Key:=File_Key+'_Key.bmp';
BitMap.SaveToFile(File_Key);

// Преобразуем текст в двоичный код
S_Binary:=TextToBinary(Form1.Memo1.Text);

// Кодируем текст - "помещаем" его в изображение
n:=Length(S_Binary);
k:=1;
i:=0;
While i{здесь знак "меньше"}=H-1 do
begin //********************************************
S_Line:=BitMap.ScanLine[i];
j:=0;
While j{здесь знак "меньше"}=3*L-1 do
begin
If S_Binary[k]='1' Then S_Line[j]:=S_Line[j]+1;
j:=j+1;
k:=k+1;
If k>n Then // если весь текст помещен в рисунок
begin //- завершаем все циклы
j:=3*L;
i:=H;
end;
end;
i:=i+1;
end; //*********************************************

// Сохраняем подготовленное закодированное изображение
BitMap.SaveToFile(File_Code);
BitMap.Free;
END;
end;

procedure DecodingText; // декодирование текста
Var i,j: Integer;
k,n: LongInt;
S_Line_Key,S_Line_Code: pByteArray;
File_Key,File_Code: String;
S_Binary: AnsiString;
BitMap_Key,BitMap_Code: TBitMap;
L_Key,H_Key,L_Code,H_Code: Integer;
Length_Key: Integer; //длина кодированного текста
St_Key: String; // двоичное строковое представление длины кодированного текста
begin
Form1.Memo1.Clear;
File_Key:='';
File_Code:='';

Form1.OpenDialog1.Title:='Выбор ключевого файла';
If Form1.OpenDialog1.Execute Then File_Key:=Form1.OpenDialog1.FileName;

Form1.OpenDialog1.Title:='Выбор кодированного файла';
If Form1.OpenDialog1.Execute Then File_Code:=Form1.OpenDialog1.FileName;

If (File_Key{здесь знак "меньше"}>'') and (File_Code{здесь знак "меньше"}>'') Then
BEGIN
// Создаем BitMap_Key в оперативной памяти
BitMap_Key:= TBitMap.Create;
BitMap_Key.PixelFormat:=pf24bit; // формат изображения
BitMap_Key.LoadFromFile(File_Key);
L_Key:=BitMap_Key.Width; // ширина изображения
H_Key:=BitMap_Key.Height; // высота изображения

// Создаем BitMap_Code в оперативной памяти
BitMap_Code:= TBitMap.Create;
BitMap_Code.PixelFormat:=pf24bit; // формат изображения
BitMap_Code.LoadFromFile(File_Code);
L_Code:=BitMap_Code.Width; // ширина изображения
H_Code:=BitMap_Code.Height; // высота изображения

If (L_Key=L_Code) and (H_Key=H_Code) Then
BeGiN
// получаем длину кодированного текста
S_Line_Key:=BitMap_Key.ScanLine[0];
S_Line_Code:=BitMap_Code.ScanLine[0];
St_Key:='';
For i:=0 To 23 do St_Key:=St_Key+IntToStr(S_Line_Code[i]-S_Line_Key[i]);
Length_Key:=GetKey_Length(St_Key);

// получаем бинарную последовательность кодов текста
k:=1;
n:=Length_Key*8;
S_Binary:='';
i:=0;
While i{здесь знак "меньше"}=H_Key-1 do
begin //********************************************
S_Line_Key:=BitMap_Key.ScanLine[i];
S_Line_Code:=BitMap_Code.ScanLine[i];
j:=0;
If i=0 Then j:=24; //пропускаем 3 первых байта, хранящих длину текста
While j{здесь знак "меньше"}=3*L_Key-1 do
begin
S_Binary:=S_Binary+IntToStr(S_Line_Code[j]-S_Line_Key[j]);
j:=j+1;
k:=k+1;
If k>n Then // если весь текст извлечен из рисунок
begin // - завершаем все циклы
j:=3*L_Key;
i:=H_Key;
end;
end;
i:=i+1;
end; //*********************************************

// восстанавливаем текст на основе двоичной последовательности
Form1.Memo1.Text:=BinaryToText(S_Binary);
EnD ElSe
MessageDLG(' Размеры ключевого и кодированного файлов'+
#13+'не совпадают. Очевидно, выбраны не те файлы.'+
#13+' Выберите файлы правильно и повторите операцию.',
mtInformation,[mbOk],0);
BitMap_Key.Free;
BitMap_Code.Free;
END;
end;

{$R *.dfm}

procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
EncodingText;
end;

procedure TForm1.SpeedButton2Click(Sender: TObject);
begin
DecodingText;
end;

end.

Еще на форму киньте компонент OpenDialog1.

Строка Stop_Process: Boolean; не нужна - забыл удалить...


:: 2010-12-02 21:15:57 :: продолжение...

пользователь: Владислав.

Место, где написано {здесь знак "меньше"} замените на знак "меньше". Просто знак "меньше" воспринимается этим сайтом как тег... поэтому и не добавлялся код...

Надеюсь, приведенный код кому-то будет полезен.


:: 2010-12-02 21:32:38 :: замечание по статье автора

пользователь: Владислав.

Вопрос автору статьи: не понятно, зачем выделять 8 байт для хранения информации о длине текста и коэффициенте: 3 байта уже могут хранить число, большее 16 миллионов - вряд-ли текст будет содержать большее число символов (соответственно, изображение для размещения этого текста потребуется более чем 6600 х 6600 точек)...


:: 2010-12-06 14:14:13 :: re:Вшивание информации в растровые рисунки

пользователь: kan.

Спасибо, за интересную реализацию.
Координаты автора есть в заголовке статьи.


Ваше мнение или вопрос к статье в виде простого текста (Tag <a href=... Disabled). Все комментарии модерируются, модератор оставляет за собой право удалить непонравившейся ему комментарий.

заголовок

e-mail

Ваше имя

Сообщение

Введите код




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Шифрование ::


реклама



©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru Rambler's Top100

Время компиляции файла: 2024-04-26 19:56:12
2024-04-26 23:53:27/0.010562896728516/2