//статическая функция для замены НЗБ (внедрения текста в изображение путем замены битов) public static StegoBitmap Hide(StegoBitmap bitmap, string text, Colours colour) { byte[] sourceColour = bitmap.GetColour(colour); // массив байтов указанного цвета для данного StegoBitmap byte[] stgText = Encoding.GetEncoding(1251).GetBytes(text); // массив байтов введеных символов byte[] len = CommonFunc.LenInBytes(text.Length, CommonFunc.Size(text.Length)); // массив байтов из длины текста в соответвии с размером его длины byte[] stgBytes = new byte[2 + len.Length + stgText.Length]; //массив для скрытого текста размером в длину текста + размер длины + два дополнительных байта stgBytes[0] = Convert.ToByte('L'); // первый элемент массива содержит пометку, что в изображении скрыт текст stgBytes[1] = CommonFunc.Size(text.Length); // второй элемент массива размер длины int ind = 0; for (int i = 0; i < len.Length; i++) { stgBytes[i + 2] = len[ind++]; // сохраняем в массив байты из длины текста в соответвии с размером его длины } ind = 0; for (int i = 0; i < stgText.Length; i++) { stgBytes[i + 2 + len.Length] = stgText[ind++]; // сохраняем в массив скрываемый текст } // булевский массив из массива байтов bool[] stgBoolBits = stgBytes.SelectMany(e => CommonFunc.ByteBoolArr(e)).ToArray(); // SelectMany используется для создания выходной последовательности с проекцией "один ко многим" из входной последовательности. Select вернет один выходной элемент для каждого входного элемента, SelectMany - ноль или более выходных элементов для каждого входного // проходим по элементам булевского массива текста. если бит текста true и байт цвета четен, то увеличиваем его на 1, в обратной ситуации - уменьшаем на 1, в остальный случаях байт цвета остается не изменным (0 и 0 => 0, 1 и 1 => 1, 0 и 1 => 1, 1 и 0 => 0) for (int i = 0; i < stgBoolBits.Length; i++) { if ((sourceColour[i] % 2 == 0) && stgBoolBits[i]) { sourceColour[i]++; } else if ((sourceColour[i] % 2 == 1) && !stgBoolBits[i]) { sourceColour[i]--; } } return(new StegoBitmap(bitmap, sourceColour, colour)); }
// внедрение текста private static void SetText(byte[] txt, ref List <double[, ]> DCT, int coefDif) { List <int> freePos = new List <int>(); // свободные позиции в соответствии с размером списка коэффициентов ДКП for (int i = 0; i < DCT.Count; i++) { freePos.Add(i); } for (int i = 0; i < txt.Length; i++) { bool[] bitsSymb = CommonFunc.ByteBoolArr(txt[i]); //перевод байтого символа текста в булевый массив for (int j = 0; j < 8; j++) { bool currentBit = bitsSymb[j]; int pos = freePos[0]; // позиция freePos.RemoveAt(0); // берем значения коэффициентов ДКП по модулю double AbsP1 = Math.Abs(DCT[pos][p1.X, p1.Y]); double AbsP2 = Math.Abs(DCT[pos][p2.X, p2.Y]); int z1 = 1, z2 = 1; // переменные для сохранения знака первичных значений коэффициентов ДКП по модулю if (DCT[pos][p1.X, p1.Y] < 0) { z1 = -1; } if (DCT[pos][p2.X, p2.Y] < 0) { z2 = -1; } if (currentBit) //для передачи бита "1" стремяться, чтобы разница абсолютных значений коэффициентов ДКП была меньше по сравнению с некоторой отрицательной величиной { if (AbsP1 - AbsP2 >= -coefDif) { AbsP2 = coefDif + AbsP1 + 1; } } else //для передачи бита "0" стремятся, чтобы разница абсолютных значений коэффициентов ДКП превышала некоторую положительную величину { if (AbsP1 - AbsP2 <= coefDif) { AbsP1 = coefDif + AbsP2 + 1; } } // присваиваем коэффициентам ДКП новые значения DCT[pos][p1.X, p1.Y] = z1 * AbsP1; DCT[pos][p2.X, p2.Y] = z2 * AbsP2; } } }
// внедрение текста private static void SetText(byte[] txt, ref List <double[, ]> blocks, int coefDif) { List <int> freePos = new List <int>(); // свободные позиции в соответствии с размером списка коэффициентов ДКП for (int i = 0; i < blocks.Count; i++) { freePos.Add(i); } for (int i = 0; i < txt.Length; i++) { bool[] bitsSymb = CommonFunc.ByteBoolArr(txt[i]); //перевод байтого символа текста в булевый массив for (int j = 0; j < 8; j++) { bool currentBit = bitsSymb[j]; int pos = freePos[0]; // позиция freePos.RemoveAt(0); // берем значения коэффициентов ДКП в точках double P1 = blocks[pos][p1.X, p1.Y]; double P2 = blocks[pos][p2.X, p2.Y]; double P3 = blocks[pos][p3.X, p3.Y]; bool HideBit = false; if (ValidMoonotony(blocks[pos]) && ValidSharpness(blocks[pos])) { if (currentBit) //для передачи бита "1" стремятся, чтобы третий коэффициент стал большим по сравнению с первым и вторым коэффициентами { if ((Math.Max(P1, P2) - P3) > 0.001 || Math.Abs(P3 - Math.Max(P1, P2)) < 0.001) { P3 = Math.Max(P1, P2) + coefDif / 2; if (P1 > P2) { P1 -= coefDif / 2; } else { P2 -= coefDif / 2; } } } else //для передачи бита "0" стремятся, чтобы третий коэффициент стал меньше любого из первых двух { if ((P3 - Math.Min(P1, P2)) > 0.001 || Math.Abs(P3 - Math.Min(P1, P2)) < 0.001) { P3 = Math.Min(P1, P2) - coefDif / 2; if (P1 < P2) { P1 += coefDif / 2; } else { P2 += coefDif / 2; } } } HideBit = true; } //присваиваем коэффициентам ДКП новые значения blocks[pos][p1.X, p1.Y] = P1; blocks[pos][p2.X, p2.Y] = P2; blocks[pos][p3.X, p3.Y] = P3; if (HideBit && TestValidInver(blocks[pos])) { j++; } j--; } } }
// функция превращает последний бит байта в булевую переменную private static bool[] LastBit(byte b, int num) { var arr = CommonFunc.ByteBoolArr(b); // переводим массив байтов в булев return(arr.Skip(8 - num).Take(num).ToArray()); // берем только последний элемент массива (отвечающий за последний бит) }