Exemple #1
0
        public Cubic1D2(float2[] data)
        {
            Data = data;
            Breaks = new[] { data[0].X, data[1].X };

            Coefficients = new float4[1];

            float h = data[1].X - data[0].X;
            float del = (data[1].Y - data[0].Y) / h;
            float[] slopes = { del, del };

            float[] dzzdx = new float[1];
            for (int i = 0; i < 1; i++)
                dzzdx[i] = (del - slopes[i]) / h;

            float[] dzdxdx = new float[1];
            for (int i = 0; i < 1; i++)
                dzdxdx[i] = (slopes[i + 1] - del) / h;

            for (int i = 0; i < 1; i++)
                Coefficients[i] = new float4((dzdxdx[i] - dzzdx[i]) / h,
                                             2f * dzzdx[i] - dzdxdx[i],
                                             slopes[i],
                                             data[i].Y);
        }
Exemple #2
0
        public Cubic1D(float2[] data)
        {
            // Sort points to go strictly from left to right.
            List<float2> DataList = data.ToList();
            DataList.Sort((p1, p2) => p1.X.CompareTo(p2.X));
            data = DataList.ToArray();

            Data = data;
            Breaks = data.Select(i => i.X).ToArray();
            Coefficients = new float4[data.Length - 1];

            float[] h = MathHelper.Diff(data.Select(i => i.X).ToArray());
            float[] del = MathHelper.Div(MathHelper.Diff(data.Select(i => i.Y).ToArray()), h);
            float[] slopes = GetPCHIPSlopes(data, del);

            float[] dzzdx = new float[del.Length];
            for (int i = 0; i < dzzdx.Length; i++)
                dzzdx[i] = (del[i] - slopes[i]) / h[i];

            float[] dzdxdx = new float[del.Length];
            for (int i = 0; i < dzdxdx.Length; i++)
                dzdxdx[i] = (slopes[i + 1] - del[i]) / h[i];

            for (int i = 0; i < Coefficients.Length; i++)
                Coefficients[i] = new float4((dzdxdx[i] - dzzdx[i]) / h[i],
                                             2f * dzzdx[i] - dzdxdx[i],
                                             slopes[i],
                                             data[i].Y);
        }
Exemple #3
0
        public static float2 Mean(IEnumerable<float2> data)
        {
            float2 Sum = new float2(0, 0);
            foreach (var p in data)
                Sum += p;

            return Sum / data.Count();
        }
Exemple #4
0
        public Image(float2[][] data, int3 dims, bool isft = false, bool ishalf = false)
        {
            Dims = dims;
            IsFT = isft;
            IsComplex = true;
            IsHalf = ishalf;

            UpdateHostWithComplex(data);
            IsHostDirty = true;
        }
Exemple #5
0
        public Image GetSubtomoImages(Image tiltStack, int size, float3[] coords, bool normalize = false, float imageScale = 1.0f)
        {
            float3[] ImagePositions = GetPositionInImages(coords);

            if (imageScale != 1.0f)
                for (int i = 0; i < ImagePositions.Length; i++)
                    ImagePositions[i] *= imageScale;

            Image Result = new Image(new int3(size, size, NTilts));
            float[][] ResultData = Result.GetHost(Intent.Write);
            float3[] Shifts = new float3[NTilts];

            int3 DimsStack = tiltStack.Dims;

            Parallel.For(0, NTilts, t =>
            {
                ImagePositions[t] -= size / 2;
                int2 IntPosition = new int2((int)ImagePositions[t].X, (int)ImagePositions[t].Y);
                float2 Residual = new float2(-(ImagePositions[t].X - IntPosition.X), -(ImagePositions[t].Y - IntPosition.Y));
                Shifts[t] = new float3(Residual);

                float[] OriginalData;
                lock (tiltStack)
                    OriginalData = tiltStack.GetHost(Intent.Read)[t];

                float[] ImageData = ResultData[t];
                for (int y = 0; y < size; y++)
                {
                    int PosY = (y + IntPosition.Y + DimsStack.Y) % DimsStack.Y;
                    for (int x = 0; x < size; x++)
                    {
                        int PosX = (x + IntPosition.X + DimsStack.X) % DimsStack.X;
                        ImageData[y * size + x] = OriginalData[PosY * DimsStack.X + PosX];
                    }
                }
            });

            if (normalize)
                GPU.NormParticles(Result.GetDevice(Intent.Read),
                                  Result.GetDevice(Intent.Write),
                                  Result.Dims.Slice(),
                                  (uint)(MainWindow.Options.ExportParticleRadius / CTF.PixelSize),
                                  true,
                                  (uint)NTilts);

            Result.RemapToFT();
            Result.ShiftSlices(Shifts);

            Image ResultFT = Result.AsFFT();
            Result.Dispose();

            return ResultFT;
        }
        private void ImageDisplay_OnPreviewMouseMove(object sender, MouseEventArgs e)
        {
            CanvasTrack.Children.Clear();
            if (Movie == null)
                return;

            double Scale = ScaleFactor * 10;

            // Get motion track at mouse position.
            {
                Point MousePos = e.GetPosition(CanvasTrack);
                float2 NormalizedPosition = new float2((float)(MousePos.X / CanvasTrack.Width),
                                                       (float)(MousePos.Y / CanvasTrack.Height));
                float2[] TrackData = Movie.GetMotionTrack(NormalizedPosition, 1);
                if (TrackData == null)
                    return;
                Point[] TrackPoints = TrackData.Select(v => new Point((-v.X + TrackData[0].X) * Scale, (-v.Y + TrackData[0].Y) * Scale)).ToArray();

                // Construct path.
                Path TrackPath = new Path()
                {
                    Stroke = new SolidColorBrush(Colors.DeepSkyBlue),
                    StrokeThickness = 2.5
                };
                PolyLineSegment PlotSegment = new PolyLineSegment(TrackPoints, true);
                PathFigure PlotFigure = new PathFigure
                {
                    Segments = new PathSegmentCollection { PlotSegment },
                    StartPoint = TrackPoints[0]
                };
                TrackPath.Data = new PathGeometry { Figures = new PathFigureCollection { PlotFigure } };

                var TrackShadow = new DropShadowEffect
                {
                    Opacity = 2,
                    Color = Colors.Black,
                    BlurRadius = 6,
                    ShadowDepth = 0,
                    RenderingBias = RenderingBias.Quality
                };
                TrackPath.Effect = TrackShadow;

                TrackPath.PreviewMouseWheel += ImageDisplay_MouseWheel;
                TrackPath.PreviewMouseMove += ImageDisplay_OnPreviewMouseMove;

                CanvasTrack.Children.Add(TrackPath);
                Canvas.SetLeft(TrackPath, MousePos.X);
                Canvas.SetTop(TrackPath, MousePos.Y);

                for (int z = 0; z < TrackPoints.Length / 1 + 1; z++)
                {
                    Point DotPosition = TrackPoints[Math.Min(TrackPoints.Length - 1, z * 1)];
                    Ellipse Dot = new Ellipse()
                    {
                        Width = 4,
                        Height = 4,
                        Fill = new SolidColorBrush(Colors.DeepSkyBlue),
                        StrokeThickness = 0,
                        Effect = TrackShadow
                    };
                    Dot.PreviewMouseWheel += ImageDisplay_MouseWheel;
                    Dot.PreviewMouseMove += ImageDisplay_OnPreviewMouseMove;

                    CanvasTrack.Children.Add(Dot);
                    Canvas.SetLeft(Dot, MousePos.X + DotPosition.X - 2.0);
                    Canvas.SetTop(Dot, MousePos.Y + DotPosition.Y - 2.0);
                }
            }

            Movie TempMovie = Movie;

            Parallel.For(0, 10, y =>
            {
                for (int x = 0; x < 10; x++)
                {
                    float2 NormalizedPosition = new float2((x + 1) * (1f / 12f), (y + 1) * (1f / 12f));

                    float2[] TrackData = TempMovie.GetMotionTrack(NormalizedPosition, 1);
                    if (TrackData == null)
                        continue;
                    Point[] TrackPoints = TrackData.Select(v => new Point((-v.X + TrackData[0].X) * Scale, (-v.Y + TrackData[0].Y) * Scale)).ToArray();

                    // Construct path.
                    CanvasTrack.Dispatcher.InvokeAsync(() =>
                    {
                        Path TrackPath = new Path()
                        {
                            Stroke = new SolidColorBrush(Colors.White),
                            StrokeThickness = 2.0
                        };
                        PolyLineSegment PlotSegment = new PolyLineSegment(TrackPoints, true);
                        PathFigure PlotFigure = new PathFigure
                        {
                            Segments = new PathSegmentCollection { PlotSegment },
                            StartPoint = TrackPoints[0]
                        };
                        TrackPath.Data = new PathGeometry { Figures = new PathFigureCollection { PlotFigure } };

                        TrackPath.PreviewMouseWheel += ImageDisplay_MouseWheel;
                        TrackPath.PreviewMouseMove += ImageDisplay_OnPreviewMouseMove;

                        CanvasTrack.Children.Add(TrackPath);
                        Canvas.SetLeft(TrackPath, NormalizedPosition.X * CanvasTrack.Width);
                        Canvas.SetTop(TrackPath, NormalizedPosition.Y * CanvasTrack.Height);
                    });
                }
            });
        }
Exemple #7
0
        public void GetSubtomo(Image tiltStack, float3 coords, float3 angles, Image ctfCoords, out Image subtomo, out Image subtomoCTF, int planForw = -1, int planBack = -1, int planForwCTF = -1)
        {
            int Size = ctfCoords.Dims.X;
            float3[] ImageAngles = GetAngleInImages(coords);

            Image ImagesFT = GetSubtomoImages(tiltStack, Size, coords, true);
            Image CTFs = GetSubtomoCTFs(coords, ctfCoords, true, false, false);
            //Image CTFWeights = GetSubtomoCTFs(coords, ctfCoords, true, true);

            ImagesFT.Multiply(CTFs);    // Weight and phase-flip image FTs by CTF, which still has its sign here
            //ImagesFT.Multiply(CTFWeights);
            CTFs.Abs();                 // CTF has to be positive from here on since image FT phases are now flipped

            // CTF has to be converted to complex numbers with imag = 0, and weighted by itself
            float2[] CTFsComplexData = new float2[CTFs.ElementsComplex];
            float[] CTFsContinuousData = CTFs.GetHostContinuousCopy();
            for (int i = 0; i < CTFsComplexData.Length; i++)
                CTFsComplexData[i] = new float2(CTFsContinuousData[i] * CTFsContinuousData[i], 0);

            Image CTFsComplex = new Image(CTFsComplexData, CTFs.Dims, true);

            Projector ProjSubtomo = new Projector(new int3(Size, Size, Size), 2);
            lock (GPU.Sync)
                ProjSubtomo.BackProject(ImagesFT, CTFs, ImageAngles);
            subtomo = ProjSubtomo.Reconstruct(false, planForw, planBack, planForwCTF);
            ProjSubtomo.Dispose();

            GPU.NormParticles(subtomo.GetDevice(Intent.Read),
                              subtomo.GetDevice(Intent.Write),
                              subtomo.Dims,
                              (uint)(MainWindow.Options.ExportParticleRadius / CTF.PixelSize),
                              false,
                              1);
            //subtomo = new Image(new int3(1, 1, 1));

            Projector ProjCTF = new Projector(new int3(Size, Size, Size), 2);
            lock (GPU.Sync)
                ProjCTF.BackProject(CTFsComplex, CTFs, ImageAngles);
            subtomoCTF = ProjCTF.Reconstruct(true, planForw, planBack, planForwCTF);
            ProjCTF.Dispose();
            //subtomoCTF = new Image(new int3(1, 1, 1));

            ImagesFT.Dispose();
            CTFs.Dispose();
            //CTFWeights.Dispose();
            CTFsComplex.Dispose();
        }
Exemple #8
0
 public float3(float2 v)
 {
     X = v.X;
     Y = v.Y;
     Z = 0f;
 }
Exemple #9
0
        private void UpdateLines()
        {
            MainCanvas.Children.Clear();

            float2 Center = new float2((float)ActualWidth / 2f, (float)ActualHeight / 2f);
            float StepX = PointsX > 1 ? 1f / (float)(PointsX - 1) : 0f;
            float StepY = PointsY > 1 ? 1f / (float)(PointsY - 1) : 0f;
            float2 Origin = new float2(PointsX > 1 ? 0 : 0.5f, PointsY > 1 ? 0 : 0.5f);

            float2 VecX = new float2((float) ActualWidth / 2f, (float) ActualHeight / 2f);
            float2 VecY = new float2(-(float) ActualWidth / 2f, (float) ActualHeight / 2f);
            float2 Offset = new float2((float)ActualWidth / 2f, 0f);

            for (int x = 0; x < PointsX; x++)
            {
                float2 From = new float2(Origin.X + x * StepX, Origin.Y);
                From = new float2(VecX.X * From.X + VecY.X * From.Y + Offset.X, VecX.Y * From.X + VecY.Y * From.Y + Offset.Y);
                float2 To = new float2(Origin.X + x * StepX, Origin.Y + (PointsY - 1) * StepY);
                To = new float2(VecX.X * To.X + VecY.X * To.Y + Offset.X, VecX.Y * To.X + VecY.Y * To.Y + Offset.Y);

                if (PointsY > 1)
                {
                    Line XLine = new Line();
                    XLine.Stroke = new SolidColorBrush(Colors.Black);

                    XLine.X1 = From.X;
                    XLine.Y1 = From.Y;
                    XLine.X2 = To.X;
                    XLine.Y2 = To.Y;

                    MainCanvas.Children.Add(XLine);
                }
                else
                {
                    Ellipse XCircle = new Ellipse();
                    XCircle.Fill = new SolidColorBrush(Colors.Black);
                    XCircle.Width = 4;
                    XCircle.Height = 4;

                    MainCanvas.Children.Add(XCircle);
                    Canvas.SetLeft(XCircle, From.X - 2f);
                    Canvas.SetTop(XCircle, From.Y - 2f);
                }

                if (PointsZ > 1 && (x == PointsX - 1 || PointsY <= 1))
                {
                    for (int z = 1; z < Math.Min(4, PointsZ); z++)
                    {
                        float GreyValue = z / 5f;
                        Color LineColor = Color.FromScRgb(1f, GreyValue, GreyValue, GreyValue);

                        if (PointsY > 1)
                        {
                            Line XLine = new Line();
                            XLine.Stroke = new SolidColorBrush(LineColor);

                            XLine.X1 = From.X;
                            XLine.Y1 = From.Y + 4 * z;
                            XLine.X2 = To.X;
                            XLine.Y2 = To.Y + 4 * z;

                            MainCanvas.Children.Add(XLine);
                        }
                        else
                        {
                            Ellipse XCircle = new Ellipse();
                            XCircle.Fill = new SolidColorBrush(LineColor);
                            XCircle.Width = 4;
                            XCircle.Height = 4;

                            MainCanvas.Children.Add(XCircle);
                            Canvas.SetLeft(XCircle, From.X - 2f);
                            Canvas.SetTop(XCircle, From.Y - 2f + 4 * z);
                        }
                    }
                }
            }

            for (int y = 0; y < PointsY; y++)
            {
                float2 From = new float2(Origin.X, Origin.Y + y * StepY);
                From = new float2(VecX.X * From.X + VecY.X * From.Y + Offset.X, VecX.Y * From.X + VecY.Y * From.Y + Offset.Y);
                float2 To = new float2(Origin.X + (PointsX - 1) * StepX, Origin.Y + y * StepY);
                To = new float2(VecX.X * To.X + VecY.X * To.Y + Offset.X, VecX.Y * To.X + VecY.Y * To.Y + Offset.Y);

                if (PointsX > 1)
                {
                    Line YLine = new Line();
                    YLine.Stroke = new SolidColorBrush(Colors.Black);

                    YLine.X1 = From.X;
                    YLine.Y1 = From.Y;
                    YLine.X2 = To.X;
                    YLine.Y2 = To.Y;

                    MainCanvas.Children.Add(YLine);
                }
                else
                {
                    Ellipse YCircle = new Ellipse();
                    YCircle.Fill = new SolidColorBrush(Colors.Black);
                    YCircle.Width = 4;
                    YCircle.Height = 4;

                    MainCanvas.Children.Add(YCircle);
                    Canvas.SetLeft(YCircle, From.X - 2f);
                    Canvas.SetTop(YCircle, From.Y - 2f);
                }

                if (PointsZ > 1 && (y == PointsY - 1 || PointsX <= 1))
                {
                    for (int z = 1; z < Math.Min(4, PointsZ); z++)
                    {
                        float GreyValue = z / 5f;
                        Color LineColor = Color.FromScRgb(1f, GreyValue, GreyValue, GreyValue);

                        if (PointsX > 1)
                        {
                            Line YLine = new Line();
                            YLine.Stroke = new SolidColorBrush(LineColor);

                            YLine.X1 = From.X;
                            YLine.Y1 = From.Y + 4 * z;
                            YLine.X2 = To.X;
                            YLine.Y2 = To.Y + 4 * z;

                            MainCanvas.Children.Add(YLine);
                        }
                        else
                        {
                            Ellipse YCircle = new Ellipse();
                            YCircle.Fill = new SolidColorBrush(LineColor);
                            YCircle.Width = 4;
                            YCircle.Height = 4;

                            MainCanvas.Children.Add(YCircle);
                            Canvas.SetLeft(YCircle, From.X - 2f);
                            Canvas.SetTop(YCircle, From.Y - 2f + 4 * z);
                        }
                    }
                }
            }
        }
Exemple #10
0
        public Image AlignOneTiltMovie(Image tiltMovie, Image template, float initialAngle, float2 initialShift, float resolution)
        {
            float DownscaleFactor = (float)CTF.PixelSize * 2 / resolution;

            //template.Bandpass(0.02f, DownscaleFactor, false);
            //tiltMovie.Bandpass(0.02f, DownscaleFactor, false);

            //template = template.AsPadded(new int2(template.Dims) - 512);

            int2 DimsTemplate = new int2(template.Dims);
            int2 DimsTemplateCoarse = new int2(DimsTemplate) * DownscaleFactor / 2 * 2;
            int2 DimsFrame = new int2(tiltMovie.Dims);
            int NFrames = tiltMovie.Dims.Z;

            //GPU.Normalize(tiltMovie.GetDevice(Intent.Read),
            //              tiltMovie.GetDevice(Intent.Write),
            //              (uint)tiltMovie.ElementsSliceReal,
            //              (uint)tiltMovie.Dims.Z);

            Image TemplateCoarse = template.AsScaled(DimsTemplateCoarse);

            float GlobalAngle = initialAngle;
            float ConditioningAngle = 180f / DimsFrame.X;
            CubicGrid GridFrameX = new CubicGrid(new int3(1, 1, 1), initialShift.X, initialShift.X, Dimension.X);
            CubicGrid GridFrameY = new CubicGrid(new int3(1, 1, 1), initialShift.Y, initialShift.Y, Dimension.X);

            Action<double[]> SetFromVector = input =>
            {
                GlobalAngle = (float)input[0] * ConditioningAngle;
                GridFrameX = new CubicGrid(GridFrameX.Dimensions, input.Skip(1).Take((int)GridFrameX.Dimensions.Elements()).Select(v => (float)v).ToArray());
                GridFrameY = new CubicGrid(GridFrameY.Dimensions, input.Skip(1 + (int)GridFrameX.Dimensions.Elements()).Take((int)GridFrameY.Dimensions.Elements()).Select(v => (float)v).ToArray());
            };

            Func<double[], double[]> EvalIndividual = input =>
            {
                SetFromVector(input);

                Image Transformed;

                float GridStep = 1f / Math.Max(NFrames - 1, 1);
                float2[] FrameShifts = new float2[NFrames];
                for (int i = 0; i < NFrames; i++)
                    FrameShifts[i] = new float2(GridFrameX.GetInterpolated(new float3(0.5f, 0.5f, i * GridStep)),
                                                GridFrameY.GetInterpolated(new float3(0.5f, 0.5f, i * GridStep)));

                float[] FrameAngles = new float[NFrames].Select(v => -GlobalAngle * Helper.ToRad).ToArray();

                Image MovieCopy = new Image(IntPtr.Zero, tiltMovie.Dims);
                //MovieCopy.ShiftSlicesMassive(FrameShifts);

                GPU.ShiftAndRotate2D(tiltMovie.GetDevice(Intent.Read),
                                     MovieCopy.GetDevice(Intent.Write),
                                     DimsFrame,
                                     Helper.ToInterleaved(FrameShifts),
                                     FrameAngles,
                                     (uint)NFrames);

                Transformed = MovieCopy.AsPadded(DimsTemplate);
                MovieCopy.Dispose();

                Transformed.MultiplySlices(template);

                Image Sums = new Image(IntPtr.Zero, new int3(NFrames, 1, 1));
                GPU.Sum(Transformed.GetDevice(Intent.Read),
                        Sums.GetDevice(Intent.Write),
                        (uint)Transformed.ElementsSliceReal,
                        (uint)NFrames);

                Transformed.Dispose();

                double[] Result = new double[NFrames];
                for (int i = 0; i < NFrames; i++)
                    Result[i] = Sums.GetHost(Intent.Read)[0][i] / Transformed.ElementsSliceReal * 100;

                return Result;
            };

            Func<double[], double> Eval = input =>
            {
                double[] Scores = EvalIndividual(input);
                double Score = Scores.Sum();
                Debug.WriteLine(Score);

                return Score;
            };

            Func<double[], double[]> Grad = input =>
            {
                double Delta = 0.1 / DownscaleFactor;
                double[] Result = new double[input.Length];

                for (int i = 0; i < input.Length; i++)
                {
                    double[] InputPlus = input.ToArray();
                    InputPlus[i] += Delta;
                    double ScorePlus = EvalIndividual(InputPlus).Sum();

                    double[] InputMinus = input.ToArray();
                    InputMinus[i] -= Delta;
                    double ScoreMinus = EvalIndividual(InputMinus).Sum();

                    Result[i] = (ScorePlus - ScoreMinus) / (Delta * 2);
                }

                return Result;
            };

            List<double> StartList = new List<double>();
            StartList.Add(GlobalAngle / ConditioningAngle);
            StartList.AddRange(GridFrameX.FlatValues.Select(v => (double)v));
            StartList.AddRange(GridFrameY.FlatValues.Select(v => (double)v));
            double[] StartVector = StartList.ToArray();

            BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(StartVector.Length, Eval, Grad);
            Optimizer.Maximize(StartVector);

            TemplateCoarse.Dispose();

            return null;
        }
Exemple #11
0
        public void Correlate(Image tiltStack, Image reference, int size, float lowpassAngstrom, float highpassAngstrom, int3 volumeDimensions, int healpixOrder, string symmetry = "C1")
        {
            if (!Directory.Exists(CorrelationDir))
                Directory.CreateDirectory(CorrelationDir);

            float DownscaleFactor = lowpassAngstrom / (float)(CTF.PixelSize * 2);
            Image DownscaledStack = tiltStack.AsScaledMassive(new int2(tiltStack.Dims) / DownscaleFactor / 2 * 2);
            tiltStack.FreeDevice();

            float HighpassNyquist = (float)(CTF.PixelSize * 2) * DownscaleFactor / highpassAngstrom;
            DownscaledStack.Bandpass(HighpassNyquist, 1, false);

            Image ScaledReference = reference.AsScaled(reference.Dims / DownscaleFactor / 2 * 2);
            reference.FreeDevice();
            Image PaddedReference = ScaledReference.AsPadded(new int3(size, size, size));
            ScaledReference.Dispose();
            PaddedReference.Bandpass(HighpassNyquist, 1, true);
            Projector ProjectorReference = new Projector(PaddedReference, 2);
            PaddedReference.Dispose();

            VolumeDimensions = volumeDimensions / DownscaleFactor / 2 * 2;
            int SizeCropped = size / 2;

            int3 Grid = (VolumeDimensions + SizeCropped - 1) / SizeCropped;
            List<float3> GridCoords = new List<float3>();
            for (int z = 0; z < Grid.Z; z++)
                for (int y = 0; y < Grid.Y; y++)
                    for (int x = 0; x < Grid.X; x++)
                        GridCoords.Add(new float3(x * SizeCropped + SizeCropped / 2,
                                                  y * SizeCropped + SizeCropped / 2,
                                                  z * SizeCropped + SizeCropped / 2));

            float3[] HealpixAngles = Helper.GetHealpixAngles(healpixOrder, symmetry).Select(a => a * Helper.ToRad).ToArray();

            Image CTFCoords = GetCTFCoords(size, (int)(size * DownscaleFactor));

            float[] OutputCorr = new float[VolumeDimensions.Elements()];

            int PlanForw, PlanBack, PlanForwCTF;
            Projector.GetPlans(new int3(size, size, size), 2, out PlanForw, out PlanBack, out PlanForwCTF);

            int BatchSize = 16;
            for (int b = 0; b < GridCoords.Count; b += BatchSize)
            {
                int CurBatch = Math.Min(BatchSize, GridCoords.Count - b);

                Image Subtomos = new Image(IntPtr.Zero, new int3(size, size, size * CurBatch), true, true);
                Image SubtomoCTFs = new Image(IntPtr.Zero, new int3(size, size, size * CurBatch), true);

                for (int st = 0; st < CurBatch; st++)
                {
                    Image ImagesFT = GetSubtomoImages(DownscaledStack, size, GridCoords[b + st], true);
                    Image CTFs = GetSubtomoCTFs(GridCoords[b + st], CTFCoords);
                    //Image CTFWeights = GetSubtomoCTFs(GridCoords[b + st], CTFCoords, true, true);

                    ImagesFT.Multiply(CTFs);    // Weight and phase-flip image FTs by CTF, which still has its sign here
                    //ImagesFT.Multiply(CTFWeights);
                    CTFs.Abs();                 // CTF has to be positive from here on since image FT phases are now flipped

                    // CTF has to be converted to complex numbers with imag = 0, and weighted by itself
                    float2[] CTFsComplexData = new float2[CTFs.ElementsComplex];
                    float[] CTFsContinuousData = CTFs.GetHostContinuousCopy();
                    for (int i = 0; i < CTFsComplexData.Length; i++)
                        CTFsComplexData[i] = new float2(CTFsContinuousData[i] * CTFsContinuousData[i], 0);

                    Image CTFsComplex = new Image(CTFsComplexData, CTFs.Dims, true);

                    Projector ProjSubtomo = new Projector(new int3(size, size, size), 2);
                    lock (GPU.Sync)
                        ProjSubtomo.BackProject(ImagesFT, CTFs, GetAngleInImages(GridCoords[b + st]));
                    Image Subtomo = ProjSubtomo.Reconstruct(false, PlanForw, PlanBack, PlanForwCTF);
                    ProjSubtomo.Dispose();

                    /*Image CroppedSubtomo = Subtomo.AsPadded(new int3(SizeCropped, SizeCropped, SizeCropped));
                    CroppedSubtomo.WriteMRC(ParticlesDir + RootName + "_" + (b + st).ToString("D5") + ".mrc");
                    CroppedSubtomo.Dispose();*/

                    Projector ProjCTF = new Projector(new int3(size, size, size), 2);
                    lock (GPU.Sync)
                        ProjCTF.BackProject(CTFsComplex, CTFs, GetAngleInImages(GridCoords[b + st]));
                    Image SubtomoCTF = ProjCTF.Reconstruct(true, PlanForw, PlanBack, PlanForwCTF);
                    ProjCTF.Dispose();

                    GPU.FFT(Subtomo.GetDevice(Intent.Read), Subtomos.GetDeviceSlice(size * st, Intent.Write), Subtomo.Dims, 1);
                    GPU.CopyDeviceToDevice(SubtomoCTF.GetDevice(Intent.Read), SubtomoCTFs.GetDeviceSlice(size * st, Intent.Write), SubtomoCTF.ElementsReal);

                    ImagesFT.Dispose();
                    CTFs.Dispose();
                    //CTFWeights.Dispose();
                    CTFsComplex.Dispose();

                    Subtomo.Dispose();
                    SubtomoCTF.Dispose();
                }

                Image BestCorrelation = new Image(IntPtr.Zero, new int3(size, size, size * CurBatch));
                Image BestRot = new Image(IntPtr.Zero, new int3(size, size, size * CurBatch));
                Image BestTilt = new Image(IntPtr.Zero, new int3(size, size, size * CurBatch));
                Image BestPsi = new Image(IntPtr.Zero, new int3(size, size, size * CurBatch));

                GPU.CorrelateSubTomos(ProjectorReference.Data.GetDevice(Intent.Read),
                                      ProjectorReference.Oversampling,
                                      ProjectorReference.Data.Dims,
                                      Subtomos.GetDevice(Intent.Read),
                                      SubtomoCTFs.GetDevice(Intent.Read),
                                      new int3(size, size, size),
                                      (uint)CurBatch,
                                      Helper.ToInterleaved(HealpixAngles),
                                      (uint)HealpixAngles.Length,
                                      MainWindow.Options.ExportParticleRadius / ((float)CTF.PixelSize * DownscaleFactor),
                                      BestCorrelation.GetDevice(Intent.Write),
                                      BestRot.GetDevice(Intent.Write),
                                      BestTilt.GetDevice(Intent.Write),
                                      BestPsi.GetDevice(Intent.Write));

                for (int st = 0; st < CurBatch; st++)
                {
                    Image ThisCorrelation = new Image(BestCorrelation.GetDeviceSlice(size * st, Intent.Read), new int3(size, size, size));
                    Image CroppedCorrelation = ThisCorrelation.AsPadded(new int3(SizeCropped, SizeCropped, SizeCropped));

                    //CroppedCorrelation.WriteMRC(CorrelationDir + RootName + "_" + (b + st).ToString("D5") + ".mrc");
                    float[] SubCorr = CroppedCorrelation.GetHostContinuousCopy();
                    int3 Origin = new int3(GridCoords[b + st]) - SizeCropped / 2;
                    for (int z = 0; z < SizeCropped; z++)
                    {
                        int zVol = Origin.Z + z;
                        if (zVol >= VolumeDimensions.Z)
                            continue;

                        for (int y = 0; y < SizeCropped; y++)
                        {
                            int yVol = Origin.Y + y;
                            if (yVol >= VolumeDimensions.Y)
                                continue;

                            for (int x = 0; x < SizeCropped; x++)
                            {
                                int xVol = Origin.X + x;
                                if (xVol >= VolumeDimensions.X)
                                    continue;

                                OutputCorr[(zVol * VolumeDimensions.Y + yVol) * VolumeDimensions.X + xVol] = SubCorr[(z * SizeCropped + y) * SizeCropped + x];
                            }
                        }
                    }

                    CroppedCorrelation.Dispose();
                    ThisCorrelation.Dispose();
                }

                Subtomos.Dispose();
                SubtomoCTFs.Dispose();

                BestCorrelation.Dispose();
                BestRot.Dispose();
                BestTilt.Dispose();
                BestPsi.Dispose();
            }

            GPU.DestroyFFTPlan(PlanForw);
            GPU.DestroyFFTPlan(PlanBack);
            GPU.DestroyFFTPlan(PlanForwCTF);

            CTFCoords.Dispose();
            ProjectorReference.Dispose();
            DownscaledStack.Dispose();

            Image OutputCorrImage = new Image(OutputCorr, VolumeDimensions);
            OutputCorrImage.WriteMRC(CorrelationDir + RootName + ".mrc");
            OutputCorrImage.Dispose();
        }
Exemple #12
0
        public void PerformOptimizationStep(Star tableIn, Image tiltStack, int size, int3 volumeDimensions, Dictionary<int, Projector> references, float resolution, Dictionary<int, Projector> outReconstructions, Dictionary<int, Projector> outCTFReconstructions)
        {
            VolumeDimensions = volumeDimensions;

            #region Get rows from table

            List<int> RowIndices = new List<int>();
            string[] ColumnMicrographName = tableIn.GetColumn("rlnMicrographName");
            for (int i = 0; i < ColumnMicrographName.Length; i++)
                if (ColumnMicrographName[i].Contains(RootName + "."))
                    RowIndices.Add(i);

            if (RowIndices.Count == 0)
                return;

            int NParticles = RowIndices.Count;

            #endregion

            #region Make sure all columns and directories are there

            if (!tableIn.HasColumn("rlnImageName"))
                tableIn.AddColumn("rlnImageName");
            if (!tableIn.HasColumn("rlnCtfImage"))
                tableIn.AddColumn("rlnCtfImage");
            if (!tableIn.HasColumn("rlnParticleSelectZScore"))
                tableIn.AddColumn("rlnParticleSelectZScore");

            if (!Directory.Exists(ParticlesDir))
                Directory.CreateDirectory(ParticlesDir);
            if (!Directory.Exists(ParticleCTFDir))
                Directory.CreateDirectory(ParticleCTFDir);

            #endregion

            #region Get subtomo positions from table

            float3[] ParticleOrigins = new float3[NParticles];
            float3[] ParticleOrigins2 = new float3[NParticles];
            float3[] ParticleAngles = new float3[NParticles];
            float3[] ParticleAngles2 = new float3[NParticles];
            int[] ParticleSubset = new int[NParticles];
            {
                string[] ColumnPosX = tableIn.GetColumn("rlnCoordinateX");
                string[] ColumnPosY = tableIn.GetColumn("rlnCoordinateY");
                string[] ColumnPosZ = tableIn.GetColumn("rlnCoordinateZ");
                string[] ColumnOriginX = tableIn.GetColumn("rlnOriginX");
                string[] ColumnOriginY = tableIn.GetColumn("rlnOriginY");
                string[] ColumnOriginZ = tableIn.GetColumn("rlnOriginZ");
                string[] ColumnAngleRot = tableIn.GetColumn("rlnAngleRot");
                string[] ColumnAngleTilt = tableIn.GetColumn("rlnAngleTilt");
                string[] ColumnAnglePsi = tableIn.GetColumn("rlnAnglePsi");
                string[] ColumnSubset = tableIn.GetColumn("rlnRandomSubset");

                string[] ColumnPosX2 = tableIn.GetColumn("rlnOriginXPrior");
                string[] ColumnPosY2 = tableIn.GetColumn("rlnOriginYPrior");
                string[] ColumnPosZ2 = tableIn.GetColumn("rlnOriginZPrior");
                string[] ColumnAngleRot2 = tableIn.GetColumn("rlnAngleRotPrior");
                string[] ColumnAngleTilt2 = tableIn.GetColumn("rlnAngleTiltPrior");
                string[] ColumnAnglePsi2 = tableIn.GetColumn("rlnAnglePsiPrior");

                for (int i = 0; i < NParticles; i++)
                {
                    float3 Pos = new float3(float.Parse(ColumnPosX[RowIndices[i]], CultureInfo.InvariantCulture),
                                            float.Parse(ColumnPosY[RowIndices[i]], CultureInfo.InvariantCulture),
                                            float.Parse(ColumnPosZ[RowIndices[i]], CultureInfo.InvariantCulture));
                    float3 Pos2 = Pos;
                    //if (ColumnPosX2 != null && ColumnPosY2 != null && ColumnPosZ2 != null)
                    //    Pos2 = new float3(float.Parse(ColumnPosX2[RowIndices[i]], CultureInfo.InvariantCulture),
                    //                      float.Parse(ColumnPosY2[RowIndices[i]], CultureInfo.InvariantCulture),
                    //                      float.Parse(ColumnPosZ2[RowIndices[i]], CultureInfo.InvariantCulture));

                    float3 Shift = new float3(float.Parse(ColumnOriginX[RowIndices[i]], CultureInfo.InvariantCulture),
                                              float.Parse(ColumnOriginY[RowIndices[i]], CultureInfo.InvariantCulture),
                                              float.Parse(ColumnOriginZ[RowIndices[i]], CultureInfo.InvariantCulture));

                    ParticleOrigins[i] = Pos - Shift;
                    ParticleOrigins2[i] = Pos2 - Shift;

                    float3 Angle = new float3(float.Parse(ColumnAngleRot[RowIndices[i]], CultureInfo.InvariantCulture),
                                              float.Parse(ColumnAngleTilt[RowIndices[i]], CultureInfo.InvariantCulture),
                                              float.Parse(ColumnAnglePsi[RowIndices[i]], CultureInfo.InvariantCulture));
                    float3 Angle2 = Angle;
                    //if (ColumnAngleRot2 != null && ColumnAngleTilt2 != null && ColumnAnglePsi2 != null)
                    //    Angle2 = new float3(float.Parse(ColumnAngleRot2[RowIndices[i]], CultureInfo.InvariantCulture),
                    //                        float.Parse(ColumnAngleTilt2[RowIndices[i]], CultureInfo.InvariantCulture),
                    //                        float.Parse(ColumnAnglePsi2[RowIndices[i]], CultureInfo.InvariantCulture));

                    ParticleAngles[i] = Angle;
                    ParticleAngles2[i] = Angle2;

                    ParticleSubset[i] = int.Parse(ColumnSubset[RowIndices[i]]);

                    tableIn.SetRowValue(RowIndices[i], "rlnCoordinateX", ParticleOrigins[i].X.ToString(CultureInfo.InvariantCulture));
                    tableIn.SetRowValue(RowIndices[i], "rlnCoordinateY", ParticleOrigins[i].Y.ToString(CultureInfo.InvariantCulture));
                    tableIn.SetRowValue(RowIndices[i], "rlnCoordinateZ", ParticleOrigins[i].Z.ToString(CultureInfo.InvariantCulture));
                    tableIn.SetRowValue(RowIndices[i], "rlnOriginX", "0.0");
                    tableIn.SetRowValue(RowIndices[i], "rlnOriginY", "0.0");
                    tableIn.SetRowValue(RowIndices[i], "rlnOriginZ", "0.0");
                }
            }

            #endregion

            #region Deal with subsets

            List<int> SubsetIDs = new List<int>();
            foreach (var i in ParticleSubset)
                if (!SubsetIDs.Contains(i))
                    SubsetIDs.Add(i);
            SubsetIDs.Sort();

            // For each subset, create a list of its particle IDs
            Dictionary<int, List<int>> SubsetParticleIDs = SubsetIDs.ToDictionary(subsetID => subsetID, subsetID => new List<int>());
            for (int i = 0; i < ParticleSubset.Length; i++)
                SubsetParticleIDs[ParticleSubset[i]].Add(i);
            foreach (var list in SubsetParticleIDs.Values)
                list.Sort();

            // Note where each subset starts and ends in a unified, sorted (by subset) particle ID list
            Dictionary<int, Tuple<int, int>> SubsetRanges = new Dictionary<int, Tuple<int, int>>();
            {
                int Start = 0;
                foreach (var pair in SubsetParticleIDs)
                {
                    SubsetRanges.Add(pair.Key, new Tuple<int, int>(Start, Start + pair.Value.Count));
                    Start += pair.Value.Count;
                }
            }

            List<int> SubsetContinuousIDs = new List<int>();
            foreach (var pair in SubsetParticleIDs)
                SubsetContinuousIDs.AddRange(pair.Value);

            // Reorder particle information to match the order of SubsetContinuousIDs
            ParticleOrigins = SubsetContinuousIDs.Select(i => ParticleOrigins[i]).ToArray();
            ParticleOrigins2 = SubsetContinuousIDs.Select(i => ParticleOrigins2[i]).ToArray();
            ParticleAngles = SubsetContinuousIDs.Select(i => ParticleAngles[i]).ToArray();
            ParticleAngles2 = SubsetContinuousIDs.Select(i => ParticleAngles2[i]).ToArray();
            ParticleSubset = SubsetContinuousIDs.Select(i => ParticleSubset[i]).ToArray();

            #endregion

            if (GridMovementX.Dimensions.Elements() == 1)
            {
                int MaxSlice = SubsetRanges.Last().Value.Item2 > 100 ? 1 : 1;

                GridMovementX = new CubicGrid(new int3(MaxSlice, MaxSlice, NTilts));
                GridMovementY = new CubicGrid(new int3(MaxSlice, MaxSlice, NTilts));

                //GridLocalX = new CubicGrid(new int3(4, 4, 4));
                //GridLocalY = new CubicGrid(new int3(4, 4, 4));
                //GridLocalZ = new CubicGrid(new int3(4, 4, 4));

                GridAngleX = new CubicGrid(new int3(1, 1, NTilts));
                GridAngleY = new CubicGrid(new int3(1, 1, NTilts));
                GridAngleZ = new CubicGrid(new int3(1, 1, NTilts));
            }
            if (GridLocalX.Dimensions.Elements() == 1)
            {
                GridLocalX = new CubicGrid(new int3(4, 4, 4));
                GridLocalY = new CubicGrid(new int3(4, 4, 4));
                GridLocalZ = new CubicGrid(new int3(4, 4, 4));
            }
            //else
            //{
            //    GridMovementX = GridMovementX.Resize(new int3(4, 4, NTilts));
            //    GridMovementY = GridMovementY.Resize(new int3(4, 4, NTilts));
            //}

            int CoarseSize = (int)Math.Round(size * ((float)CTF.PixelSize * 2 / resolution)) / 2 * 2;
            int3 CoarseDims = new int3(CoarseSize, CoarseSize, 1);

            // Positions the particles were extracted at/shifted to, to calculate effectively needed shifts later
            float2[] ExtractedAt = new float2[NParticles * NTilts];

            // Extract images, mask and resize them, create CTFs
            Image ParticleImages = new Image(new int3(CoarseSize, CoarseSize, NParticles * NTilts), true, true);
            Image ParticleCTFs = new Image(new int3(CoarseSize, CoarseSize, NParticles * NTilts), true);
            Image ParticleWeights = null;
            Image ShiftFactors = null;

            #region Preflight
            float KeepBFac = GlobalBfactor;
            GlobalBfactor = 0;
            {
                Image CTFCoords = GetCTFCoords(CoarseSize, size);

                #region Precalculate vectors for shifts in Fourier space
                {
                    float2[] ShiftFactorsData = new float2[(CoarseSize / 2 + 1) * CoarseSize];
                    for (int y = 0; y < CoarseSize; y++)
                        for (int x = 0; x < CoarseSize / 2 + 1; x++)
                        {
                            int xx = x;
                            int yy = y < CoarseSize / 2 + 1 ? y : y - CoarseSize;

                            ShiftFactorsData[y * (CoarseSize / 2 + 1) + x] = new float2((float)-xx / size * 2f * (float)Math.PI,
                                                                                          (float)-yy / size * 2f * (float)Math.PI);
                        }

                    ShiftFactors = new Image(ShiftFactorsData, new int3(CoarseSize, CoarseSize, 1), true);
                }
                #endregion

                #region Create mask with soft edge
                Image Mask;
                Image MaskSubt;
                {
                    Image MaskBig = new Image(new int3(size, size, 1));
                    float MaskRadius = MainWindow.Options.ExportParticleRadius / (float)CTF.PixelSize;
                    float SoftEdge = 16f;

                    float[] MaskBigData = MaskBig.GetHost(Intent.Write)[0];
                    for (int y = 0; y < size; y++)
                    {
                        int yy = y - size / 2;
                        yy *= yy;
                        for (int x = 0; x < size; x++)
                        {
                            int xx = x - size / 2;
                            xx *= xx;
                            float R = (float)Math.Sqrt(xx + yy);

                            if (R <= MaskRadius)
                                MaskBigData[y * size + x] = 1;
                            else
                                MaskBigData[y * size + x] = (float)(Math.Cos(Math.Min(1, (R - MaskRadius) / SoftEdge) * Math.PI) * 0.5 + 0.5);
                        }
                    }
                    //MaskBig.WriteMRC("d_maskbig.mrc");

                    Mask = MaskBig.AsScaled(new int2(CoarseSize, CoarseSize));
                    Mask.RemapToFT();

                    MaskBigData = MaskBig.GetHost(Intent.Write)[0];
                    for (int y = 0; y < size; y++)
                    {
                        int yy = y - size / 2;
                        yy *= yy;
                        for (int x = 0; x < size; x++)
                        {
                            int xx = x - size / 2;
                            xx *= xx;
                            float R = (float)Math.Sqrt(xx + yy);

                            if (R <= 30)
                                MaskBigData[y * size + x] = 1;
                            else
                                MaskBigData[y * size + x] = 0;
                        }
                    }

                    MaskSubt = MaskBig.AsScaled(new int2(CoarseSize, CoarseSize));
                    MaskSubt.RemapToFT();

                    MaskBig.Dispose();
                }
                //Mask.WriteMRC("d_masksmall.mrc");
                #endregion

                #region Create Fourier space mask
                Image FourierMask = new Image(CoarseDims, true);
                {
                    float[] FourierMaskData = FourierMask.GetHost(Intent.Write)[0];
                    int MaxR2 = CoarseSize * CoarseSize / 4;
                    for (int y = 0; y < CoarseSize; y++)
                    {
                        int yy = y < CoarseSize / 2 + 1 ? y : y - CoarseSize;
                        yy *= yy;

                        for (int x = 0; x < CoarseSize / 2 + 1; x++)
                        {
                            int xx = x * x;
                            int R2 = yy + xx;

                            FourierMaskData[y * (CoarseSize / 2 + 1) + x] = R2 < MaxR2 ? 1 : 0;
                        }
                    }
                }
                #endregion

                #region For each particle, create CTFs and extract & preprocess images for entire tilt series
                for (int p = 0; p < NParticles; p++)
                {
                    float3 ParticleCoords = ParticleOrigins[p];
                    float3[] Positions = GetPositionInImages(ParticleCoords);
                    float3[] ProjAngles = GetParticleAngleInImages(ParticleCoords, ParticleAngles[p]);

                    Image Extracted = new Image(new int3(size, size, NTilts));
                    float[][] ExtractedData = Extracted.GetHost(Intent.Write);
                    float3[] Residuals = new float3[NTilts];

                    Image SubtrahendsCTF = new Image(new int3(CoarseSize, CoarseSize, NTilts), true);

                    // Create CTFs
                    {
                        CTFStruct[] CTFParams = new CTFStruct[NTilts];

                        float GridStep = 1f / (NTilts - 1);
                        CTFStruct[] Params = new CTFStruct[NTilts];
                        for (int t = 0; t < NTilts; t++)
                        {
                            decimal Defocus = (decimal)Positions[t].Z;
                            decimal DefocusDelta = (decimal)GridCTFDefocusDelta.GetInterpolated(new float3(0.5f, 0.5f, t * GridStep));
                            decimal DefocusAngle = (decimal)GridCTFDefocusAngle.GetInterpolated(new float3(0.5f, 0.5f, t * GridStep));

                            CTF CurrCTF = CTF.GetCopy();
                            CurrCTF.Defocus = Defocus;
                            CurrCTF.DefocusDelta = DefocusDelta;
                            CurrCTF.DefocusAngle = DefocusAngle;
                            CurrCTF.Scale = (decimal)Math.Cos(Angles[t] * Helper.ToRad);
                            CurrCTF.Bfactor = (decimal)-Dose[t] * 8;

                            Params[t] = CurrCTF.ToStruct();
                        }

                        GPU.CreateCTF(ParticleCTFs.GetDeviceSlice(NTilts * p, Intent.Write),
                                      CTFCoords.GetDevice(Intent.Read),
                                      (uint)CoarseDims.ElementsFFT(),
                                      Params,
                                      false,
                                      (uint)NTilts);
                    }
                    //{
                    //    CTFStruct[] CTFParams = new CTFStruct[NTilts];

                    //    float GridStep = 1f / (NTilts - 1);
                    //    CTFStruct[] Params = new CTFStruct[NTilts];
                    //    for (int t = 0; t < NTilts; t++)
                    //    {
                    //        decimal Defocus = (decimal)Positions[t].Z;
                    //        decimal DefocusDelta = (decimal)GridCTFDefocusDelta.GetInterpolated(new float3(0.5f, 0.5f, t * GridStep));
                    //        decimal DefocusAngle = (decimal)GridCTFDefocusAngle.GetInterpolated(new float3(0.5f, 0.5f, t * GridStep));

                    //        CTF CurrCTF = CTF.GetCopy();
                    //        CurrCTF.Defocus = Defocus;
                    //        CurrCTF.DefocusDelta = DefocusDelta;
                    //        CurrCTF.DefocusAngle = DefocusAngle;
                    //        CurrCTF.Scale = 1;
                    //        CurrCTF.Bfactor = 0;

                    //        Params[t] = CurrCTF.ToStruct();
                    //    }

                    //    GPU.CreateCTF(SubtrahendsCTF.GetDevice(Intent.Write),
                    //                  CTFCoords.GetDevice(Intent.Read),
                    //                  (uint)CoarseDims.ElementsFFT(),
                    //                  Params,
                    //                  false,
                    //                  (uint)NTilts);
                    //}

                    // Extract images
                    {
                        for (int t = 0; t < NTilts; t++)
                        {
                            ExtractedAt[p * NTilts + t] = new float2(Positions[t].X, Positions[t].Y);

                            Positions[t] -= size / 2;
                            int2 IntPosition = new int2((int)Positions[t].X, (int)Positions[t].Y);
                            float2 Residual = new float2(-(Positions[t].X - IntPosition.X), -(Positions[t].Y - IntPosition.Y));
                            Residuals[t] = new float3(Residual / size * CoarseSize);

                            float[] OriginalData;
                            lock (tiltStack)
                                OriginalData = tiltStack.GetHost(Intent.Read)[t];

                            float[] ImageData = ExtractedData[t];
                            for (int y = 0; y < size; y++)
                            {
                                int PosY = (y + IntPosition.Y + tiltStack.Dims.Y) % tiltStack.Dims.Y;
                                for (int x = 0; x < size; x++)
                                {
                                    int PosX = (x + IntPosition.X + tiltStack.Dims.X) % tiltStack.Dims.X;
                                    ImageData[y * size + x] = OriginalData[PosY * tiltStack.Dims.X + PosX];
                                }
                            }
                        }

                        GPU.NormParticles(Extracted.GetDevice(Intent.Read),
                                          Extracted.GetDevice(Intent.Write),
                                          new int3(size, size, 1),
                                          (uint)(MainWindow.Options.ExportParticleRadius / CTF.PixelSize),
                                          true,
                                          (uint)NTilts);

                        Image Scaled = Extracted.AsScaled(new int2(CoarseSize, CoarseSize));
                        //Scaled.WriteMRC("d_scaled.mrc");
                        Extracted.Dispose();

                        Scaled.ShiftSlices(Residuals);
                        Scaled.RemapToFT();

                        //GPU.NormalizeMasked(Scaled.GetDevice(Intent.Read),
                        //              Scaled.GetDevice(Intent.Write),
                        //              MaskSubt.GetDevice(Intent.Read),
                        //              (uint)Scaled.ElementsSliceReal,
                        //              (uint)NTilts);

                        //{
                        //    //Image SubtrahendsFT = subtrahendReference.Project(new int2(CoarseSize, CoarseSize), ProjAngles, CoarseSize / 2);
                        //    //SubtrahendsFT.Multiply(SubtrahendsCTF);

                        //    //Image Subtrahends = SubtrahendsFT.AsIFFT();
                        //    //SubtrahendsFT.Dispose();

                        //    ////GPU.NormalizeMasked(Subtrahends.GetDevice(Intent.Read),
                        //    ////                    Subtrahends.GetDevice(Intent.Write),
                        //    ////                    MaskSubt.GetDevice(Intent.Read),
                        //    ////                    (uint)Subtrahends.ElementsSliceReal,
                        //    ////                    (uint)NTilts);

                        //    //Scaled.Subtract(Subtrahends);
                        //    //Subtrahends.Dispose();

                        //    Image FocusMaskFT = maskReference.Project(new int2(CoarseSize, CoarseSize), ProjAngles, CoarseSize / 2);
                        //    Image FocusMask = FocusMaskFT.AsIFFT();
                        //    FocusMaskFT.Dispose();

                        //    Scaled.Multiply(FocusMask);
                        //    FocusMask.Dispose();
                        //}

                        Scaled.MultiplySlices(Mask);

                        GPU.FFT(Scaled.GetDevice(Intent.Read),
                                ParticleImages.GetDeviceSlice(p * NTilts, Intent.Write),
                                CoarseDims,
                                (uint)NTilts);

                        Scaled.Dispose();
                        SubtrahendsCTF.Dispose();
                    }
                }
                #endregion

                ParticleCTFs.MultiplySlices(FourierMask);

                Mask.Dispose();
                FourierMask.Dispose();
                MaskSubt.Dispose();

                Image ParticleCTFsAbs = new Image(ParticleCTFs.GetDevice(Intent.Read), ParticleCTFs.Dims, true);
                ParticleCTFsAbs.Abs();
                ParticleWeights = ParticleCTFsAbs.AsSum2D();
                ParticleCTFsAbs.Dispose();
                {
                    float[] ParticleWeightsData = ParticleWeights.GetHost(Intent.ReadWrite)[0];
                    float Max = MathHelper.Max(ParticleWeightsData);
                    for (int i = 0; i < ParticleWeightsData.Length; i++)
                        ParticleWeightsData[i] /= Max;
                }

                CTFCoords.Dispose();

                //Image CheckImages = ParticleImages.AsIFFT();
                //CheckImages.WriteMRC("d_particleimages.mrc");
                //CheckImages.Dispose();

                //ParticleCTFs.WriteMRC("d_particlectfs.mrc");
            }
            GlobalBfactor = KeepBFac;
            #endregion

            bool DoPerParticleMotion = true;
            bool DoImageAlignment = true;

            #region BFGS evaluation and gradient

            double[] StartParams;

            Func<double[], Tuple<float2[], float3[]>> GetImageShiftsAndAngles;
            Func<double[], float2[]> GetImageShifts;
            Func<float3[], Image> GetProjections;
            Func<double[], double[]> EvalIndividual;
            Func<double[], double> Eval;
            Func<double[], double[]> Gradient;
            {
                List<double> StartParamsList = new List<double>();
                StartParamsList.AddRange(CreateVectorFromGrids(Dimensions.X));
                StartParamsList.AddRange(CreateVectorFromParameters(ParticleOrigins, ParticleOrigins2, ParticleAngles, ParticleAngles2, size));
                StartParams = StartParamsList.ToArray();

                // Remember where the values for each grid are stored in the optimized vector
                List<Tuple<int, int>> VectorGridRanges = new List<Tuple<int, int>>();
                List<int> GridElements = new List<int>();
                List<int> GridSliceElements = new List<int>();
                {
                    int Start = 0;
                    VectorGridRanges.Add(new Tuple<int, int>(Start, Start + (int)GridMovementX.Dimensions.Elements()));
                    Start += (int)GridMovementX.Dimensions.Elements();
                    VectorGridRanges.Add(new Tuple<int, int>(Start, Start + (int)GridMovementY.Dimensions.Elements()));
                    Start += (int)GridMovementY.Dimensions.Elements();

                    VectorGridRanges.Add(new Tuple<int, int>(Start, Start + (int)GridAngleX.Dimensions.Elements()));
                    Start += (int)GridAngleX.Dimensions.Elements();
                    VectorGridRanges.Add(new Tuple<int, int>(Start, Start + (int)GridAngleY.Dimensions.Elements()));
                    Start += (int)GridAngleY.Dimensions.Elements();
                    VectorGridRanges.Add(new Tuple<int, int>(Start, Start + (int)GridAngleZ.Dimensions.Elements()));
                    Start += (int)GridAngleZ.Dimensions.Elements();

                    VectorGridRanges.Add(new Tuple<int, int>(Start, Start + (int)GridLocalX.Dimensions.Elements()));
                    Start += (int)GridLocalX.Dimensions.Elements();
                    VectorGridRanges.Add(new Tuple<int, int>(Start, Start + (int)GridLocalY.Dimensions.Elements()));
                    Start += (int)GridLocalY.Dimensions.Elements();
                    VectorGridRanges.Add(new Tuple<int, int>(Start, Start + (int)GridLocalZ.Dimensions.Elements()));

                    GridElements.Add((int)GridMovementX.Dimensions.Elements());
                    GridElements.Add((int)GridMovementY.Dimensions.Elements());

                    GridElements.Add((int)GridAngleX.Dimensions.Elements());
                    GridElements.Add((int)GridAngleY.Dimensions.Elements());
                    GridElements.Add((int)GridAngleZ.Dimensions.Elements());

                    GridElements.Add((int)GridLocalX.Dimensions.Elements());
                    GridElements.Add((int)GridLocalY.Dimensions.Elements());
                    GridElements.Add((int)GridLocalZ.Dimensions.Elements());

                    GridSliceElements.Add((int)GridMovementX.Dimensions.ElementsSlice());
                    GridSliceElements.Add((int)GridMovementY.Dimensions.ElementsSlice());

                    GridSliceElements.Add((int)GridAngleX.Dimensions.ElementsSlice());
                    GridSliceElements.Add((int)GridAngleY.Dimensions.ElementsSlice());
                    GridSliceElements.Add((int)GridAngleZ.Dimensions.ElementsSlice());

                    GridSliceElements.Add((int)GridLocalX.Dimensions.ElementsSlice());
                    GridSliceElements.Add((int)GridLocalY.Dimensions.ElementsSlice());
                    GridSliceElements.Add((int)GridLocalZ.Dimensions.ElementsSlice());
                }
                int NVectorGridParams = VectorGridRanges.Last().Item2;
                int NVectorParticleParams = NParticles * 12;

                GetImageShiftsAndAngles = input =>
                {
                    // Retrieve particle positions & angles, and grids from input vector
                    float3[] NewPositions, NewPositions2, NewAngles, NewAngles2;
                    GetParametersFromVector(input, NParticles, size, out NewPositions, out NewPositions2, out NewAngles, out NewAngles2);
                    SetGridsFromVector(input, Dimensions.X);

                    // Using current positions, angles and grids, get parameters for image shifts and reference projection angles
                    float2[] ImageShifts = new float2[NParticles * NTilts];
                    float3[] ImageAngles = new float3[NParticles * NTilts];
                    float3[] PerTiltPositions = new float3[NParticles * NTilts];
                    float3[] PerTiltAngles = new float3[NParticles * NTilts];
                    int[] SortedDosePrecalc = IndicesSortedDose;
                    for (int p = 0; p < NParticles; p++)
                    {
                        if (DoPerParticleMotion)
                        {
                            float3 CoordsDiff = NewPositions2[p] - NewPositions[p];
                            float3 AnglesDiff = NewAngles2[p] - NewAngles[p];
                            for (int t = 0; t < NTilts; t++)
                            {
                                float DoseID = SortedDosePrecalc[t] / (float)(NTilts - 1);
                                PerTiltPositions[p * NTilts + t] = NewPositions[p] + CoordsDiff * DoseID;
                                PerTiltAngles[p * NTilts + t] = NewAngles[p] + AnglesDiff * DoseID;
                            }
                        }
                        else
                        {
                            for (int t = 0; t < NTilts; t++)
                            {
                                PerTiltPositions[p * NTilts + t] = NewPositions[p];
                                PerTiltAngles[p * NTilts + t] = NewAngles[p];
                            }
                        }
                    }

                    float3[] CurrPositions = GetPositionInImages(PerTiltPositions);
                    float3[] CurrAngles = GetParticleAngleInImages(PerTiltPositions, PerTiltAngles);
                    for (int i = 0; i < ImageShifts.Length; i++)
                    {
                        ImageShifts[i] = new float2(ExtractedAt[i].X - CurrPositions[i].X,
                                                    ExtractedAt[i].Y - CurrPositions[i].Y); // -diff because those are extraction positions, i. e. opposite direction of shifts
                        ImageAngles[i] = CurrAngles[i];
                    }

                    return new Tuple<float2[], float3[]>(ImageShifts, ImageAngles);
                };

                GetImageShifts = input =>
                {
                    // Retrieve particle positions & angles, and grids from input vector
                    float3[] NewPositions, NewPositions2, NewAngles, NewAngles2;
                    GetParametersFromVector(input, NParticles, size, out NewPositions, out NewPositions2, out NewAngles, out NewAngles2);
                    SetGridsFromVector(input, Dimensions.X);

                    // Using current positions, angles and grids, get parameters for image shifts and reference projection angles
                    float2[] ImageShifts = new float2[NParticles * NTilts];
                    float3[] PerTiltPositions = new float3[NParticles * NTilts];
                    int[] SortedDosePrecalc = IndicesSortedDose;
                    for (int p = 0; p < NParticles; p++)
                    {
                        if (DoPerParticleMotion)
                        {
                            float3 CoordsDiff = NewPositions2[p] - NewPositions[p];
                            float3 AnglesDiff = NewAngles2[p] - NewAngles[p];
                            for (int t = 0; t < NTilts; t++)
                            {
                                float DoseID = SortedDosePrecalc[t] / (float)(NTilts - 1);
                                PerTiltPositions[p * NTilts + t] = NewPositions[p] + CoordsDiff * DoseID;
                            }
                        }
                        else
                        {
                            for (int t = 0; t < NTilts; t++)
                                PerTiltPositions[p * NTilts + t] = NewPositions[p];
                        }
                    }

                    float3[] CurrPositions = GetPositionInImages(PerTiltPositions);
                    for (int i = 0; i < ImageShifts.Length; i++)
                        ImageShifts[i] = new float2(ExtractedAt[i].X - CurrPositions[i].X,
                                                    ExtractedAt[i].Y - CurrPositions[i].Y); // -diff because those are extraction positions, i. e. opposite direction of shifts

                    return ImageShifts;
                };

                GetProjections = imageAngles =>
                {
                    Image Projections = new Image(IntPtr.Zero, new int3(CoarseSize, CoarseSize, NParticles * NTilts), true, true);
                    foreach (var subset in SubsetRanges)
                    {
                        Projector Reference = references[subset.Key];
                        int SubsetStart = subset.Value.Item1 * NTilts;
                        int SubsetEnd = subset.Value.Item2 * NTilts;
                        float3[] SubsetAngles = imageAngles.Skip(SubsetStart).Take(SubsetEnd - SubsetStart).ToArray();

                        GPU.ProjectForward(Reference.Data.GetDevice(Intent.Read),
                                           Projections.GetDeviceSlice(SubsetStart, Intent.Write),
                                           Reference.Data.Dims,
                                           new int2(CoarseSize, CoarseSize),
                                           Helper.ToInterleaved(SubsetAngles),
                                           Reference.Oversampling,
                                           (uint)(SubsetEnd - SubsetStart));
                    }

                    /*Image CheckProjections = Projections.AsIFFT();
                    //CheckProjections.RemapFromFT();
                    CheckProjections.WriteMRC("d_projections.mrc");
                    CheckProjections.Dispose();*/

                    return Projections;
                };

                EvalIndividual = input =>
                {
                    Tuple<float2[], float3[]> ShiftsAndAngles = GetImageShiftsAndAngles(input);

                    Image Projections = GetProjections(ShiftsAndAngles.Item2);

                    float[] Results = new float[NParticles * NTilts];

                    GPU.TomoRefineGetDiff(ParticleImages.GetDevice(Intent.Read),
                                          Projections.GetDevice(Intent.Read),
                                          ShiftFactors.GetDevice(Intent.Read),
                                          ParticleCTFs.GetDevice(Intent.Read),
                                          ParticleWeights.GetDevice(Intent.Read),
                                          new int2(CoarseSize, CoarseSize),
                                          Helper.ToInterleaved(ShiftsAndAngles.Item1),
                                          Results,
                                          (uint)(NParticles * NTilts));

                    Projections.Dispose();

                    return Results.Select(i => (double)i).ToArray();
                };

                int OptimizationIterations = 0;
                bool GetOut = false;

                double Delta = 0.1;
                float Delta2 = 2 * (float)Delta;

                int[] WarpGridIDs = { 5, 6, 7 };
                Dictionary<int, float2[][]> WiggleWeightsWarp = new Dictionary<int, float2[][]>();
                foreach (var gridID in WarpGridIDs)
                {
                    int NElements = GridElements[gridID];
                    WiggleWeightsWarp.Add(gridID, new float2[NElements][]);

                    for (int ge = 0; ge < NElements; ge++)
                    {
                        double[] InputMinus = new double[StartParams.Length], InputPlus = new double[StartParams.Length];
                        for (int i = 0; i < StartParams.Length; i++)
                        {
                            InputMinus[i] = StartParams[i];
                            InputPlus[i] = StartParams[i];
                        }

                        InputMinus[VectorGridRanges[gridID].Item1 + ge] -= Delta;
                        InputPlus[VectorGridRanges[gridID].Item1 + ge] += Delta;

                        float2[] ImageShiftsPlus = GetImageShifts(InputPlus);
                        float2[] ImageShiftsMinus = GetImageShifts(InputMinus);

                        float2[] Weights = new float2[ImageShiftsPlus.Length];

                        for (int i = 0; i < ImageShiftsPlus.Length; i++)
                            Weights[i] = (ImageShiftsPlus[i] - ImageShiftsMinus[i]) / Delta2;

                        WiggleWeightsWarp[gridID][ge] = Weights;
                    }
                }

                Eval = input =>
                {
                    double Result = EvalIndividual(input).Sum();
                    lock (tableIn)
                        Debug.WriteLine(GPU.GetDevice() + ", " + RootName + ": " + Result);
                    OptimizationIterations++;

                    return Result;
                };

                Func<double[], double[], double, double[]> GradientParticles = (inputMinus, inputPlus, delta) =>
                {
                    double[] EvalMinus = EvalIndividual(inputMinus);
                    double[] EvalPlus = EvalIndividual(inputPlus);

                    double[] Diff = new double[EvalMinus.Length];
                    for (int i = 0; i < Diff.Length; i++)
                        Diff[i] = (EvalPlus[i] - EvalMinus[i]) / (2 * delta);

                    return Diff;
                };

                Gradient = input =>
                {
                    double[] Result = new double[input.Length];

                    if (OptimizationIterations > 60)
                        return Result;

                    float2[] ImageShiftGradients = new float2[NParticles * NTilts];
                    #region Compute gradient for individual image shifts
                    {
                        Tuple<float2[], float3[]> ShiftsAndAngles = GetImageShiftsAndAngles(input);
                        Image Projections = GetProjections(ShiftsAndAngles.Item2);

                        float2[] ShiftsXPlus = new float2[NParticles * NTilts];
                        float2[] ShiftsXMinus = new float2[NParticles * NTilts];
                        float2[] ShiftsYPlus = new float2[NParticles * NTilts];
                        float2[] ShiftsYMinus = new float2[NParticles * NTilts];

                        float2 DeltaX = new float2((float)Delta, 0);
                        float2 DeltaY = new float2(0, (float)Delta);

                        for (int i = 0; i < ShiftsXPlus.Length; i++)
                        {
                            ShiftsXPlus[i] = ShiftsAndAngles.Item1[i] + DeltaX;
                            ShiftsXMinus[i] = ShiftsAndAngles.Item1[i] - DeltaX;

                            ShiftsYPlus[i] = ShiftsAndAngles.Item1[i] + DeltaY;
                            ShiftsYMinus[i] = ShiftsAndAngles.Item1[i] - DeltaY;
                        }

                        float[] ScoresXPlus = new float[NParticles * NTilts];
                        float[] ScoresXMinus = new float[NParticles * NTilts];
                        float[] ScoresYPlus = new float[NParticles * NTilts];
                        float[] ScoresYMinus = new float[NParticles * NTilts];

                        GPU.TomoRefineGetDiff(ParticleImages.GetDevice(Intent.Read),
                                              Projections.GetDevice(Intent.Read),
                                              ShiftFactors.GetDevice(Intent.Read),
                                              ParticleCTFs.GetDevice(Intent.Read),
                                              ParticleWeights.GetDevice(Intent.Read),
                                              new int2(CoarseSize, CoarseSize),
                                              Helper.ToInterleaved(ShiftsXPlus),
                                              ScoresXPlus,
                                              (uint)(NParticles * NTilts));
                        GPU.TomoRefineGetDiff(ParticleImages.GetDevice(Intent.Read),
                                              Projections.GetDevice(Intent.Read),
                                              ShiftFactors.GetDevice(Intent.Read),
                                              ParticleCTFs.GetDevice(Intent.Read),
                                              ParticleWeights.GetDevice(Intent.Read),
                                              new int2(CoarseSize, CoarseSize),
                                              Helper.ToInterleaved(ShiftsXMinus),
                                              ScoresXMinus,
                                              (uint)(NParticles * NTilts));
                        GPU.TomoRefineGetDiff(ParticleImages.GetDevice(Intent.Read),
                                              Projections.GetDevice(Intent.Read),
                                              ShiftFactors.GetDevice(Intent.Read),
                                              ParticleCTFs.GetDevice(Intent.Read),
                                              ParticleWeights.GetDevice(Intent.Read),
                                              new int2(CoarseSize, CoarseSize),
                                              Helper.ToInterleaved(ShiftsYPlus),
                                              ScoresYPlus,
                                              (uint)(NParticles * NTilts));
                        GPU.TomoRefineGetDiff(ParticleImages.GetDevice(Intent.Read),
                                              Projections.GetDevice(Intent.Read),
                                              ShiftFactors.GetDevice(Intent.Read),
                                              ParticleCTFs.GetDevice(Intent.Read),
                                              ParticleWeights.GetDevice(Intent.Read),
                                              new int2(CoarseSize, CoarseSize),
                                              Helper.ToInterleaved(ShiftsYMinus),
                                              ScoresYMinus,
                                              (uint)(NParticles * NTilts));

                        Projections.Dispose();

                        for (int i = 0; i < ImageShiftGradients.Length; i++)
                        {
                            ImageShiftGradients[i] = new float2((ScoresXPlus[i] - ScoresXMinus[i]) / Delta2,
                                                           (ScoresYPlus[i] - ScoresYMinus[i]) / Delta2);
                        }
                    }
                    #endregion

                    // First, do particle parameters, i. e. 3D position within tomogram, rotation, across 2 points in time
                    // Altering each particle's parameters results in a change in its NTilts images, but nothing else
                    {
                        int[] TranslationIDs = DoPerParticleMotion ? new[] { 0, 1, 2, 3, 4, 5 } : new[] { 0, 1, 2 };
                        int[] RotationIDs = DoPerParticleMotion ? new [] {6, 7, 8, 9, 10, 11} : new [] { 6, 7, 8};
                        foreach (var paramID in RotationIDs)
                        {
                            double[] InputMinus = new double[input.Length], InputPlus = new double[input.Length];
                            for (int i = 0; i < input.Length; i++)
                            {
                                InputMinus[i] = input[i];
                                InputPlus[i] = input[i];
                            }
                            for (int p = 0; p < NParticles; p++)
                            {
                                InputMinus[NVectorGridParams + p * 12 + paramID] -= Delta;
                                InputPlus[NVectorGridParams + p * 12 + paramID] += Delta;
                            }

                            double[] ResultParticles = GradientParticles(InputMinus, InputPlus, Delta);
                            for (int p = 0; p < NParticles; p++)
                            {
                                double ParticleSum = 0;
                                for (int t = 0; t < NTilts; t++)
                                    ParticleSum += ResultParticles[p * NTilts + t];

                                Result[NVectorGridParams + p * 12 + paramID] = ParticleSum;
                            }
                        }

                        // Translation-related gradients can all be computed efficiently from previously retrieved per-image gradients
                        foreach (var paramID in TranslationIDs)
                        {
                            double[] InputMinus = new double[input.Length], InputPlus = new double[input.Length];
                            for (int i = 0; i < input.Length; i++)
                            {
                                InputMinus[i] = input[i];
                                InputPlus[i] = input[i];
                            }
                            for (int p = 0; p < NParticles; p++)
                            {
                                InputMinus[NVectorGridParams + p * 12 + paramID] -= Delta;
                                InputPlus[NVectorGridParams + p * 12 + paramID] += Delta;
                            }

                            float2[] ImageShiftsPlus = GetImageShifts(InputPlus);
                            float2[] ImageShiftsMinus = GetImageShifts(InputMinus);

                            for (int p = 0; p < NParticles; p++)
                            {
                                double ParticleSum = 0;
                                for (int t = 0; t < NTilts; t++)
                                {
                                    int i = p * NTilts + t;
                                    float2 ShiftDelta = (ImageShiftsPlus[i] - ImageShiftsMinus[i]) / Delta2;
                                    float ShiftGradient = ShiftDelta.X * ImageShiftGradients[i].X + ShiftDelta.Y * ImageShiftGradients[i].Y;

                                    ParticleSum += ShiftGradient;
                                }

                                Result[NVectorGridParams + p * 12 + paramID] = ParticleSum;
                            }
                        }

                        // If there is no per-particle motion, just copy the gradients for these parameters from parameterIDs 0-5
                        if (!DoPerParticleMotion)
                        {
                            int[] RedundantIDs = { 3, 4, 5, 9, 10, 11 };
                            foreach (var paramID in RedundantIDs)
                                for (int p = 0; p < NParticles; p++)
                                    Result[NVectorGridParams + p * 12 + paramID] = Result[NVectorGridParams + p * 12 + paramID - 3];
                        }
                    }

                    // Now deal with grids. Each grid slice (i. e. temporal point) will correspond to one tilt only, thus the gradient
                    // for each slice is the (weighted, in case of spatial resolution) sum of NParticles images in the corresponding tilt.
                    if (DoImageAlignment)
                    {
                        int[] RotationGridIDs = { 2, 3, 4 };
                        foreach (var gridID in RotationGridIDs)
                        {
                            int SliceElements = GridSliceElements[gridID];

                            for (int se = 0; se < SliceElements; se++)
                            {
                                double[] InputMinus = new double[input.Length], InputPlus = new double[input.Length];
                                for (int i = 0; i < input.Length; i++)
                                {
                                    InputMinus[i] = input[i];
                                    InputPlus[i] = input[i];
                                }
                                for (int gp = VectorGridRanges[gridID].Item1 + se; gp < VectorGridRanges[gridID].Item2; gp += SliceElements)
                                {
                                    InputMinus[gp] -= Delta;
                                    InputPlus[gp] += Delta;
                                }

                                double[] ResultParticles = GradientParticles(InputMinus, InputPlus, Delta);
                                for (int i = 0; i < ResultParticles.Length; i++)
                                {
                                    int GridTime = i % NTilts;
                                    Result[VectorGridRanges[gridID].Item1 + GridTime * SliceElements + se] += ResultParticles[i];
                                }
                            }
                        }

                        // Translation-related gradients can all be computed efficiently from previously retrieved per-image gradients
                        int[] TranslationGridIDs = { 0, 1 };
                        foreach (var gridID in TranslationGridIDs)
                        {
                            int SliceElements = GridSliceElements[gridID];

                            for (int se = 0; se < SliceElements; se++)
                            {
                                double[] InputMinus = new double[input.Length], InputPlus = new double[input.Length];
                                for (int i = 0; i < input.Length; i++)
                                {
                                    InputMinus[i] = input[i];
                                    InputPlus[i] = input[i];
                                }
                                for (int gp = VectorGridRanges[gridID].Item1 + se; gp < VectorGridRanges[gridID].Item2; gp += SliceElements)
                                {
                                    InputMinus[gp] -= Delta;
                                    InputPlus[gp] += Delta;
                                }

                                float2[] ImageShiftsPlus = GetImageShifts(InputPlus);
                                float2[] ImageShiftsMinus = GetImageShifts(InputMinus);

                                for (int i = 0; i < ImageShiftsPlus.Length; i++)
                                {
                                    float2 ShiftDelta = (ImageShiftsPlus[i] - ImageShiftsMinus[i]) / Delta2;
                                    float ShiftGradient = ShiftDelta.X * ImageShiftGradients[i].X + ShiftDelta.Y * ImageShiftGradients[i].Y;

                                    int GridSlice = i % NTilts;
                                    Result[VectorGridRanges[gridID].Item1 + GridSlice * SliceElements + se] += ShiftGradient;
                                }
                            }
                        }
                        // Warp grids don't have any shortcuts for getting multiple gradients at once, so they use pre-calculated wiggle weights
                        foreach (var gridID in WarpGridIDs)
                        {
                            int NElements = GridElements[gridID];

                            for (int ge = 0; ge < NElements; ge++)
                            {
                                float2[] Weights = WiggleWeightsWarp[gridID][ge];

                                for (int i = 0; i < Weights.Length; i++)
                                {
                                    float2 ShiftDelta = Weights[i];
                                    float ShiftGradient = ShiftDelta.X * ImageShiftGradients[i].X + ShiftDelta.Y * ImageShiftGradients[i].Y;

                                    Result[VectorGridRanges[gridID].Item1 + ge] += ShiftGradient;
                                }
                            }
                        }
                    }

                    return Result;
                };
            }

            #endregion

            BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(StartParams.Length, Eval, Gradient);
            Optimizer.Epsilon = 3e-7;

            Optimizer.Maximize(StartParams);

            float3[] OptimizedOrigins, OptimizedOrigins2, OptimizedAngles, OptimizedAngles2;
            GetParametersFromVector(StartParams, NParticles, size, out OptimizedOrigins, out OptimizedOrigins2, out OptimizedAngles, out OptimizedAngles2);
            SetGridsFromVector(StartParams, Dimensions.X);

            #region Calculate correlation scores, update table with new positions and angles
            {
                double[] ImageScores = EvalIndividual(StartParams);
                float[] ParticleScores = new float[NParticles];
                for (int i = 0; i < ImageScores.Length; i++)
                    ParticleScores[i / NTilts] += (float)ImageScores[i];

                //if (!tableIn.HasColumn("rlnOriginXPrior"))
                //    tableIn.AddColumn("rlnOriginXPrior");
                //if (!tableIn.HasColumn("rlnOriginYPrior"))
                //    tableIn.AddColumn("rlnOriginYPrior");
                //if (!tableIn.HasColumn("rlnOriginZPrior"))
                //    tableIn.AddColumn("rlnOriginZPrior");

                //if (!tableIn.HasColumn("rlnAngleRotPrior"))
                //    tableIn.AddColumn("rlnAngleRotPrior");
                //if (!tableIn.HasColumn("rlnAngleTiltPrior"))
                //    tableIn.AddColumn("rlnAngleTiltPrior");
                //if (!tableIn.HasColumn("rlnAnglePsiPrior"))
                //    tableIn.AddColumn("rlnAnglePsiPrior");

                lock (tableIn)
                    for (int p = 0; p < NParticles; p++)
                    {
                        int Row = RowIndices[SubsetContinuousIDs[p]];

                        tableIn.SetRowValue(Row, "rlnCoordinateX", OptimizedOrigins[p].X.ToString(CultureInfo.InvariantCulture));
                        tableIn.SetRowValue(Row, "rlnCoordinateY", OptimizedOrigins[p].Y.ToString(CultureInfo.InvariantCulture));
                        tableIn.SetRowValue(Row, "rlnCoordinateZ", OptimizedOrigins[p].Z.ToString(CultureInfo.InvariantCulture));

                        //tableIn.SetRowValue(Row, "rlnOriginXPrior", OptimizedOrigins2[p].X.ToString(CultureInfo.InvariantCulture));
                        //tableIn.SetRowValue(Row, "rlnOriginYPrior", OptimizedOrigins2[p].Y.ToString(CultureInfo.InvariantCulture));
                        //tableIn.SetRowValue(Row, "rlnOriginZPrior", OptimizedOrigins2[p].Z.ToString(CultureInfo.InvariantCulture));

                        tableIn.SetRowValue(Row, "rlnAngleRot", OptimizedAngles[p].X.ToString(CultureInfo.InvariantCulture));
                        tableIn.SetRowValue(Row, "rlnAngleTilt", OptimizedAngles[p].Y.ToString(CultureInfo.InvariantCulture));
                        tableIn.SetRowValue(Row, "rlnAnglePsi", OptimizedAngles[p].Z.ToString(CultureInfo.InvariantCulture));

                        //tableIn.SetRowValue(Row, "rlnAngleRotPrior", OptimizedAngles2[p].X.ToString(CultureInfo.InvariantCulture));
                        //tableIn.SetRowValue(Row, "rlnAngleTiltPrior", OptimizedAngles2[p].Y.ToString(CultureInfo.InvariantCulture));
                        //tableIn.SetRowValue(Row, "rlnAnglePsiPrior", OptimizedAngles2[p].Z.ToString(CultureInfo.InvariantCulture));

                        tableIn.SetRowValue(Row, "rlnParticleSelectZScore", ParticleScores[p].ToString(CultureInfo.InvariantCulture));
                    }
            }
            #endregion

            ParticleImages?.Dispose();
            ParticleCTFs?.Dispose();
            ParticleWeights?.Dispose();
            ShiftFactors?.Dispose();

            #region Extract particles at full resolution and back-project them into the reconstruction volumes
            {
                GPU.SetDevice(0);

                Image CTFCoords = GetCTFCoords(size, size);
                int[] SortedDosePrecalc = IndicesSortedDose;

                foreach (var subsetRange in SubsetRanges)
                {
                    lock (outReconstructions[subsetRange.Key])
                    {
                        for (int p = subsetRange.Value.Item1; p < subsetRange.Value.Item2; p++)
                        {
                            float3[] PerTiltPositions = new float3[NTilts];
                            float3[] PerTiltAngles = new float3[NTilts];
                            float3 CoordsDiff = OptimizedOrigins2[p] - OptimizedOrigins[p];
                            float3 AnglesDiff = OptimizedAngles2[p] - OptimizedAngles[p];
                            for (int t = 0; t < NTilts; t++)
                            {
                                float DoseID = SortedDosePrecalc[t] / (float)(NTilts - 1);
                                PerTiltPositions[t] = OptimizedOrigins[p] + CoordsDiff * DoseID;
                                PerTiltAngles[t] = OptimizedAngles[p] + AnglesDiff * DoseID;
                            }

                            Image FullParticleImages = GetSubtomoImages(tiltStack, size, PerTiltPositions, true);
                            Image FullParticleCTFs = GetSubtomoCTFs(PerTiltPositions, CTFCoords);

                            FullParticleImages.Multiply(FullParticleCTFs);
                            FullParticleCTFs.Abs();

                            float3[] FullParticleAngles = GetParticleAngleInImages(PerTiltPositions, PerTiltAngles);

                            outReconstructions[subsetRange.Key].BackProject(FullParticleImages, FullParticleCTFs, FullParticleAngles);

                            FullParticleImages.Dispose();
                            FullParticleCTFs.Dispose();
                        }

                        for (int p = subsetRange.Value.Item1; p < subsetRange.Value.Item2; p++)
                        {
                            float3[] PerTiltPositions = new float3[NTilts];
                            float3[] PerTiltAngles = new float3[NTilts];
                            float3 CoordsDiff = OptimizedOrigins2[p] - OptimizedOrigins[p];
                            float3 AnglesDiff = OptimizedAngles2[p] - OptimizedAngles[p];
                            for (int t = 0; t < NTilts; t++)
                            {
                                float DoseID = SortedDosePrecalc[t] / (float)(NTilts - 1);
                                PerTiltPositions[t] = OptimizedOrigins[p] + CoordsDiff * DoseID;
                                PerTiltAngles[t] = OptimizedAngles[p] + AnglesDiff * DoseID;
                            }

                            float3[] FullParticleAngles = GetParticleAngleInImages(PerTiltPositions, PerTiltAngles);

                            Image FullParticleCTFs = GetSubtomoCTFs(PerTiltPositions, CTFCoords, false);
                            Image FullParticleCTFWeights = GetSubtomoCTFs(PerTiltPositions, CTFCoords, true);

                            // CTF has to be converted to complex numbers with imag = 0
                            float2[] CTFsComplexData = new float2[FullParticleCTFs.ElementsComplex];
                            float[] CTFWeightsData = new float[FullParticleCTFs.ElementsComplex];
                            float[] CTFsContinuousData = FullParticleCTFs.GetHostContinuousCopy();
                            float[] CTFWeightsContinuousData = FullParticleCTFWeights.GetHostContinuousCopy();
                            for (int i = 0; i < CTFsComplexData.Length; i++)
                            {
                                CTFsComplexData[i] = new float2(Math.Abs(CTFsContinuousData[i] * CTFWeightsContinuousData[i]), 0);
                                CTFWeightsData[i] = Math.Abs(CTFWeightsContinuousData[i]);
                            }

                            Image CTFsComplex = new Image(CTFsComplexData, FullParticleCTFs.Dims, true);
                            Image CTFWeights = new Image(CTFWeightsData, FullParticleCTFs.Dims, true);

                            outCTFReconstructions[subsetRange.Key].BackProject(CTFsComplex, CTFWeights, FullParticleAngles);

                            FullParticleCTFs.Dispose();
                            FullParticleCTFWeights.Dispose();
                            CTFsComplex.Dispose();
                            CTFWeights.Dispose();
                        }

                        outReconstructions[subsetRange.Key].FreeDevice();
                        outCTFReconstructions[subsetRange.Key].FreeDevice();
                    }
                }

                CTFCoords.Dispose();
            }
            #endregion

            SaveMeta();
        }
Exemple #13
0
        public void AddToReconstructionOneAngle(Image tiltStack,
                                                int subset,
                                                int size,
                                                float3[] particleOrigins,
                                                float3[] particleOrigins2,
                                                float3[] particleAngles,
                                                float3[] particleAngles2,
                                                int[] particleSubset,
                                                int angleID,
                                                Projector mapProjector,
                                                Projector weightProjector)
        {
            List<int> ParticleIDs = new List<int>();
            for (int i = 0; i < particleSubset.Length; i++)
                if (particleSubset[i] == subset)
                    ParticleIDs.Add(i);
            int NParticles = ParticleIDs.Count;

            particleOrigins = ParticleIDs.Select(i => particleOrigins[i]).ToArray();
            particleOrigins2 = ParticleIDs.Select(i => particleOrigins2[i]).ToArray();
            particleAngles = ParticleIDs.Select(i => particleAngles[i]).ToArray();
            particleAngles2 = ParticleIDs.Select(i => particleAngles2[i]).ToArray();

            Image CTFCoords = GetCTFCoords(size, size);

            float DoseID = IndicesSortedDose[angleID] / (float)(NTilts - 1);

            for (int b = 0; b < NParticles; b += 1024)
            {
                int NBatch = Math.Min(1024, NParticles - b);

                float3[] ParticleOriginsInterp = new float3[NBatch];
                float3[] ParticleAnglesInterp = new float3[NBatch];
                for (int n = 0; n < NBatch; n++)
                {
                    float3 OriginDiff = particleOrigins2[b + n] - particleOrigins[b + n];
                    float3 AngleDiff = particleAngles2[b + n] - particleAngles[b + n];
                    ParticleOriginsInterp[n] = particleOrigins[b + n] + OriginDiff * DoseID;
                    ParticleAnglesInterp[n] = particleAngles[b + n] + AngleDiff * DoseID;
                }

                Image ParticleImages = GetImagesOneAngle(tiltStack, size, ParticleOriginsInterp, angleID, true);
                Image ParticleCTFs = GetCTFsOneAngle(tiltStack, CTFCoords, ParticleOriginsInterp, angleID, false);

                ParticleImages.Multiply(ParticleCTFs);
                //ParticleCTFs.Multiply(ParticleCTFs);
                ParticleCTFs.Abs();

                mapProjector.BackProject(ParticleImages,
                                         ParticleCTFs,
                                         GetAnglesInOneAngle(ParticleOriginsInterp,
                                                             ParticleAnglesInterp,
                                                             angleID));

                ParticleImages.Dispose();
                ParticleCTFs.Dispose();

                // Now reconstruct the weights which will be needed during optimization later
                ParticleCTFs = GetCTFsOneAngle(tiltStack, CTFCoords, ParticleOriginsInterp, angleID, false);

                // CTF has to be converted to complex numbers with imag = 0
                float2[] CTFsComplexData = new float2[ParticleCTFs.ElementsComplex];
                float[] CTFWeightsData = new float[ParticleCTFs.ElementsComplex];
                float[] CTFsContinuousData = ParticleCTFs.GetHostContinuousCopy();
                for (int i = 0; i < CTFsComplexData.Length; i++)
                {
                    CTFsComplexData[i] = new float2(Math.Abs(CTFsContinuousData[i] * CTFsContinuousData[i]), 0);
                    CTFWeightsData[i] = Math.Abs(CTFsContinuousData[i]);
                }

                Image CTFsComplex = new Image(CTFsComplexData, ParticleCTFs.Dims, true);
                Image CTFWeights = new Image(CTFWeightsData, ParticleCTFs.Dims, true);

                weightProjector.BackProject(CTFsComplex,
                                            CTFWeights,
                                            GetAnglesInOneAngle(ParticleOriginsInterp,
                                                                ParticleAnglesInterp,
                                                                angleID));

                ParticleCTFs.Dispose();
                CTFsComplex.Dispose();
                CTFWeights.Dispose();
            }

            CTFCoords.Dispose();
        }
Exemple #14
0
        public static void FitCTF(float2[] data, Func<float[], float[]> approximation, float[] zeros, float[] peaks, out Cubic1D background, out Cubic1D scale)
        {
            float MinX = MathHelper.Min(data.Select(p => p.X)), MaxX = MathHelper.Max(data.Select(p => p.X)), ScaleX = 1f / (MaxX - MinX);
            float MinY = MathHelper.Min(data.Select(p => p.Y)), MaxY = MathHelper.Max(data.Select(p => p.Y)), ScaleY = 1f / (MaxY - MinY);
            if (float.IsNaN(ScaleY))
                ScaleY = 1f;

            peaks = peaks.Where(v => v >= MinX && v <= MaxX).Where((v, i) => i % 1 == 0).ToArray();
            zeros = zeros.Where(v => v >= MinX && v <= MaxX).Where((v, i) => i % 1 == 0).ToArray();

            float2[] ScaledData = data.Select(p => new float2((p.X - MinX) * ScaleX, (p.Y - MinY) * ScaleY)).ToArray();
            float StdY = MathHelper.StdDev(data.Select(p => p.Y).ToArray());

            double[] Start = new double[zeros.Length + peaks.Length];
            double[] NodeX = new double[zeros.Length + peaks.Length];
            Cubic1D DataSpline = new Cubic1D(data);
            for (int i = 0; i < zeros.Length; i++)
            {
                NodeX[i] = (zeros[i] - MinX) * ScaleX;
                Start[i] = DataSpline.Interp(zeros[i]) - MinY;
            }
            {
                Cubic1D PreliminaryBackground = new Cubic1D(Helper.Zip(NodeX.Take(zeros.Length).Select(v => (float)v).ToArray(),
                                                                       Start.Take(zeros.Length).Select(v => (float)v).ToArray()));
                float[] PreliminaryInterpolated = PreliminaryBackground.Interp(data.Select(v => (v.X - MinX) * ScaleX).ToArray());
                float2[] BackgroundSubtracted = data.Select((v, i) => new float2(v.X, v.Y - MinY - PreliminaryInterpolated[i])).ToArray();
                Cubic1D BackgroundSpline = new Cubic1D(BackgroundSubtracted);

                for (int i = 0; i < peaks.Length; i++)
                {
                    NodeX[i + zeros.Length] = (peaks[i] - MinX) * ScaleX;
                    float PeakValue = BackgroundSpline.Interp(peaks[i]);
                    Start[i + zeros.Length] = Math.Max(0.0001f, PeakValue);
                }
            }

            float[] DataX = ScaledData.Select(p => p.X).ToArray();
            float[] OriginalDataX = data.Select(p => p.X).ToArray();
            float[] SimulatedCTF = approximation(OriginalDataX);

            float2[] NodesBackground = new float2[zeros.Length];
            for (int i = 0; i < NodesBackground.Length; i++)
                NodesBackground[i] = new float2((float)NodeX[i], 0f);
            float2[] NodesScale = new float2[peaks.Length];
            for (int i = 0; i < NodesScale.Length; i++)
                NodesScale[i] = new float2((float)NodeX[i + zeros.Length], 0f);

            Func<double[], double> Eval = input =>
            {
                float2[] NodesBackgroundCopy = new float2[NodesBackground.Length];
                for (int i = 0; i < zeros.Length; i++)
                    NodesBackgroundCopy[i] = new float2(NodesBackground[i].X, (float)input[i]);

                float2[] NodesScaleCopy = new float2[NodesScale.Length];
                for (int i = 0; i < peaks.Length; i++)
                    NodesScaleCopy[i] = new float2(NodesScale[i].X, (float)input[i + zeros.Length]);

                float[] InterpolatedBackground = new Cubic1D(NodesBackgroundCopy).Interp(DataX);
                float[] InterpolatedScale = new Cubic1D(NodesScaleCopy).Interp(DataX);

                double Sum = 0f;
                for (int i = 0; i < ScaledData.Length; i++)
                {
                    double Diff = ScaledData[i].Y - (InterpolatedBackground[i] + SimulatedCTF[i] * (double)InterpolatedScale[i]) * ScaleY;
                    Sum += Diff * Diff;
                    if (InterpolatedScale[i] < 0.0005f)
                        Sum += (0.0005 - InterpolatedScale[i]) * 10;
                }

                //return Math.Sqrt(Sum / data.Length) * 10;
                return 0;
            };

            Func<double[], double[]> Gradient = input =>
            {
                double[] Result = new double[input.Length];

                //Parallel.For(0, input.Length, i =>
                for (int i = 0; i < input.Length; i++)
                {
                    double[] UpperInput = new double[input.Length];
                    input.CopyTo(UpperInput, 0);
                    UpperInput[i] += 0.0001;
                    double UpperValue = Eval(UpperInput);

                    double[] LowerInput = new double[input.Length];
                    input.CopyTo(LowerInput, 0);
                    LowerInput[i] -= 0.0001;
                    double LowerValue = Eval(LowerInput);

                    Result[i] = (UpperValue - LowerValue) / 0.0002;
                }//);

                return Result;
            };

            BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(Start.Length, Eval, Gradient);
            Optimizer.Minimize(Start);

            {
                for (int i = 0; i < zeros.Length; i++)
                    NodesBackground[i] = new float2((float) NodeX[i] / ScaleX + MinX, (float) Optimizer.Solution[i] + MinY);
                for (int i = 0; i < peaks.Length; i++)
                    NodesScale[i] = new float2((float)NodeX[i + zeros.Length] / ScaleX + MinX, Math.Max(0.001f, (float)Optimizer.Solution[i + zeros.Length]));

                background = new Cubic1D(NodesBackground);
                scale = new Cubic1D(NodesScale);
            }
        }
Exemple #15
0
        public Tuple<Image, Image> MakeReconstructionOneTomogram(Image tiltStack, int subset, int size, float3[] particleOrigins, float3[] particleOrigins2, float3[] particleAngles, float3[] particleAngles2, int[] particleSubset)
        {
            Projector MapProjector = new Projector(new int3(size, size, size), 2);
            Projector WeightProjector = new Projector(new int3(size, size, size), 2);

            List<int> ParticleIDs = new List<int>();
            for (int i = 0; i < particleSubset.Length; i++)
                if (particleSubset[i] == subset)
                    ParticleIDs.Add(i);
            int NParticles = ParticleIDs.Count;

            particleOrigins = ParticleIDs.Select(i => particleOrigins[i]).ToArray();
            particleOrigins2 = ParticleIDs.Select(i => particleOrigins2[i]).ToArray();
            particleAngles = ParticleIDs.Select(i => particleAngles[i]).ToArray();
            particleAngles2 = ParticleIDs.Select(i => particleAngles2[i]).ToArray();

            Image CTFCoords = GetCTFCoords(size, size);

            for (int angleID = 0; angleID < NTilts; angleID++)
            {
                float DoseID = IndicesSortedDose[angleID] / (float)(NTilts - 1);

                for (int b = 0; b < NParticles; b += 1024)
                {
                    int NBatch = Math.Min(1024, NParticles - b);

                    float3[] ParticleOriginsInterp = new float3[NBatch];
                    float3[] ParticleAnglesInterp = new float3[NBatch];
                    for (int n = 0; n < NBatch; n++)
                    {
                        float3 OriginDiff = particleOrigins2[b + n] - particleOrigins[b + n];
                        float3 AngleDiff = particleAngles2[b + n] - particleAngles[b + n];
                        ParticleOriginsInterp[n] = particleOrigins[b + n] + OriginDiff * DoseID;
                        ParticleAnglesInterp[n] = particleAngles[b + n] + AngleDiff * DoseID;
                    }

                    Image ParticleImages = GetImagesOneAngle(tiltStack, size, ParticleOriginsInterp, angleID, true);
                    Image ParticleCTFs = GetCTFsOneAngle(tiltStack, CTFCoords, ParticleOriginsInterp, angleID, true);

                    ParticleImages.Multiply(ParticleCTFs);
                    //ParticleCTFs.Multiply(ParticleCTFs);
                    ParticleCTFs.Abs();

                    MapProjector.BackProject(ParticleImages,
                                             ParticleCTFs,
                                             GetAnglesInOneAngle(ParticleOriginsInterp,
                                                                 ParticleAnglesInterp,
                                                                 angleID));

                    ParticleImages.Dispose();
                    ParticleCTFs.Dispose();

                    // Now reconstruct the weights which will be needed during optimization later
                    ParticleCTFs = GetCTFsOneAngle(tiltStack, CTFCoords, ParticleOriginsInterp, angleID, true);

                    // CTF has to be converted to complex numbers with imag = 0
                    float2[] CTFsComplexData = new float2[ParticleCTFs.ElementsComplex];
                    float[] CTFWeightsData = new float[ParticleCTFs.ElementsComplex];
                    float[] CTFsContinuousData = ParticleCTFs.GetHostContinuousCopy();
                    for (int i = 0; i < CTFsComplexData.Length; i++)
                    {
                        CTFsComplexData[i] = new float2(Math.Abs(CTFsContinuousData[i] * CTFsContinuousData[i]), 0);
                        CTFWeightsData[i] = Math.Abs(CTFsContinuousData[i]);
                    }

                    Image CTFsComplex = new Image(CTFsComplexData, ParticleCTFs.Dims, true);
                    Image CTFWeights = new Image(CTFWeightsData, ParticleCTFs.Dims, true);

                    WeightProjector.BackProject(CTFsComplex,
                                                CTFWeights,
                                                GetAnglesInOneAngle(ParticleOriginsInterp,
                                                                    ParticleAnglesInterp,
                                                                    angleID));

                    ParticleCTFs.Dispose();
                    CTFsComplex.Dispose();
                    CTFWeights.Dispose();
                }
            }

            CTFCoords.Dispose();

            //MapProjector.Weights.WriteMRC($"d_weights{angleID:D3}.mrc");
            Image Reconstruction = MapProjector.Reconstruct(false);
            MapProjector.Dispose();

            foreach (var slice in WeightProjector.Weights.GetHost(Intent.ReadWrite))
                for (int i = 0; i < slice.Length; i++)
                    slice[i] = Math.Min(slice[i], 1);

            Image ReconstructionWeights = WeightProjector.Reconstruct(true);
            WeightProjector.Dispose();

            return new Tuple<Image, Image>(Reconstruction, ReconstructionWeights);
        }
Exemple #16
0
 public int2(float2 v)
 {
     X = (int)v.X;
     Y = (int)v.Y;
 }
Exemple #17
0
 public static void UnNaN(float2[] data)
 {
     for (int i = 0; i < data.Length; i++)
     {
         if (float.IsNaN(data[i].X))
             data[i].X = 0;
         if (float.IsNaN(data[i].Y))
             data[i].Y = 0;
     }
 }
Exemple #18
0
        public Cubic1D3(float2[] data)
        {
            Data = data;
            Breaks = new[] { data[0].X, data[1].X, data[2].X };

            Coefficients = new float4[2];

            float[] h = { data[1].X - data[0].X, data[2].X - data[1].X };
            float[] del = { (data[1].Y - data[0].Y) / h[0], (data[2].Y - data[1].Y) / h[1] };
            float[] slopes = GetPCHIPSlopes(data, del);

            float[] dzzdx = new float[2];
            for (int i = 0; i < 2; i++)
                dzzdx[i] = (del[i] - slopes[i]) / h[i];

            float[] dzdxdx = new float[2];
            for (int i = 0; i < 2; i++)
                dzdxdx[i] = (slopes[i + 1] - del[i]) / h[i];

            for (int i = 0; i < 2; i++)
                Coefficients[i] = new float4((dzdxdx[i] - dzzdx[i]) / h[i],
                                             2f * dzzdx[i] - dzdxdx[i],
                                             slopes[i],
                                             data[i].Y);
        }
Exemple #19
0
        public static Cubic1DShort GetInterpolator(float2[] data)
        {
            if (data.Length == 4)
                return new Cubic1D4(data);
            if (data.Length == 3)
                return new Cubic1D3(data);

            return new Cubic1D2(data);
        }
Exemple #20
0
        private static float[] GetPCHIPSlopes(float2[] data, float[] del)
        {
            float[] d = new float[4];
            float[] h = { data[1].X - data[0].X, data[2].X - data[1].X, data[3].X - data[2].X };
            for (int k = 0; k < 2; k++)
            {
                if (del[k] * del[k + 1] <= 0f)
                    continue;

                float hs = h[k] + h[k + 1];
                float w1 = (h[k] + hs) / (3f * hs);
                float w2 = (hs + h[k + 1]) / (3f * hs);
                float dmax = Math.Max(Math.Abs(del[k]), Math.Abs(del[k + 1]));
                float dmin = Math.Min(Math.Abs(del[k]), Math.Abs(del[k + 1]));
                d[k + 1] = dmin / (w1 * (del[k] / dmax) + w2 * (del[k + 1] / dmax));
            }

            d[0] = ((2f * h[0] + h[1]) * del[0] - h[0] * del[1]) / (h[0] + h[1]);
            if (Math.Sign(d[0]) != Math.Sign(del[0]))
                d[0] = 0;
            else if (Math.Sign(del[0]) != Math.Sign(del[1]) && Math.Abs(d[0]) > Math.Abs(3f * del[0]))
                d[0] = 3f * del[0];

            int n = 4 - 1;
            d[n] = ((2 * h[n - 1] + h[n - 2]) * del[n - 1] - h[n - 1] * del[n - 2]) / (h[n - 1] + h[n - 2]);
            if (Math.Sign(d[n]) != Math.Sign(del[n - 1]))
                d[n] = 0;
            else if (Math.Sign(del[n - 1]) != Math.Sign(del[n - 2]) && Math.Abs(d[n]) > Math.Abs(3f * del[n - 1]))
                d[n] = 3f * del[n - 1];

            return d;
        }
Exemple #21
0
        public void PerformGlobalParticleAlignment(Star tableIn,
                                                   Image tiltStack,
                                                   int size,
                                                   int3 volumeDimensions,
                                                   Dictionary<int, Projector> references,
                                                   float resolution,
                                                   int healpixOrder,
                                                   string symmetry,
                                                   float offsetRange,
                                                   float offsetStep,
                                                   Dictionary<int, Projector> outReconstructions,
                                                   Dictionary<int, Projector> outCTFReconstructions)
        {
            VolumeDimensions = volumeDimensions;

            #region Get rows from table

            List<int> RowIndices = new List<int>();
            string[] ColumnMicrographName = tableIn.GetColumn("rlnMicrographName");
            for (int i = 0; i < ColumnMicrographName.Length; i++)
                if (ColumnMicrographName[i].Contains(RootName + "."))
                    RowIndices.Add(i);

            if (RowIndices.Count == 0)
                return;

            int NParticles = RowIndices.Count;

            #endregion

            #region Make sure all columns and directories are there

            if (!tableIn.HasColumn("rlnImageName"))
                tableIn.AddColumn("rlnImageName");
            if (!tableIn.HasColumn("rlnCtfImage"))
                tableIn.AddColumn("rlnCtfImage");
            if (!tableIn.HasColumn("rlnParticleSelectZScore"))
                tableIn.AddColumn("rlnParticleSelectZScore");

            if (!Directory.Exists(ParticlesDir))
                Directory.CreateDirectory(ParticlesDir);
            if (!Directory.Exists(ParticleCTFDir))
                Directory.CreateDirectory(ParticleCTFDir);

            #endregion

            #region Get subtomo positions from table

            float3[] ParticleOrigins = new float3[NParticles];
            float3[] ParticleOrigins2 = new float3[NParticles];
            float3[] ParticleAngles = new float3[NParticles];
            float3[] ParticleAngles2 = new float3[NParticles];
            int[] ParticleSubset = new int[NParticles];
            {
                string[] ColumnPosX = tableIn.GetColumn("rlnCoordinateX");
                string[] ColumnPosY = tableIn.GetColumn("rlnCoordinateY");
                string[] ColumnPosZ = tableIn.GetColumn("rlnCoordinateZ");
                string[] ColumnOriginX = tableIn.GetColumn("rlnOriginX");
                string[] ColumnOriginY = tableIn.GetColumn("rlnOriginY");
                string[] ColumnOriginZ = tableIn.GetColumn("rlnOriginZ");
                string[] ColumnAngleRot = tableIn.GetColumn("rlnAngleRot");
                string[] ColumnAngleTilt = tableIn.GetColumn("rlnAngleTilt");
                string[] ColumnAnglePsi = tableIn.GetColumn("rlnAnglePsi");
                string[] ColumnSubset = tableIn.GetColumn("rlnRandomSubset");

                for (int i = 0; i < NParticles; i++)
                {
                    float3 Pos = new float3(float.Parse(ColumnPosX[RowIndices[i]], CultureInfo.InvariantCulture),
                                            float.Parse(ColumnPosY[RowIndices[i]], CultureInfo.InvariantCulture),
                                            float.Parse(ColumnPosZ[RowIndices[i]], CultureInfo.InvariantCulture));
                    float3 Pos2 = Pos;

                    float3 Shift = new float3(float.Parse(ColumnOriginX[RowIndices[i]], CultureInfo.InvariantCulture),
                                              float.Parse(ColumnOriginY[RowIndices[i]], CultureInfo.InvariantCulture),
                                              float.Parse(ColumnOriginZ[RowIndices[i]], CultureInfo.InvariantCulture));

                    ParticleOrigins[i] = Pos - Shift;
                    ParticleOrigins2[i] = Pos2 - Shift;

                    float3 Angle = new float3(float.Parse(ColumnAngleRot[RowIndices[i]], CultureInfo.InvariantCulture),
                                              float.Parse(ColumnAngleTilt[RowIndices[i]], CultureInfo.InvariantCulture),
                                              float.Parse(ColumnAnglePsi[RowIndices[i]], CultureInfo.InvariantCulture));
                    float3 Angle2 = Angle;

                    ParticleAngles[i] = Angle;
                    ParticleAngles2[i] = Angle2;

                    ParticleSubset[i] = int.Parse(ColumnSubset[RowIndices[i]]);

                    tableIn.SetRowValue(RowIndices[i], "rlnCoordinateX", ParticleOrigins[i].X.ToString(CultureInfo.InvariantCulture));
                    tableIn.SetRowValue(RowIndices[i], "rlnCoordinateY", ParticleOrigins[i].Y.ToString(CultureInfo.InvariantCulture));
                    tableIn.SetRowValue(RowIndices[i], "rlnCoordinateZ", ParticleOrigins[i].Z.ToString(CultureInfo.InvariantCulture));
                    tableIn.SetRowValue(RowIndices[i], "rlnOriginX", "0.0");
                    tableIn.SetRowValue(RowIndices[i], "rlnOriginY", "0.0");
                    tableIn.SetRowValue(RowIndices[i], "rlnOriginZ", "0.0");
                }
            }

            #endregion

            #region Deal with subsets

            List<int> SubsetIDs = new List<int>();
            foreach (var i in ParticleSubset)
                if (!SubsetIDs.Contains(i))
                    SubsetIDs.Add(i);
            SubsetIDs.Sort();

            // For each subset, create a list of its particle IDs
            Dictionary<int, List<int>> SubsetParticleIDs = SubsetIDs.ToDictionary(subsetID => subsetID, subsetID => new List<int>());
            for (int i = 0; i < ParticleSubset.Length; i++)
                SubsetParticleIDs[ParticleSubset[i]].Add(i);
            foreach (var list in SubsetParticleIDs.Values)
                list.Sort();

            // Note where each subset starts and ends in a unified, sorted (by subset) particle ID list
            Dictionary<int, Tuple<int, int>> SubsetRanges = new Dictionary<int, Tuple<int, int>>();
            {
                int Start = 0;
                foreach (var pair in SubsetParticleIDs)
                {
                    SubsetRanges.Add(pair.Key, new Tuple<int, int>(Start, Start + pair.Value.Count));
                    Start += pair.Value.Count;
                }
            }

            List<int> SubsetContinuousIDs = new List<int>();
            foreach (var pair in SubsetParticleIDs)
                SubsetContinuousIDs.AddRange(pair.Value);

            // Reorder particle information to match the order of SubsetContinuousIDs
            ParticleOrigins = SubsetContinuousIDs.Select(i => ParticleOrigins[i]).ToArray();
            ParticleOrigins2 = SubsetContinuousIDs.Select(i => ParticleOrigins2[i]).ToArray();
            ParticleAngles = SubsetContinuousIDs.Select(i => ParticleAngles[i]).ToArray();
            ParticleAngles2 = SubsetContinuousIDs.Select(i => ParticleAngles2[i]).ToArray();
            ParticleSubset = SubsetContinuousIDs.Select(i => ParticleSubset[i]).ToArray();

            #endregion

            int CoarseSize = (int)Math.Round(size * ((float)CTF.PixelSize * 2 / resolution)) / 2 * 2;
            int3 CoarseDims = new int3(CoarseSize, CoarseSize, 1);

            // Positions the particles were extracted at/shifted to, to calculate effectively needed shifts later
            float2[] ExtractedAt = new float2[NParticles * NTilts];

            // Extract images, mask and resize them, create CTFs
            Image ParticleImages = new Image(new int3(CoarseSize, CoarseSize, NParticles * NTilts), true, true);
            Image ParticleCTFs = new Image(new int3(CoarseSize, CoarseSize, NParticles * NTilts), true);
            Image ParticleWeights = null;
            Image ShiftFactors = null;

            #region Preflight

            float KeepBFac = GlobalBfactor;
            GlobalBfactor = 0;
            {
                Image CTFCoords = GetCTFCoords(CoarseSize, size);

                #region Precalculate vectors for shifts in Fourier space

                {
                    float2[] ShiftFactorsData = new float2[(CoarseSize / 2 + 1) * CoarseSize];
                    for (int y = 0; y < CoarseSize; y++)
                        for (int x = 0; x < CoarseSize / 2 + 1; x++)
                        {
                            int xx = x;
                            int yy = y < CoarseSize / 2 + 1 ? y : y - CoarseSize;

                            ShiftFactorsData[y * (CoarseSize / 2 + 1) + x] = new float2((float)-xx / size * 2f * (float)Math.PI,
                                                                                        (float)-yy / size * 2f * (float)Math.PI);
                        }

                    ShiftFactors = new Image(ShiftFactorsData, new int3(CoarseSize, CoarseSize, 1), true);
                }

                #endregion

                #region Create mask with soft edge

                Image Mask;
                Image MaskSubt;
                {
                    Image MaskBig = new Image(new int3(size, size, 1));
                    float MaskRadius = MainWindow.Options.ExportParticleRadius / (float)CTF.PixelSize;
                    float SoftEdge = 16f;

                    float[] MaskBigData = MaskBig.GetHost(Intent.Write)[0];
                    for (int y = 0; y < size; y++)
                    {
                        int yy = y - size / 2;
                        yy *= yy;
                        for (int x = 0; x < size; x++)
                        {
                            int xx = x - size / 2;
                            xx *= xx;
                            float R = (float)Math.Sqrt(xx + yy);

                            if (R <= MaskRadius)
                                MaskBigData[y * size + x] = 1;
                            else
                                MaskBigData[y * size + x] = (float)(Math.Cos(Math.Min(1, (R - MaskRadius) / SoftEdge) * Math.PI) * 0.5 + 0.5);
                        }
                    }
                    //MaskBig.WriteMRC("d_maskbig.mrc");

                    Mask = MaskBig.AsScaled(new int2(CoarseSize, CoarseSize));
                    Mask.RemapToFT();

                    MaskBigData = MaskBig.GetHost(Intent.Write)[0];
                    for (int y = 0; y < size; y++)
                    {
                        int yy = y - size / 2;
                        yy *= yy;
                        for (int x = 0; x < size; x++)
                        {
                            int xx = x - size / 2;
                            xx *= xx;
                            float R = (float)Math.Sqrt(xx + yy);

                            if (R <= 30)
                                MaskBigData[y * size + x] = 1;
                            else
                                MaskBigData[y * size + x] = 0;
                        }
                    }

                    MaskSubt = MaskBig.AsScaled(new int2(CoarseSize, CoarseSize));
                    MaskSubt.RemapToFT();

                    MaskBig.Dispose();
                }
                //Mask.WriteMRC("d_masksmall.mrc");

                #endregion

                #region Create Fourier space mask

                Image FourierMask = new Image(CoarseDims, true);
                {
                    float[] FourierMaskData = FourierMask.GetHost(Intent.Write)[0];
                    int MaxR2 = CoarseSize * CoarseSize / 4;
                    for (int y = 0; y < CoarseSize; y++)
                    {
                        int yy = y < CoarseSize / 2 + 1 ? y : y - CoarseSize;
                        yy *= yy;

                        for (int x = 0; x < CoarseSize / 2 + 1; x++)
                        {
                            int xx = x * x;
                            int R2 = yy + xx;

                            FourierMaskData[y * (CoarseSize / 2 + 1) + x] = R2 < MaxR2 ? 1 : 0;
                        }
                    }
                }

                #endregion

                #region For each particle, create CTFs and extract & preprocess images for entire tilt series

                for (int p = 0; p < NParticles; p++)
                {
                    float3 ParticleCoords = ParticleOrigins[p];
                    float3[] Positions = GetPositionInImages(ParticleCoords);
                    float3[] ProjAngles = GetParticleAngleInImages(ParticleCoords, ParticleAngles[p]);

                    Image Extracted = new Image(new int3(size, size, NTilts));
                    float[][] ExtractedData = Extracted.GetHost(Intent.Write);
                    float3[] Residuals = new float3[NTilts];

                    Image SubtrahendsCTF = new Image(new int3(CoarseSize, CoarseSize, NTilts), true);

                    // Create CTFs
                    {
                        CTFStruct[] CTFParams = new CTFStruct[NTilts];

                        float GridStep = 1f / (NTilts - 1);
                        CTFStruct[] Params = new CTFStruct[NTilts];
                        for (int t = 0; t < NTilts; t++)
                        {
                            decimal Defocus = (decimal)Positions[t].Z;
                            decimal DefocusDelta = (decimal)GridCTFDefocusDelta.GetInterpolated(new float3(0.5f, 0.5f, t * GridStep));
                            decimal DefocusAngle = (decimal)GridCTFDefocusAngle.GetInterpolated(new float3(0.5f, 0.5f, t * GridStep));

                            CTF CurrCTF = CTF.GetCopy();
                            CurrCTF.Defocus = Defocus;
                            CurrCTF.DefocusDelta = DefocusDelta;
                            CurrCTF.DefocusAngle = DefocusAngle;
                            CurrCTF.Scale = (decimal)Math.Cos(Angles[t] * Helper.ToRad);
                            CurrCTF.Bfactor = (decimal)-Dose[t] * 8;

                            Params[t] = CurrCTF.ToStruct();
                        }

                        GPU.CreateCTF(ParticleCTFs.GetDeviceSlice(NTilts * p, Intent.Write),
                                      CTFCoords.GetDevice(Intent.Read),
                                      (uint)CoarseDims.ElementsFFT(),
                                      Params,
                                      false,
                                      (uint)NTilts);
                    }

                    // Extract images
                    {
                        for (int t = 0; t < NTilts; t++)
                        {
                            ExtractedAt[p * NTilts + t] = new float2(Positions[t].X, Positions[t].Y);

                            Positions[t] -= size / 2;
                            int2 IntPosition = new int2((int)Positions[t].X, (int)Positions[t].Y);
                            float2 Residual = new float2(-(Positions[t].X - IntPosition.X), -(Positions[t].Y - IntPosition.Y));
                            Residuals[t] = new float3(Residual / size * CoarseSize);

                            float[] OriginalData;
                            lock (tiltStack)
                                OriginalData = tiltStack.GetHost(Intent.Read)[t];

                            float[] ImageData = ExtractedData[t];
                            for (int y = 0; y < size; y++)
                            {
                                int PosY = (y + IntPosition.Y + tiltStack.Dims.Y) % tiltStack.Dims.Y;
                                for (int x = 0; x < size; x++)
                                {
                                    int PosX = (x + IntPosition.X + tiltStack.Dims.X) % tiltStack.Dims.X;
                                    ImageData[y * size + x] = OriginalData[PosY * tiltStack.Dims.X + PosX];
                                }
                            }
                        }

                        GPU.NormParticles(Extracted.GetDevice(Intent.Read),
                                          Extracted.GetDevice(Intent.Write),
                                          new int3(size, size, 1),
                                          (uint)(MainWindow.Options.ExportParticleRadius / CTF.PixelSize),
                                          true,
                                          (uint)NTilts);

                        Image Scaled = Extracted.AsScaled(new int2(CoarseSize, CoarseSize));
                        //Scaled.WriteMRC("d_scaled.mrc");
                        Extracted.Dispose();

                        Scaled.ShiftSlices(Residuals);
                        Scaled.RemapToFT();

                        //GPU.NormalizeMasked(Scaled.GetDevice(Intent.Read),
                        //              Scaled.GetDevice(Intent.Write),
                        //              MaskSubt.GetDevice(Intent.Read),
                        //              (uint)Scaled.ElementsSliceReal,
                        //              (uint)NTilts);

                        //{
                        //    //Image SubtrahendsFT = subtrahendReference.Project(new int2(CoarseSize, CoarseSize), ProjAngles, CoarseSize / 2);
                        //    //SubtrahendsFT.Multiply(SubtrahendsCTF);

                        //    //Image Subtrahends = SubtrahendsFT.AsIFFT();
                        //    //SubtrahendsFT.Dispose();

                        //    ////GPU.NormalizeMasked(Subtrahends.GetDevice(Intent.Read),
                        //    ////                    Subtrahends.GetDevice(Intent.Write),
                        //    ////                    MaskSubt.GetDevice(Intent.Read),
                        //    ////                    (uint)Subtrahends.ElementsSliceReal,
                        //    ////                    (uint)NTilts);

                        //    //Scaled.Subtract(Subtrahends);
                        //    //Subtrahends.Dispose();

                        //    Image FocusMaskFT = maskReference.Project(new int2(CoarseSize, CoarseSize), ProjAngles, CoarseSize / 2);
                        //    Image FocusMask = FocusMaskFT.AsIFFT();
                        //    FocusMaskFT.Dispose();

                        //    Scaled.Multiply(FocusMask);
                        //    FocusMask.Dispose();
                        //}

                        Scaled.MultiplySlices(Mask);

                        GPU.FFT(Scaled.GetDevice(Intent.Read),
                                ParticleImages.GetDeviceSlice(p * NTilts, Intent.Write),
                                CoarseDims,
                                (uint)NTilts);

                        Scaled.Dispose();
                        SubtrahendsCTF.Dispose();
                    }
                }

                #endregion

                ParticleCTFs.MultiplySlices(FourierMask);

                Mask.Dispose();
                FourierMask.Dispose();
                MaskSubt.Dispose();

                Image ParticleCTFsAbs = new Image(ParticleCTFs.GetDevice(Intent.Read), ParticleCTFs.Dims, true);
                ParticleCTFsAbs.Abs();
                ParticleWeights = ParticleCTFsAbs.AsSum2D();
                ParticleCTFsAbs.Dispose();
                {
                    float[] ParticleWeightsData = ParticleWeights.GetHost(Intent.ReadWrite)[0];
                    float Max = MathHelper.Max(ParticleWeightsData);
                    for (int i = 0; i < ParticleWeightsData.Length; i++)
                        ParticleWeightsData[i] /= Max;
                }

                CTFCoords.Dispose();

                //Image CheckImages = ParticleImages.AsIFFT();
                //CheckImages.WriteMRC("d_particleimages.mrc");
                //CheckImages.Dispose();

                //ParticleCTFs.WriteMRC("d_particlectfs.mrc");
            }
            GlobalBfactor = KeepBFac;

            #endregion

            #region Global alignment

            Func<float3[], float2[]> GetImageShifts = input =>
                {
                    // Using current positions, angles and grids, get parameters for image shifts
                    float2[] ImageShifts = new float2[NParticles * NTilts];
                    float3[] PerTiltPositions = new float3[NParticles * NTilts];
                    for (int p = 0; p < NParticles; p++)
                        for (int t = 0; t < NTilts; t++)
                            PerTiltPositions[p * NTilts + t] = input[p];

                    float3[] CurrPositions = GetPositionInImages(PerTiltPositions);
                    for (int i = 0; i < ImageShifts.Length; i++)
                        ImageShifts[i] = new float2(ExtractedAt[i].X - CurrPositions[i].X,
                                                    ExtractedAt[i].Y - CurrPositions[i].Y); // -diff because those are extraction positions, i. e. opposite direction of shifts

                    return ImageShifts;
                };

            Func<float3[], float3[]> GetImageAngles = input =>
                {
                    int NAngles = input.Length;
                    float3 VolumeCenter = new float3(VolumeDimensions.X / 2, VolumeDimensions.Y / 2, VolumeDimensions.Z / 2);
                    float3[] PerTiltPositions = new float3[NAngles * NTilts];
                    float3[] PerTiltAngles = new float3[NAngles * NTilts];
                    for (int a = 0; a < NAngles; a++)
                        for (int t = 0; t < NTilts; t++)
                        {
                            PerTiltPositions[a * NTilts + t] = VolumeCenter;
                            PerTiltAngles[a * NTilts + t] = input[a];
                        }

                    float3[] ImageAngles = GetParticleAngleInImages(PerTiltPositions, PerTiltAngles);

                    return ImageAngles;
                };

            float3[] RelativeOffsets;
            {
                List<float3> RelativeOffsetList = new List<float3>();
                int NSteps = (int)Math.Ceiling(offsetRange / offsetStep);
                for (int z = -NSteps; z <= NSteps; z++)
                    for (int y = -NSteps; y <= NSteps; y++)
                        for (int x = -NSteps; x <= NSteps; x++)
                        {
                            float R = (float)Math.Sqrt(x * x + y * y + z * z) * offsetStep;
                            if (R > offsetRange + 1e-6f)
                                continue;

                            RelativeOffsetList.Add(new float3(x * offsetStep, y * offsetStep, z * offsetStep));
                        }

                RelativeOffsets = RelativeOffsetList.ToArray();
            }

            float3[] HealpixAngles = Helper.GetHealpixAngles(healpixOrder, symmetry).Select(a => a * Helper.ToRad).ToArray();
            float3[] ProjectionAngles = GetImageAngles(HealpixAngles);

            float3[] OptimizedOrigins = new float3[NParticles];
            float3[] OptimizedAngles = new float3[NParticles];
            float[] BestScores = new float[NParticles].Select(v => -float.MaxValue).ToArray();

            int BatchAngles = 128;
            Image Projections = new Image(new int3(CoarseSize, CoarseSize, BatchAngles * NTilts), true, true);

            foreach (var subset in SubsetRanges)
            {
                int NSubset = subset.Value.Item2 - subset.Value.Item1;

                float[] ImageOffsets = new float[NSubset * NTilts * RelativeOffsets.Length * 2];
                for (int o = 0; o < RelativeOffsets.Length; o++)
                {
                    float3[] OffsetOrigins = new float3[NSubset];
                    for (int p = 0; p < NSubset; p++)
                        OffsetOrigins[p] = ParticleOrigins[subset.Value.Item1 + p] + RelativeOffsets[o];

                    float[] TheseOffsets = Helper.ToInterleaved(GetImageShifts(OffsetOrigins));
                    Array.Copy(TheseOffsets, 0, ImageOffsets, TheseOffsets.Length * o, TheseOffsets.Length);
                }

                int[] ShiftIDs = new int[NSubset];
                int[] AngleIDs = new int[NSubset];
                float[] SubsetScores = new float[NSubset];

                GPU.TomoGlobalAlign(ParticleImages.GetDeviceSlice(subset.Value.Item1 * NTilts, Intent.Read),
                                    ShiftFactors.GetDevice(Intent.Read),
                                    ParticleCTFs.GetDeviceSlice(subset.Value.Item1 * NTilts, Intent.Read),
                                    ParticleWeights.GetDeviceSlice(subset.Value.Item1 * NTilts, Intent.Read),
                                    new int2(CoarseDims),
                                    references[subset.Key].Data.GetDevice(Intent.Read),
                                    references[subset.Key].Data.Dims,
                                    references[subset.Key].Oversampling,
                                    Helper.ToInterleaved(ProjectionAngles),
                                    (uint)HealpixAngles.Length,
                                    ImageOffsets,
                                    (uint)RelativeOffsets.Length,
                                    (uint)NSubset,
                                    (uint)NTilts,
                                    AngleIDs,
                                    ShiftIDs,
                                    SubsetScores);

                for (int i = 0; i < NSubset; i++)
                {
                    OptimizedOrigins[subset.Value.Item1 + i] = ParticleOrigins[subset.Value.Item1 + i] + RelativeOffsets[ShiftIDs[i]];
                    OptimizedAngles[subset.Value.Item1 + i] = HealpixAngles[AngleIDs[i]];
                    BestScores[subset.Value.Item1 + i] = SubsetScores[i];
                }
            }

            Projections.Dispose();

            #endregion

            ParticleImages?.Dispose();
            ParticleCTFs?.Dispose();
            ParticleWeights?.Dispose();
            ShiftFactors?.Dispose();

            #region Extract particles at full resolution and back-project them into the reconstruction volumes

            {
                GPU.SetDevice(0);

                Image CTFCoords = GetCTFCoords(size, size);
                int[] SortedDosePrecalc = IndicesSortedDose;

                foreach (var subsetRange in SubsetRanges)
                {
                    lock (outReconstructions[subsetRange.Key])
                    {
                        for (int p = subsetRange.Value.Item1; p < subsetRange.Value.Item2; p++)
                        {
                            float3[] PerTiltPositions = new float3[NTilts];
                            float3[] PerTiltAngles = new float3[NTilts];
                            for (int t = 0; t < NTilts; t++)
                            {
                                PerTiltPositions[t] = OptimizedOrigins[p];
                                PerTiltAngles[t] = OptimizedAngles[p];
                            }

                            Image FullParticleImages = GetSubtomoImages(tiltStack, size, PerTiltPositions, true);
                            Image FullParticleCTFs = GetSubtomoCTFs(PerTiltPositions, CTFCoords);

                            FullParticleImages.Multiply(FullParticleCTFs);
                            FullParticleCTFs.Abs();

                            float3[] FullParticleAngles = GetParticleAngleInImages(PerTiltPositions, PerTiltAngles);

                            outReconstructions[subsetRange.Key].BackProject(FullParticleImages, FullParticleCTFs, FullParticleAngles);

                            FullParticleImages.Dispose();
                            FullParticleCTFs.Dispose();
                        }

                        for (int p = subsetRange.Value.Item1; p < subsetRange.Value.Item2; p++)
                        {
                            float3[] PerTiltPositions = new float3[NTilts];
                            float3[] PerTiltAngles = new float3[NTilts];
                            for (int t = 0; t < NTilts; t++)
                            {
                                PerTiltPositions[t] = OptimizedOrigins[p];
                                PerTiltAngles[t] = OptimizedAngles[p];
                            }

                            float3[] FullParticleAngles = GetParticleAngleInImages(PerTiltPositions, PerTiltAngles);

                            Image FullParticleCTFs = GetSubtomoCTFs(PerTiltPositions, CTFCoords, false);
                            Image FullParticleCTFWeights = GetSubtomoCTFs(PerTiltPositions, CTFCoords, true);

                            // CTF has to be converted to complex numbers with imag = 0
                            float2[] CTFsComplexData = new float2[FullParticleCTFs.ElementsComplex];
                            float[] CTFWeightsData = new float[FullParticleCTFs.ElementsComplex];
                            float[] CTFsContinuousData = FullParticleCTFs.GetHostContinuousCopy();
                            float[] CTFWeightsContinuousData = FullParticleCTFWeights.GetHostContinuousCopy();
                            for (int i = 0; i < CTFsComplexData.Length; i++)
                            {
                                CTFsComplexData[i] = new float2(Math.Abs(CTFsContinuousData[i] * CTFWeightsContinuousData[i]), 0);
                                CTFWeightsData[i] = Math.Abs(CTFWeightsContinuousData[i]);
                            }

                            Image CTFsComplex = new Image(CTFsComplexData, FullParticleCTFs.Dims, true);
                            Image CTFWeights = new Image(CTFWeightsData, FullParticleCTFs.Dims, true);

                            outCTFReconstructions[subsetRange.Key].BackProject(CTFsComplex, CTFWeights, FullParticleAngles);

                            FullParticleCTFs.Dispose();
                            FullParticleCTFWeights.Dispose();
                            CTFsComplex.Dispose();
                            CTFWeights.Dispose();
                        }

                        outReconstructions[subsetRange.Key].FreeDevice();
                        outCTFReconstructions[subsetRange.Key].FreeDevice();
                    }
                }

                CTFCoords.Dispose();
            }

            #endregion

            SaveMeta();
        }
Exemple #22
0
        private static float[] GetPCHIPSlopes(float2[] data, float[] del)
        {
            if (data.Length == 2)
                return new[] { del[0], del[0] };   // Do only linear

            float[] d = new float[data.Length];
            float[] h = MathHelper.Diff(data.Select(i => i.X).ToArray());
            for (int k = 0; k < del.Length - 1; k++)
            {
                if (del[k] * del[k + 1] <= 0f)
                    continue;

                float hs = h[k] + h[k + 1];
                float w1 = (h[k] + hs) / (3f * hs);
                float w2 = (hs + h[k + 1]) / (3f * hs);
                float dmax = Math.Max(Math.Abs(del[k]), Math.Abs(del[k + 1]));
                float dmin = Math.Min(Math.Abs(del[k]), Math.Abs(del[k + 1]));
                d[k + 1] = dmin / (w1 * (del[k] / dmax) + w2 * (del[k + 1] / dmax));
            }

            d[0] = ((2f * h[0] + h[1]) * del[0] - h[0] * del[1]) / (h[0] + h[1]);
            if (Math.Sign(d[0]) != Math.Sign(del[0]))
                d[0] = 0;
            else if (Math.Sign(del[0]) != Math.Sign(del[1]) && Math.Abs(d[0]) > Math.Abs(3f * del[0]))
                d[0] = 3f * del[0];

            int n = d.Length - 1;
            d[n] = ((2 * h[n - 1] + h[n - 2]) * del[n - 1] - h[n - 1] * del[n - 2]) / (h[n - 1] + h[n - 2]);
            if (Math.Sign(d[n]) != Math.Sign(del[n - 1]))
                d[n] = 0;
            else if (Math.Sign(del[n - 1]) != Math.Sign(del[n - 2]) && Math.Abs(d[n]) > Math.Abs(3f * del[n - 1]))
                d[n] = 3f * del[n - 1];

            return d;
        }
Exemple #23
0
        public override void ProcessCTF(MapHeader originalHeader, Image originalStack, bool doastigmatism, decimal scaleFactor)
        {
            if (!Directory.Exists(PowerSpectrumDir))
                Directory.CreateDirectory(PowerSpectrumDir);

            AreAnglesInverted = false;
            float LastFittedAngle = 9999f;
            float AverageDose = Dose[IndicesSortedDose.Last()] / NTilts;
            List<int> ProcessedIndices = new List<int>();

            CTF[] FitCTF = new CTF[NTilts];
            CubicGrid[] FitGrids = new CubicGrid[NTilts];
            float2[][] FitPS1D = new float2[NTilts][];
            Cubic1D[] FitBackground = new Cubic1D[NTilts];
            Cubic1D[] FitScale = new Cubic1D[NTilts];
            Image[] FitPS2D = new Image[NTilts];

            float[][] StackData = originalStack.GetHost(Intent.Read);

            #region Get astigmatism from lower tilts

            List<float> AstigmatismDeltas = new List<float>();
            List<float> AstigmatismAngles = new List<float>();

            for (int i = 0; i < Math.Min(NTilts, 6); i++)
            {
                int AngleID = IndicesSortedAbsoluteAngle[i];
                Image UncroppedAngleImage = new Image(StackData[AngleID], originalStack.Dims.Slice());
                Image AngleImage = UncroppedAngleImage.AsPadded(new int2(3500, 3500));
                UncroppedAngleImage.Dispose();

                int BestPrevious = -1;
                if (Math.Abs(LastFittedAngle - Angles[AngleID]) <= 5.1f)
                    BestPrevious = IndicesSortedAbsoluteAngle[i - 1];
                else if (ProcessedIndices.Count > 0)
                {
                    List<int> SortedProcessed = new List<int>(ProcessedIndices);
                    SortedProcessed.Sort((a, b) => Math.Abs(Angles[AngleID] - Angles[a]).CompareTo(Math.Abs(Angles[AngleID] - Angles[b])));
                    if (Math.Abs(Dose[SortedProcessed.First()] - Dose[AngleID]) < AverageDose * 5f)
                        BestPrevious = SortedProcessed.First();
                }

                CTF ThisCTF;
                CubicGrid ThisGrid;
                float2[] ThisPS1D;
                Cubic1D ThisBackground, ThisScale;
                Image ThisPS2D;

                CTF PrevCTF = BestPrevious >= 0 ? FitCTF[BestPrevious] : null;
                CubicGrid PrevGrid = BestPrevious >= 0 ? FitGrids[BestPrevious] : null;
                Cubic1D PrevBackground = BestPrevious >= 0 ? FitBackground[BestPrevious] : null;
                Cubic1D PrevScale = BestPrevious >= 0 ? FitScale[BestPrevious] : null;

                ProcessCTFOneAngle(AngleImage,
                                   Angles[AngleID],
                                   BestPrevious < 0,
                                   false,
                                   new float2(0, 0),
                                   PrevCTF,
                                   PrevGrid,
                                   PrevBackground,
                                   PrevScale,
                                   out ThisCTF,
                                   out ThisGrid,
                                   out ThisPS1D,
                                   out ThisBackground,
                                   out ThisScale,
                                   out ThisPS2D);
                AngleImage.Dispose();

                FitCTF[AngleID] = ThisCTF;
                FitGrids[AngleID] = ThisGrid;
                FitPS1D[AngleID] = ThisPS1D;
                FitBackground[AngleID] = ThisBackground;
                FitScale[AngleID] = ThisScale;
                FitPS2D[AngleID] = ThisPS2D;

                LastFittedAngle = Angles[AngleID];
                ProcessedIndices.Add(AngleID);

                AstigmatismDeltas.Add((float)ThisCTF.DefocusDelta);
                AstigmatismAngles.Add((float)ThisCTF.DefocusAngle);
            }

            ProcessedIndices.Clear();
            LastFittedAngle = 9999;
            int[] GoodIndices = MathHelper.WithinNStdFromMedianIndices(AstigmatismDeltas.ToArray(), 1f);
            float MeanAstigmatismDelta = MathHelper.Mean(GoodIndices.Select(i => AstigmatismDeltas[i]));
            float2 MeanAstigmatismVector = MathHelper.Mean(GoodIndices.Select(i => new float2((float)Math.Cos(AstigmatismAngles[i] * Helper.ToRad),
                                                                                              (float)Math.Sin(AstigmatismAngles[i] * Helper.ToRad))));
            float MeanAstigmatismAngle = (float)Math.Atan2(MeanAstigmatismVector.Y, MeanAstigmatismVector.X) * Helper.ToDeg;

            #endregion

            #region Fit every tilt

            for (int i = 0; i < NTilts; i++)
            {
                int AngleID = IndicesSortedDose[i];
                Image UncroppedAngleImage = new Image(StackData[AngleID], originalStack.Dims.Slice());
                Image AngleImage = UncroppedAngleImage.AsPadded(new int2(3500, 3500));
                UncroppedAngleImage.Dispose();

                int BestPrevious = -1;
                if (Math.Abs(LastFittedAngle - Angles[AngleID]) <= 5.1f)
                    BestPrevious = IndicesSortedDose[i - 1];
                else if (ProcessedIndices.Count > 0)
                {
                    List<int> SortedProcessed = new List<int>(ProcessedIndices);
                    SortedProcessed.Sort((a, b) => Math.Abs(Angles[AngleID] - Angles[a]).CompareTo(Math.Abs(Angles[AngleID] - Angles[b])));
                    if (Math.Abs(Dose[SortedProcessed.First()] - Dose[AngleID]) < AverageDose * 5f)
                        BestPrevious = SortedProcessed.First();
                }

                CTF ThisCTF;
                CubicGrid ThisGrid;
                float2[] ThisPS1D;
                Cubic1D ThisBackground, ThisScale;
                Image ThisPS2D;

                CTF PrevCTF = BestPrevious >= 0 ? FitCTF[BestPrevious] : null;
                CubicGrid PrevGrid = BestPrevious >= 0 ? FitGrids[BestPrevious] : null;
                Cubic1D PrevBackground = BestPrevious >= 0 ? FitBackground[BestPrevious] : null;
                Cubic1D PrevScale = BestPrevious >= 0 ? FitScale[BestPrevious] : null;

                ProcessCTFOneAngle(AngleImage,
                                   Angles[AngleID],
                                   BestPrevious < 0,
                                   true,
                                   new float2(MeanAstigmatismDelta, MeanAstigmatismAngle),
                                   PrevCTF,
                                   PrevGrid,
                                   PrevBackground,
                                   PrevScale,
                                   out ThisCTF,
                                   out ThisGrid,
                                   out ThisPS1D,
                                   out ThisBackground,
                                   out ThisScale,
                                   out ThisPS2D);
                AngleImage.Dispose();

                FitCTF[AngleID] = ThisCTF;
                FitGrids[AngleID] = ThisGrid;
                FitPS1D[AngleID] = ThisPS1D;
                FitBackground[AngleID] = ThisBackground;
                FitScale[AngleID] = ThisScale;
                FitPS2D[AngleID] = ThisPS2D;

                LastFittedAngle = Angles[AngleID];
                ProcessedIndices.Add(AngleID);
            }

            #endregion

            CTF = FitCTF[IndicesSortedDose[0]];

            #region Determine if angles are inverted compared to actual defocus

            {
                float[] UnbiasedAngles = FitGrids.Select(g =>
                {
                    float X1 = (g.FlatValues[0] + g.FlatValues[2]) * 0.5f;
                    float X2 = (g.FlatValues[1] + g.FlatValues[3]) * 0.5f;
                    float Delta = (X2 - X1) * 10000;
                    float Distance = (float)MainWindow.Options.BinnedPixelSize * 3000;// originalHeader.Dimensions.X;
                    return (float)Math.Atan2(Delta, Distance) * Helper.ToDeg;
                }).ToArray();

                float Unbiased1 = 0, Unbiased2 = 0, Original1 = 0, Original2 = 0;
                for (int i = 0; i < NTilts; i++)
                {
                    int ii = IndicesSortedAngle[i];
                    if (i < NTilts / 2)
                    {
                        Unbiased1 += UnbiasedAngles[ii];
                        Original1 += Angles[ii];
                    }
                    else
                    {
                        Unbiased2 += UnbiasedAngles[ii];
                        Original2 += Angles[ii];
                    }
                }

                if ((Unbiased1 > Unbiased2) != (Original1 > Original2))
                    AreAnglesInverted = true;
            }

            #endregion

            // Create grids for fitted CTF params
            {
                float[] DefocusValues = new float[NTilts];
                float[] DeltaValues = new float[NTilts];
                float[] AngleValues = new float[NTilts];
                for (int i = 0; i < NTilts; i++)
                {
                    DefocusValues[i] = (float)FitCTF[i].Defocus;
                    DeltaValues[i] = (float)FitCTF[i].DefocusDelta;
                    AngleValues[i] = (float)FitCTF[i].DefocusAngle;
                }

                GridCTF = new CubicGrid(new int3(1, 1, NTilts), DefocusValues);
                GridCTFDefocusDelta = new CubicGrid(new int3(1, 1, NTilts), DeltaValues);
                GridCTFDefocusAngle = new CubicGrid(new int3(1, 1, NTilts), AngleValues);
            }

            // Put all 2D spectra into one stack and write it to disk for display purposes
            {
                Image AllPS2D = new Image(new int3(FitPS2D[0].Dims.X, FitPS2D[0].Dims.Y, NTilts));
                float[][] AllPS2DData = AllPS2D.GetHost(Intent.Write);
                for (int i = 0; i < NTilts; i++)
                {
                    AllPS2DData[i] = FitPS2D[i].GetHost(Intent.Read)[0];
                    FitPS2D[i].Dispose();
                }

                AllPS2D.WriteMRC(PowerSpectrumPath);
            }

            // Store 1D spectrum data
            TiltPS1D.Clear();
            TiltSimulatedBackground.Clear();
            TiltSimulatedScale.Clear();
            for (int i = 0; i < NTilts; i++)
            {
                TiltPS1D.Add(FitPS1D[i]);
                TiltSimulatedBackground.Add(new Cubic1D(FitBackground[i].Data.Select(v => new float2(v.X, 0)).ToArray()));
                TiltSimulatedScale.Add(FitScale[i]);
            }

            PS1D = FitPS1D[IndicesSortedDose[0]];
            SimulatedBackground = TiltSimulatedBackground[IndicesSortedDose[0]];
            SimulatedScale = FitScale[IndicesSortedDose[0]];

            OnPropertyChanged("PS1D");

            Simulated1D = GetSimulated1D();
            CTFQuality = GetCTFQuality();

            SaveMeta();
        }
Exemple #24
0
        public static Cubic1D Fit(float2[] data, int numnodes)
        {
            List<float2> Fitted = new List<float2>();

            int Extent = Math.Max(data.Length / (numnodes - 1) / 2, 1);
            for (int n = 0; n < numnodes; n++)
            {
                float Sum = 0;
                int Samples = 0;
                int Middle = Math.Min(data.Length - 1, n * (data.Length / (numnodes - 1)));
                int Start = Math.Max(0, Middle - Extent);
                int Finish = Math.Min(data.Length, Middle + Extent);

                for (int i = Start; i < Finish; i++, Samples++)
                    Sum += data[i].Y;

                Fitted.Add(new float2(data[Middle].X, Sum / Samples));
            }

            return new Cubic1D(Fitted.ToArray());

            /*float MinX = MathHelper.Min(data.Select(p => p.X)), MaxX = MathHelper.Max(data.Select(p => p.X)), ScaleX = 1f / (MaxX - MinX);
            float MinY = MathHelper.Min(data.Select(p => p.Y)), MaxY = MathHelper.Max(data.Select(p => p.Y)), ScaleY = 1f / (MaxY - MinY);
            if (float.IsNaN(ScaleY))
                ScaleY = 1f;

            float2[] ScaledData = data.Select(p => new float2((p.X - MinX) * ScaleX, (p.Y - MinY) * ScaleY)).ToArray();

            double[] Start = new double[numnodes];
            double[] NodeX = new double[numnodes];
            for (int i = 0; i < numnodes; i++)
            {
                NodeX[i] = Math.Pow(i, 0.75) / Math.Pow(numnodes - 1, 0.75) * (MaxX - MinX) * ScaleX;
                Start[i] = ScaledData[(int)(Math.Pow(i, 0.75) / Math.Pow(numnodes - 1, 0.75) * (data.Length - 1))].Y;
                //NodeX[i] = (double)i / (numnodes - 1) * (MaxX - MinX) * ScaleX;
                //Start[i] = ScaledData[(int)((double)i / (numnodes - 1) * (data.Length - 1))].Y;
            }

            float[] DataX = ScaledData.Select(p => p.X).ToArray();

            Func<double[], double> Eval = input =>
            {
                float2[] Nodes = new float2[numnodes];
                for (int i = 0; i < numnodes; i++)
                    Nodes[i] = new float2((float)NodeX[i], (float)input[i]);
                Cubic1D Splines = new Cubic1D(Nodes);
                float[] Interpolated = Splines.Interp(DataX);

                float Sum = 0f;
                for (int i = 0; i < ScaledData.Length; i++)
                {
                    float Diff = ScaledData[i].Y - Interpolated[i];
                    Sum += Diff * Diff;
                }

                return Math.Sqrt(Sum / data.Length) * 1000;
            };

            Func<double[], double[]> Gradient = input =>
            {
                double[] Result = new double[input.Length];

                for (int i = 0; i < input.Length; i++)
                {
                    double[] UpperInput = new double[input.Length];
                    input.CopyTo(UpperInput, 0);
                    UpperInput[i] += 0.01;
                    double UpperValue = Eval(UpperInput);

                    double[] LowerInput = new double[input.Length];
                    input.CopyTo(LowerInput, 0);
                    LowerInput[i] -= 0.01;
                    double LowerValue = Eval(LowerInput);

                    Result[i] = (UpperValue - LowerValue) / 0.02;
                }

                return Result;
            };

            BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(Start.Length, Eval, Gradient);
            Optimizer.Minimize(Start);

            {
                float2[] Nodes = new float2[numnodes];
                for (int i = 0; i < numnodes; i++)
                    Nodes[i] = new float2((float)NodeX[i] / ScaleX + MinX, (float)Optimizer.Solution[i] / ScaleY + MinY);

                return new Cubic1D(Nodes);
            }*/
        }
Exemple #25
0
        public void ProcessCTFOneAngle(Image angleImage,
                                       float angle,
                                       bool fromScratch,
                                       bool fixAstigmatism,
                                       float2 astigmatism,
                                       CTF previousCTF,
                                       CubicGrid previousGrid,
                                       Cubic1D previousBackground,
                                       Cubic1D previousScale,
                                       out CTF thisCTF,
                                       out CubicGrid thisGrid,
                                       out float2[] thisPS1D,
                                       out Cubic1D thisBackground,
                                       out Cubic1D thisScale,
                                       out Image thisPS2D)
        {
            CTF TempCTF = previousCTF != null ? previousCTF.GetCopy() : new CTF();
            float2[] TempPS1D = null;
            Cubic1D TempBackground = null, TempScale = null;
            CubicGrid TempGrid = null;

            #region Dimensions and grids

            int NFrames = angleImage.Dims.Z;
            int2 DimsImage = angleImage.DimsSlice;
            int2 DimsRegion = new int2(MainWindow.Options.CTFWindow, MainWindow.Options.CTFWindow);

            float OverlapFraction = 0.5f;
            int2 DimsPositionGrid;
            int3[] PositionGrid = Helper.GetEqualGridSpacing(DimsImage, new int2(DimsRegion.X, DimsRegion.Y), OverlapFraction, out DimsPositionGrid);
            int NPositions = (int)DimsPositionGrid.Elements();

            if (previousGrid == null)
                TempGrid = new CubicGrid(new int3(2, 2, 1));
            else
                TempGrid = new CubicGrid(new int3(2, 2, 1), previousGrid.FlatValues);

            bool CTFSpace = true;
            bool CTFTime = false;
            int3 CTFSpectraGrid = new int3(DimsPositionGrid.X, DimsPositionGrid.Y, 1);

            int MinFreqInclusive = (int)(MainWindow.Options.CTFRangeMin * DimsRegion.X / 2);
            int MaxFreqExclusive = (int)(MainWindow.Options.CTFRangeMax * DimsRegion.X / 2);
            int NFreq = MaxFreqExclusive - MinFreqInclusive;

            #endregion

            #region Allocate GPU memory

            Image CTFSpectra = new Image(IntPtr.Zero, new int3(DimsRegion.X, DimsRegion.X, (int)CTFSpectraGrid.Elements()), true);
            Image CTFMean = new Image(IntPtr.Zero, new int3(DimsRegion), true);
            Image CTFCoordsCart = new Image(new int3(DimsRegion), true, true);
            Image CTFCoordsPolarTrimmed = new Image(new int3(NFreq, DimsRegion.X, 1), false, true);

            #endregion

            // Extract movie regions, create individual spectra in Cartesian coordinates and their mean.

            #region Create spectra

            GPU.CreateSpectra(angleImage.GetDevice(Intent.Read),
                              DimsImage,
                              NFrames,
                              PositionGrid,
                              NPositions,
                              DimsRegion,
                              CTFSpectraGrid,
                              CTFSpectra.GetDevice(Intent.Write),
                              CTFMean.GetDevice(Intent.Write));
            angleImage.FreeDevice(); // Won't need it in this method anymore.

            #endregion

            // Populate address arrays for later.

            #region Init addresses

            {
                float2[] CoordsData = new float2[CTFCoordsCart.ElementsSliceComplex];

                Helper.ForEachElementFT(DimsRegion, (x, y, xx, yy, r, a) => CoordsData[y * (DimsRegion.X / 2 + 1) + x] = new float2(r, a));
                CTFCoordsCart.UpdateHostWithComplex(new[] { CoordsData });

                CoordsData = new float2[NFreq * DimsRegion.X];
                Helper.ForEachElement(CTFCoordsPolarTrimmed.DimsSlice, (x, y) =>
                {
                    float Angle = ((float)y / DimsRegion.X + 0.5f) * (float)Math.PI;
                    float Ny = 1f / DimsRegion.X;
                    CoordsData[y * NFreq + x] = new float2((x + MinFreqInclusive) * Ny, Angle);
                });
                CTFCoordsPolarTrimmed.UpdateHostWithComplex(new[] { CoordsData });
            }

            #endregion

            // Retrieve average 1D spectrum from CTFMean (not corrected for astigmatism yet).

            #region Initial 1D spectrum

            {
                Image CTFAverage1D = new Image(IntPtr.Zero, new int3(DimsRegion.X / 2, 1, 1));

                GPU.CTFMakeAverage(CTFMean.GetDevice(Intent.Read),
                                   CTFCoordsCart.GetDevice(Intent.Read),
                                   (uint)CTFMean.ElementsSliceReal,
                                   (uint)DimsRegion.X,
                                   new[] { new CTF().ToStruct() },
                                   new CTF().ToStruct(),
                                   0,
                                   (uint)DimsRegion.X / 2,
                                   null,
                                   1,
                                   CTFAverage1D.GetDevice(Intent.Write));

                //CTFAverage1D.WriteMRC("CTFAverage1D.mrc");

                float[] CTFAverage1DData = CTFAverage1D.GetHost(Intent.Read)[0];
                float2[] ForPS1D = new float2[DimsRegion.X / 2];
                for (int i = 0; i < ForPS1D.Length; i++)
                    ForPS1D[i] = new float2((float)i / DimsRegion.X, (float)Math.Round(CTFAverage1DData[i], 4));
                TempPS1D = ForPS1D;

                CTFAverage1D.Dispose();
            }

            #endregion

            #region Background fitting methods

            Action UpdateBackgroundFit = () =>
            {
                float2[] ForPS1D = TempPS1D.Skip(Math.Max(5, MinFreqInclusive / 2)).ToArray();
                Cubic1D.FitCTF(ForPS1D,
                               v => v.Select(x => TempCTF.Get1D(x / (float)TempCTF.PixelSize, true)).ToArray(),
                               TempCTF.GetZeros(),
                               TempCTF.GetPeaks(),
                               out TempBackground,
                               out TempScale);
            };

            Action<bool> UpdateRotationalAverage = keepbackground =>
            {
                float[] MeanData = CTFMean.GetHost(Intent.Read)[0];

                Image CTFMeanCorrected = new Image(new int3(DimsRegion), true);
                float[] MeanCorrectedData = CTFMeanCorrected.GetHost(Intent.Write)[0];

                // Subtract current background estimate from spectra, populate coords.
                Helper.ForEachElementFT(DimsRegion,
                                        (x, y, xx, yy, r, a) =>
                                        {
                                            int i = y * (DimsRegion.X / 2 + 1) + x;
                                            MeanCorrectedData[i] = MeanData[i] - TempBackground.Interp(r / DimsRegion.X);
                                        });

                Image CTFAverage1D = new Image(IntPtr.Zero, new int3(DimsRegion.X / 2, 1, 1));

                GPU.CTFMakeAverage(CTFMeanCorrected.GetDevice(Intent.Read),
                                   CTFCoordsCart.GetDevice(Intent.Read),
                                   (uint)CTFMeanCorrected.DimsEffective.ElementsSlice(),
                                   (uint)DimsRegion.X,
                                   new[] { TempCTF.ToStruct() },
                                   TempCTF.ToStruct(),
                                   0,
                                   (uint)DimsRegion.X / 2,
                                   null,
                                   1,
                                   CTFAverage1D.GetDevice(Intent.Write));

                //CTFAverage1D.WriteMRC("CTFAverage1D.mrc");

                float[] RotationalAverageData = CTFAverage1D.GetHost(Intent.Read)[0];
                float2[] ForPS1D = new float2[TempPS1D.Length];
                if (keepbackground)
                    for (int i = 0; i < ForPS1D.Length; i++)
                        ForPS1D[i] = new float2((float)i / DimsRegion.X, RotationalAverageData[i] + TempBackground.Interp((float)i / DimsRegion.X));
                else
                    for (int i = 0; i < ForPS1D.Length; i++)
                        ForPS1D[i] = new float2((float)i / DimsRegion.X, RotationalAverageData[i]);
                MathHelper.UnNaN(ForPS1D);

                TempPS1D = ForPS1D;

                CTFMeanCorrected.Dispose();
                CTFAverage1D.Dispose();
            };

            #endregion

            // Fit background to currently best average (not corrected for astigmatism yet).
            {
                float2[] ForPS1D = TempPS1D.Skip(MinFreqInclusive).Take(Math.Max(2, NFreq / 2)).ToArray();

                float[] CurrentBackground;
                //if (previousBackground == null)
                {
                    int NumNodes = Math.Max(3, (int)((MainWindow.Options.CTFRangeMax - MainWindow.Options.CTFRangeMin) * 5M));
                    TempBackground = Cubic1D.Fit(ForPS1D, NumNodes); // This won't fit falloff and scale, because approx function is 0

                    CurrentBackground = TempBackground.Interp(TempPS1D.Select(p => p.X).ToArray()).Skip(MinFreqInclusive).Take(NFreq / 2).ToArray();
                }
                /*else
                {
                    CurrentBackground = previousBackground.Interp(TempPS1D.Select(p => p.X).ToArray()).Skip(MinFreqInclusive).Take(NFreq / 2).ToArray();
                    TempBackground = new Cubic1D(previousBackground.Data);
                }*/

                float[] Subtracted1D = new float[ForPS1D.Length];
                for (int i = 0; i < ForPS1D.Length; i++)
                    Subtracted1D[i] = ForPS1D[i].Y - CurrentBackground[i];
                MathHelper.NormalizeInPlace(Subtracted1D);

                float ZMin = (float)MainWindow.Options.CTFZMin;
                float ZMax = (float)MainWindow.Options.CTFZMax;
                float PhaseMin = 0f;
                float PhaseMax = MainWindow.Options.CTFDoPhase ? 1f : 0f;

                if (previousCTF != null)
                {
                    ZMin = (float)previousCTF.Defocus - 0.5f;
                    ZMax = (float)previousCTF.Defocus + 0.5f;
                    if (PhaseMax > 0)
                    {
                        PhaseMin = (float)previousCTF.PhaseShift - 0.3f;
                        PhaseMax = (float)previousCTF.PhaseShift + 0.3f;
                    }
                }

                float ZStep = (ZMax - ZMin) / 100f;

                float BestZ = 0, BestPhase = 0, BestScore = -999;
                for (float z = ZMin; z <= ZMax + 1e-5f; z += ZStep)
                {
                    for (float p = PhaseMin; p <= PhaseMax; p += 0.01f)
                    {
                        CTF CurrentParams = new CTF
                        {
                            PixelSize = (MainWindow.Options.CTFPixelMin + MainWindow.Options.CTFPixelMax) * 0.5M,

                            Defocus = (decimal)z,
                            PhaseShift = (decimal)p,

                            Cs = MainWindow.Options.CTFCs,
                            Voltage = MainWindow.Options.CTFVoltage,
                            Amplitude = MainWindow.Options.CTFAmplitude
                        };
                        float[] SimulatedCTF = CurrentParams.Get1D(TempPS1D.Length, true).Skip(MinFreqInclusive).Take(Math.Max(2, NFreq / 2)).ToArray();
                        MathHelper.NormalizeInPlace(SimulatedCTF);
                        float Score = MathHelper.CrossCorrelate(Subtracted1D, SimulatedCTF);
                        if (Score > BestScore)
                        {
                            BestScore = Score;
                            BestZ = z;
                            BestPhase = p;
                        }
                    }
                }

                TempCTF = new CTF
                {
                    PixelSize = (MainWindow.Options.CTFPixelMin + MainWindow.Options.CTFPixelMax) * 0.5M,

                    Defocus = (decimal)BestZ,
                    PhaseShift = (decimal)BestPhase,

                    Cs = MainWindow.Options.CTFCs,
                    Voltage = MainWindow.Options.CTFVoltage,
                    Amplitude = MainWindow.Options.CTFAmplitude
                };

                UpdateRotationalAverage(true); // This doesn't have a nice background yet.
                UpdateBackgroundFit(); // Now get a reasonably nice background.
            }

            // Fit defocus, (phase shift), (astigmatism) to average background-subtracted spectrum,
            // which is in polar coords at this point (for equal weighting of all frequencies).

            #region Grid search

            if (fromScratch)
            {
                Image CTFMeanPolarTrimmed = CTFMean.AsPolar((uint)MinFreqInclusive, (uint)(MinFreqInclusive + NFreq / 1));

                // Subtract current background.
                Image CurrentBackground = new Image(TempBackground.Interp(TempPS1D.Select(p => p.X).ToArray()).Skip(MinFreqInclusive).Take(NFreq / 1).ToArray());
                CTFMeanPolarTrimmed.SubtractFromLines(CurrentBackground);
                CurrentBackground.Dispose();

                // Normalize for CC (not strictly needed, but it's converted for fp16 later, so let's be on the safe side of the fp16 range.
                GPU.Normalize(CTFMeanPolarTrimmed.GetDevice(Intent.Read), CTFMeanPolarTrimmed.GetDevice(Intent.Write), (uint)CTFMeanPolarTrimmed.ElementsReal, 1);
                //CTFMeanPolarTrimmed.WriteMRC("ctfmeanpolartrimmed.mrc");

                CTF StartParams = new CTF
                {
                    PixelSize = (MainWindow.Options.CTFPixelMin + MainWindow.Options.CTFPixelMax) * 0.5M,
                    PixelSizeDelta = Math.Abs(MainWindow.Options.CTFPixelMax - MainWindow.Options.CTFPixelMin),
                    PixelSizeAngle = MainWindow.Options.CTFPixelAngle,

                    Defocus = TempCTF.Defocus, // (MainWindow.Options.CTFZMin + MainWindow.Options.CTFZMax) * 0.5M,
                    DefocusDelta = 0,
                    DefocusAngle = 0,

                    PhaseShift = TempCTF.PhaseShift,

                    Cs = MainWindow.Options.CTFCs,
                    Voltage = MainWindow.Options.CTFVoltage,
                    Amplitude = MainWindow.Options.CTFAmplitude
                };

                CTFFitStruct FitParams = new CTFFitStruct
                {
                    Defocus = new float3(-0.4e-6f,
                                         0.4e-6f,
                                         0.025e-6f),

                    Defocusdelta = new float3(0, 0.8e-6f, 0.02e-6f),
                    Astigmatismangle = new float3(0, 2 * (float)Math.PI, 1 * (float)Math.PI / 18),
                    Phaseshift = MainWindow.Options.CTFDoPhase ? new float3(-0.2f * (float)Math.PI, 0.2f * (float)Math.PI, 0.025f * (float)Math.PI) : new float3(0, 0, 0)
                };

                CTFStruct ResultStruct = GPU.CTFFitMean(CTFMeanPolarTrimmed.GetDevice(Intent.Read),
                                                        CTFCoordsPolarTrimmed.GetDevice(Intent.Read),
                                                        CTFMeanPolarTrimmed.DimsSlice,
                                                        StartParams.ToStruct(),
                                                        FitParams,
                                                        true);
                TempCTF.FromStruct(ResultStruct);
                TempCTF.Defocus = Math.Max(TempCTF.Defocus, MainWindow.Options.CTFZMin);

                CTFMeanPolarTrimmed.Dispose();

                UpdateRotationalAverage(true); // This doesn't have a nice background yet.
                UpdateBackgroundFit(); // Now get a reasonably nice background.

                UpdateRotationalAverage(true); // This time, with the nice background.
                UpdateBackgroundFit(); // Make the background even nicer!
            }
            else if (previousCTF != null)
            {
                TempCTF.DefocusDelta = previousCTF.DefocusDelta;
                TempCTF.DefocusAngle = previousCTF.DefocusAngle;
            }

            if (fixAstigmatism)
            {
                TempCTF.DefocusDelta = (decimal)astigmatism.X;
                TempCTF.DefocusAngle = (decimal)astigmatism.Y;
            }

            #endregion

            if (previousGrid == null)
                TempGrid = new CubicGrid(TempGrid.Dimensions, (float)TempCTF.Defocus, (float)TempCTF.Defocus, Dimension.X);

            // Do BFGS optimization of defocus, astigmatism and phase shift,
            // using 2D simulation for comparison

            #region BFGS

            bool[] CTFSpectraConsider = new bool[CTFSpectraGrid.Elements()];
            for (int i = 0; i < CTFSpectraConsider.Length; i++)
                CTFSpectraConsider[i] = true;
            int NCTFSpectraConsider = CTFSpectraConsider.Length;

            {
                Image CTFSpectraPolarTrimmed = CTFSpectra.AsPolar((uint)MinFreqInclusive, (uint)(MinFreqInclusive + NFreq));
                CTFSpectra.FreeDevice(); // This will only be needed again for the final PS1D.

                #region Create background and scale

                float[] CurrentScale = TempScale.Interp(TempPS1D.Select(p => p.X).ToArray());

                Image CTFSpectraScale = new Image(new int3(NFreq, DimsRegion.X, 1));
                float[] CTFSpectraScaleData = CTFSpectraScale.GetHost(Intent.Write)[0];

                // Trim polar to relevant frequencies, and populate coordinates.
                Parallel.For(0, DimsRegion.X, y =>
                {
                    for (int x = 0; x < NFreq; x++)
                        CTFSpectraScaleData[y * NFreq + x] = CurrentScale[x + MinFreqInclusive];
                });
                //CTFSpectraScale.WriteMRC("ctfspectrascale.mrc");

                // Background is just 1 line since we're in polar.
                Image CurrentBackground = new Image(TempBackground.Interp(TempPS1D.Select(p => p.X).ToArray()).Skip(MinFreqInclusive).Take(NFreq).ToArray());

                #endregion

                CTFSpectraPolarTrimmed.SubtractFromLines(CurrentBackground);
                CurrentBackground.Dispose();

                // Normalize background-subtracted spectra.
                GPU.Normalize(CTFSpectraPolarTrimmed.GetDevice(Intent.Read),
                              CTFSpectraPolarTrimmed.GetDevice(Intent.Write),
                              (uint)CTFSpectraPolarTrimmed.ElementsSliceReal,
                              (uint)CTFSpectraGrid.Elements());
                //CTFSpectraPolarTrimmed.WriteMRC("ctfspectrapolartrimmed.mrc");

                #region Convert to fp16

                Image CTFSpectraPolarTrimmedHalf = CTFSpectraPolarTrimmed.AsHalf();
                CTFSpectraPolarTrimmed.Dispose();

                Image CTFSpectraScaleHalf = CTFSpectraScale.AsHalf();
                CTFSpectraScale.Dispose();
                Image CTFCoordsPolarTrimmedHalf = CTFCoordsPolarTrimmed.AsHalf();

                #endregion

                // Wiggle weights show how the defocus on the spectra grid is altered
                // by changes in individual anchor points of the spline grid.
                // They are used later to compute the dScore/dDefocus values for each spectrum
                // only once, and derive the values for each anchor point from them.
                float[][] WiggleWeights = TempGrid.GetWiggleWeights(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0));

                // Helper method for getting CTFStructs for the entire spectra grid.
                Func<double[], CTF, float[], CTFStruct[]> EvalGetCTF = (input, ctf, defocusValues) =>
                {
                    decimal AlteredPhase = MainWindow.Options.CTFDoPhase ? (decimal)input[input.Length - 3] : 0;
                    decimal AlteredDelta = (decimal)input[input.Length - 2];
                    decimal AlteredAngle = (decimal)(input[input.Length - 1] * 20 / (Math.PI / 180));

                    CTF Local = ctf.GetCopy();
                    Local.PhaseShift = AlteredPhase;
                    Local.DefocusDelta = AlteredDelta;
                    Local.DefocusAngle = AlteredAngle;

                    CTFStruct LocalStruct = Local.ToStruct();
                    CTFStruct[] LocalParams = new CTFStruct[defocusValues.Length];
                    for (int i = 0; i < LocalParams.Length; i++)
                    {
                        LocalParams[i] = LocalStruct;
                        LocalParams[i].Defocus = defocusValues[i] * -1e-6f;
                    }

                    return LocalParams;
                };

                // Simulate with adjusted CTF, compare to originals

                #region Eval and Gradient methods

                Func<double[], double> Eval = input =>
                {
                    CubicGrid Altered = new CubicGrid(TempGrid.Dimensions, input.Take((int)TempGrid.Dimensions.Elements()).Select(v => (float)v).ToArray());
                    float[] DefocusValues = Altered.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0));

                    CTFStruct[] LocalParams = EvalGetCTF(input, TempCTF, DefocusValues);

                    float[] Result = new float[LocalParams.Length];

                    GPU.CTFCompareToSim(CTFSpectraPolarTrimmedHalf.GetDevice(Intent.Read),
                                        CTFCoordsPolarTrimmedHalf.GetDevice(Intent.Read),
                                        CTFSpectraScaleHalf.GetDevice(Intent.Read),
                                        (uint)CTFSpectraPolarTrimmedHalf.ElementsSliceReal,
                                        LocalParams,
                                        Result,
                                        (uint)LocalParams.Length);

                    float Score = 0;
                    for (int i = 0; i < Result.Length; i++)
                        if (CTFSpectraConsider[i])
                            Score += Result[i];

                    Score /= NCTFSpectraConsider;

                    if (float.IsNaN(Score) || float.IsInfinity(Score))
                        throw new Exception("Bad score.");

                    return (1.0 - Score) * 1000.0;
                };

                Func<double[], double[]> Gradient = input =>
                {
                    const float Step = 0.005f;
                    double[] Result = new double[input.Length];

                    // In 0D grid case, just get gradient for all 4 parameters.
                    // In 1+D grid case, do simple gradient for astigmatism and phase...
                    int StartComponent = input.Length - 3;
                    //int StartComponent = 0;
                    for (int i = StartComponent; i < input.Length; i++)
                    {
                        if (fixAstigmatism && i > StartComponent)
                            continue;

                        double[] UpperInput = new double[input.Length];
                        input.CopyTo(UpperInput, 0);
                        UpperInput[i] += Step;
                        double UpperValue = Eval(UpperInput);

                        double[] LowerInput = new double[input.Length];
                        input.CopyTo(LowerInput, 0);
                        LowerInput[i] -= Step;
                        double LowerValue = Eval(LowerInput);

                        Result[i] = (UpperValue - LowerValue) / (2f * Step);
                    }

                    float[] ResultPlus = new float[CTFSpectraGrid.Elements()];
                    float[] ResultMinus = new float[CTFSpectraGrid.Elements()];

                    // ..., take shortcut for defoci...
                    {
                        {
                            CubicGrid AlteredPlus = new CubicGrid(TempGrid.Dimensions, input.Take((int)TempGrid.Dimensions.Elements()).Select(v => (float)v + Step).ToArray());
                            float[] DefocusValues = AlteredPlus.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0));

                            CTFStruct[] LocalParams = EvalGetCTF(input, TempCTF, DefocusValues);

                            GPU.CTFCompareToSim(CTFSpectraPolarTrimmedHalf.GetDevice(Intent.Read),
                                                CTFCoordsPolarTrimmedHalf.GetDevice(Intent.Read),
                                                CTFSpectraScaleHalf.GetDevice(Intent.Read),
                                                (uint)CTFSpectraPolarTrimmedHalf.ElementsSliceReal,
                                                LocalParams,
                                                ResultPlus,
                                                (uint)LocalParams.Length);
                        }
                        {
                            CubicGrid AlteredMinus = new CubicGrid(TempGrid.Dimensions, input.Take((int)TempGrid.Dimensions.Elements()).Select(v => (float)v - Step).ToArray());
                            float[] DefocusValues = AlteredMinus.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0));

                            CTFStruct[] LocalParams = EvalGetCTF(input, TempCTF, DefocusValues);

                            GPU.CTFCompareToSim(CTFSpectraPolarTrimmedHalf.GetDevice(Intent.Read),
                                                CTFCoordsPolarTrimmedHalf.GetDevice(Intent.Read),
                                                CTFSpectraScaleHalf.GetDevice(Intent.Read),
                                                (uint)CTFSpectraPolarTrimmedHalf.ElementsSliceReal,
                                                LocalParams,
                                                ResultMinus,
                                                (uint)LocalParams.Length);
                        }
                        float[] LocalGradients = new float[ResultPlus.Length];
                        for (int i = 0; i < LocalGradients.Length; i++)
                            LocalGradients[i] = ResultMinus[i] - ResultPlus[i];

                        // Now compute gradients per grid anchor point using the precomputed individual gradients and wiggle factors.
                        Parallel.For(0, TempGrid.Dimensions.Elements(), i => Result[i] = MathHelper.ReduceWeighted(LocalGradients, WiggleWeights[i]) / LocalGradients.Length / (2f * Step) * 1000f);
                    }

                    foreach (var i in Result)
                        if (double.IsNaN(i) || double.IsInfinity(i))
                            throw new Exception("Bad score.");

                    return Result;
                };

                #endregion

                #region Minimize first time with potential outpiers

                double[] StartParams = new double[TempGrid.Dimensions.Elements() + 3];
                for (int i = 0; i < TempGrid.Dimensions.Elements(); i++)
                    StartParams[i] = TempGrid.FlatValues[i];
                StartParams[StartParams.Length - 3] = (double)TempCTF.PhaseShift;
                StartParams[StartParams.Length - 2] = (double)TempCTF.DefocusDelta;
                StartParams[StartParams.Length - 1] = (double)TempCTF.DefocusAngle / 20 * (Math.PI / 180);

                // Compute correlation for individual spectra, and throw away those that are >.75 sigma worse than mean.

                BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(StartParams.Length, Eval, Gradient)
                {
                    Past = 1,
                    Delta = 1e-6,
                    MaxLineSearch = 15,
                    Corrections = 20
                };
                Optimizer.Minimize(StartParams);

                #endregion

                #region Retrieve parameters

                TempCTF.Defocus = (decimal)MathHelper.Mean(Optimizer.Solution.Take((int)TempGrid.Dimensions.Elements()).Select(v => (float)v));
                TempCTF.PhaseShift = (decimal)Optimizer.Solution[StartParams.Length - 3];
                TempCTF.DefocusDelta = (decimal)Optimizer.Solution[StartParams.Length - 2];
                TempCTF.DefocusAngle = (decimal)(Optimizer.Solution[StartParams.Length - 1] * 20 / (Math.PI / 180));

                if (TempCTF.DefocusDelta < 0)
                {
                    TempCTF.DefocusAngle += 90;
                    TempCTF.DefocusDelta *= -1;
                }
                TempCTF.DefocusAngle = ((int)TempCTF.DefocusAngle + 180 * 99) % 180;

                TempGrid = new CubicGrid(TempGrid.Dimensions, Optimizer.Solution.Take((int)TempGrid.Dimensions.Elements()).Select(v => (float)v).ToArray());

                #endregion

                // Dispose GPU resources manually because GC can't be bothered to do it in time.
                CTFSpectraPolarTrimmedHalf.Dispose();
                CTFCoordsPolarTrimmedHalf.Dispose();
                CTFSpectraScaleHalf.Dispose();

                #region Get nicer envelope fit

                {
                    {
                        Image CTFSpectraBackground = new Image(new int3(DimsRegion), true);
                        float[] CTFSpectraBackgroundData = CTFSpectraBackground.GetHost(Intent.Write)[0];

                        // Construct background in Cartesian coordinates.
                        Helper.ForEachElementFT(DimsRegion, (x, y, xx, yy, r, a) =>
                        {
                            CTFSpectraBackgroundData[y * CTFSpectraBackground.DimsEffective.X + x] = TempBackground.Interp(r / DimsRegion.X);
                        });

                        CTFSpectra.SubtractFromSlices(CTFSpectraBackground);

                        float[] DefocusValues = TempGrid.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 0));
                        CTFStruct[] LocalParams = DefocusValues.Select(v =>
                        {
                            CTF Local = TempCTF.GetCopy();
                            Local.Defocus = (decimal)v + 0.0M;

                            return Local.ToStruct();
                        }).ToArray();

                        Image CTFAverage1D = new Image(IntPtr.Zero, new int3(DimsRegion.X / 2, 1, 1));

                        CTF CTFAug = TempCTF.GetCopy();
                        CTFAug.Defocus += 0.0M;
                        GPU.CTFMakeAverage(CTFSpectra.GetDevice(Intent.Read),
                                           CTFCoordsCart.GetDevice(Intent.Read),
                                           (uint)CTFSpectra.ElementsSliceReal,
                                           (uint)DimsRegion.X,
                                           LocalParams,
                                           CTFAug.ToStruct(),
                                           0,
                                           (uint)DimsRegion.X / 2,
                                           CTFSpectraConsider.Select(v => v ? 1 : 0).ToArray(),
                                           (uint)CTFSpectraGrid.Elements(),
                                           CTFAverage1D.GetDevice(Intent.Write));

                        CTFSpectra.AddToSlices(CTFSpectraBackground);

                        float[] RotationalAverageData = CTFAverage1D.GetHost(Intent.Read)[0];
                        float2[] ForPS1D = new float2[TempPS1D.Length];
                        for (int i = 0; i < ForPS1D.Length; i++)
                            ForPS1D[i] = new float2((float)i / DimsRegion.X, (float)Math.Round(RotationalAverageData[i], 4) + TempBackground.Interp((float)i / DimsRegion.X));
                        MathHelper.UnNaN(ForPS1D);
                        TempPS1D = ForPS1D;

                        CTFSpectraBackground.Dispose();
                        CTFAverage1D.Dispose();
                        CTFSpectra.FreeDevice();
                    }

                    TempCTF.Defocus = Math.Max(TempCTF.Defocus, MainWindow.Options.CTFZMin);
                    UpdateBackgroundFit();
                }

                #endregion
            }

            #endregion

            // Subtract background from 2D average and write it to disk.
            // This image is used for quick visualization purposes only.

            #region PS2D update

            {
                int3 DimsAverage = new int3(DimsRegion.X, DimsRegion.X / 2, 1);
                float[] Average2DData = new float[DimsAverage.Elements()];
                float[] OriginalAverageData = CTFMean.GetHost(Intent.Read)[0];

                for (int y = 0; y < DimsAverage.Y; y++)
                {
                    int yy = y * y;
                    for (int x = 0; x < DimsAverage.Y; x++)
                    {
                        int xx = DimsRegion.X / 2 - x - 1;
                        xx *= xx;
                        float r = (float)Math.Sqrt(xx + yy) / DimsRegion.X;
                        Average2DData[y * DimsAverage.X + x] = OriginalAverageData[(y + DimsRegion.X / 2) * (DimsRegion.X / 2 + 1) + x] - TempBackground.Interp(r);
                    }

                    for (int x = 0; x < DimsRegion.X / 2; x++)
                    {
                        int xx = x * x;
                        float r = (float)Math.Sqrt(xx + yy) / DimsRegion.X;
                        Average2DData[y * DimsAverage.X + x + DimsRegion.X / 2] = OriginalAverageData[(DimsRegion.X / 2 - y) * (DimsRegion.X / 2 + 1) + (DimsRegion.X / 2 - 1 - x)] - TempBackground.Interp(r);
                    }
                }

                thisPS2D = new Image(Average2DData, DimsAverage);
            }

            #endregion

            for (int i = 0; i < TempPS1D.Length; i++)
                TempPS1D[i].Y -= TempBackground.Interp(TempPS1D[i].X);

            CTFSpectra.Dispose();
            CTFMean.Dispose();
            CTFCoordsCart.Dispose();
            CTFCoordsPolarTrimmed.Dispose();

            thisPS1D = TempPS1D;
            thisBackground = TempBackground;
            thisScale = TempScale;
            thisCTF = TempCTF;
            thisGrid = TempGrid;
        }
Exemple #26
0
 public int2(float2 v)
 {
     X = (int)v.X;
     Y = (int)v.Y;
 }
Exemple #27
0
        public void AlignTiltMovies(Star tableIn, int3 stackDimensions, int size, int3 volumeDimensions, Dictionary<int, Projector> references, float resolution)
        {
            Star TableSeries = new Star(DirectoryName + RootName + ".star");

            Image SimulatedSeries = StageDataLoad.LoadMap("d_simulatedseries.mrc", new int2(1, 1), 0, typeof (float));
            //Image SimulatedSeries = StageDataLoad.LoadMap(DirectoryName + RootName + ".ali", new int2(1, 1), 0, typeof(float));
            Image AlignedSeries = new Image(SimulatedSeries.Dims);

            for (int t = 26; t < NTilts; t++)
            {
                Image TiltMovie = StageDataLoad.LoadMap(DirectoryName + TableSeries.GetRowValue(t, "wrpMovieName"), new int2(1, 1), 0, typeof (float));
                Image Template = new Image(SimulatedSeries.GetHost(Intent.Read)[t], SimulatedSeries.Dims.Slice());

                float MovieAngle = float.Parse(TableSeries.GetRowValue(t, "wrpAnglePsi"), CultureInfo.InvariantCulture);
                //float2 MovieShift = new float2(-float.Parse(TableSeries.GetRowValue(t, "wrpShiftX"), CultureInfo.InvariantCulture),
                //                               -float.Parse(TableSeries.GetRowValue(t, "wrpShiftY"), CultureInfo.InvariantCulture));
                float2 MovieShift = new float2(5.64f, -21f);

                Image Aligned = AlignOneTiltMovie(TiltMovie, Template, MovieAngle, MovieShift, resolution);

                AlignedSeries.GetHost(Intent.Write)[t] = Aligned.GetHost(Intent.Read)[0];

                Aligned.Dispose();
                TiltMovie.Dispose();
                Template.Dispose();
            }

            SimulatedSeries.Dispose();

            AlignedSeries.WriteMRC(DirectoryName + RootName + ".aligned");
            AlignedSeries.Dispose();
        }
Exemple #28
0
 public float3(float2 v)
 {
     X = v.X;
     Y = v.Y;
     Z = 0f;
 }
Exemple #29
0
        public Image GetCTFCoords(int size, int originalSize)
        {
            float PixelSize = (float)MainWindow.Options.BinnedPixelSize;
            Image CTFCoords;
            {
                float2[] CTFCoordsData = new float2[(size / 2 + 1) * size];
                for (int y = 0; y < size; y++)
                    for (int x = 0; x < size / 2 + 1; x++)
                    {
                        int xx = x;
                        int yy = y < size / 2 + 1 ? y : y - size;

                        float xs = xx / (float)originalSize;
                        float ys = yy / (float)originalSize;
                        float r = (float)Math.Sqrt(xs * xs + ys * ys);
                        float angle = (float)Math.Atan2(yy, xx);

                        CTFCoordsData[y * (size / 2 + 1) + x] = new float2(r, angle);
                    }

                CTFCoords = new Image(CTFCoordsData, new int3(size, size, 1), true);
            }

            return CTFCoords;
        }
Exemple #30
0
 public float3(float2 v12, float v3)
 {
     X = v12.X;
     Y = v12.Y;
     Z = v3;
 }
Exemple #31
0
        public float[] Get2D(float2[] coordinates, bool ampsquared, bool ignorebfactor = false, bool ignorescale = false)
        {
            float[] Output = new float[coordinates.Length];

            float pixelsize = (float) PixelSize;
            float pixeldelta = (float) PixelSizeDelta;
            float pixelangle = (float) PixelSizeAngle / (float)(180.0 / Math.PI);
            float voltage = (float)Voltage * 1e3f;
            float lambda = 12.2643247f / (float)Math.Sqrt(voltage * (1.0f + voltage * 0.978466e-6f));
            float defocus = -(float)Defocus * 1e4f;
            float defocusdelta = -(float)DefocusDelta * 1e4f * 0.5f;
            float astigmatismangle = (float) DefocusAngle / (float)(180.0 / Math.PI);
            float cs = (float)Cs * 1e7f;
            float amplitude = (float)Amplitude;
            float scale = (float)Scale;
            float phaseshift = (float)PhaseShift * (float)Math.PI;
            float K1 = (float)Math.PI * lambda;
            float K2 = (float)Math.PI * 0.5f * cs * lambda * lambda * lambda;
            float K3 = (float)Math.Sqrt(1f - amplitude * amplitude);
            float K4 = (float)Bfactor * 0.25f;

            Parallel.For(0, coordinates.Length, i =>
            {
                float angle = coordinates[i].Y;
                float r = coordinates[i].X / (pixelsize + pixeldelta * (float) Math.Cos(2.0 * (angle - pixelangle)));
                float r2 = r * r;
                float r4 = r2 * r2;

                float deltaf = defocus + defocusdelta * (float) Math.Cos(2.0 * (angle - astigmatismangle));
                float argument = K1 * deltaf * r2 + K2 * r4 - phaseshift;
                float retval = amplitude * (float) Math.Cos(argument) - K3 * (float) Math.Sin(argument);

                if (!ignorebfactor && K4 != 0)
                    retval *= (float) Math.Exp(K4 * r2);

                if (ampsquared)
                    retval = Math.Abs(retval);// * retval);

                if (!ignorescale)
                    Output[i] = scale * retval;
                else
                    Output[i] = retval;
            });

            return Output;
        }
Exemple #32
0
 public static float Dot(float2 a, float2 b)
 {
     return(a.X * b.X + a.Y * b.Y);
 }
Exemple #33
0
        public float GetInterpolated(float3 coords)
        {
            coords /= Spacing;  // from [0, 1] to [0, dim - 1]

            float3 coord_grid = coords;
            float3 index = coord_grid.Floor();

            float result = 0.0f;

            int MinX = Math.Max(0, (int)index.X - 1), MaxX = Math.Min((int)index.X + 2, Dimensions.X - 1);
            int MinY = Math.Max(0, (int)index.Y - 1), MaxY = Math.Min((int)index.Y + 2, Dimensions.Y - 1);
            int MinZ = Math.Max(0, (int)index.Z - 1), MaxZ = Math.Min((int)index.Z + 2, Dimensions.Z - 1);

            float[,] InterpX = new float[MaxZ - MinZ + 1, MaxY - MinY + 1];
            for (int z = MinZ; z <= MaxZ; z++)
            {
                for (int y = MinY; y <= MaxY; y++)
                {
                    float2[] Points = new float2[MaxX - MinX + 1];
                    if (Points.Length == 1)
                        InterpX[z - MinZ, y - MinY] = Values[z, y, 0];
                    else
                    {
                        for (int x = MinX; x <= MaxX; x++)
                            Points[x - MinX] = new float2(x, Values[z, y, x]);
                        Cubic1DShort Spline = Cubic1DShort.GetInterpolator(Points);
                        InterpX[z - MinZ, y - MinY] = Spline.Interp(coords.X);
                    }
                }
            }

            float[] InterpXY = new float[MaxZ - MinZ + 1];
            for (int z = MinZ; z <= MaxZ; z++)
            {
                float2[] Points = new float2[MaxY - MinY + 1];
                if (Points.Length == 1)
                    InterpXY[z - MinZ] = InterpX[z - MinZ, 0];
                else
                {
                    for (int y = MinY; y <= MaxY; y++)
                        Points[y - MinY] = new float2(y, InterpX[z - MinZ, y - MinY]);
                    Cubic1DShort Spline = Cubic1DShort.GetInterpolator(Points);
                    InterpXY[z - MinZ] = Spline.Interp(coords.Y);
                }
            }

            {
                float2[] Points = new float2[MaxZ - MinZ + 1];
                if (Points.Length == 1)
                    result = InterpXY[0];
                else
                {
                    for (int z = MinZ; z <= MaxZ; z++)
                        Points[z - MinZ] = new float2(z, InterpXY[z - MinZ]);
                    Cubic1DShort Spline = Cubic1DShort.GetInterpolator(Points);
                    result = Spline.Interp(coords.Z);
                }
            }

            return result;
        }
Exemple #34
0
        public Image GetImagesOneAngle(Image tiltStack, int size, float3[] particleOrigins, int angleID, bool normalize)
        {
            int NParticles = particleOrigins.Length;

            float3[] ImagePositions = GetPositionsInOneAngle(particleOrigins, angleID);

            Image Result = new Image(new int3(size, size, NParticles));
            float[][] ResultData = Result.GetHost(Intent.Write);
            float3[] Shifts = new float3[NParticles];

            int3 DimsStack = tiltStack.Dims;

            Parallel.For(0, NParticles, p =>
            {
                ImagePositions[p] -= new float3(size / 2, size / 2, 0);
                int2 IntPosition = new int2((int)ImagePositions[p].X, (int)ImagePositions[p].Y);
                float2 Residual = new float2(-(ImagePositions[p].X - IntPosition.X), -(ImagePositions[p].Y - IntPosition.Y));
                Residual -= size / 2;
                Shifts[p] = new float3(Residual);

                float[] OriginalData;
                lock (tiltStack)
                    OriginalData = tiltStack.GetHost(Intent.Read)[angleID];

                float[] ImageData = ResultData[p];
                for (int y = 0; y < size; y++)
                {
                    int PosY = (y + IntPosition.Y + DimsStack.Y) % DimsStack.Y;
                    for (int x = 0; x < size; x++)
                    {
                        int PosX = (x + IntPosition.X + DimsStack.X) % DimsStack.X;
                        ImageData[y * size + x] = OriginalData[PosY * DimsStack.X + PosX];
                    }
                }
            });
            if (normalize)
                GPU.NormParticles(Result.GetDevice(Intent.Read),
                                  Result.GetDevice(Intent.Write),
                                  Result.Dims.Slice(),
                                  (uint)(MainWindow.Options.ExportParticleRadius / CTF.PixelSize),
                                  true,
                                  (uint)NParticles);
            //Result.WriteMRC($"d_paticleimages_{angleID:D3}.mrc");

            Result.ShiftSlices(Shifts);

            Image ResultFT = Result.AsFFT();
            Result.Dispose();

            return ResultFT;
        }