private void EmbedWatermark(double[,] data)
        {
            double[,] watermarkData = new double[_watermarkPixels.Width, _watermarkPixels.Height];
            for (int x = 0; x < _watermarkPixels.Width; x++)
            {
                for (int y = 0; y < _watermarkPixels.Height; y++)
                {
                    watermarkData[x, y] = _watermarkPixels.R[x, y] > 125 ? 255 : 0;
                }
            }

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

            Parallel.For(0, _watermarkPixels.Height, y =>
            {
                for (int x = 0; x < _watermarkPixels.Width; x++)
                {
                    var block = subband.Submatrix(x * BlockSize, x * BlockSize + BlockSize - 1, y * BlockSize, y * BlockSize + BlockSize - 1);

                    CosineTransform.DCT(block);

                    var midbandSum = Math.Max(2, Math.Abs(MidBand(block).Sum()));
                    var sigma      = (watermarkData[x, y] > 125 ? 3 : -3);

                    block[1, 2] += midbandSum * sigma;
                    block[2, 0] += midbandSum * sigma;
                    block[2, 1] += midbandSum * sigma;
                    block[2, 2] += midbandSum * sigma;

                    CosineTransform.IDCT(block);

                    for (int i = 0; i < BlockSize; i++)
                    {
                        for (int j = 0; j < BlockSize; j++)
                        {
                            subband[x * BlockSize + i, y * BlockSize + j] = block[i, j];
                        }
                    }
                }
            });

            BackApplySubBand(data, subband);
            ParallelHaar.IWT(data, 2);
        }
        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));
        }