public static byte[] GetWaterMarkDct(Bitmap stegoImage, int bitIndex, ChannelType channelType = ChannelType.Y) { var imageColorContent = ImageHelpers.ImageToArray(stegoImage); int currentBitIndex = 0; var byteList = new List<Byte>(); byte currentByte = 0; QuantizationTableType quantizationType = channelType == ChannelType.Y ? QuantizationTableType.Luminance : QuantizationTableType.Chrominance; var zigzagAccessor = new ZigZagAccessorFactory<double>(8, 8); var imageBlocks = MathUtilities.Split2DArrayToBlocks(imageColorContent, 8); var quantizationTable = JpegUtilitites.GetQuantizationTable(stegoImage, quantizationType); bool headerLoaded = false; int activeContentLength = imageBlocks.Count*8; int byteAppended=0; for (int index = 0; index < imageBlocks.Count; index++) { _debugDecodeStringBuilder.AppendLine("Appending byte:" + (byteAppended +1)); var imageBlock = imageBlocks[index]; bool isEven = GetWatermarkDctForBlock(imageBlock, bitIndex, zigzagAccessor, quantizationTable, channelType, true); MathUtilities.AddBit(ref currentByte, !isEven); if (currentBitIndex%8 == 7) { byteList.Add(currentByte); byteAppended++; if (!headerLoaded && byteList.Count == 2) { activeContentLength = BitConverter.ToUInt16(byteList.ToArray(), 0); headerLoaded = true; byteList.Clear(); } currentByte = new byte(); } currentBitIndex++; if (byteList.Count == activeContentLength) { break; } } File.WriteAllText(_debugDecodeTextFile, _debugDecodeStringBuilder.ToString()); return byteList.ToArray(); }
public static bool GetWatermarkDctForBlock(MatrixBase<Color> block, int bitIndex, ZigZagAccessorFactory<double> zigzagAccessorFactory, Double[,] quantizationTable, ChannelType channelType, bool log = false) { var yCbCrColorBlock = block.ToYCbCr(); var yChannel = yCbCrColorBlock.GetChannel(channelType); var shiftetBlock = yChannel - 128; var dctBlockTransform = (DiscreteCosineTransform.ForwardDct8Block(shiftetBlock).CrossDivide(quantizationTable)).Round(); if (log) { _debugDecodeStringBuilder.AppendLine(dctBlockTransform.ToString()); } var accessor = zigzagAccessorFactory.Access(dctBlockTransform); var coefficient = accessor[bitIndex]; var curentElelement = (int) Math.Round(coefficient); bool isEven = curentElelement%2 == 0; return isEven; }
private static MatrixBase<Color> WatermarkDctBlock(MatrixBase<Color> block, ZigZagAccessorFactory<Double> accessorFactory, double[,] quantizationTable, bool canEncrypt, byte[] secretMessage, ref int embeddingSecretMessageBitIndex, int byteIndexForEncryption, ChannelType channelType) { if (!canEncrypt) { return block; } var byteIndex = embeddingSecretMessageBitIndex / 8; if (embeddingSecretMessageBitIndex % 8 == 0) { byteIndex = embeddingSecretMessageBitIndex / 8; _debugEncodeStringBuilder.AppendLine("Appending byte:" + (byteIndex + 1)); } var correction = 1; bool embeddCorrectly; MatrixBase<Color> ret; //#region Convert block var yCbCrColorBlock = block.ToYCbCr(); var channel = yCbCrColorBlock.GetChannel(channelType); channel = channel - 128; DoubleMatrix dctBlockTransform = null; //#endregion do { // For luminance channel yMax = 127, ymin = -128 DoubleMatrix stegoBlock; bool redo = false; var nextSecretBit = MathUtilities.GetBit(secretMessage, embeddingSecretMessageBitIndex); bool changed = false; dctBlockTransform = DiscreteCosineTransform.ForwardDct8Block(channel); dctBlockTransform = (dctBlockTransform.CrossDivide(quantizationTable)); dctBlockTransform = dctBlockTransform.Round(); var accessor = accessorFactory.Access(dctBlockTransform); var curentElelement = accessor[byteIndexForEncryption]; bool isEven = ((int) Math.Round(curentElelement))%2 == 0; double possibleValue = curentElelement; DoubleMatrix oldStegoBlock = null; do { if ((isEven && nextSecretBit) || (!isEven && !nextSecretBit)) { if (!redo) { possibleValue = isEven ? curentElelement - correction : curentElelement + correction; } else { possibleValue = isEven ? curentElelement + correction : curentElelement - correction; } changed = true; } accessor[byteIndexForEncryption] = possibleValue; stegoBlock = dctBlockTransform.CrossProduct(quantizationTable); stegoBlock = DiscreteCosineTransform.InverseDct8Block(stegoBlock); if (!redo && changed) { for (var i = 0; i < 8; i++) { for (var j = 0; j < 8; j++) { var value = Math.Round(stegoBlock[i, j]); if (value < -128 || value > 127) { redo = true; oldStegoBlock = stegoBlock; break; } } if (redo) { break; } } } else { if (changed) { var maximumValue = oldStegoBlock.MaximumValue; var minimumValue = oldStegoBlock.MinimumValue; var currentMaximumValue = stegoBlock.MaximumValue; var currentMinimumValue = stegoBlock.MinimumValue; if (currentMinimumValue < -128 || currentMaximumValue > 127) { if ((currentMinimumValue < -128 && minimumValue > currentMinimumValue) || (currentMaximumValue > 127 && maximumValue < currentMaximumValue)) { stegoBlock = oldStegoBlock; } } } redo = false; } } while (redo); stegoBlock = stegoBlock + 128; // var tmpyCbCrColorBlock = yCbCrColorBlock.Copy; tmpyCbCrColorBlock.UpdateChannel(channelType, stegoBlock); ret = tmpyCbCrColorBlock.ToRgb(); #region Verification (check if embeddding is correctly (rounding problems might occurs)) if (changed) { embeddCorrectly = !GetWatermarkDctForBlock(ret, byteIndexForEncryption, accessorFactory, quantizationTable, channelType) == nextSecretBit; if (!embeddCorrectly) { correction = correction + 2; } else { correction = 1; } } else { embeddCorrectly = true; correction = 1; } #endregion } while (!embeddCorrectly); _debugEncodeStringBuilder.AppendLine(dctBlockTransform.ToString()); embeddingSecretMessageBitIndex++; return ret; }
/// <summary> /// Watermark an image using DCt transform /// </summary> /// <param name="inputImage">the input image usually a jpeg image</param> /// <param name="secretmessage">the hidden message</param> /// <param name="bitIndex">the byte index from the block in wich the message will be added</param> /// <param name="channelType"></param> /// <returns></returns> public static Image WatermarkDctSingleBitPerBlock(Bitmap inputImage, byte[] secretmessage, int bitIndex, ChannelType channelType) { var image = ImageHelpers.ImageToArray(inputImage); var messageLength = (UInt16)secretmessage.Length; var messageToEmbedd = BitConverter.GetBytes(messageLength).Concat(secretmessage).ToArray(); var embeddingSecretMessageBitIndex = 0; bool canEmbed = embeddingSecretMessageBitIndex < messageToEmbedd.Length*8; var zigzagAccessor = new ZigZagAccessorFactory<double>(8, 8); if (!canEmbed) { return new Bitmap(inputImage); } QuantizationTableType quantizationType = channelType == ChannelType.Y ? QuantizationTableType.Luminance : QuantizationTableType.Chrominance; var quantizationTable = JpegUtilitites.GetQuantizationTable(inputImage, quantizationType); var imageBlocks = MathUtilities.Split2DArrayToBlocks(image, 8); var stegoBlocks = new List<MatrixBase<Color>>(); for (var index = 0; index < imageBlocks.Count; index++) { var imageBlock = imageBlocks[index]; var block = WatermarkDctBlock(imageBlock, zigzagAccessor, quantizationTable, canEmbed, messageToEmbedd, ref embeddingSecretMessageBitIndex, bitIndex, channelType); canEmbed = embeddingSecretMessageBitIndex < messageToEmbedd.Length * 8; stegoBlocks.Add(block); } File.WriteAllText(_debugEncodeTextFile, _debugEncodeStringBuilder.ToString()); var stegoImageArray = MathUtilities.Merge2DArraysBlocks(stegoBlocks, inputImage.Height, inputImage.Width); var stegoImage = ImageHelpers.ArrayToImage(stegoImageArray); return stegoImage; }