Защита видеоинформации

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 01:59, 6 июня 2016.
Open book.svg Авторство
Чичварин Н. В.
Согласовано: 06.05.2016
Статья по учебной дисциплине
Название дисциплины:

Обнаружение и распознавание сигналов

Раздел:

11. Кодирование/декодирование/защита передаваемых сообщений. Различные методы защиты информации.

Глава:

11.2 Методы защиты видеоинформации.

Преподаватель:

Чичварин Н. В.

Введение

На сегодняшний день существует два основных направления решения задачи защиты информации: криптография и стеганография. Цель криптографии – сокрытие содержимого сообщения за счет его шифрования. При стеганографии скрывается сам факт существования тайного сообщения.

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

Развитие средств вычислительной техники в последнее десятилетие дало новый толчок развитию компьютерной стеганографии.

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

Теоретическая часть

Математическая модель стегосистемы

Стегосистема может быть рассмотрена как система связи, в которой передается шифрованное изображение и которое далее будет называться цифровой водяной знак – ЦВЗ.

Алгоритм встраивания ЦВЗ состоит из двух этапов: генерации ЦВЗ и непосредственно встраивания ЦВЗ в кодере,

Этап 1 - Генерация ЦВЗ

Пусть W*, K*, I*, B* есть множества возможных ЦВЗ, ключей, контейнеров и скрываемых сообщений, соответственно. Тогда генерация ЦВЗ может быть представлена в виде

где W, К, I, В - представители соответствующих множеств.

Вообще говоря, функция F может быть произвольной, но на практике требования робастности ЦВЗ накладывают на нее определенные ограничения. Так, в большинстве случаев, то есть незначительно измененный контейнер не приводит к изменению ЦВЗ. Функция F обычно является составной:

где и

то есть ЦВЗ зависит от свойств контейнера. Функция G реализуется при помощи криптографически безопасного генератора псевдослучайной последовательности с К в качестве начального значения.

Для повышения робастности ЦВЗ могут применяться помехоустойчивые коды, например коды БЧХ, сверхточные коды. В ряде публикаций отмечены хорошие результаты, достигаемые при встраивании ЦВЗ в области вейвлет-преобразования с использованием турбо-кодов. Отсчеты ЦВЗ принимают обычно значения из множества {-1, 1}, при этом для отображения {0, 1} → {-1, 1} может применяться двоичная относительная фазовая модуляция (BPSK).

Оператор Т модифицирует кодовые слова С*, в результате чего получается ЦВЗ W*. На эту функцию можно не накладывать ограничения необратимости, так как соответствующий выбор G уже гарантирует необратимость F. Функция Т должна быть выбрана так, чтобы незаполненный контейнер I0, заполненный контейнер IW и незначительно модифицированный заполненный контейнер I'W порождали бы один и тот же ЦВЗ:

то есть она должна быть устойчивой к малым изменениям контейнера.

Этап 2 - Встраивание ЦВЗ в кодере

Процесс встраивания ЦВЗ W(i,j) в исходное изображение I0(i,j) может быть описан как суперпозиция двух сигналов:

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

Проектирующая функция осуществляет «распределение» ЦВЗ по области изображения. Ее использование может рассматриваться как реализация разнесения сигнала по параллельным каналам. Кроме того, эта функция имеет определенную пространственную структуру и корреляционные свойства, использующиеся для противодействия геометрическим атакам.

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

1.png

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

2 класс: информация о канале известна только кодеру (А замкнут, Б разомкнут). Особенностью схемы является то, что, будучи слепой, она имеет ту же теоретическую пропускную способность, что и схема с наличием исходного контейнера в декодере. К недостаткам стегосистем 2 класса можно отнести высокую сложность кодера (необходимость построения кодовой книги для каждого изображения), а также отсутствие адаптации схемы к возможным атакам. В последнее время предложен ряд практических подходов, преодолевающих эти недостатки. В частности, для снижения сложности кодера предлагается использовать структурированные кодовые книги, а декодер рассчитывать на случай наихудшей атаки.

3 класс: дополнительная информация известна только декодеру (А разомкнут, Б замкнут). В этих схемах декодер строится с учетом возможных атак. В результате получаются робастные к геометрическим атакам системы. Одним из методов достижения этой цели является использование так называемого опорного ЦВЗ (аналог пилот-сигнала в радиосвязи). Опорный ЦВЗ — небольшое число бит, внедряемых в инвариантные к преобразованиям коэффициенты сигнала. Например, можно выполнить встраивание в амплитудные коэффициенты преобразования Фурье, которые инвариантны к аффинным преобразованиям. Тогда опорный ЦВЗ «покажет», какое преобразование выполнил со стего атакующий. Другим назначением пилотного ЦВЗ является борьба с замираниями по аналогии с радиосвязью. Замираниями в данном случае можно считать изменение значений отсчетов сигнала при встраивании данных, атаках, добавлении негауссовского шума и т. д. В радиосвязи для борьбы с замираниями используется метод разнесенного приема (по частоте, времени, пространству, коду). В стеганографии же используется разнесение ЦВЗ по пространству контейнера. Пилотный ЦВЗ генерируется в декодере на основе ключа.

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

Этап 3 - Обнаружение ЦВЗ в детекторе

Также как в радиосвязи наиболее важным устройством является приемник, в стегосистеме главным является стегодетектор. В зависимости от типа он может выдавать двоичные либо М-ичные решения о наличии/отсутствии ЦВЗ (в случае детектора с мягкими решениями). Рассмотрим вначале более простой случай «жесткого» детектора стего. Обозначим операцию детектирования через D. Тогда

В качестве декодера ЦВЗ зачастую используют корреляционный приемник, изображенный на рис. 2.

2.png

Пусть у половины пикселей изображения значения яркости увеличено на 1, а у остальных — осталось неизменным либо уменьшено на 1. Тогда где . Коррелятор детектора ЦВЗ вычисляет величину . Так как W может принимать значения ±1, то будет весьма мало, а будет всегда положительно. Поэтому будет очень близко к . Тогда можно воспользоваться результатами теории связи и записать вероятность неверного обнаружения стего как дополнительную (комплементарную) функцию ошибок от корня квадратного из отношения («энергии сигнала») к дисперсии значений пикселей яркости («энергия шума»). Для случая мягкого детектора и закрытой стегосистемы имеем две основные меры подобия:

где нормированный коэффициент взаимной корреляции и
где расстояние по Хэммингу.

В детекторе возможно возникновение двух типов ошибок. Существует вероятность того, что детектор не обнаружит имеющийся ЦВЗ и вероятность ложного нахождения ЦВЗ в пустом контейнере (вероятность ложной тревоги). Снижение одной вероятности приводит к увеличению другой. Надежность работы детектора характеризуют вероятностью ложного обнаружения. Система ЦВЗ должна быть построена таким образом, чтобы минимизировать вероятности возникновения обеих ошибок, так как каждая из них может привести к отказу от обслуживания.

Обоснование выбора методов сокрытия данных в неподвижных изображениях

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

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

Большинство исследований посвящено использованию в качестве стегоконтейнеров изображений. Это обусловлено следующими причинами:

  • Существованием практически значимой задачи защиты фотографий, картин, видео от незаконного тиражирования и распространения;
  • Относительно большим объемом цифрового представления изображений, что позволяет внедрять цифровые водяные знаки (ЦВЗ) большого объема либо повышать робастность внедрения, то есть повышать устойчивость ЦВЗ к различным преобразованиям, производимым над файлом-контейнером;
  • Заранее известным размером контейнера, отсутствием ограничений, накладываемых требованиями реального времени;
  • Слабой чувствительностью человеческого глаза к незначительным изменениям цветов изображения, его яркости, контрастности, содержанию в нем шума, искажениям вблизи контуров;
  • Хорошо разработанными в последнее время методами цифровой обработки изображений.

Надо отметить, что последняя причина вызывает и значительные трудности в обеспечении робастности ЦВЗ: чем более совершенными становятся методы сжатия, тем меньше остается возможностей для встраивания посторонней информации. Развитие теории и практики алгоритмов сжатия изображений привело к изменению представлений о технике внедрения ЦВЗ. Если первоначально предлагалось вкладывать информацию в незначащие биты для уменьшения визуальной заметности, то современный подход заключается во встраивании ЦВЗ в наиболее существенные области изображений, разрушение которых приведет к полной деградации самого изображения. Не случайно поэтому стегоалгоритмы учитывают свойства системы человеческого зрения (СЧЗ) аналогично алгоритмам сжатия изображений. В стегоалгоритмах зачастую используются те же преобразования, что и в современных алгоритмах сжатия (дискретное косинусное преобразование в JPEG, вейвлет-преобразование в JPEG2000). При этом существуют три возможности. Вложение информации может производиться в исходное изображение либо одновременно с осуществлением сжатия изображения-контейнера, либо в уже сжатое алгоритмом JPEG изображение.

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

Предложенные алгоритмы внедряют информацию в область исходного изображения. Их преимущество в том, что для ее внедрения нет необходимости выполнять вычислительно громоздкие линейные преобразования изображений. Данные внедряются за счет манипуляций яркостью и цветовыми составляющими (r, g, b). Алгоритм Куттера можно отнести к методам классической стеганографии, а алгоритм Patchwork позволяет внедрять и обнаруживать в изображении водяные знаки.

Свойства системы человеческого зрения, используемые в стеганографии

Свойства СЧЗ можно разделить на две группы: низкоуровневые («физиологические») и высокоуровневые («психофизиологические»). Вплоть до середины 90-х годов исследователи принимали во внимание, главным образом, низкоуровневые свойства зрения. В последние годы наметилась тенденция построения стегоалгоритмов с учетом и высокоуровневых характеристик СЧЗ.

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

Чувствительность к изменению яркости можно определить следующим образом. Испытуемому показывают некоторую однотонную картинку (Рис. 3, а). После того как глаз к ее освещенности I, «настроился на нее», постепенно изменяют яркость вокруг центрального пятна. Изменение освещенности I продолжают до тех пор, пока оно не будет обнаружено. На рис. 3, б показана зависимость минимального контраста I / I от яркости I. Как видно из рисунка, для среднего диапазона изменения яркости контраст примерно постоянен (аналогия с кратномасштабным анализом и вейвлетами), тогда как для малых и больших яркостей значение порога неразличимо возрастает. Было установлено, что для средних значений яркости.

3n.png

Результаты новейших исследований противоречат «классической» точке зрения и показывают, что при малых значениях яркости порог неразличимости уменьшается, то есть СЧЗ более чувствительна к шуму в этом диапазоне.

Частотная чувствительность СЧЗ проявляется в том, что человек гораздо более восприимчив к низкочастотному («красному»), чем к высокочастотному («синему») шуму. Это связано с неравномерностью амплитудно-частотных характеристики системы зрения человека. Экспериментально ее можно определить при помощи того же опыта, что и при яркостной чувствительности. Но на этот раз в центральном пятне изменяют пространственные частоты до тех пор, пока изменения не станут заметными.

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

Можно показать, что частотная чувствительность тесно связана с яркостной. Известно также и выражение для определения порога маскирования на основе известной яркостной чувствительности, что позволяет найти метрику искажения изображения, учитывающую свойства СЧЗ. Такого типа математические модели хорошо разработаны для случая квантования коэффициентов дискретного косинусного преобразования изображения, так как именно оно применяется в стандарте JPEG.

Эффект маскирования в пространственной области может быть объяснен путем построения стохастических моделей изображения. При этом изображение представляется в виде марковского случайного поля, распределение вероятностей которого подчиняется, например, обобщенному гауссовскому закону.

Таким образом, можно предложить следующую обобщенную схему внедрения данных в изображение:

  1. Выполнить фильтрацию изображения при помощи ориентированных полосовых фильтров. При этом получим распределение энергии по частотно-пространственным компонентам.
  2. Вычислить порог маскирования на основе знания локальной величины энергии.
  3. Масштабировать значение энергии внедряемого ЦВЗ в каждом компоненте так, чтобы оно было меньше порога маскирования.

Многие алгоритмы встраивания информации, так или иначе, используют эту схему.

Высокоуровневые свойства СЧЗ пока редко учитываются при построении стегоалгоритмов. Их отличием от низкоуровневых является то, что эти свойства проявляются «вторично», обработавший первичную информацию от СЧЗ мозг выдает команды на ее «подстройку» под изображение. Основные из этих свойств:

  1. Чувствительность к контрасту. Высококонтрастные участки изображения, перепады яркости обращают на себя значительное внимание.
  2. Чувствительность к размеру. Большие участки изображения «заметнее» меньших размеров. Причем существует порог насыщения, когда дальнейшее увеличение размера не существенно.
  3. Чувствительность к форме. Длинные и тонкие объекты обращают на себя большее внимание, чем круглые однородные.
  4. Чувствительность к местоположению. Человек склонен, в первую очередь, рассматривать центр изображения.
  5. Чувствительность к цвету. Некоторые цвета (например, красный) «заметнее» других. Этот эффект усиливается, если фон заднего плана отличается от цвета фигур на нем.
  6. Люди обычно внимательнее к изображениям переднего плана, чем заднего.
  7. Если на изображении есть люди, в первую очередь человек обратит свое внимание на них. На фотографии человек обращает первоочередное внимание на лицо, глаза, рот, руки.
  8. Чувствительность к внешним раздражителям. Движение глаз наблюдателя зависит от конкретной обстановки, от полученных им перед просмотром или во время него инструкций, дополнительной информации.

Алгоритм Куттера

Алгоритм внедрения

Если 24-битное изображение имеет RGB-кодировку, то встраивание выполняется в канал синего цвета, так как к синему цвету система человеческого зрения наименее чувствительна. Рассмотрим алгоритм передачи одного бита секретной информации.

Пусть si – встраиваемый бит, I = {R,G,B} – контейнер, p = (x,y) - позиция, в которой выполняется вложение. Секретный бит встраивается в канал синего цвета путем модификации яркости

где q - константа, определяющая энергию встраиваемого сигнала.

Ее величина зависит от предназначения схемы. Чем больше q, тем выше робастность вложения, но тем сильнее его заметность.

Алгоритм извлечения

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

Оценка b получается в виде

где счисло пикселей сверху (снизу, слева, справа) от оцениваемого пикселя (с = 3).

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

Знак этой разности определяет значение встроенного бита.

Примеры программной реализации алгоритма Куттера

Формат входных и выходных данных

Входными и выходными данными являются BMP-файлы, имеющие расширение *.bmp. Формат файла BMP (сокращенно от BitMaP) - это "родной" формат растровой графики для Windows, поскольку он наиболее близко соответствует внутреннему формату Windows, в котором эта система хранит свои растровые массивы. Для имени файла, представленного в BMP-формате, чаще всего используется расширение BMP, хотя некоторые файлы имеют расширение RLE, означающее run length encoding (кодирование длины серий). Расширение RLE имени файла обычно указывает на то, что произведено сжатие растровой информации файла одним из двух способов сжатия RLE, которые допустимы для файлов BMP-формата.

В файлах BMP информация о цвете каждого пикселя кодируется 1, 4, 8, 16 или 24 бит (бит/пиксель). Числом бит/пиксель, называемым также глубиной представления цвета, определяется максимальное число цветов в изображении. Изображение при глубине 1 бит/пиксель может иметь всего два цвета, а при глубине 24 бит/пиксель - более 16 млн. различных цветов.

Структура ВМР файла
Заголовок файла растровой графики (14 байт)

Сигнатура файла BMP (2 байт)

Размер файла (4 байт)

Не используется (2 байт)

Не используется (2 байт)

Местонахождение данных растрового массива (4 байт)

Информационный заголовок растрового массива (40 байт)

Длина этого заголовка (4 байт)

Ширина изображения (4 байт)

Высота изображения (4 байт)

Число цветовых плоскостей (2 байт)

Бит/пиксель (2 байт)

Метод сжатия (4 байт)

Длина растрового массива (4 байт)

Горизонтальное разрешение (4 байт)

Вертикальное разрешение (4 байт)

Число цветов изображения (4 байт)

Число основных цветов (4 байт)

Таблица цветов (длина изменяется от 8 до 1024 байт)
Собственно данные растрового массива (длина переменная)

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

Заголовок файла растровой графики можно считать, используя структуру:

 typedef struct tagBITMAPFILEHEADER {
         WORD    bfType;
         DWORD   bfSize;
         WORD    bfReserved1;
         WORD    bfReserved2;
         DWORD   bfOffBits;
 } BITMAPFILEHEADER;

А для считывания информационного заголовка растрового массива потребуется использовать следующую структуру:

 typedef struct tagBITMAPINFOHEADER{
    DWORD  biSize;
    DWORD  biWidth;
    DWORD  biHeight;
    WORD   biPlanes;
    WORD   biBitCount
    DWORD  biCompression;
    DWORD  biSizeImage;
    DWORD  biXPelsPerMeter;
    DWORD  biYPelsPerMeter;
    DWORD  biClrUsed;
    DWORD  biClrImportant;
 } BITMAPINFOHEADER;

Каждый пиксель изображения, или каждый цвет в палитре, описывается как:

 typedef struct tagRGBQUAD {
    BYTE    rgbBlue;
    BYTE    rgbGreen;
    BYTE    rgbRed;
    BYTE    rgbReserved;
 } RGBQUAD;

Формат собственно данных растрового массива в файле BMP зависит от числа бит, используемых для кодирования данных о цвете каждого пикселя. При 256-цветном изображении каждый пиксель в той части файла, где содержатся собственно данные растрового массива, описывается одним байтом (8 бит). Это описание пикселя не представляет значений цветов RGB, а служит указателем для входа в таблицу цветов файла. Таким образом, если в качестве первого значения цвета RGB в таблице цветов файла BMP хранится R/G/B=255/0/0, то значению пикселя 0 в растровом массиве будет поставлен в соответствие ярко-красный цвет. Значения пикселей хранятся в порядке их расположения слева направо, начиная (как правило) с нижней строки изображения. Таким образом, в 256-цветном BMP-файле первый байт данных растрового массива представляет собой индекс для цвета пикселя, находящегося в нижнем левом углу изображения; второй байт представляет индекс для цвета соседнего справа пикселя и т. д. Если число байт в каждой строке нечетно, то к каждой строке добавляется дополнительный байт, чтобы выровнять данные растрового массива по 16-бит границам.

Не все файлы BMP имеют структуру, подобную показанной на схеме. Например, файлы BMP с глубиной 16 и 24 бит/пиксель не имеют таблиц цветов; в этих файлах значения пикселей растрового массива непосредственно характеризуют значения цветов RGB. Также могут различаться внутренние форматы хранения отдельных разделов файла. Например, информация растрового массива в некоторых 16 и 256-цветных BMP-файлах может сжиматься посредством алгоритма RLE, который заменяет последовательности идентичных пикселей изображения на лексемы, определяющие число пикселей в последовательности и их цвет. В Windows допускается работа с BMP-файлами стиля OS/2, в которых используются различные форматы информационного заголовка растрового массива и таблицы цветов.

Программная реализация алгоритма Куттера

В качестве внедряемого сигнала используется последовательность чередующихся нулей и единиц (0, 1, 0, 1, 0, 1, …). Каждый бит кодируется вертикальной полосой, проходящей через все изображение, координаты полос по оси X кратны 100. Такие действия увеличивают надежность внедрения и распознавания, а также устойчивость к негеометрическим атакам.

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

Оценка b получается в виде:

Знак разности и значение

определяют значение встроенного бита.

Порядок работы функции, внедряющей информацию в изображение:

  1. Входными параметрами функции void InsertData(CString sourceFileName, CString destFileName) являются названия файла-контейнера и файла, который будет содержать внедряемые данные. Файл-контейнер открывается для чтения, если на это имеются права доступа. В противном случае на экран выводится сообщение, что в доступе к файлу отказано. В объекты описанных выше структур BITMAPFILEHEADER и BITMAPINFOHEADER считывается заголовок файла-контейнера, и проверяется глубина цвета изображения. Если она не равна 24, то программа не сможет работать с таким файлом, поэтому работа функции прекращается и пользователь оповещается сообщением о том, что в выбранный им файл невозможно внедрить данные используемыми в программе методами.
  2. Если глубина цвета файла-контейнера равна 24, то создается файл для записи с именем, указанным во втором параметре функции InsertData – destFileName. Если открыть файл на запись с указанным именем не удается, то пользователь оповещается об этом сообщением. В созданный файл переписывается заголовок файла-контейнера.
  3. Построчно считываются пиксели изображения. Если номер пикселя кратен четному числу сотен, то из значения его синей цветовой компоненты вычитается величина l, рассчитываемая по формуле:
     const double q = 0.1;
     l = (BYTE) (0.299 * RGBQuad.rgbRed + 0.587 * RGBQuad.rgbGreen + 0.114 * RGBQuad.rgbBlue) * q;
     if (RGBQuad.rgbBlue > l)
          RGBQuad.rgbBlue -= l;
     else
          RGBQuad.rgbBlue = 0;
    
    Если номер пикселя кратен нечетному числу сотен, то значение его синей цветовой составляющей увеличивается на величину l. В противном случае модификации не выполняются. Значения всех цветовых составляющих считываемых пикселей записываются в созданный файл. Таким образом, формируется изображение, содержащее некоторые данные.

Порядок работы функции, извлекающей данные из изображения:

  1. Входными параметрами функции void StatisticFindData(CString sourceFileName, CString maskFileName) являются имена файла, предположительно содержащего данные, и файла, в который будет занесена найденная информация. Файл, содержащий данные, открывается для чтения, если на это имеются права доступа. В противном случае на экран выводится сообщение, что в доступе к файлу отказано. В объекты описанных выше структур BITMAPFILEHEADER и BITMAPINFOHEADER считывается заголовок этого файла, и проверяется глубина цвета изображения. Если она не равна 24, то программа не сможет работать с таким файлом, поэтому работа функции прекращается и пользователь оповещается сообщением о том, что в выбранный им файл невозможно внедрить данные используемыми в программе методами.
  2. Если глубина цвета равна 24, то создается файл для записи с именем, указанным во втором параметре функции StatisticFindData – maskFileName. Если открыть файл на запись с указанным именем не удается, то пользователь оповещается об этом сообщением. В созданный файл переписывается заголовок.
  3. Последовательно считываются пиксели изображения и циклически заносятся в массив из семи элементов. При каждом занесении нового элемента рассчитывается среднее арифметическое значений всего массива, и эта величина сравнивается со значением элемента массива, номер которого на 3 меньше номера текущего элемента. Если разница больше 10, либо меньше -10, то выносится решение о том, что пиксель закодирован в «0», либо «1» соответственно. Изображение файла, содержащего извлекаемые данные, формируется следующим образом: незакодированные пиксели изображаются серым цветом, закодированные в «0» – черным, а в «1» - красным. Это позволяет наглядно оценить погрешность извлечения данных.

Разработанная программа встраивает и извлекает сигнал в 24-битный bmp-файл. Сигнал представляется в виде вертикальных полос: красные пиксели соответствуют «1», а черные – «0», серые пиксели не содержат информации. В результате раскодирования создаются файлы, отображающие вид встроенного сигнала. В случае использования этого метода точность распознавания сигнала не высока, но, тем не менее, полосы четко различимы на фоне всего изображения. Размер исходного файла при внедрении в него информации не изменяется.

Описание интерфейса пользователя

Для удобства работы пользователя с программой, реализующей описанные выше методы, был разработан графический интерфейс пользователя. Он проектировался с использованием библиотеки классов MFC в среде разработки Microsoft Visual Studio.NET 2003. интерфейс поддерживается платформами семейства Windows.

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

4.png

Порядок работы с программой:

  • из выпадающего списка Method необходимо выбрать название метода, с помощью которого требуется обработать данные;
  • в списке Operation необходимо выбрать действие, производимое над файлами: внедрить данные (Insert), извлечь данные (Get) или сравнить содержимое двух файлов (Compare);
  • в текстовых полях необходимо ввести полный путь к файлам, с которыми производятся действия. Source File - файл, который содержит исходное изображение, подлежащее обработке (внедрению или извлечению данных); Destination File – файл, в который будет записано модифицированное исходное изображение; Mask File – файл, в который извлекается информация; Stego File – файл, который предположительно содержит секретную информацию; Host File – файл, из которого получили Stego File путем внедрения информации. Кнопки справа от текстовых полей помогут осуществить выбор файлов путем просмотра содержимого дисков компьютера;
  • для начала работы над файлами указанных операций необходимо нажать кнопку Start;
  • для завершения работы с программой необходимо нажать кнопку Exit.

Исследовательская часть: исследование преимуществ и недостатков анализируемых методов

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

Основным недостатком является неустойчивость алгоритма Patchwork и, в меньшей степени, алгоритма Куттера к аффинным преобразованиям, то есть поворотам, сдвигу, масштабированию. Также алгоритмы модифицируют только синюю цветовую компоненту яркости пикселя, что накладывает ограничения на содержимое контейнера. Используя данные алгоритмы, пользователю придется самостоятельно подбирать файлы-контейнеры таким образом, чтобы погрешность обнаружения не превышала необходимого значения.

Если сравнивать данные методы между собой, то легко заметить, что метод Куттера позволяет внедрять в изображение сообщение (пусть даже небольшой длины), а Patchwork такой возможностью не обладает. Это влечет за собой различные области применения алгоритмов. Метод Куттера преимущественно используется для передачи секретной информации, а Patchwork – для защиты авторских прав и интеллектуальной собственности. Также алгоритмы обладают различными соотношениями между пропускной способностью и устойчивостью к модификациям. Алгоритм Patchwork обладает очень низкой пропускной способностью, зато более устойчив к преобразованиям, и результат распознавания довольно мало зависит от содержимого файла-контейнера. В случае алгоритма Куттера наблюдается совершенно противоположная картина: пропускная способность в несколько раз выше, однако результат очень сильно зависит от исходного изображения. Причем чем более контрастным будет файл-контейнер, тем больше будет погрешность в определении внедренного сообщения.

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

5.png
6.png
7.png
8.png
9.png
10.png
11.png
12.png
13.png

По представленным рисункам можно сделать вывод, что метод Куттера не может быть применим к очень темным изображениям, а также к изображениям, содержащим синий цвет в незначительном количестве. Это объясняется незначительными изменениями, вносимыми при кодировании, которые не могут быть обнаружены при раскодировании. Желательно также, чтоб исходное изображение имело как можно меньше мелких фигур, так как контуры фигур вносят искажения в извлекаемую информацию. Наиболее подходящим объектом для внедрения данных методом Куттера можно считать изображение на Рис. 14.

14.png
15.png
16.png

Алгоритм Patchwork при работе с предложенными изображениями сбоев не дал. Однако существуют изображения, для которых неприменимы оба алгоритма. Это изображения, в которых среднее значение синей цветовой составляющей пикселя близко к 0 или к 255.

Список литературы

  1. Грибунин В.Г., Оков И.Н., Туринцев И.В. Цифровая стеганография. – М.: СОЛОН-Пресс, 2002.
  2. Murray J.D., vanRyper W. Encyclopedia of Graphics File Formats // O'Reilly & Associates, Inc. 1996.
  3. Bender W., Gruhl D., Morimoto N., Lu A. Techniques for Data Hiding // IBM System Journal. 1996.

Приложение. Листинг кода основных функций программы

void CSteganographyMethodsDlg::InsertData(CString sourceFileName, CString destFileName)
{
	BITMAPFILEHEADER BmpHeader;	
	BITMAPINFOHEADER    BmpInfoHeader;
	RGBQUAD RGBQuad;
	DWORD dwCount;
	BYTE l;
	int Rest;
	
	const double q = 0.1;

	HANDLE hSrcFile = CreateFile(sourceFileName, GENERIC_READ,
					0, NULL, OPEN_EXISTING, 0, NULL);
	if((int)hSrcFile == 0xffffffff)
	{
		MessageBox("Error: Source file access denied.", "ERROR", MB_OK | MB_ICONSTOP);
		return;
	}

	SetFilePointer(hSrcFile, 0, NULL, FILE_BEGIN);
	ReadFile(hSrcFile, &BmpHeader, sizeof(BITMAPFILEHEADER), &dwCount, NULL);
	ReadFile(hSrcFile, &BmpInfoHeader, sizeof(BITMAPINFOHEADER), &dwCount, NULL);


	if (BmpInfoHeader.biBitCount != 24)
	{
		MessageBox ("Depth of color in the image is not 24 bits per pixel!", 
				"File format is not supported!", MB_OK | MB_ICONSTOP);
		CloseHandle(hSrcFile);
		return;
	}

	HANDLE hDestFile = CreateFile(destFileName, GENERIC_WRITE,
					0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if((int)hDestFile == 0xffffffff)
	{
		MessageBox("Error: Destination file access denied.", "ERROR", 
MB_OK | MB_ICONSTOP);
		return;
	}

	WriteFile(hDestFile, &BmpHeader, sizeof(BITMAPFILEHEADER), &dwCount, NULL);	
	WriteFile(hDestFile, &BmpInfoHeader, sizeof(BITMAPINFOHEADER), &dwCount, NULL);
	Rest = (BmpInfoHeader.biWidth * BmpInfoHeader.biBitCount / 8) % 4;
	for(int i = 0; i < fabs((long double)BmpInfoHeader.biHeight); i++)
	{
		for (int j = 0; j< BmpInfoHeader.biWidth; j++)
		{
			ReadFile(hSrcFile, &RGBQuad, BmpInfoHeader.biBitCount / 8, &dwCount, NULL);	
			if((j % 200) == 0)
			{
l = (BYTE) (0.299 * RGBQuad.rgbRed + 0.587 * RGBQuad.rgbGreen + 0.114 * RGBQuad.rgbBlue) * q;
				
if (RGBQuad.rgbBlue >  l)
					RGBQuad.rgbBlue -= l;
				else
					RGBQuad.rgbBlue = 0;
WriteFile(hDestFile, &RGBQuad, BmpInfoHeader.biBitCount / 8, &dwCount, NULL);
			}
			else if ((j % 100 ) == 0 && (j % 200) != 0)
			{
l = (BYTE) (0.299 * RGBQuad.rgbRed + 0.587 * RGBQuad.rgbGreen + 0.114 * RGBQuad.rgbBlue) * q;

				if ((RGBQuad.rgbBlue + l) < 255)
					RGBQuad.rgbBlue += l;
				else
					RGBQuad.rgbBlue = 255;
WriteFile(hDestFile, &RGBQuad, BmpInfoHeader.biBitCount / 8, &dwCount, NULL);
			}
			
else
			{
WriteFile(hDestFile, &RGBQuad, BmpInfoHeader.biBitCount / 8, &dwCount, NULL);
			}
		}
		if(Rest != 0)
		{
			for(int k = 0; k < (4 - Rest); k++)
			{
				ReadFile(hSrcFile, &l, 1, &dwCount, NULL);	
				WriteFile(hDestFile, &l, 1, &dwCount, NULL);
			}
		}
	}
	CloseHandle(hSrcFile);
	CloseHandle(hDestFile);
}


void CSteganographyMethodsDlg::StatisticFindData(CString sourceFileName, CString maskFileName)
{
	RGBQUAD RGBArray[7];
	RGBQUAD RGBQuad;
	DWORD dwCount;
	BITMAPFILEHEADER BmpHeader;
	BITMAPINFOHEADER BmpInfoHeader;
	int Rest, n, Top;
	float Average;
	BYTE l;

	HANDLE hSrcFile = CreateFile(sourceFileName, GENERIC_READ,
					0, NULL, OPEN_EXISTING, 0, NULL);
	if((int)hSrcFile == 0xffffffff)
	{
		MessageBox("Error: Source file access denied.", "ERROR", MB_OK | MB_ICONSTOP);
		return;
	}

	ReadFile(hSrcFile, &BmpHeader, sizeof(BITMAPFILEHEADER), &dwCount, NULL);	
	ReadFile(hSrcFile, &BmpInfoHeader, sizeof(BITMAPINFOHEADER), &dwCount, NULL);

	if (BmpInfoHeader.biBitCount != 24)
	{
		MessageBox ("Depth of color in the image is not 24 bits per pixel!", 
				"File format is not supported!", MB_OK | MB_ICONSTOP);
		CloseHandle(hSrcFile);
		return;
	}

	HANDLE hMaskFile = CreateFile(maskFileName, GENERIC_WRITE,
					0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if((int)hMaskFile == 0xffffffff)
	{
		MessageBox("Error: Mask file access denied.", "ERROR", MB_OK | MB_ICONSTOP);
		return;
	}

	WriteFile(hMaskFile, &BmpHeader, sizeof(BITMAPFILEHEADER), &dwCount, NULL);
	WriteFile(hMaskFile, &BmpInfoHeader, sizeof(BITMAPINFOHEADER), &dwCount, NULL);

	Rest = (BmpInfoHeader.biWidth * BmpInfoHeader.biBitCount / 8) % 4;

	for (int i = 0; i < fabs((long double)BmpInfoHeader.biHeight); i++)
	{
		memset(RGBArray, 0,sizeof(RGBArray));
		ReadFile(hSrcFile, &RGBArray[0], BmpInfoHeader.biBitCount / 8, &dwCount, NULL);
		ReadFile(hSrcFile, &RGBArray[1], BmpInfoHeader.biBitCount / 8, &dwCount, NULL);
		ReadFile(hSrcFile, &RGBArray[2], BmpInfoHeader.biBitCount / 8, &dwCount, NULL);

		for (int j = 0; j < BmpInfoHeader.biWidth; j++)
		{
			if (j < BmpInfoHeader.biWidth - 3)
ReadFile(hSrcFile, &RGBArray[(j + 3) % 7], BmpInfoHeader.biBitCount / 8, &dwCount, NULL);

			if ((j+4) <= 7)
			{
				n = j + 4;
				Top = 0;
			}
			else 
				if (j > BmpInfoHeader.biWidth - 4)
				{
					n = BmpInfoHeader.biWidth - j + 3;
					Top = (j - 3) % 7;
				}
				else
				{
					n = 7;
					Top = 0;
				}
			Average = Avg(RGBArray, n, Top);

			if((Average - RGBArray[j % 7].rgbBlue) > 10)
			{
				RGBQuad.rgbBlue = 0;
				RGBQuad.rgbGreen = 0;
				RGBQuad.rgbRed = 255;
WriteFile(hMaskFile, &RGBQuad, BmpInfoHeader.biBitCount / 8, &dwCount, NULL);
			}
			else if ((Average - RGBArray[j % 7].rgbBlue) < -10)
			{
				RGBQuad.rgbBlue = 0;
				RGBQuad.rgbGreen = 0;
				RGBQuad.rgbRed = 0;
WriteFile(hMaskFile, &RGBQuad, BmpInfoHeader.biBitCount / 8, &dwCount, NULL);
			}
			else 
			{
				RGBQuad.rgbBlue = 190;
				RGBQuad.rgbGreen = 190;
				RGBQuad.rgbRed = 190;
WriteFile(hMaskFile, &RGBQuad, BmpInfoHeader.biBitCount / 8, &dwCount, NULL);
			}
		}
		if(Rest != 0)
		{
			for(int k = 0; k < (4 - Rest); k++)
			{
				ReadFile(hSrcFile, &l, 1, &dwCount, NULL);	
				WriteFile(hMaskFile, &l, 1, &dwCount, NULL);
			}
		}
	}
	CloseHandle(hSrcFile);
	CloseHandle(hMaskFile);
}


float CSteganographyMethodsDlg::Avg(RGBQUAD *RGBArray, int n, int Top)
{
	int Sum = 0;

	for(int i = 0; i < n; i++)
		Sum += RGBArray[(i + Top) % 7].rgbBlue;
	return ((float)Sum / n);
}


void CSteganographyMethodsDlg::CompareFiles(CString stegoFileName, CString maskFileName, CString hostFileName)
{
	BITMAPFILEHEADER BmpHeader, BmpHeader1;
	BITMAPINFOHEADER    BmpInfoHeader, BmpInfoHeader1;
	RGBQUAD RGBQuad, RGBQuad1;
	DWORD dwCount;
	int Rest;
	BYTE l;
	
	HANDLE hStegoFile = CreateFile(stegoFileName, GENERIC_READ,
					0, NULL, OPEN_EXISTING, 0, NULL);

	HANDLE hHostFile = CreateFile(hostFileName, GENERIC_READ,
					0, NULL, OPEN_EXISTING, 0, NULL);
	if(((int)hStegoFile == 0xffffffff) || ((int)hHostFile == 0xffffffff))
	{
		MessageBox("Error: File access denied.", "ERROR", MB_OK | MB_ICONSTOP);
		return;
	}

	ReadFile(hHostFile, &BmpHeader, sizeof(BITMAPFILEHEADER), &dwCount, NULL);
	ReadFile(hStegoFile, &BmpHeader1, sizeof(BITMAPFILEHEADER), &dwCount, NULL);	
	ReadFile(hHostFile, &BmpInfoHeader, sizeof(BITMAPINFOHEADER), &dwCount, NULL);
	ReadFile(hStegoFile, &BmpInfoHeader1, sizeof(BITMAPINFOHEADER), &dwCount, NULL);

	if ((BmpInfoHeader.biBitCount != 24) || (BmpInfoHeader1.biBitCount != 24))
	{
		MessageBox ("Depth of color in the image is not 24 bits per pixel!", 
				"File format is not supported!", MB_OK | MB_ICONSTOP);
		CloseHandle(hStegoFile);
		CloseHandle(hHostFile);
		return;
	}

	HANDLE hMaskFile = CreateFile(maskFileName,GENERIC_READ | GENERIC_WRITE,
					0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if((int)hMaskFile == 0xffffffff)
	{
		MessageBox("Error: Mask file access denied.", "ERROR", MB_OK | MB_ICONSTOP);
		return;
	}

	WriteFile(hMaskFile, &BmpHeader, sizeof(BITMAPFILEHEADER), &dwCount, NULL);
	WriteFile(hMaskFile, &BmpInfoHeader, sizeof(BITMAPINFOHEADER), &dwCount, NULL);

	Rest = (BmpInfoHeader.biWidth * BmpInfoHeader.biBitCount / 8) % 4;

	for (int i = 0; i < fabs((long double)BmpInfoHeader.biHeight); i++)
	{
		for (int j = 0; j < BmpInfoHeader.biWidth; j++)
		{
ReadFile(hHostFile, &RGBQuad, BmpInfoHeader.biBitCount / 8, &dwCount, NULL);
ReadFile(hStegoFile, &RGBQuad1, BmpInfoHeader.biBitCount / 8, &dwCount, NULL);	
		
			if(RGBQuad.rgbBlue > RGBQuad1.rgbBlue)
			{
			
				RGBQuad.rgbBlue = 0;
				RGBQuad.rgbGreen = 0;
				RGBQuad.rgbRed = 255;
WriteFile(hMaskFile, &RGBQuad, BmpInfoHeader.biBitCount / 8, &dwCount, NULL);
			}
			else if (RGBQuad.rgbBlue < RGBQuad1.rgbBlue)
			{
				RGBQuad.rgbBlue = 0;
				RGBQuad.rgbGreen = 0;
				RGBQuad.rgbRed = 0;
WriteFile(hMaskFile, &RGBQuad, BmpInfoHeader.biBitCount / 8, &dwCount, NULL);
			}
			else 
			{
				RGBQuad.rgbBlue = 190;
				RGBQuad.rgbGreen = 190;
				RGBQuad.rgbRed = 190;
WriteFile(hMaskFile, &RGBQuad, BmpInfoHeader.biBitCount / 8, &dwCount, NULL);
			}
		}
		if(Rest != 0)
		{
			for(int k = 0; k < (4 - Rest); k++)
			{
				ReadFile(hHostFile, &l, 1, &dwCount, NULL);
				ReadFile(hStegoFile, &l, 1, &dwCount, NULL);
				WriteFile(hMaskFile, &l, 1, &dwCount, NULL);
			}
		}
	}
	CloseHandle(hStegoFile);
	CloseHandle(hMaskFile);
	CloseHandle(hHostFile);
}



void CSteganographyMethodsDlg::InsertPatchwork(CString sourceFileName, CString destFileName)
{
	BITMAPFILEHEADER BmpHeader;
	BITMAPINFOHEADER    BmpInfoHeader;
	RGBQUAD RGBQuad;
	DWORD dwCount;
	int LuminanceOffset = 3;
	int Addition, Remainder, BlockQuantity;
	int OneBlockPixelQuantity = 32768, LastBlockPixelQuantity;
	int SummaryPointQuantity = 20000, OneBlockPointQuantity, LastBlockPointQuantity;
	BOOL B;

	HANDLE hSrcFile = CreateFile(sourceFileName, GENERIC_READ,
					0, NULL, OPEN_EXISTING, 0, NULL);
	if((int)hSrcFile == 0xffffffff)
	{
		MessageBox("Error: Source file access denied.", "ERROR", MB_OK | MB_ICONSTOP);
		return;
	}

	ReadFile(hSrcFile, &BmpHeader, sizeof(BITMAPFILEHEADER), &dwCount, NULL);	
	ReadFile(hSrcFile, &BmpInfoHeader, sizeof(BITMAPINFOHEADER), &dwCount, NULL);

	if (BmpInfoHeader.biBitCount != 24)
	{
		MessageBox ("Depth of color in the image is not 24 bits per pixel!", 
				"File format is not supported!", MB_OK | MB_ICONSTOP);
		CloseHandle(hSrcFile);
		return;
	}
	CloseHandle(hSrcFile);
	
	B = CopyFile(sourceFileName, destFileName, FALSE);
	if(!B)
	{
MessageBox("Error: can't create a copy of source file", "ERROR", MB_OK | MB_ICONSTOP);
		return;
	}

	HANDLE hDestFile = CreateFile(destFileName, GENERIC_READ | GENERIC_WRITE,
					0, NULL, OPEN_EXISTING, 0, NULL);
	if((int)hDestFile == 0xffffffff)
	{
MessageBox("Error: Destination file access denied.", "ERROR", MB_OK | MB_ICONSTOP);
		return;
	}

	SetFilePointer(hDestFile, 0, NULL, FILE_BEGIN);
	ReadFile(hDestFile, &BmpHeader, sizeof(BITMAPFILEHEADER), &dwCount, NULL);
	ReadFile(hDestFile, &BmpInfoHeader, sizeof(BITMAPINFOHEADER), &dwCount, NULL);
	
	Remainder = (BmpInfoHeader.biWidth * BmpInfoHeader.biBitCount / 8) % 4;

	if(Remainder)
		Addition = 4 - Remainder;
	else
		Addition = 0;

BlockQuantity = (int)ceil((float)BmpInfoHeader.biHeight * BmpInfoHeader.biWidth / OneBlockPixelQuantity );

LastBlockPixelQuantity = BmpInfoHeader.biHeight * BmpInfoHeader.biWidth % OneBlockPixelQuantity;

	OneBlockPointQuantity = (int)((float)(SummaryPointQuantity) / ((float)BlockQuantity - 1.0
	+ ((float)LastBlockPixelQuantity / (float)OneBlockPixelQuantity)));

LastBlockPointQuantity = (int)((float)OneBlockPointQuantity * (float)LastBlockPixelQuantity / (float)OneBlockPixelQuantity);

	if(OneBlockPointQuantity % 2)	
		OneBlockPointQuantity--;

	if(LastBlockPointQuantity % 2)
		LastBlockPointQuantity--;
	
	long CurrentTop;
	int BitesOffset, TopPixelInLine, CurrentPoint;

	for (int i = 0; i < BlockQuantity; i++)
	{
		CurrentTop = i * OneBlockPixelQuantity * BmpInfoHeader.biBitCount / 8
			+ ((i * OneBlockPixelQuantity) / BmpInfoHeader.biWidth) * Addition;		// вершина каждого блока

		TopPixelInLine = (i * OneBlockPixelQuantity) % BmpInfoHeader.biWidth;			//координата вершины в строке

		srand(i);
for(int j = 0; (i == (BlockQuantity - 1)) ? j < LastBlockPointQuantity : j < OneBlockPointQuantity; j++)
		{	//устанавливаем указатель на начало блока
SetFilePointer(hDestFile, CurrentTop + BmpHeader.bfOffBits, NULL, FILE_BEGIN);
			
			if (i != (BlockQuantity - 1))
				CurrentPoint = rand() % OneBlockPixelQuantity;
			else
				CurrentPoint = rand() % LastBlockPixelQuantity;

			BitesOffset = CurrentPoint * BmpInfoHeader.biBitCount / 8 + ((CurrentPoint 
			+ TopPixelInLine) / BmpInfoHeader.biWidth) * Addition;
//количество байт для установки указателя в случайную точку

			SetFilePointer(hDestFile, BitesOffset, NULL, FILE_CURRENT);
ReadFile(hDestFile, &RGBQuad, BmpInfoHeader.biBitCount / 8, &dwCount, NULL);

			if(j % 2)
			{
				if(RGBQuad.rgbBlue > LuminanceOffset)
					RGBQuad.rgbBlue -= LuminanceOffset;
				else
					RGBQuad.rgbBlue = 0;
			}
			else
			{
				if((RGBQuad.rgbBlue + LuminanceOffset) < 255)
					RGBQuad.rgbBlue += LuminanceOffset;
				else
					RGBQuad.rgbBlue = 255;
			}
			//возвращаем указатель на текущую точку
SetFilePointer(hDestFile, -(BmpInfoHeader.biBitCount / 8), NULL, FILE_CURRENT);
WriteFile(hDestFile, &RGBQuad, BmpInfoHeader.biBitCount / 8, &dwCount, NULL);
		}
	}
	
	CloseHandle(hDestFile);
}


void CSteganographyMethodsDlg::FindPatchwork(CString sourceFileName)
{
	BITMAPFILEHEADER BmpHeader;
	BITMAPINFOHEADER    BmpInfoHeader;
	RGBQUAD RGBQuad;
	DWORD dwCount;
	int Addition, Remainder, BlockQuantity;
	int OneBlockPixelQuantity = 32768, LastBlockPixelQuantity;
	int SummaryPointQuantity = 20000, OneBlockPointQuantity, LastBlockPointQuantity;
	long SumOfDifferences = 0;    //Сумма разностей значений яркости пикселей

	HANDLE hSrcFile = CreateFile(sourceFileName, GENERIC_READ,
					0, NULL, OPEN_EXISTING, 0, NULL);

	SetFilePointer(hSrcFile, 0, NULL, FILE_BEGIN);
	ReadFile(hSrcFile, &BmpHeader, sizeof(BITMAPFILEHEADER), &dwCount, NULL);
	ReadFile(hSrcFile, &BmpInfoHeader, sizeof(BITMAPINFOHEADER), &dwCount, NULL);
	
	Remainder = (BmpInfoHeader.biWidth * BmpInfoHeader.biBitCount / 8) % 4;

	if(Remainder)		// Проверка на необходимость дополнения строки до длины слова
		Addition = 4 - Remainder;
	else
		Addition = 0;

	
BlockQuantity = (int)ceil((float)BmpInfoHeader.biHeight * BmpInfoHeader.biWidth / OneBlockPixelQuantity );

LastBlockPixelQuantity = BmpInfoHeader.biHeight * BmpInfoHeader.biWidth % OneBlockPixelQuantity;

	OneBlockPointQuantity = (int)((float)(SummaryPointQuantity) / ((float)BlockQuantity - 1.0
	+ ((float)LastBlockPixelQuantity / (float)OneBlockPixelQuantity)));

	LastBlockPointQuantity = (int)((float)OneBlockPointQuantity *(float)LastBlockPixelQuantity 
	/ (float)OneBlockPixelQuantity);

	if(OneBlockPointQuantity % 2)
		OneBlockPointQuantity--;

	if(LastBlockPointQuantity % 2)
		LastBlockPointQuantity--;
	
	long CurrentTop;
	int BitesOffset, TopPixelInLine, CurrentPoint;

	for (int i = 0; i < BlockQuantity; i++)
	{
		CurrentTop = i * OneBlockPixelQuantity * BmpInfoHeader.biBitCount / 8
			+ ((i * OneBlockPixelQuantity) / BmpInfoHeader.biWidth) * Addition;

		TopPixelInLine = (i * OneBlockPixelQuantity) % BmpInfoHeader.biWidth;

		srand(i);
for(int j = 0; (i == (BlockQuantity - 1)) ? j < LastBlockPointQuantity : j < OneBlockPointQuantity; j++)
		{
SetFilePointer(hSrcFile, CurrentTop + BmpHeader.bfOffBits, NULL, FILE_BEGIN);
			
			if (i != (BlockQuantity - 1))
				CurrentPoint = rand() % OneBlockPixelQuantity;
			else
				CurrentPoint = rand() % LastBlockPixelQuantity;

			BitesOffset = CurrentPoint * BmpInfoHeader.biBitCount / 8 + ((CurrentPoint 
				+ TopPixelInLine) / BmpInfoHeader.biWidth) * Addition;

			SetFilePointer(hSrcFile, BitesOffset, NULL, FILE_CURRENT);
			ReadFile(hSrcFile, &RGBQuad, BmpInfoHeader.biBitCount / 8, &dwCount, NULL);

			if(j % 2)
			{
				SumOfDifferences -= RGBQuad.rgbBlue;
			}
			else
			{
				SumOfDifferences += RGBQuad.rgbBlue;
			}
		}
	}
	
	if(SumOfDifferences > 30000)
		MessageBox("Digital watermark is FOUND", "Results", MB_OK | MB_ICONINFORMATION);
	else
MessageBox("Digital watermark is NOT FOUND", "Results", MB_OK | MB_ICONINFORMATION);

	CloseHandle(hSrcFile);
}