// (x, y) pairs of continious vector field
        private static Tuple <double, double>[,] GenerateLowPassFilteredContiniousVectorField(int[,] image)
        {
            int maxY = image.GetLength(0);
            int maxX = image.GetLength(1);
            var cvf  = new Tuple <double, double> [maxY, maxX];
            var lsq  = GenerateLeastSquareEstimate(image);

            for (int i = 0; i < maxY; i++)
            {
                for (int j = 0; j < maxX; j++)
                {
                    cvf[i, j] = new Tuple <double, double>(Math.Cos(2 * lsq[i, j]), Math.Sin(2 * lsq[i, j]));
                }
            }
            return(KernelHelper.Zip2D(GenerateBlur(cvf.Select2D(x => x.Item1)), GenerateBlur(cvf.Select2D(x => x.Item2)),
                                      (x, y) => new Tuple <double, double>(x, y)));
        }
        // (x, y) pairs of continious vector field
        private static Tuple<double, double>[,] GenerateLowPassFilteredContiniousVectorField(int[,] image)
        {
            int maxY = image.GetLength(0);
            int maxX = image.GetLength(1);
            var cvf = new Tuple<double, double>[maxY, maxX];
            var lsq = GenerateLeastSquareEstimate(image);

            for (int i = 0; i < maxY; i++)
            {
                for (int j = 0; j < maxX; j++)
                {
                    cvf[i, j] = new Tuple<double, double>(Math.Cos(2 * lsq[i, j]), Math.Sin(2 * lsq[i, j]));
                }
            }
            return KernelHelper.Zip2D(GenerateBlur(cvf.Select2D(x => x.Item1)), GenerateBlur(cvf.Select2D(x => x.Item2)), 
                (x, y) => new Tuple<double, double>(x, y));
        }