private void GenerateWatermarkDiff()
        {
            double[,] gray = new double[DiffWidth, DiffHeight];
            for (int i = 0; i < DiffWidth; i++)
            {
                for (int j = 0; j < DiffHeight; j++)
                {
                    gray[i, j] = 0;
                }
            }

            var rgbData = new RgbData(gray);
            var yuv     = rgbData.ToYuv();

            EmbedWatermark(yuv.U);

            var rgb = yuv.ToRgb();

            double[,] red   = new double[DiffWidth, DiffHeight];
            double[,] green = new double[DiffWidth, DiffHeight];
            double[,] blue  = new double[DiffWidth, DiffHeight];

            for (int i = 0; i < DiffWidth; i++)
            {
                for (int j = 0; j < DiffHeight; j++)
                {
                    red[i, j]   = 128 - rgb.R[i, j];
                    green[i, j] = 128 - rgb.G[i, j];
                    blue[i, j]  = 128 - rgb.B[i, j];
                }
            }

            _watermarkDiff = _imageHelper.SavePixels(new RgbData(red, green, blue));
        }
        public byte[] SavePixels(RgbData data)
        {
            var width     = data.R.GetUpperBound(0) + 1;
            var height    = data.R.GetUpperBound(1) + 1;
            var pixelSize = PixelFormats.Bgr32.BitsPerPixel / 8;

            byte[] pixels = new byte[width * pixelSize * height];
            Parallel.For(0, height, h =>
            {
                var hPos = h * width * pixelSize;
                for (int w = 0; w < width; w++)
                {
                    var i = hPos + (w * pixelSize);

                    pixels[i]     = ToByte(data.B[w, h]);
                    pixels[i + 1] = ToByte(data.G[w, h]);
                    pixels[i + 2] = ToByte(data.R[w, h]);
                }
            });

            var frame = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgr32, BitmapPalettes.WebPalette);

            frame.WritePixels(new Int32Rect(0, 0, width, height), pixels, width * pixelSize, 0);

            using (var encoderMemoryStream = new MemoryStream())
            {
                var encoder = new JpegBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create(frame));
                encoder.Save(encoderMemoryStream);

                return(encoderMemoryStream.ToArray());
            }
        }
        /// <summary>
        /// Creates a new watermarking class.
        /// <para>This class caches some watermark data, so it's more efficient to keep an instance rather than create a new one for every embed operation.</para>
        /// <para>Defaults clipping sizes are 1024x768, 911x683, 832x624, 800x600, 744x558, 700x525, 640x480, 600x450, 568x426, 508x373, 480x360, 448x336, 400x300, 360x270, 333x250.</para>
        /// </summary>
        /// <param name="watermarkBytes">32x32 watermark image</param>
        /// <param name="clipSupport">Whether the algorithm should support clipping</param>
        /// <param name="clippingWidths">Custom clipping widths</param>
        /// <param name="clippingHeights">Custom clipping heights</param>
        public Watermark(byte[] watermarkBytes, bool clipSupport = false, IEnumerable <int> clippingWidths = null, IEnumerable <int> clippingHeights = null)
        {
            _imageHelper = new ImageHelpers(clipSupport, clippingWidths, clippingHeights);

            _watermarkPixels = _imageHelper.ReadPixels(watermarkBytes);
            if (_watermarkPixels.Height != WatermarkSize || _watermarkPixels.Width != WatermarkSize)
            {
                throw new WatermarkException("Watermark must be 32x32 image");
            }
            GenerateWatermarkDiff();
        }
        public YuvData(RgbData rgb)
        {
            Y = new double[rgb.Width, rgb.Height];
            U = new double[rgb.Width, rgb.Height];
            V = new double[rgb.Width, rgb.Height];

            for (int i = 0; i < rgb.Width; i++)
            {
                for (int j = 0; j < rgb.Height; j++)
                {
                    Y[i, j] = ColorSpaceConversion.RgbToY(rgb.R[i, j], rgb.G[i, j], rgb.B[i, j]);
                    U[i, j] = ColorSpaceConversion.RgbToU(rgb.R[i, j], rgb.G[i, j], rgb.B[i, j]);
                    V[i, j] = ColorSpaceConversion.RgbToV(rgb.R[i, j], rgb.G[i, j], rgb.B[i, j]);
                }
            }
        }
        private WatermarkResult RetrieveWatermark(double[,] data)
        {
            var origWidth  = data.GetUpperBound(0) + 1;
            var origHeight = data.GetUpperBound(1) + 1;

            if (DiffWidth - origWidth > 0 || DiffHeight - origHeight > 0)
            {
                var top    = (DiffHeight - origHeight) / 2;
                var left   = (DiffWidth - origWidth) / 2;
                var bottom = (DiffHeight - origHeight) - top;
                var right  = (DiffWidth - origWidth) - left;
                data = data.Pad(left, top, right, bottom);
            }

            double[,] recoveredWatermarkData = new double[_watermarkPixels.Width, _watermarkPixels.Height];

            ParallelHaar.FWT(data, 2);
            var subband = LL2(data);

            var width  = subband.GetUpperBound(0) + 1;
            var height = subband.GetUpperBound(1) + 1;

            Parallel.For(0, _watermarkPixels.Height, y =>
            {
                for (int x = 0; x < _watermarkPixels.Width; x++)
                {
                    if (x * BlockSize + BlockSize > width)
                    {
                        return;
                    }
                    if (y * BlockSize + BlockSize > height)
                    {
                        return;
                    }

                    var block = subband.Submatrix(x * BlockSize, x * BlockSize + BlockSize - 1, y * BlockSize, y * BlockSize + BlockSize - 1);

                    CosineTransform.DCT(block);
                    recoveredWatermarkData[x, y] = (MidBand(block).Sum() > 0) ? 255 : 0;
                }
            });

            double similiar = 0;
            double total    = (width / 4) * (height / 4);

            for (int x = 0; x < _watermarkPixels.Width; x++)
            {
                for (int y = 0; y < _watermarkPixels.Height; y++)
                {
                    var oldValue = _watermarkPixels.R[x, y] > 125 ? 255 : 0;
                    if (recoveredWatermarkData[x, y] == oldValue)
                    {
                        similiar++;
                    }
                }
            }

            var similarity = Math.Round((similiar / total) * 100);

            var recoveredData           = new RgbData(recoveredWatermarkData);
            var recoveredWatermarkBytes = _imageHelper.SavePixels(recoveredData);

            return(new WatermarkResult(similarity, recoveredWatermarkBytes));
        }