private const int BitsPerByte = 8; // Количество битов в байте #endregion Fields #region Methods /// <summary> /// Применение метода широкополосного сигнала /// Внедрение данных в графический файл /// </summary> /// <param name="bbsOptions">Параметры алгоритма включая исходные данные</param> /// <returns>Графический файл с внедрёнными данными</returns> public static CvBitmap Pack(BbsOptions bbsOptions) { Debug.WriteLine(bbsOptions.ToString()); string key = bbsOptions.Key; int expandSize = bbsOptions.ExpandSize; int codeSize = bbsOptions.EccCodeSize; int dataSize = bbsOptions.EccDataSize; int alpha = bbsOptions.Alpha; int betta = 0; bool autoAlpha = bbsOptions.AutoAlpha; bool autoResize = bbsOptions.AutoResize; bool maximumGamma = bbsOptions.MaximumGamma; int politicIndex = bbsOptions.PoliticIndex; string politicText = bbsOptions.PoliticText; int eccIndex = bbsOptions.EccIndex; int mixerIndex = bbsOptions.MixerIndex; int gammaIndex = bbsOptions.GammaIndex; int archiverIndex = bbsOptions.ArchiverIndex; int barcodeIndex = bbsOptions.BarcodeIndex; int filterStep = bbsOptions.FilterStep; CvBitmap sampleBitmap = bbsOptions.SampleBitmap; Size minSize = sampleBitmap.Size; CvBitmap barcodeBitmap = null; try { using (var barcode = new Barcode(barcodeIndex) { ArchiverIndex = archiverIndex, EccIndex = eccIndex, MixerIndex = mixerIndex, GammaIndex = gammaIndex, ExpandSize = expandSize, EccCodeSize = codeSize, EccDataSize = dataSize, MaximumGamma = maximumGamma, Key = key, }) { // Формирование баркода с параметрами для используемых алгоритмов barcodeBitmap = new CvBitmap(barcode.Encode()); Size barcodeSize = barcodeBitmap.Size; minSize.Width = Math.Max(minSize.Width, 4*barcodeSize.Width); minSize.Height = Math.Max(minSize.Height, 4*barcodeSize.Height); } } catch (ArgumentNullException exception) { } byte[] bytes = Encoding.Default.GetBytes(bbsOptions.RtfText); Debug.WriteLine(string.Join("", bytes.Select(x => x.ToString("X02")))); // В дополнение к самому методу широкополосного сигнала данные могут быть сжаты алгоритмом компрессии данных, // добавлены коды исправления ошибок, последовательности бит могут размещаться в изображении не последовательно, а в // соответствии с выбранным алгоритмом. IStreamTransform[] streamTransforms = { new Archiver(archiverIndex), // Алгоритм сжатия данных new Envelope(), // Добавление конверта new Ecc(eccIndex, codeSize, dataSize) // Алгоритм коррекции ошибок }; var input = new MemoryStream(bytes); Debug.WriteLine("input {0}", input.Length); foreach (IStreamTransform transform in streamTransforms) { using (MemoryStream prev = input) transform.Forward(prev, input = new MemoryStream()); input.Seek(0, SeekOrigin.Begin); Debug.WriteLine("{0} {1}", transform, input.Length); } // для каждого бита сообщения нужно N байт носителя long inputLength = input.Length; // Количество байт передаваемых данных long requiredLength = inputLength*expandSize*BitsPerByte; // Требуемое число пикселей Size sampleSize = sampleBitmap.Size; long sampleLength = sampleBitmap.Length; double ratio = Math.Sqrt(1+(double) requiredLength/sampleLength); ratio = Math.Max(ratio, (double) minSize.Width/sampleSize.Width); ratio = Math.Max(ratio, (double) minSize.Height/sampleSize.Height); minSize.Width = (int) Math.Max(minSize.Width, Math.Ceiling(ratio*sampleSize.Width)); minSize.Height = (int) Math.Max(minSize.Height, Math.Ceiling(ratio*sampleSize.Height)); CvBitmap bitmap; using (var stretchBuilder = new StretchBuilder(minSize)) bitmap = new CvBitmap(sampleBitmap, stretchBuilder, autoResize); long length = bitmap.Length; Size size = bitmap.Size; if (requiredLength > length) throw new Exception( string.Format("Размер изображения недостаточен для сохранения данных {0}/{1}", requiredLength, sampleLength)); if (minSize.Width > size.Width || minSize.Height > size.Height) throw new Exception( string.Format( "Размер изображения недостаточен для сохранения данных {0}x{1}/{2}x{3}", size.Width, size.Height, minSize.Width, minSize.Height)); // Внедрения в передаваемое изображение баркода с настроечными параметрами для используемых алгоритмов if (barcodeBitmap != null) bitmap.DrawCopyright(barcodeBitmap); using (IStreamTransform streamTransform = new Politic(politicIndex, politicText, expandSize, bitmap)) using (MemoryStream prev = input) streamTransform.Forward(prev, input = new MemoryStream()); input.Seek(0, SeekOrigin.Begin); Debug.WriteLine("input {0}", input.Length); using (var reader = new BinaryReader(input)) { byte[] data = reader.ReadBytes((int) input.Length); Debug.WriteLine(string.Join("", data.Select(x => x.ToString("X02")))); var index = new int[length]; var colors = new byte[length]; var cw = new byte[length]; var gamma = new byte[maximumGamma ? ((length + BitsPerByte - 1)/BitsPerByte) : ((expandSize + BitsPerByte - 1)/BitsPerByte)]; using (var builder = new Mixer(mixerIndex, key)) builder.GetInts(index); using (var builder = new Gamma(gammaIndex, key)) builder.GetBytes(gamma); bitmap.Select(index, colors); if (autoAlpha) { using (var blurBuilder = new BlurBuilder(filterStep)) using (var median = new CvBitmap(bitmap, blurBuilder)) { if (barcodeBitmap != null) median.DrawCopyright(barcodeBitmap); var medianColors = new byte[length]; median.Select(index, medianColors); double e1 = colors.Zip(medianColors, (x, y) => (int) x - (int) y).Average(x => (double) x); double e2 = colors.Zip(medianColors, (x, y) => (int) x - (int) y).Average(x => (double) x*x); alpha = (int) Math.Sqrt(e2 - e1*e1); bbsOptions.Alpha = alpha; // Вычисление оценки смещение яркости относительно средней яркости исходного изображения при прямом и дословном применении алгоритма // Построение массивов, содержащих статистическую информацию о исходных данных и псевдослучайной последовательности // То есть построение гистограмм исходных данных и псевдослучайной последовательности var countData = new int[256]; var countGamma = new int[256]; foreach (byte ch in data) countData[ch]++; foreach (byte ch in gamma) countGamma[ch]++; // Построение массива, где каждый элемент содержит количество ненулевых бит в бинарном разложении индекса элемента var count = new int[256]; count[0] = 0; for (int k = 1; k < 256; k <<= 1) for (int i = 0; i < k; i++) count[k + i] = count[i] + 1; // Использование псевдослучайной последовательности с характеристиками приближенными к равновероятной, для кодирования // данных, позволяет сохранить среднюю яркость пикселей у исходного графического изображения и у изображения, // содержащего внедрённые данные // Однако при прямом и дословном применении алгоритма средняя яркость пикселей могла бы иметь смещение относительно средней яркости у исходного изображения // Поэтому производим статистическую оценку такого смещения и вводим её в качестве компенсирующего слагаемого в алгоритм // Вычисление количества единиц в исходных данных и псевдослучайной последовательности double trueData = count.Zip(countData, (x, y) => (double) ((long) x*y)).Sum(); double trueGamma = count.Zip(countGamma, (x, y) => (double) ((long) x*y)).Sum(); // Вычисление количества нулей в исходных данных и псевдослучайной последовательности double falseData = (long) data.Length*BitsPerByte - trueData; double falseGamma = (long) gamma.Length*BitsPerByte - trueGamma; // Вычисление оценки количества единиц и нулей при смешивании исходных данных и псевдослучайной последовательности double trueCount = trueGamma*falseData + falseGamma*trueData; double falseCount = trueGamma*trueData + falseGamma*falseData; betta = (int) ((falseCount - trueCount)*alpha/(trueCount + falseCount)); bbsOptions.Alpha = alpha; } } using (var bbSignals = new BbSignals(expandSize, maximumGamma)) bbSignals.Combine(colors, data, gamma, alpha, betta, cw); bitmap.Replace(index, cw); return bbsOptions.OutputBitmap = bitmap; } }
/// <summary> /// Применение метода широкополосного сигнала /// Извлечение данных из графического файла /// </summary> /// <param name="bbsOptions">Параметры алгоритма включая графический файл с внедрёнными данными</param> /// <returns>Извлечённые данные</returns> public static string Unpack(BbsOptions bbsOptions) { Debug.WriteLine(bbsOptions.ToString()); string key = bbsOptions.Key; int expandSize = bbsOptions.ExpandSize; int codeSize = bbsOptions.EccCodeSize; int dataSize = bbsOptions.EccDataSize; int filterStep = bbsOptions.FilterStep; int eccIndex = bbsOptions.EccIndex; int mixerIndex = bbsOptions.MixerIndex; int gammaIndex = bbsOptions.GammaIndex; int archiverIndex = bbsOptions.ArchiverIndex; int alpha = bbsOptions.Alpha; int betta = 0; bool autoAlpha = bbsOptions.AutoAlpha; bool maximumGamma = bbsOptions.MaximumGamma; bool extractBarcode = bbsOptions.ExtractBarcode; CvBitmap bitmap = bbsOptions.InputBitmap; if (extractBarcode) using (var barcode = new Barcode(bitmap.Bitmap)) { // Извлечение параметров из внедрённого в изображение баркода // и использование этих извлечённых параметров для используемых алгоритмов barcode.Decode(); archiverIndex = barcode.ArchiverIndex; eccIndex = barcode.EccIndex; mixerIndex = barcode.MixerIndex; gammaIndex = barcode.GammaIndex; expandSize = barcode.ExpandSize; codeSize = barcode.EccCodeSize; dataSize = barcode.EccDataSize; maximumGamma = barcode.MaximumGamma; key = barcode.Key; } using (var builder = new BlurBuilder(filterStep)) bbsOptions.MedianBitmap = new CvBitmap(bitmap, builder); CvBitmap median = bbsOptions.MedianBitmap; long length = bitmap.Length; var index = new int[length]; var colors = new byte[length]; var medianColors = new byte[length]; var data = new byte[length/expandSize/BitsPerByte]; using (var builder = new Mixer(mixerIndex, key)) builder.GetInts(index); var gamma = new byte[maximumGamma ? ((length + BitsPerByte - 1)/BitsPerByte) : ((expandSize + BitsPerByte - 1)/BitsPerByte)]; using (var builder = new Gamma(gammaIndex, key)) builder.GetBytes(gamma); bitmap.Select(index, colors); median.Select(index, medianColors); if (autoAlpha) { double e1 = colors.Zip(medianColors, (x, y) => (int) x - (int) y).Average(x => (double) x); double e2 = colors.Zip(medianColors, (x, y) => (int) x - (int) y).Average(x => (double) x*x); betta = (int) e1; alpha = (int) Math.Sqrt(e2 - e1*e1); bbsOptions.Alpha = alpha; } using (var bbSignals = new BbSignals(expandSize, maximumGamma)) bbSignals.Extract(colors, medianColors, gamma, alpha, betta, data); Debug.WriteLine(string.Join("", data.Select(x => x.ToString("X02")))); // В дополнение к самому методу широкополосного сигнала данные могут быть сжаты алгоритмом компрессии данных, // добавлены коды исправления ошибок, последовательности бит могут размещаться в изображении не последовательно, а в // соответствии с выбранным алгоритмом. IStreamTransform[] streamTransforms = { new Ecc(eccIndex, codeSize, dataSize), // Алгоритм коррекции ошибок new Envelope(), // Извлечение из конверта new Archiver(archiverIndex) // Алгоритм извлечения из сжатого архива }; var input = new MemoryStream(data); Debug.WriteLine("input {0}", input.Length); foreach (IStreamTransform transform in streamTransforms) { using (MemoryStream prev = input) transform.Backward(prev, input = new MemoryStream()); input.Seek(0, SeekOrigin.Begin); Debug.WriteLine("{0} {1}", transform, input.Length); } using (var reader = new BinaryReader(input)) { byte[] bytes = reader.ReadBytes((int) input.Length); Debug.WriteLine(string.Join("", bytes.Select(x => x.ToString("X02")))); return bbsOptions.RtfText = Encoding.Default.GetString(bytes); } }