Example #1
0
        public static void Run()
        {
            //var frequencies = FitsIO.ReadFrequencies(@"C:\Users\Jon\github\p9-data\small\fits\simulation_point\freq.fits");
            //var uvw = FitsIO.ReadUVW(@"C:\Users\Jon\github\p9-data\small\fits\simulation_point\uvw.fits");
            var frequencies = FitsIO.ReadFrequencies(@"C:\dev\GitHub\p9-data\small\fits\simulation_point\freq.fits");
            var uvw         = FitsIO.ReadUVW(@"C:\dev\GitHub\p9-data\small\fits\simulation_point\uvw.fits");
            var flags       = new bool[uvw.GetLength(0), uvw.GetLength(1), frequencies.Length]; //completely unflagged dataset

            var    visibilitiesCount = flags.Length;
            int    gridSize          = 64;
            int    subgridsize       = 16;
            int    kernelSize        = 8;
            int    max_nr_timesteps  = 64;
            double cellSize          = 2.0 / 3600.0 * PI / 180.0;
            var    c = new GriddingConstants(visibilitiesCount, gridSize, subgridsize, kernelSize, max_nr_timesteps, (float)cellSize, 1, 0.0f);

            var metadata = Partitioner.CreatePartition(c, uvw, frequencies);

            var psfGrid = IDG.GridPSF(c, metadata, uvw, flags, frequencies);
            var psf     = FFT.Backward(psfGrid, c.VisibilitiesCount);

            FFT.Shift(psf);
            var maxPsf = psf[gridSize / 2, gridSize / 2];

            for (int i = 0; i < psf.GetLength(0); i++)
            {
                for (int j = 0; j < psf.GetLength(1); j++)
                {
                    psf[i, j] = psf[i, j] / maxPsf;
                }
            }
            FitsIO.Write(psf, "psf.fits");

            var truth = new double[64, 64];

            //truth[40, 50] = 1.5;
            truth[0, 0] = 1.7;
            var dirty = ConvolveFFTPadded(truth, psf);

            FitsIO.Write(truth, "truth.fits");
            FitsIO.Write(dirty, "dirty.fits");

            var psf2 = ConvolveFFT(psf, psf);
            var b    = ConvolveFFTPadded(dirty, psf);
            var a    = psf2[gridSize / 2, gridSize / 2];

            /*
             * var integral = CalcPSf2Integral(psf);
             * FitsIO.Write(integral, "psfIntegral.fits");
             * var c0 = new double[64, 64];
             * var qY = 0;
             * var qX = 0;
             * c0[qY, qX] = 1.0;
             * c0 = Convolve(c0, psf);
             * FitsIO.Write(c0, "cx0.fits");
             * var cx = ConvolveFFT(c0, psf);
             * FitsIO.Write(cx, "cx1.fits");
             * var a2 = cx[qY, qX];
             * var res = QueryIntegral(integral, qY, qX);*/

            var x = new double[gridSize, gridSize];
            //Deconv(x, dirty, psf, psf2, a, 0.0);

            var dCopy = new double[gridSize, gridSize];

            for (int i = 0; i < b.GetLength(0); i++)
            {
                for (int j = 0; j < b.GetLength(1); j++)
                {
                    dCopy[i, j] = dirty[i, j];
                }
            }
            var x2        = new double[gridSize, gridSize];
            var converged = GreedyCD.Deconvolve2(x2, dirty, psf, 0.0, 1.0, 500, dCopy);
        }
Example #2
0
        public static bool Deconvolve2(double[,] xImage, double[,] res, double[,] psf, double lambda, double alpha, Rectangle rec, int maxIteration = 100, double[,] dirtyCopy = null)
        {
            var yPsfHalf  = psf.GetLength(0) / 2;
            var xPsfHalf  = psf.GetLength(1) / 2;
            var integral  = CommonDeprecated.PSF.CalcPSFScan(psf);
            var resPadded = new double[res.GetLength(0) + psf.GetLength(0), res.GetLength(1) + psf.GetLength(1)];

            for (int y = 0; y < res.GetLength(0); y++)
            {
                for (int x = 0; x < res.GetLength(1); x++)
                {
                    resPadded[y + yPsfHalf, x + xPsfHalf] = res[y, x];
                }
            }

            //invert the PSF, since we actually do want to correlate the psf with the residuals. (The FFT already inverts the psf, so we need to invert it again to not invert it. Trust me.)
            var psfPadded  = new double[res.GetLength(0) + psf.GetLength(0), res.GetLength(1) + psf.GetLength(1)];
            var psfYOffset = res.GetLength(0) / 2;
            var psfXOffset = res.GetLength(1) / 2;

            for (int y = 0; y < psf.GetLength(0); y++)
            {
                for (int x = 0; x < psf.GetLength(1); x++)
                {
                    psfPadded[y + psfYOffset + 1, x + psfXOffset + 1] = psf[psf.GetLength(0) - y - 1, psf.GetLength(1) - x - 1];
                }
            }
            FFT.Shift(psfPadded);
            var PSFPadded = FFT.Forward(psfPadded, 1.0);

            DeconvolveGreedy2(xImage, resPadded, res, psf, PSFPadded, integral, lambda, alpha, rec, 100);

            var    xCummulatedDiff = new double[xImage.GetLength(0), xImage.GetLength(1)];
            int    iter            = 0;
            bool   converged       = false;
            double epsilon         = 1e-4;
            double objective       = double.MaxValue;

            while (!converged & iter < maxIteration)
            {
                var oOld = objective;
                objective  = GreedyCD.CalcElasticNetObjective(xImage, res, integral, lambda, alpha, rec.Y, rec.X);
                objective += GreedyCD.CalcDataObjective(resPadded, res, yPsfHalf, yPsfHalf);
                Console.WriteLine("Objective \t" + objective);

                if (oOld < objective)
                {
                    Console.Write("error");
                }
                var RES = FFT.Forward(resPadded, 1.0);
                var B   = Common.Fourier2D.Multiply(RES, PSFPadded);
                var b   = FFT.Backward(B, (double)(B.GetLength(0) * B.GetLength(1)));

                var activeSet = new List <Tuple <int, int> >();
                for (int y = rec.Y; y < rec.YLength; y++)
                {
                    for (int x = rec.X; x < rec.XLength; x++)
                    {
                        var yLocal   = y - rec.Y;
                        var xLocal   = x - rec.X;
                        var currentA = CommonDeprecated.PSF.QueryScan(integral, y, x, res.GetLength(0), res.GetLength(1));
                        var old      = xImage[yLocal, xLocal];
                        var xTmp     = old + b[y + yPsfHalf, x + xPsfHalf] / currentA;
                        xTmp = CommonDeprecated.ShrinkElasticNet(xTmp, lambda, alpha);
                        var xDiff = old - xTmp;

                        if (Math.Abs(xDiff) > epsilon)
                        {
                            activeSet.Add(new Tuple <int, int>(y, x));
                        }
                    }
                }

                //active set iterations
                Console.WriteLine("--------------------count:" + activeSet.Count + "------------------");
                converged = activeSet.Count == 0;
                bool activeSetConverged = activeSet.Count == 0;
                var  innerMax           = 40;
                var  innerIter          = 0;
                while (!activeSetConverged & innerIter <= innerMax)
                {
                    var oTest = GreedyCD.CalcElasticNetObjective(xImage, res, integral, lambda, alpha, rec.Y, rec.X);
                    oTest += GreedyCD.CalcDataObjective(resPadded, res, yPsfHalf, yPsfHalf);

                    activeSetConverged = true;
                    var delete = new List <Tuple <int, int> >();
                    foreach (var pixel in activeSet)
                    {
                        var y        = pixel.Item1;
                        var x        = pixel.Item2;
                        var yLocal   = y - rec.Y;
                        var xLocal   = x - rec.X;
                        var xOld     = xImage[yLocal, xLocal];
                        var currentB = CalculateB(resPadded, res, psf, y, x);

                        //calculate minimum of parabola, eg -2b/a
                        var xTmp = xOld + currentB / CommonDeprecated.PSF.QueryScan(integral, y, x, res.GetLength(0), res.GetLength(1));
                        xTmp = GreedyCD.ShrinkPositive(xTmp, lambda * alpha) / (1 + lambda * (1 - alpha));
                        var xDiff = xOld - xTmp;

                        if (Math.Abs(xDiff) > epsilon)
                        {
                            activeSetConverged = false;
                            //Console.WriteLine(Math.Abs(xOld - xTmp) + "\t" + y + "\t" + x);
                            xImage[yLocal, xLocal]           = xTmp;
                            xCummulatedDiff[yLocal, xLocal] += xDiff;
                            GreedyCD.UpdateResiduals2(resPadded, res, psf, y, x, xDiff, yPsfHalf, xPsfHalf);
                        }
                        else if (xTmp == 0.0)
                        {
                            // zero, remove from active set
                            activeSetConverged               = false;
                            xImage[yLocal, xLocal]           = 0.0;
                            xCummulatedDiff[yLocal, xLocal] += xOld;
                            GreedyCD.UpdateResiduals2(resPadded, res, psf, y, x, xDiff, yPsfHalf, xPsfHalf);
                            delete.Add(pixel);
                            //Console.WriteLine("drop pixel \t" + xTmp + "\t" + y + "\t" + x);
                        }
                    }
                    innerIter++;
                }

                /*
                 * foreach (var pixel in delete)
                 *  activeSet.Remove(pixel);
                 *
                 * //exchange with other nodes
                 * var allXDiff = new List<PixelExchange>();
                 * for (int y = 0; y < xCummulatedDiff.GetLength(0); y++)
                 *  for (int x = 0; x < xCummulatedDiff.GetLength(1); x++)
                 *  {
                 *      if (xCummulatedDiff[y, x] > 0.0)
                 *      {
                 *          var p = new PixelExchange();
                 *          p.Rank = comm.Rank;
                 *          p.Y = rec.Y + y;
                 *          p.X = rec.X + x;
                 *          p.Value = xCummulatedDiff[y, x];
                 *          allXDiff.Add(p);
                 *          xCummulatedDiff[y, x] = 0.0;
                 *      }
                 *  }
                 *
                 * var allNonZeros = comm.Allreduce(allXDiff, (aC, bC) =>
                 * {
                 *  aC.AddRange(bC);
                 *  return aC;
                 * });
                 *
                 * foreach (var p in allXDiff)
                 *  if (p.Rank != comm.Rank)
                 *      GreedyCD.UpdateResiduals2(resPadded, res, psf, p.Y, p.X, p.Value, yPsfHalf, xPsfHalf);*/

                RES = FFT.Forward(resPadded, 1.0);
                B   = Common.Fourier2D.Multiply(RES, PSFPadded);
                b   = FFT.Backward(B, (double)(B.GetLength(0) * B.GetLength(1)));

                iter++;
            }

            //copy back the residuals
            for (int y = 0; y < res.GetLength(0); y++)
            {
                for (int x = 0; x < res.GetLength(1); x++)
                {
                    res[y, x] = resPadded[y + yPsfHalf, x + xPsfHalf];
                }
            }

            return(converged);
        }
        public static bool Deconvolve(double[,] xImage, double[,] res, double[,] psf, double lambda, double alpha, int maxIteration = 100, double[,] dirtyCopy = null)
        {
            var yPsfHalf  = psf.GetLength(0) / 2;
            var xPsfHalf  = psf.GetLength(1) / 2;
            var integral  = CommonDeprecated.PSF.CalcPSFScan(psf);
            var resPadded = new double[res.GetLength(0) + psf.GetLength(0), res.GetLength(1) + psf.GetLength(1)];

            for (int y = 0; y < res.GetLength(0); y++)
            {
                for (int x = 0; x < res.GetLength(1); x++)
                {
                    resPadded[y + yPsfHalf, x + xPsfHalf] = res[y, x];
                }
            }

            //invert the PSF, since we actually do want to correlate the psf with the residuals. (The FFT already inverts the psf, so we need to invert it again to not invert it. Trust me.)
            var psfPadded  = new double[res.GetLength(0) + psf.GetLength(0), res.GetLength(1) + psf.GetLength(1)];
            var psfYOffset = res.GetLength(0) / 2;
            var psfXOffset = res.GetLength(1) / 2;

            for (int y = 0; y < psf.GetLength(0); y++)
            {
                for (int x = 0; x < psf.GetLength(1); x++)
                {
                    psfPadded[y + psfYOffset + 1, x + psfXOffset + 1] = psf[psf.GetLength(0) - y - 1, psf.GetLength(1) - x - 1];
                }
            }
            FFT.Shift(psfPadded);
            var PSFPaddedCorr = FFT.Forward(psfPadded, 1.0);

            var psfPaddedConv = new double[res.GetLength(0) + psf.GetLength(0), res.GetLength(1) + psf.GetLength(1)];

            for (int y = 0; y < psf.GetLength(0); y++)
            {
                for (int x = 0; x < psf.GetLength(1); x++)
                {
                    psfPaddedConv[y + psfYOffset + 1, x + psfXOffset + 1] = psf[y, x];
                }
            }
            FFT.Shift(psfPaddedConv);
            var PSFPaddedConv = FFT.Forward(psfPaddedConv, 1.0);

            DeconvolveGreedy(xImage, resPadded, res, psf, PSFPaddedCorr, integral, lambda, alpha, 100);

            var    xCummulatedDiff = new double[res.GetLength(0) + psf.GetLength(0), res.GetLength(1) + psf.GetLength(1)];
            int    iter            = 0;
            bool   converged       = false;
            double epsilon         = 1e-6;

            while (!converged & iter < maxIteration)
            {
                var RES = FFT.Forward(resPadded, 1.0);
                var B   = Common.Fourier2D.Multiply(RES, PSFPaddedCorr);
                var b   = FFT.Backward(B, (double)(B.GetLength(0) * B.GetLength(1)));

                var activeSet = new List <Tuple <int, int> >();
                for (int y = 0; y < xImage.GetLength(0); y++)
                {
                    for (int x = 0; x < xImage.GetLength(1); x++)
                    {
                        var currentA = CommonDeprecated.PSF.QueryScan(integral, y, x, xImage.GetLength(0), xImage.GetLength(1));
                        var old      = xImage[y, x];
                        var xTmp     = old + b[y + yPsfHalf, x + xPsfHalf] / currentA;
                        xTmp = GreedyCD.ShrinkPositive(xTmp, lambda * alpha) / (1 + lambda * (1 - alpha));
                        var xDiff = old - xTmp;

                        if (Math.Abs(xDiff) > epsilon / 50.0)
                        {
                            activeSet.Add(new Tuple <int, int>(y, x));
                        }
                    }
                }

                var objective = GreedyCD.CalcElasticNetObjective(xImage, res, integral, lambda, alpha, 0, 0);
                objective += GreedyCD.CalcDataObjective(resPadded, res, yPsfHalf, yPsfHalf);
                Console.WriteLine("Objective test \t" + objective);
                Console.WriteLine("--------------------count:" + activeSet.Count + "------------------");

                //active set iterations
                converged = activeSet.Count == 0;
                bool activeSetConverged = activeSet.Count == 0;
                var  innerMax           = 2000;
                var  innerIter          = 0;
                Randomize(activeSet);
                while (!activeSetConverged & innerIter <= innerMax)
                {
                    activeSetConverged = true;
                    var delete = new List <Tuple <int, int> >();
                    var i      = 0;
                    foreach (var pixel in activeSet)
                    {
                        var y        = pixel.Item1;
                        var x        = pixel.Item2;
                        var xOld     = xImage[y, x];
                        var currentB = b[y + yPsfHalf, x + xPsfHalf];

                        var xTmp = xOld + currentB / CommonDeprecated.PSF.QueryScan(integral, y, x, xImage.GetLength(0), xImage.GetLength(1));;
                        xTmp = GreedyCD.ShrinkPositive(xTmp, lambda * alpha) / (1 + lambda * (1 - alpha));
                        var xDiff = (xOld - xTmp) / 50.0;

                        if (Math.Abs(xDiff) > epsilon / 50.0)
                        {
                            activeSetConverged = false;
                            //Console.WriteLine(Math.Abs(xOld - xTmp) + "\t" + y + "\t" + x);
                            xImage[y, x] = xTmp;
                            xCummulatedDiff[y + yPsfHalf, x + xPsfHalf] += xDiff;
                        }
                        else if (xTmp == 0.0)
                        {
                            //approximately zero, remove from active set
                            activeSetConverged = false;
                            xImage[y, x]       = 0.0;
                            xCummulatedDiff[y + yPsfHalf, x + xPsfHalf] += xOld;
                            delete.Add(pixel);
                            //Console.WriteLine("drop pixel \t" + xTmp + "\t" + y + "\t" + x);
                        }

                        if (i % 50 == 0)
                        {
                            var Xdiff   = FFT.Forward(xCummulatedDiff, 1.0);
                            var RESdiff = Common.Fourier2D.Multiply(Xdiff, PSFPaddedConv);
                            var resDiff = FFT.Backward(RESdiff, (double)(RESdiff.GetLength(0) * RESdiff.GetLength(1)));

                            for (int y2 = 0; y2 < res.GetLength(0); y2++)
                            {
                                for (int x2 = 0; x2 < res.GetLength(1); x2++)
                                {
                                    resPadded[y2 + yPsfHalf, x2 + xPsfHalf] += resDiff[y2 + yPsfHalf, x2 + xPsfHalf];
                                }
                            }

                            xCummulatedDiff = new double[res.GetLength(0) + psf.GetLength(0), res.GetLength(1) + psf.GetLength(1)];
                            RES             = FFT.Forward(resPadded, 1.0);
                            B = Common.Fourier2D.Multiply(RES, PSFPaddedCorr);
                            b = FFT.Backward(B, (double)(B.GetLength(0) * B.GetLength(1)));

                            var objective2 = GreedyCD.CalcElasticNetObjective(xImage, res, integral, lambda, alpha, 0, 0);
                            objective2 += GreedyCD.CalcDataObjective(resPadded, res, yPsfHalf, yPsfHalf);
                            Console.WriteLine("Objective test \t" + objective2);
                        }
                        i++;
                    }
                    var Xdiff2   = FFT.Forward(xCummulatedDiff, 1.0);
                    var RESdiff2 = Common.Fourier2D.Multiply(Xdiff2, PSFPaddedConv);
                    var resDiff2 = FFT.Backward(RESdiff2, (double)(RESdiff2.GetLength(0) * RESdiff2.GetLength(1)));

                    for (int y2 = 0; y2 < res.GetLength(0); y2++)
                    {
                        for (int x2 = 0; x2 < res.GetLength(1); x2++)
                        {
                            resPadded[y2 + yPsfHalf, x2 + xPsfHalf] += resDiff2[y2 + yPsfHalf, x2 + xPsfHalf];
                        }
                    }

                    xCummulatedDiff = new double[res.GetLength(0) + psf.GetLength(0), res.GetLength(1) + psf.GetLength(1)];
                    RES             = FFT.Forward(resPadded, 1.0);
                    B = Common.Fourier2D.Multiply(RES, PSFPaddedCorr);
                    b = FFT.Backward(B, (double)(B.GetLength(0) * B.GetLength(1)));

                    var objective3 = GreedyCD.CalcElasticNetObjective(xImage, res, integral, lambda, alpha, 0, 0);
                    objective3 += GreedyCD.CalcDataObjective(resPadded, res, yPsfHalf, yPsfHalf);
                    Console.WriteLine("Objective done iteration \t" + objective3);

                    //FitsIO.Write(resPadded, "debugResiduals.fits");

                    /*
                     * foreach (var pixel in activeSet)
                     * {
                     *  //serial descent
                     *  var y = pixel.Item1;
                     *  var x = pixel.Item2;
                     *  var xOld = xImage[y, x];
                     *  var currentB = CalculateB(resPadded, xImage, psf, y, x);
                     *
                     *  //calculate minimum of parabola, eg -2b/a
                     *  var xTmp = xOld + currentB / Common.PSF.QueryScan(integral, y, x, xImage.GetLength(0), xImage.GetLength(1)); ;
                     *  xTmp = GreedyCD.ShrinkPositive(xTmp, lambda * alpha) / (1 + lambda * (1 - alpha));
                     *  var xDiff = xOld - xTmp;
                     *
                     *
                     *  if (Math.Abs(xDiff) > epsilon)
                     *  {
                     *      activeSetConverged = false;
                     *      //Console.WriteLine(Math.Abs(xOld - xTmp) + "\t" + y + "\t" + x);
                     *      xImage[y, x] = xTmp;
                     *      xCummulatedDiff[y, x] += xDiff;
                     *      GreedyCD.UpdateResiduals2(resPadded, xImage, psf, y, x, xDiff, yPsfHalf, xPsfHalf);
                     *  }
                     *  else if (xTmp == 0.0)
                     *  {
                     *      //approximately zero, remove from active set
                     *      activeSetConverged = false;
                     *      xImage[y, x] = 0.0;
                     *      xCummulatedDiff[y, x] += xOld;
                     *      GreedyCD.UpdateResiduals2(resPadded, xImage, psf, y, x, xDiff, yPsfHalf, xPsfHalf);
                     *      delete.Add(pixel);
                     *      //Console.WriteLine("drop pixel \t" + xTmp + "\t" + y + "\t" + x);
                     *  }
                     * }*/
                    innerIter++;

                    foreach (var pixel in delete)
                    {
                        activeSet.Remove(pixel);
                    }
                }

                RES = FFT.Forward(resPadded, 1.0);
                B   = Common.Fourier2D.Multiply(RES, PSFPaddedCorr);
                b   = FFT.Backward(B, (double)(B.GetLength(0) * B.GetLength(1)));

                iter++;
            }

            //copy back the residuals
            for (int y = 0; y < res.GetLength(0); y++)
            {
                for (int x = 0; x < res.GetLength(1); x++)
                {
                    res[y, x] = resPadded[y + yPsfHalf, x + xPsfHalf];
                }
            }

            return(converged);
        }
Example #4
0
        public static bool DeconvolveGreedy2(double[,] xImage, double[,] resPadded, double[,] res, double[,] psf, Complex[,] PSFPadded, double[,] integral, double lambda, double alpha, Rectangle rec, int maxIteration = 100, double[,] dirtyCopy = null)
        {
            var yPsfHalf = psf.GetLength(0) / 2;
            var xPsfHalf = psf.GetLength(1) / 2;

            var RES = FFT.Forward(resPadded, 1.0);
            var B   = Common.Fourier2D.Multiply(RES, PSFPadded);
            var b   = FFT.Backward(B, (double)(B.GetLength(0) * B.GetLength(1)));

            double objective = GreedyCD.CalcElasticNetObjective(xImage, res, integral, lambda, alpha, rec.Y, rec.X);

            objective += GreedyCD.CalcDataObjective(resPadded, res, yPsfHalf, yPsfHalf);

            int    iter      = 0;
            bool   converged = false;
            double epsilon   = 1e-4;

            while (!converged & iter < maxIteration)
            {
                var yPixel = -1;
                var xPixel = -1;
                var xMax   = 0.0;
                var xNew   = 0.0;
                for (int y = rec.Y; y < rec.YLength; y++)
                {
                    for (int x = rec.X; x < rec.XLength; x++)
                    {
                        var yLocal   = y - rec.Y;
                        var xLocal   = x - rec.X;
                        var currentA = CommonDeprecated.PSF.QueryScan(integral, y, x, res.GetLength(0), res.GetLength(1));
                        var old      = xImage[yLocal, xLocal];
                        var xTmp     = old + b[y + yPsfHalf, x + xPsfHalf] / currentA;
                        xTmp = GreedyCD.ShrinkPositive(xTmp, lambda * alpha) / (1 + lambda * (1 - alpha));

                        var xDiff = old - xTmp;

                        if (Math.Abs(xDiff) > xMax)
                        {
                            yPixel = y;
                            xPixel = x;
                            xMax   = Math.Abs(xDiff);
                            xNew   = xTmp;
                        }
                    }
                }

                //exchange max

                converged = Math.Abs(xMax) < epsilon;
                if (!converged)
                {
                    var yLocal2 = yPixel - rec.Y;
                    var xLocal2 = xPixel - rec.X;
                    var xOld    = xImage[yLocal2, xLocal2];
                    xImage[yLocal2, xLocal2] = xNew;

                    Console.WriteLine(iter + "\t" + Math.Abs(xOld - xNew) + "\t" + yLocal2 + "\t" + xLocal2);
                    GreedyCD.UpdateResiduals2(resPadded, res, psf, yPixel, xPixel, xOld - xNew, yPsfHalf, xPsfHalf);
                    RES = FFT.Forward(resPadded, 1.0);
                    B   = Common.Fourier2D.Multiply(RES, PSFPadded);
                    b   = FFT.Backward(B, (double)(B.GetLength(0) * B.GetLength(1)));
                    iter++;
                }
            }

            return(converged);
        }
        public static bool Deconvolve2(double[,] xImage, double[,] res, double[,] psf, double lambda, double alpha, int maxIteration = 100, double[,] dirtyCopy = null)
        {
            var yPsfHalf = psf.GetLength(0) / 2;
            var xPsfHalf = psf.GetLength(1) / 2;
            var integral = CommonDeprecated.PSF.CalcPSFScan(psf);

            var resPadded = new double[res.GetLength(0) + psf.GetLength(0), res.GetLength(1) + psf.GetLength(1)];

            for (int y = 0; y < res.GetLength(0); y++)
            {
                for (int x = 0; x < res.GetLength(1); x++)
                {
                    resPadded[y + yPsfHalf, x + xPsfHalf] = res[y, x];
                }
            }

            //invert the PSF, since we actually do want to correlate the psf with the residuals. (The FFT already inverts the psf, so we need to invert it again to not invert it. Trust me.)
            var psfPadded  = new double[res.GetLength(0) + psf.GetLength(0), res.GetLength(1) + psf.GetLength(1)];
            var psfYOffset = res.GetLength(0) / 2;
            var psfXOffset = res.GetLength(1) / 2;

            for (int y = 0; y < psf.GetLength(0); y++)
            {
                for (int x = 0; x < psf.GetLength(1); x++)
                {
                    psfPadded[y + psfYOffset + 1, x + psfXOffset + 1] = psf[psf.GetLength(0) - y - 1, psf.GetLength(1) - x - 1];
                }
            }

            FFT.Shift(psfPadded);
            var RES       = FFT.Forward(resPadded, 1.0);
            var PSFPadded = FFT.Forward(psfPadded, 1.0);
            var B         = Common.Fourier2D.Multiply(RES, PSFPadded);
            var b         = FFT.Backward(B, (double)(B.GetLength(0) * B.GetLength(1)));


            double objective = 0;

            objective += CalcElasticNetObjective(xImage, res, integral, lambda, alpha, 0, 0);
            objective += CalcDataObjective(res);


            Console.WriteLine("Objective \t" + objective);
            int    iter      = 0;
            bool   converged = false;
            double epsilon   = 1e-4;

            while (!converged & iter < maxIteration)
            {
                var yPixel    = -1;
                var xPixel    = -1;
                var maxImprov = 0.0;
                var xNew      = 0.0;
                for (int y = 0; y < res.GetLength(0); y++)
                {
                    for (int x = 0; x < res.GetLength(1); x++)
                    {
                        var currentA = CommonDeprecated.PSF.QueryScan(integral, y, x, res.GetLength(0), res.GetLength(1));
                        var old      = xImage[y, x];
                        var xTmp     = old + b[y + yPsfHalf, x + xPsfHalf] / currentA;
                        xTmp = GreedyCD.ShrinkPositive(xTmp, lambda * alpha) / (1 + lambda * (1 - alpha));

                        var xDiff   = old - xTmp;
                        var oImprov = EstimateObjectiveImprovement2(resPadded, res, psf, y, x, xDiff);
                        var lambdaA = lambda * 2 * currentA;
                        oImprov += lambdaA * GreedyCD.ElasticNetRegularization(old, alpha);
                        oImprov -= lambdaA * GreedyCD.ElasticNetRegularization(xTmp, alpha);

                        if (oImprov > maxImprov)
                        {
                            yPixel    = y;
                            xPixel    = x;
                            maxImprov = oImprov;
                            xNew      = xTmp;
                        }
                    }
                }

                var xOld = xImage[yPixel, xPixel];
                converged = maxImprov < epsilon;
                if (!converged)
                {
                    xImage[yPixel, xPixel] = xNew;
                    objective -= maxImprov;
                    Console.WriteLine(iter + "\t" + Math.Abs(xOld - xNew) + "\t" + yPixel + "\t" + xPixel + "\t" + objective);

                    var debug = EstimateObjectiveImprovement2(resPadded, res, psf, yPixel, xPixel, xOld - xNew);
                    UpdateResiduals2(resPadded, res, psf, yPixel, xPixel, xOld - xNew, yPsfHalf, xPsfHalf);
                    RES = FFT.Forward(resPadded, 1.0);
                    B   = Common.Fourier2D.Multiply(RES, PSFPadded);
                    b   = FFT.Backward(B, (double)(B.GetLength(0) * B.GetLength(1)));
                    iter++;
                }
            }
            return(converged);
        }