Форум программистов, компьютерный форум, киберфорум
C# Windows Forms
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.86/91: Рейтинг темы: голосов - 91, средняя оценка - 4.86
0 / 0 / 0
Регистрация: 28.04.2015
Сообщений: 9

Стеганография. Метод Коха и Жао

01.05.2015, 10:45. Показов 19008. Ответов 40

Студворк — интернет-сервис помощи студентам
Метод Коха и Жао. Реализую на C#. Хотел заказать в разделе фриланса, но пообщавшись с форумчанином Ev_Hyper , понял, что тема не супер-сложная. Немного разобрался и ниже сформулировал вопросы.

Сначала опишу сам алгоритм.
Скрытие:
1. Первичное изображение разбиваем на блоки 8 на 8 пикселей.
2. К каждому блоку применяется дискретное косинусное преобразование. В результате получаем матрицы 8 на 8 коэффициентов ДКП.
3. Выбирается любой блок (в каждый блок записывается 1 бит информации)
4. Псевдослучайно выбираются два коэффициента ДКП в каждом блоке из среднечастотных коэффициентов.
5. Для передачи бита 0 необходимо, чтобы разница модулей коэффициентов ДКП превышает некоторую положительную величину (задается вручную); для передачи бита 1 разница должна быть меньшей по сравнению с некоторой отрицательной величиной. Таким образом, при передаче 0 увеличиваем модуль первого коэффициента и уменьшаем модуль второго. При передаче 1 уменьшаем модуль первого коэффициента и увеличиваем модуль второго.

где https://www.cyberforum.ru/cgi-bin/latex.cgi?\Omega - матрица 8на8 коэффициентов ДКП блока; b - номер блока;https://www.cyberforum.ru/cgi-bin/latex.cgi?{\upsilon }_{1}, {\nu }_{1} - координаты
6. Проходим по каждому блоку и выполняем пункты 4 и 5.
7. Для каждого блока выполняем обратное ДКП

Извлечение:
1. Пункты 1,2,3 скрытия.
2. Нужно «найти» те два коэффициента ДКП, которые мы изменяли, и посчитать разность их модулей.
3. В результате проверки разности модулей извлекаемому биту присваивается или 0 или 1.

4. Для каждого блока выполняем обратное ДКП.


Последний раз кодил 2.5 года назад, поэтому много непонятного по синтаксису.
С терминологией не очень, поэтому какие-то вопросы наверно звучат некорректно.
С чего начать:
1. Как в программе открыть файл с текстом? Как получить из исходного текста строку бит? Что именно нужно: строка бит или массив бит? И вообще, нужно ли заранее выделять память под такой массив(строку) или лучше в процессе срытия получать биты из исходных символов исходного текста?
2. 1-й пункт алгоритма. Как открыть изображение (*.bmp) в программе? В каком виде оно будет представлено в программе? Для наших целей нужно его представить в виде двумерного массива пикселей? Если да, то как это сделать, и как затем разбить этот двумерный массив на двумерные массивы поменьше? Не понятен сам алгоритм такого разбиения…
3. 2-й пункт алгоритма. К чему именно применяется ДКП: к матрице пикселей, к пикселю, к каждой цветовой компоненте пикселя, к каждому биту значения цветовой компоненты? Как будет выглядеть ДКП на c#?
4. 4-й пункт. Как выбрать коэффициенты псевдослучайно?
5. Как уменьшить абсолютное значение одного коэффициента и увеличить другого, чтобы разность достигла приемлемого значения? В цикле прибавлять/отнимать по 1 и сравнивать разность с нужным значением?
6. 2-й пункт извлечения. Как найти те самые коэффициенты, ведь мы их выбрали псевдослучайно?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
01.05.2015, 10:45
Ответы с готовыми решениями:

Реализовать метод скрытия информации (Коха-Жао) в коэффициентах дискретно-косинусного преобразования JPEG
Здравствуйте. Нужно реализовать метод скрытия информации (Коха-Жао) в коэффициентах дискретно-косинусного преобразования JPEG. Для...

Алгоритм Коха и Жао
Пытаюсь реализовать данный алгоритм для встраивания сообщения в bmp. Ничего не получается. Решил попробовать встроить в 4 матрицы 4 бита....

Алгоритм Коха и Жао
не могу разобраться с реализацией этого алгоритма. Понял, что на первом шаге наше изображение нужно выгрузить в массив. Массив байт. Потом...

40
Заблокирован
01.05.2015, 12:48
Цитата Сообщение от 8Ball Посмотреть сообщение
1. Как в программе открыть файл с текстом?
8Ball, Для открытия файла с текстом и изображения используйте
OpenFileDialog

Для чтения информации из выбранного файла воспользуйтесь методом из класса
File

Цитата Сообщение от 8Ball Посмотреть сообщение
Как получить из исходного текста строку бит? Что именно нужно: строка бит или массив бит? И вообще, нужно ли заранее выделять память под такой массив(строку) или лучше в процессе срытия получать биты из исходных символов исходного текста?
C#
1
2
            string message = "message"; //замените эту строку, на текст из файла
            byte[] tekst = Encoding.GetEncoding(1251).GetBytes(message);//1251 - кодировка.
Цитата Сообщение от 8Ball Посмотреть сообщение
В каком виде оно будет представлено в программе? Для наших целей нужно его представить в виде двумерного массива пикселей?
Для начала воспользуйтесь методами GetPixel, SetPixel.
Если хотите провести встраивание в "синюю компоненту", то:
C#
1
2
3
4
5
6
7
8
9
10
            if (open.ShowDialog() == DialogResult.OK)
            {
                Bitmap image = (Bitmap)Image.FromFile(open.FileName);
                int y = image.Height, x = image.Width;
                Byte[,] B = new Byte[x, y];
                //выбираем из картинки синюю компоненту RGB-модели
                for (int i = 0; i < x; i++)
                    for (int j = 0; j < y; j++)
                        B[i, j] = image.GetPixel(i, j).B;
            }
Когда будет черновой, но рабочий вариант, замените на более быстрый способ:
https://www.cyberforum.ru/blog... g3507.html
https://www.cyberforum.ru/post1936578.html
...
Цитата Сообщение от 8Ball Посмотреть сообщение
как затем разбить этот двумерный массив на двумерные массивы поменьше? Не понятен сам алгоритм такого разбиения…
в Конаховиче есть пример такого цикла. Перевод на C# из маткада, может выглядеть таким образом:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
                List<byte[,]> C = new List<byte[,]>();
                int N = 8; //размерность сегментов
                int Nc = x * y / (N * N); //общей число сегментов
                if (Nc < 8 * tekst.Length) throw new ArgumentException("Текст слшком велик");
                //разбиваем массив B на сегменты С
                int C1 = 0, C2 = N - 1;
                int r1, r2;
                for (int b = 0; b < Nc; b++)
                {
                    r1 = N * b % x;
                    r2 = r1 + N - 1;
                    C.Add(submatrix(B, r1, r2, C1, C2));
                    if (r2 == x - 1)
                    {
                        C1 += N;
                        C2 += N;
                    }
                }
C#
1
2
3
4
5
6
7
8
9
10
        static byte[,] submatrix(byte[,] one, int a, int b, int c, int d)
        {
 
            byte[,] temp = new byte[b - a + 1, d - c + 1];
            for (int i = a, k = 0; i <= b; i++, k++)
                for (int j = c, l = 0; j <= d; j++, l++)
                    temp[k, l] = one[i, j];
 
            return temp;
        }
Цитата Сообщение от 8Ball Посмотреть сообщение
Как будет выглядеть ДКП на c#?
примерно так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
        static double[,] dkp(byte[,] one)
        {
            int n = one.GetLength(0);
            double[,] two = new double[n, n];
            double U, V, temp = 0;
            for (int v = 0; v < n; v++)
            {
                for (int u = 0; u < n; u++)
                {
                    if (v == 0) V = 1.0 / Math.Sqrt(2);
                    else V = 1;
                    if (u == 0) U = 1.0 / Math.Sqrt(2);
                    else U = 1;
                    temp = 0;
                    for (int i = 0; i < n; i++)
                    {
                        for (int j = 0; j < n; j++)
                        {
                            temp += one[i, j] * Math.Cos(Math.PI * v * (2 * i + 1) / (2 * n)) *
                                Math.Cos(Math.PI * u * (2 * j + 1) / (2 * n));
                        }
                    }
                    two[v, u] = U * V * temp / (Math.Sqrt(2 * n));
                }
            }
            return two;
        }
Цитата Сообщение от 8Ball Посмотреть сообщение
4. 4-й пункт. Как выбрать коэффициенты псевдослучайно?
Использовать класс Random
Но вам все равно нужно будет их знать для извлечения.

Цитата Сообщение от 8Ball Посмотреть сообщение
5. Как уменьшить абсолютное значение одного коэффициента и увеличить другого, чтобы разность достигла приемлемого значения? В цикле прибавлять/отнимать по 1 и сравнивать разность с нужным значением?
вот этот вопрос мне не понятен. Вы имеете ввиду 5 пункт алгоритма?
2
0 / 0 / 0
Регистрация: 28.04.2015
Сообщений: 9
01.05.2015, 13:24  [ТС]
Ev_Hyper, [QUOTE]
Цитата Сообщение от Ev_Hyper Посмотреть сообщение
вот этот вопрос мне не понятен. Вы имеете ввиду 5 пункт алгоритма?
Да, 5-й пункт. Для 0 можно, например, сделать их равными и прибавить к первому значение величины, с которой сравниваем (P). А можно модуль первого постепенно увеличивать и второго постепенно уменьшать, пока разность не станет больше значения P. Но это простой цикл, я уже понял его. Хотя много циклов в циклах это тоже не очень хорошо...
Спасибо за оперативный ответ, сейчас начну пробовать!
0
Заблокирован
01.05.2015, 13:59
Цитата Сообщение от 8Ball Посмотреть сообщение
Для 0 можно, например, сделать их равными и прибавить к первому значение величины, с которой сравниваем (P). А можно модуль первого постепенно увеличивать и второго постепенно уменьшать, пока разность не станет больше значения P. Но это простой цикл, я уже понял его. Хотя много циклов в циклах это тоже не очень хорошо...
Да, именно так. Вы не пишите с 0, а "переводите" из маткада, как в книге. Так будет намного проще.

8Ball, вам нужно будет работать с битами, вот класс (взят из статьи на хабре о методе LSB):
Bits

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
    class Bits
    {
        private ArrayList bits = new ArrayList();
 
        private int len = 0, num = 0;
 
        /// <summary>
        /// Конструктор. Переводит целое значение в двоичный вид.
        /// </summary>
        public Bits(int value)
        {
            num = value; ToBits();
        }
 
        /// <summary>
        /// Конструктор. Код символа преобразует в двоичный вид.
        /// </summary>
        public Bits(char value)
        {
            num = (int)value; ToBits();
        }
 
        /// <summary>
        /// Конструктор. Код символа преобразует в двоичный вид.
        /// </summary>
        public Bits(byte value)
        {
            num = (int)value; ToBits();
        }
 
        /// <summary>
        /// Конструктор. Преобразует строку в "битовый массив"
        /// Все ненулевые элементы строки интерпритируются как '1'.
        /// </summary>
        public Bits(string value)
        {
            len = value.Length;
 
            for (int i = 0; i < len; i++)
                if (value[i] == '0') bits.Add(0);
                else bits.Add(1);
        }
 
        /// <summary>
        /// Свойство. Возвращает текущее количество бит.
        /// </summary>
        public int Length
        {
            get
            {
                return len;
            }
        }
 
        /// <summary>
        /// Свойство. Возвращает десятичное представление "битового массива".
        /// </summary>
        public int Number
        {
            get
            {
                ToInt(); return num;
            }
        }
 
        /// <summary>
        /// Свойство. Возвращает символ, который представлен текущим десятичным числом.
        /// </summary>
        public char Char
        {
            get
            {
                ToInt(); return (char)num;
            }
        }
 
        /// <summary>
        /// Операция индексации []. Возвращает i-ый элемент "битового массива".
        /// </summary>
        public int this[int i]
        {
            get
            {
                if (i >= 0 && i < len) return (int)bits[i];
                else return -1;
            }
 
            set
            {
                if (i >= 0 && i < len)
                    if (value > 0) bits[i] = 1;
                    else bits[i] = 0;
            }
        }
 
        /// <summary>
        /// Функция добавляет в конец новый элемент.
        /// Все ненулевые элементы интерпритируются как '1'.
        /// </summary>
        public void Add(object value)
        {
            if ((int)value == 0) bits.Add(0);
            else bits.Add(1);
 
            len++;
        }
 
        /// <summary>
        /// Функция вставляет в позицию index значение value
        /// Все ненулевые элементы интерпритируются как '1'.
        /// </summary>
        public void Insert(int index, object value)
        {
            if (index < 0 || index > len) return;
 
            if (value.Equals(0)) bits.Insert(index, 0);
            else bits.Insert(index, 1);
 
            len++;
        }
 
        /// <summary>
        /// Функция удаляет значение из позиции index.
        /// </summary>
        public void Erase(int index)
        {
            if (index < 0 || index > len) return;
 
            bits.RemoveAt(index); len--;
        }
 
        /// <summary>
        /// Функция инвертирует все биты. '0' = '1'; '1' = '0'.
        /// </summary>
        public void InvertBits()
        {
            for (int i = 0; i < len; i++)
                if (bits[i].Equals(0)) bits[i] = 1;
                else bits[i] = 0;
        }
 
        /// <summary>
        /// Функция разворачивает "битовый массив" задом наперед.
        /// </summary>
        public void Reverse()
        {
            bits.Reverse();
        }
 
        /// <summary>
        /// Функция преобразует текущий "битовый массив" в строку.
        /// </summary>
        public string ToString()
        {
            string str = "";
 
            for (int i = 0; i < len; i++)
                str += bits[i].ToString();
 
            return str;
        }
 
        /// <summary>
        /// Функция производит циклический сдвиг "битового массива" влево.
        /// </summary>
        public void ToLeft()
        {
            object tmp;
 
            for (int i = 1; i < len - 1; i++)
            {
                tmp = bits[i + 1]; bits[i + 1] = bits[i]; bits[i] = tmp;
            }
 
            tmp = bits[0]; bits[0] = bits[len - 1]; bits[len - 1] = tmp;
        }
 
        /// <summary>
        /// Функция производит циклический сдвиг "битового массива" вправо.
        /// </summary>
        public void ToRight()
        {
            object tmp;
 
            tmp = bits[0]; bits[0] = bits[len - 1]; bits[len - 1] = tmp;
 
            for (int i = len - 2; i != 0; i--)
            {
                tmp = bits[i]; bits[i] = bits[i + 1]; bits[i + 1] = tmp;
            }
        }
 
        /// <summary>
        /// Функция производит перевод десятичного числа в двоичное представление.
        /// </summary>
        private void ToBits()
        {
            int temp = num;
 
            for (int i = 0; i < 8; i++)
            {
                bits.Add(temp % 2); temp = (int)Math.Floor((double)temp/2);
            }
 
            len = bits.Count; bits.Reverse();
        }
 
        /// <summary>
        /// Функция производит перевод из двоичного представления в десятичное.
        /// </summary>
        private void ToInt()
        {
            num = 0;
 
            for (int i = 0; i < len; i++)
                if (bits[len - i - 1].Equals(1))
                    num += (int)Math.Pow(2, i);
        }
    }


В принципе можно воспользоватся стандартным классом BitArray, но с ним неудобно работать.
И вот обратное дискретно-косинусное преобразование:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
        static double[,] odkp(double[,] one)
        {
            int n = one.GetLength(0);
            double[,] two = new double[n, n];
            double U, V, temp = 0;
            for (int v = 0; v < n; v++)
            {
                for (int u = 0; u < n; u++)
                {
                    temp = 0;
                    for (int i = 0; i < n; i++)
                    {
                        for (int j = 0; j < n; j++)
                        {
                            if (i == 0) V = 1.0 / Math.Sqrt(2);
                            else V = 1;
                            if (j == 0) U = 1.0 / Math.Sqrt(2);
                            else U = 1;
                            temp += U*V*one[i, j] * Math.Cos(Math.PI * i * (2 * v + 1) / (2 * n)) *
                                Math.Cos(Math.PI * j * (2 * u + 1) / (2 * n));
                        }
                    }
                    two[v, u] = temp / (Math.Sqrt(2 * n));
                }
            }
            return two;
        }
Главное не забывайте, что массивы ссылочный тип данных.
2
0 / 0 / 0
Регистрация: 28.04.2015
Сообщений: 9
02.05.2015, 14:45  [ТС]
Ev_Hyper
Ну, "альфа-версия" наверно готова. Пока сделал только скрытие. Блоки беру по порядку, в блоках беру коэффициенты с координатами (4;5) и (5;4). И меняю эти два коэффициента не одновременно, а как у Конаховича в М.48. Может если плавно менять, то искажения меньше будут.. Пока изображения выглядят вот так (пересохранил пэинтом bmp в jpeg, чтоб меньше весили. визуально то же самое):
Кликните здесь для просмотра всего текста

Завтра продолжу работу. Пока продвигается медленнее, чем я думал. Если бы не ваши алгоритмы разбиения на блоки и ДКП, то вобще б застрял...)
0
Заблокирован
02.05.2015, 14:57
Цитата Сообщение от 8Ball Посмотреть сообщение
Ну, "альфа-версия" наверно готова.
Если хотите можете прикрепить архивом проект, тогда я смогу проверить правильность реализации.

Цитата Сообщение от 8Ball Посмотреть сообщение
Пока сделал только скрытие.
Самый простой вариант извлечения, когда из изображения только достается информация, переписывается из скрытия практически один в один. Только не забывайте, что желательно встраивать или длину сообщения или редкий символ, который будет ограничивать полученную информацию. Иначе в конце извлеченного сообщения будет "мусор".

Цитата Сообщение от 8Ball Посмотреть сообщение
(пересохранил пэинтом bmp в jpeg, чтоб меньше весили. визуально то же самое):
В этом и суть метода Коха-Жао, т.к. скрытие идет в частотной области, то при конвертации в jpeg скрытая информация не должна исчезнуть, как это произойдет с методами встраивания в пространственную область.
1
0 / 0 / 0
Регистрация: 28.04.2015
Сообщений: 9
03.05.2015, 11:55  [ТС]
Ev_Hyper,
Кликните здесь для просмотра всего текста

У меня с извлечением проблема... Не знаю, где ошибка. Но выяснил, что измененные на этапе скрытия матрицы 8на8 коэффициентов ДКП не совпадают с матрицами коэффициентов ДКП, которые получаем из измененного изображения.
0
Заблокирован
03.05.2015, 12:49
8Ball, на первый взгляд ошибок нет. Разве что меня немного смущает эта часть:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
            Byte[,] B1 = C_new[0];
            for (int b = 2; b <= y / N; b++)
                B1 = stack(B1, C_new[b-1]);
            Byte[,] B_ = null;
            for (int b = y / N +1; b <= C_new.Count; b++)
            {
                if (B_ == null )
                    B_ = C_new[b-1];
                else
                    B_ = stack(B_, C_new[b-1]);
                if ((b % (y / N)) == 0)
                {
                    B1 = augment(B1, B_);
                    B_ = null;
                }
            }
и ссылочное присваивание массивов - где-то может измениться не то, что нужно. Вообщем за пару минут ошибку не найти, сегодня ближе к вечеру тщательно проверю алгоритм и код и напишу что удалось выяснить.


И небольшие замечания на будущее:
1. Желательно добавить нормировку:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
        static double[,] norm(double[,] one)
        {
            double min = Double.MaxValue, max = Double.MinValue;
 
            for (int i = 0; i < one.GetLength(0); i++)
                for (int j = 0; j < one.GetLength(1); j++)
                {
                    if (one[i, j] > max) max = one[i, j];
                    if (one[i, j] < min) min = one[i, j];
                }
 
            double[,] two = new double[one.GetLength(0), one.GetLength(1)];
            for (int i = 0; i < one.GetLength(0); i++)
                for (int j = 0; j < one.GetLength(1); j++)
                    two[i, j] = 255 * (one[i, j] + Math.Abs(min)) / (max + Math.Abs(min));
 
            return two;
        }
2. Вынести параметры метода как свойства
3. Проверять исходное изображение на то что высота/ширина кратна 8.
4. В качестве модификации можно подумать над первичным анализом изображения, чтобы определять в какую компоненту лучше всего встраивать информацию.
1
0 / 0 / 0
Регистрация: 28.04.2015
Сообщений: 9
03.05.2015, 14:38  [ТС]
Ev_Hyper, кажется я понял, где ошибка.. После того, как при скрытии мы меняем коэффициенты ДКП в матрицах 8на8, я взял одну матрицу и сделал с ней ДКП(ОДКП(матрица)). В итоге получились другие коэффициенты.

Вы привели метод ДКП, в который входит массив byte, а выходит массив double. А в приведенном вами примере ОДКП входит и выходит double, и я попытался изменить его так, чтобы он возвращал массив byte. И видимо с типами данных тут загвоздка (скорее всего я неправильно меняю тип данных в ОДКП)
0
Заблокирован
03.05.2015, 14:45
Цитата Сообщение от 8Ball Посмотреть сообщение
Вы привели метод ДКП, в который входит массив byte, а выходит массив double. А в приведенном вами примере ОДКП входит и выходит double, и я попытался изменить его так, чтобы он возвращал массив byte. И видимо с типами данных тут загвоздка (скорее всего я неправильно меняю тип данных в ОДКП)
8Ball, а его не нужно менять, оставьте double как у меня, но после сделайте нормализацию.
1
0 / 0 / 0
Регистрация: 28.04.2015
Сообщений: 9
03.05.2015, 15:27  [ТС]
Ev_Hyper,
Вот так получилось.

Может так и должно быть?...
С извлечением все равно что-то не то. Ладно, завтра буду разбираться дальше.
0
0 / 0 / 0
Регистрация: 28.04.2015
Сообщений: 9
03.05.2015, 15:35  [ТС]
В предыдущем сообщении это я применял нормализацию к каждой матрице 8на8. Ее же надо ко всему массиву применить? Если да, то вот так получается. Ну, я просто устал сегодня с этой программой, поэтому туплю наверно.
0
Заблокирован
04.05.2015, 02:57
Цитата Сообщение от 8Ball Посмотреть сообщение
Ее же надо ко всему массиву применить?
Да, ко всему.

Цитата Сообщение от 8Ball Посмотреть сообщение
Ну, я просто устал сегодня с этой программой, поэтому туплю наверно.
нужно перерывы делать

Добавлено через 11 часов 4 минуты
8Ball, вообщем мне удалось разобраться в чем была ошибка. Их было несколько:
1. Массивы ссылочный тип, поэтому просто писать "=" нельзя. Вот метод-костыль, можно использовать Array.Copy, то это уже в бета-версии:
C#
1
2
3
4
5
6
7
8
        static double[,] teta(double[,] one)
        {
            double[,] two = new double[one.GetLength(0), one.GetLength(1)];
            for (int i = 0; i < one.GetLength(0); i++)
                for (int j = 0; j < one.GetLength(1); j++)
                    two[i, j] = one[i, j];
            return two;
        }
2. В методе dkp_coeffs_change неправильно записаны if-условия, правильный вариант:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var temp = teta(one);
 
            double Abs1, Abs2;
            double z1 = 0, z2 = 0;
            Abs1 = Math.Abs(temp[u1, v1]);
            Abs2 = Math.Abs(temp[u2, v2]);
            if (temp[u1, v1] >= 0) z1 = 1;
            else z1 = -1;
            if (temp[u2, v2] >= 0) z2 = 1;
            else z2 = -1;
 
            if (i == 0)
            {
                if (Abs1 - Abs2 <= P)
                    Abs1 = P + Abs2 + 1;
            }
            if (i == 1)
            {
                if (Abs1 - Abs2 >= -P)
                    Abs2 = P + Abs1 + 1;
            }
            temp[u1, v1] = z1 * Abs1;
            temp[u2, v2] = z2 * Abs2;
            return temp;
3. Кое-где неправильные границы циклов. И само объединение мне показалось не самым удачным. Поэтому итоговоый метод встраивания:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public Bitmap Hide(string text, Bitmap bmp)
        {
            byte[] allbytes = Encoding.GetEncoding(1251).GetBytes(text);//1251 - кодировка. 
            
            int y = bmp.Height, x = bmp.Width;
            Byte[,] B = new Byte[x, y];
            Byte[,] R = new Byte[x, y];
            Byte[,] G = new Byte[x, y];
            //выбираем из картинки синюю компоненту RGB-модели
            for (int i = 0; i < x; i++)
                for (int j = 0; j < y; j++)
                {
                    B[i, j] = bmp.GetPixel(i, j).B; // массив значений синей компоненты исходного изображения
                    R[i, j] = bmp.GetPixel(i, j).R;
                    G[i, j] = bmp.GetPixel(i, j).G;
                }
 
            List<byte[,]> C = new List<byte[,]>(); //разбиваем массив В на матрицы 8 на 8
            int N = 8; //размерность сегментов
            int Nc = x * y / (N * N); //общей число сегментов
            if (Nc < 8 * allbytes.Length) throw new ArgumentException("Текст слшком велик");
            //разбиваем массив B на сегменты С
            int C1 = 0, C2 = N - 1;
            int r1, r2;
            for (int b = 0; b < Nc; b++)
            {
                r1 = N * b % x;
                r2 = r1 + N - 1;
                C.Add(submatrix(B, r1, r2, C1, C2));
                if (r2 == x - 1)
                {
                    C1 += N;
                    C2 += N;
                }
            }
            List<double[,]> dkp_matrix = new List<double[,]>(); //список матриц 8на8 коэффициентов дкп
            for (int i = 0; i < Nc; i++)
                dkp_matrix.Add(dkp(C[i]));
            
            int u1 = 3, v1 = 4, u2 = 4, v2 = 3;// координаты коэффициетов , которые меняем
            int P = 25; //порог изменения коэффициентов
            int a = 0;
 
            //Меняем коэффициенты ДКП в матрицах 8на8 и записываем в список матриц
            for (int m = 0; m < allbytes.Length; m++)
            {
                Bits bits = new Bits(allbytes[m]);
                for (int i = 0; i < bits.Length; i++)
                    dkp_matrix[a++] = dkp_coeffs_change(dkp_matrix[a], bits[i], u1, v1, u2, v2, P);
            }
            //обратное дкп, возвращает измененный лист сегментов 8на8
            List<double[,]> C_new = new List<double[,]>();
            for (int i = 0; i < dkp_matrix.Count; i++)
                C_new.Add(odkp(dkp_matrix[i]));
 
            //собираем сегменты в массив
            double[,] B1 = new double[x, y];
            r1 = -N; r2 = -1; C1 = 0; C2 = N - 1;
            for (int b = 0; b < Nc; b++)
            {
                r1 += N;
                r2 += N;
                B1 = unionmatrix(B1, C_new[b], r1, r2, C1, C2);
                if (r2 == x - 1)
                {
                    r1 = -N;
                    r2 = -1;
                    C1 += N;
                    C2 += N;
                }
            }
            B1 = norm(B1);
            for (int i = 0; i < x; i++)
                for (int j = 0; j < y; j++)
                    bmp.SetPixel(i, j, Color.FromArgb(R[i, j], G[i, j], (byte)Math.Round(B1[i, j]))); 
 
            return bmp;
        }
C#
1
2
3
4
5
6
7
        static double[,] unionmatrix(double[,] one, double[,] two, int a, int b, int c, int d)
        {
            for (int i = a, k = 0; i <= b; i++, k++)
                for (int j = c, l = 0; j <= d; j++, l++)
                    one[i, j] = two[k, l];
            return one;
        }
4. В классе Bits не просто так нет конструктора по умолчанию. Так что проще использовать более кривые методы. В итоге извлечение выглядит следующим образом:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
    public string Extract(Bitmap bmp)
        {
            //string FilePath;
 
            int y = bmp.Height, x = bmp.Width;
            Byte[,] B = new Byte[x, y];
            Byte[,] R = new Byte[x, y];
            Byte[,] G = new Byte[x, y];
            //выбираем из картинки синюю компоненту RGB-модели
            for (int i = 0; i < x; i++)
                for (int j = 0; j < y; j++)
                {
                    B[i, j] = bmp.GetPixel(i, j).B; // массив значений синей компоненты исходного изображения
                    R[i, j] = bmp.GetPixel(i, j).R;
                    G[i, j] = bmp.GetPixel(i, j).G;
                }
 
            List<byte[,]> C = new List<byte[,]>(); //разбиваем массив В на матрицы 8 на 8
            int N = 8; //размерность сегментов
            int Nc = x * y / (N * N); //общей число сегментов             
            //разбиваем массив B на сегменты С
            int C1 = 0, C2 = N - 1;
            int r1, r2;
            for (int b = 0; b < Nc; b++)
            {
                r1 = N * b % x;
                r2 = r1 + N - 1;
                C.Add(submatrix(B, r1, r2, C1, C2));
                if (r2 == x - 1)
                {
                    C1 += N;
                    C2 += N;
                }
            }
 
            List<double[,]> dkp_matrix = new List<double[,]>(); //список матриц 8на8 коэффициентов дкп
            for (int i = 0; i < Nc; i++)
                dkp_matrix.Add(dkp(C[i]));
 
            double[,] dkp_8x8; //матрица 8на8 для коэффициентов ДКП
            int u1 = 3, v1 = 4, u2 = 4, v2 = 3; // координаты коэффициетов , которые меняем
            double Abs1, Abs2;
            string bits = "";
            List<byte> bytetext = new List<byte>(); 
            char[] cc = new char[dkp_matrix.Count / 8];
            
            for (int k = 0; k < Nc; k++)
            {
                dkp_8x8 = teta(dkp_matrix[k]);
                Abs1 = Math.Abs(dkp_8x8[u1, v1]);
                Abs2 = Math.Abs(dkp_8x8[u2, v2]);
                if (Abs1 > Abs2)
                    bits += "0";
                if (Abs1 < Abs2)
                    bits += "1"; 
                if (bits.Length == 8)
                {
                    bytetext.Add(Convert.ToByte(new Bits(bits).Number));
                    bits = "";
                }               
            }
 
            return Encoding.GetEncoding(1251).GetString(bytetext.ToArray());
        }
После этих изменений извлекает информацию, но в конце, что естественно много лишнего.
Так что теперь нужно привести код в порядок:
1. Вынести параметры метода как свойства класса
2. Проверять исходное изображение на то что высота/ширина кратна 8.
3. Встраивать или длину текст или спец. символ для обрезки.
4. В качестве модификации можно подумать над первичным анализом изображения, чтобы определять в какую компоненту лучше всего встраивать информацию.
5. Добавить progressBar
6. Возможно поместить контролы в лэйаутпанель, для красивого изменения при масштабировании
7. Провести непосредственно рефакторинг кода.
3
0 / 0 / 0
Регистрация: 28.04.2015
Сообщений: 9
04.05.2015, 15:57  [ТС]
Ev_Hyper, спасибо, все работает!
1. Первый пункт не совсем понял. Вы имеете ввиду, создать переменные в классе и использовать get и set? И в метод hide и extract никаких параметров не передавать?
2. Я сделал так: область, которую меняем, ограничил ближайшими размерами к размерам изображения, которые делятся на 8. Если размеры сами делятся на 8, значит их оставляем. Примерно так:
C#
1
2
3
4
5
6
7
8
9
int toSize(int a)
        {
            for (int i = 0; i < 8; i++)
                if ((a - i) % 8 == 0)
                {
                    a = a - i; break;
                }
            return a;
        }
Можно же так сделать?
3. Да, сделал символом, работает!
4. Еще сегодня сделал выбор блоков для встраивания (в Конаховиче это дальше описано) сделал скрытие и извлечение, вроде работает. Над тем, что вы предлагаете, завтра подумаю, идея хорошая.
5. Это да, нужная вещь, иначе некрасиво будет заторможенность выглядеть.
6. А что такое лэйаутпанел? Сейчас начал искать про масштабирование, похоже это не такая уж простая задача, но кое-что нашел по этому поводу. Буду пробовать.
7. Да, в коде трудно ориентироваться, несмотря на то, что он небольшой. Еще у меня есть мысль сделать интерфейс "стеганография", от него наследовать Коха и от Коха наследовать его модификацию. Но для этого нужно сначала провести тот самый рефакторинг. Вобщем пока не ясно, как это сделать.
Буду думать!
0
Заблокирован
04.05.2015, 17:26
Цитата Сообщение от 8Ball Посмотреть сообщение
1. Вы имеете ввиду, создать переменные в классе и использовать get и set? И в метод hide и extract никаких параметров не передавать?
Совершенно верно
C#
1
2
3
4
5
6
7
8
 
       // координаты коэффициетов , которые меняем
        public int v1 { get; set; }
        public int v2 { get; set; }
        public int u1 { get; set; }
        public int u2 { get; set; }
        //порог изменения коэффициентов
        public int P { get; set; }
Т.е. при желании можно легко добавить возможность задавать эти параметры пользователю через форму.

Также можно вынести некоторые важные переменные как поля:
C#
1
2
3
        int N, Nc, x, y;
        Bitmap bmp;
        byte[] allbytes;
тогда нужно будет создать конструкторы:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
                public KochSteg(Bitmap bmp)
        {
            u1 = 3; v1 = 4; u2 = 4; v2 = 3;
            P = 25;
            this.bmp = bmp;
            y = bmp.Height;
            x = bmp.Width;
            N = 8; //размерность сегментов
            Nc = x * y / (N * N); //общей число сегментов
            y = bmp.Height;
            x = bmp.Width;
        }
        public KochSteg(Bitmap bmp, string text):this(bmp)
        {
            byte[] allbytes = Encoding.GetEncoding(1251).GetBytes(text);
            if (Nc < 8 * allbytes.Length) throw new ArgumentException("Текст слшком велик");
        }
Цитата Сообщение от 8Ball Посмотреть сообщение
Можно же так сделать?
Мысль верная, а вот реализацию можно сделать проще:
C#
1
2
                if (x % 8 != 0) x -= x % 8;
                if (y % 8 != 0) y -= y % 8;
Цитата Сообщение от 8Ball Посмотреть сообщение
7. Да, в коде трудно ориентироваться, несмотря на то, что он небольшой. Еще у меня есть мысль сделать интерфейс "стеганография", от него наследовать Коха и от Коха наследовать его модификацию
Я не люблю ООП, так что польза от интерфейса мне кажется сомнительной. Но, возможно, так действительно будет лучше. Попробуйте, потом сообщите

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

и вот эту часть нужно переписать:
C#
1
2
3
4
5
6
7
            for (int i = 0; i < x; i++)
                for (int j = 0; j < y; j++)
                {
                    B[i, j] = bmp.GetPixel(i, j).B; // массив значений синей компоненты исходного изображения
                    R[i, j] = bmp.GetPixel(i, j).R;
                    G[i, j] = bmp.GetPixel(i, j).G;
                }
GetPixel и так медленный, а вы его вызываете целых 3 раза. Лучше считать в одну переменную и уже с ней работать.
0
2 / 2 / 1
Регистрация: 09.06.2012
Сообщений: 29
23.05.2015, 23:14
Здравствуйте, а не могли бы вы подсказать, каким образом можно улучшить алгоритм Коха-Жао, для снижения уровня вносимых визуальных искажений? Кроме улучшений: выбора наиболее подходящих блоков для встраивания или добавление третьего коэффициента ДКП.
Спасибо.
0
Заблокирован
23.05.2015, 23:56
Цитата Сообщение от numberone Посмотреть сообщение
а не могли бы вы подсказать, каким образом можно улучшить алгоритм Коха-Жао, для снижения уровня вносимых визуальных искажений?
numberone, попробуйте провести первичный анализ изображения, чтобы определить в какую компоненту лучше производить встраивание.
Я так понимаю раз искажение значительное, значит объем информации существенный?
Если вы уверены, что изображение будет "в целости", то можно попробывать сжать данные, чтобы уменьшить объем.
P.S. А с чем связан выбор именно метода Коха-Жао?
0
2 / 2 / 1
Регистрация: 09.06.2012
Сообщений: 29
24.05.2015, 01:15
попробуйте провести первичный анализ изображения, чтобы определить в какую компоненту лучше производить встраивание.
Если это сделать, то количество встраиваемой информации уменьшится, значительно усложнится алгоритм.
А мне, наоборот хотелось бы даже увеличить количество встраиваемой информации, пусть это даже несколько увеличит искажения, или сложность алгоритма.
Вот думаю, как можно это сделать например записывать два бита в блок, а не один.
Может быть изменить размер блока с 8х8 на 10х10 например. Не знаю, что из этого получится.
Я так понимаю раз искажение значительное, значит объем информации существенный?
Если вы уверены, что изображение будет "в целости", то можно попробывать сжать данные, чтобы уменьшить объем.
Искажение не такое уж значительное, видимо я зря употребил слово "визуальных". На глаз они не видны. Имелось ввиду, так улучшить алгоритм, чтобы увеличить отношение "сигнал/шум" выходного к входному изображению.
P.S. А с чем связан выбор именно метода Коха-Жао?
Метод Коха-Жао был выбран только потому, что он самый популярный из позволяющих производить компрессию.

В общем цель именно усовершенствовать алгоритм Коха-Жао (т.е. уменьшить вероятность успешного стегоанализа, при этом значительно те теряя пропускную способность алгоритма(а лучше ее увеличить еще).
Интересуют вопросы, как будет влиять изменение размерности блоков для ДКП? изменение количества коэффициентов? количество встраиваемых бит в блок? может перевести в другое цветовое пространство? может еще что-то?
спасибо
0
Заблокирован
24.05.2015, 08:47
Цитата Сообщение от numberone Посмотреть сообщение
Интересуют вопросы, как будет влиять изменение размерности блоков для ДКП?
В стандарте jpeg используются блоки 8 на 8.

Цитата Сообщение от numberone Посмотреть сообщение
изменение количества коэффициентов? количество встраиваемых бит в блок? может перевести в другое цветовое пространство? может еще что-то?
логично предположить, что:
1. Объем встраивания возрастет, визуальное искажение увеличиться
2. Так ведь тут есть зависимость от предыдущего вопроса, разве нет?
3. Можно перевести, но выигрыша от объема не будет.

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

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

Не по теме:

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

1
2 / 2 / 1
Регистрация: 09.06.2012
Сообщений: 29
24.05.2015, 23:07
спасибо, буду проверять

Добавлено через 10 часов 40 минут
В результате, проведенных тестов выяснил следующее:

Влияние различных факторов на отношение искажений контейнера сигнал/шум

1) изменение цветовой модели перед ДКП
значительно ухудшило результат отношения сигнал/шум

(по пункту 1)
Хотя, читал, что это должно улучшить отношение сигнал/шум

2) встраивание в высокочастотную область спектра
незначительно улучшило результат

3) встраивание в низкочастотную область спектра
значительно ухудшило результат

(по пунктам 2 и 3)
Кстати в какой-то статье читал, что наоборот писали, что встраивание в низкочастотную область улучшает отношение сигнал/шум, исследование показали обратное
Но наверное, уменьшится устойчивость к компрессии

4) увеличение блока до размерности 10х10
ухудшило результат

5) увеличение блока до размерности 16х16
ухудшило результат
(по пунктам 4 и 5)
Короче здесь тоже читал, что это улучшает результат, но он ухудшился

6) уменьшение блока до размерности 6х6
незначительно улучшило результат

7) уменьшение блока до размерности 4х4
незначительно улучшило результат, лучше чем 6х6
(по пунктам 6 и 7)
Но наверное, уменьшится устойчивость к компрессии

8) встраивание 2 бит в блок в среднечастотную область
улучшило результат
9) встраивание 2 бит в блок в срч и всч область
улучшило результат
(по пунктам 8 и 9)
странно
Но наверное, уменьшится устойчивость к компрессии

10) 3 коэффициента
значительно улучшило результаты
11) выбор подходящих блоков + 3 коэффициента
улучшило, но в 1,5 раза меньше, чем в предыдущем случае
(по пунктам 10 и 11)
единственное, что совпало теория и практика, правда опять странность, что при выборке наиболее подходящих блоков результат хуже чем просто способ с тремя коэффициентами

12) 2 бита по 3 коэффициента
результаты значительно улучшились
Но наверное, уменьшится устойчивость к компрессии

похоже это и будет моим улучшением алгоритма
плюсы по сравнению с алгоритмом Коха Жао
пропускная способность увеличивается вдвое
уменьшение успешного стегоанализа, за счет значительного улучшения отношения сигнал/шум
минусы
увеличена сложность алгоритма
нужно проверить ослабилась ли и насколько устойчивость к компрессии

Жду комментарии, уважаемого Ev_Hyper
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
24.05.2015, 23:07
Помогаю со студенческими работами здесь

Построение Анти-Снежинки Коха (Снежинка Коха внутрь) на языке Pascal.
Нужно построить антиснежинку Коха. Имеется код построения обычной снежинки Коха через линии треугольника. Кто-нибудь может помочь? ...

Стеганография. Написать метод для расшифровывания текста из картинки bmp
Помогите написать метод для расшифрования текста из картинки bmp

Стеганография: как реализовать метод избыточных пробелов сокрытия информации в текстовом файле?
Как реализовать метод избыточных пробелов сокрытия информации в текстовом файле? Не знаю, с чего начать, что использовать.

Снежинка Коха
Используя графические возможности рекурсии, постройте на экране снежинку Коха

Снежинка Коха
Задание заключается в том, чтобы построить снежинку с помощью рекурсии. Есть замечательная программа на Паскале. Вот код program...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Реализация движения на Box2D v3 - трение и коллизии с повёрнутыми стенами
8Observer8 20.02.2026
Содержание блога Box2D позволяет легко создать главного героя, который не проходит сквозь стены и перемещается с заданным трением о препятствия, которые можно располагать под углом, как верхнее. . .
Конвертировать закладки radiotray-ng в m3u-плейлист
damix 19.02.2026
Это можно сделать скриптом для PowerShell. Использование . \СonvertRadiotrayToM3U. ps1 <path_to_bookmarks. json> Рядом с файлом bookmarks. json появится файл bookmarks. m3u с результатом. # Check if. . .
Семь CDC на одном интерфейсе: 5 U[S]ARTов, 1 CAN и 1 SSI
Eddy_Em 18.02.2026
Постепенно допиливаю свою "многоинтерфейсную плату". Выглядит вот так: https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11617&stc=1&d=1771445347 Основана на STM32F303RBT6. На борту пять. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru