public MainWindow() { try { Options.DeviceCount = GPU.GetDeviceCount(); if (Options.DeviceCount <= 0) throw new Exception(); } catch (Exception) { MessageBox.Show("No CUDA devices found, shutting down."); Close(); } GPU.MemoryChanged += () => Options.UpdateGPUStats(); DataContext = Options; Options.PropertyChanged += Options_PropertyChanged; Closing += MainWindow_Closing; InitializeComponent(); DisableWhenRunning = new List<UIElement> { GridOptionsIO, GridOptionsPreprocessing, GridOptionsParticles, GridOptionsCTF, GridOptionsMovement, GridOptionsGrids, GridOptionsPostprocessing }; if (File.Exists("Previous.settings")) Options.Load("Previous.settings"); for (int i = 0; i < GPU.GetDeviceCount(); i++) { GPU.SetDevice(i); Console.WriteLine($"Device {i}:"); Console.WriteLine($"Free memory: {GPU.GetFreeMemory(i)} MB"); Console.WriteLine($"Total memory: {GPU.GetTotalMemory(i)} MB"); } GPU.SetDevice(0); Options.UpdateGPUStats(); // Create mockup { float2[] SplinePoints = { new float2(0f, 0f), new float2(1f / 3f, 1f)};//, new float2(2f / 3f, 0f)};//, new float2(1f, 1f) }; Cubic1D ReferenceSpline = new Cubic1D(SplinePoints); Cubic1DShort ShortSpline = Cubic1DShort.GetInterpolator(SplinePoints); for (float i = -1f; i < 2f; i += 0.01f) { float Reference = ReferenceSpline.Interp(i); float Test = ShortSpline.Interp(i); if (Math.Abs(Reference - Test) > 1e-6f) throw new Exception(); } Random Rnd = new Random(123); int3 GridDim = new int3(1, 1, 1); float[] GridValues = new float[GridDim.Elements()]; for (int i = 0; i < GridValues.Length; i++) GridValues[i] = (float)Rnd.NextDouble(); CubicGrid CGrid = new CubicGrid(GridDim, GridValues); float[] Managed = CGrid.GetInterpolated(new int3(16, 16, 16), new float3(0, 0, 0)); float[] Native = CGrid.GetInterpolatedNative(new int3(16, 16, 16), new float3(0, 0, 0)); for (int i = 0; i < Managed.Length; i++) if (Math.Abs(Managed[i] - Native[i]) > 1e-6f) throw new Exception(); Matrix3 A = new Matrix3(1, 2, 3, 4, 5, 6, 7, 8, 9); Matrix3 B = new Matrix3(11, 12, 13, 14, 15, 16, 17, 18, 19); Matrix3 C = A * B; // Euler matrix { Matrix3 E = Matrix3.Euler(0 * Helper.ToRad, 20 * Helper.ToRad, 0 * Helper.ToRad); float3 EE = Matrix3.EulerFromMatrix(E.Transposed()) * Helper.ToDeg; float3 Transformed = E * new float3(1, 0, 0); Transformed.Y = 0; } //float3[] HealpixAngles = Helper.GetHealpixAngles(3, "D4"); // Deconvolve reconstructions using a separate CTF //{ // for (int i = 1; i <= 24; i++) // { // Image Map = StageDataLoad.LoadMap($"F:\\stefanribo\\vlion\\warped_{i}.mrc", new int2(1, 1), 0, typeof(float)); // Image MapFT = Map.AsFFT(true); // Map.Dispose(); // Image CTF = StageDataLoad.LoadMap($"F:\\stefanribo\\vlion\\warped_ctf_{i}.mrc", new int2(1, 1), 0, typeof(float)); // foreach (var slice in CTF.GetHost(Intent.ReadWrite)) // for (int s = 0; s < slice.Length; s++) // slice[s] = Math.Max(1e-3f, slice[s]); // MapFT.Divide(CTF); // Map = MapFT.AsIFFT(true); // MapFT.Dispose(); // Map.WriteMRC($"F:\\stefanribo\\vlion\\warped_deconv_{i}.mrc"); // Map.Dispose(); // } //} //{ // Image SumFT = new Image(new int3(220, 220, 220), true, true); // Image SumWeights = new Image(new int3(220, 220, 220), true); // int read = 0; // foreach (var tomoPath in Directory.EnumerateFiles("F:\\stefanribo\\oridata\\particles", "tomo*.mrc")) // { // FileInfo Info = new FileInfo(tomoPath); // Image Tomo = StageDataLoad.LoadMap(tomoPath, new int2(1, 1), 0, typeof(float)); // Image TomoFT = Tomo.AsFFT(true); // Tomo.Dispose(); // Image TomoWeights = StageDataLoad.LoadMap("F:\\stefanribo\\oridata\\particlectf\\" + Info.Name, new int2(1, 1), 0, typeof(float)); // TomoFT.Multiply(TomoWeights); // TomoWeights.Multiply(TomoWeights); // SumFT.Add(TomoFT); // SumWeights.Add(TomoWeights); // TomoFT.Dispose(); // TomoWeights.Dispose(); // Debug.WriteLine(read++); // } // foreach (var slice in SumWeights.GetHost(Intent.ReadWrite)) // { // for (int i = 0; i < slice.Length; i++) // { // slice[i] = Math.Max(1e-3f, slice[i]); // } // } // SumFT.Divide(SumWeights); // Image Sum = SumFT.AsIFFT(true); // Sum.WriteMRC("F:\\stefanribo\\oridata\\particles\\weightedaverage.mrc"); // SumFT.Dispose(); // SumWeights.Dispose(); // Sum.Dispose(); //} //{ // Image Subtrahend = StageDataLoad.LoadMap("E:\\martinsried\\stefan\\membranebound\\vlion\\relion_subtrahend.mrc", new int2(1, 1), 0, typeof(float)); // Image SubtrahendFT = Subtrahend.AsFFT(true); // int read = 0; // foreach (var tomoPath in Directory.EnumerateFiles("E:\\martinsried\\stefan\\membranebound\\oridata\\particles", "tomo*.mrc")) // { // FileInfo Info = new FileInfo(tomoPath); // Image Tomo = StageDataLoad.LoadMap(tomoPath, new int2(1, 1), 0, typeof(float)); // Image TomoFT = Tomo.AsFFT(true); // Tomo.Dispose(); // Image TomoWeights = StageDataLoad.LoadMap("E:\\martinsried\\stefan\\membranebound\\oridata\\particlectf\\" + Info.Name, new int2(1, 1), 0, typeof(float)); // Image SubtrahendFTMult = new Image(SubtrahendFT.GetDevice(Intent.Read), SubtrahendFT.Dims, true, true); // SubtrahendFTMult.Multiply(TomoWeights); // TomoFT.Subtract(SubtrahendFTMult); // Tomo = TomoFT.AsIFFT(true); // Tomo.WriteMRC("D:\\stefanribo\\particles\\" + Info.Name); // Tomo.Dispose(); // TomoFT.Dispose(); // SubtrahendFTMult.Dispose(); // TomoWeights.Dispose(); // Debug.WriteLine(read++); // } //} //{ // Image SubtRef1 = StageDataLoad.LoadMap("E:\\martinsried\\stefan\\membranebound\\vlion\\warp_subtrahend.mrc", new int2(1, 1), 0, typeof(float)); // Projector Subt = new Projector(SubtRef1, 2); // SubtRef1.Dispose(); // Image ProjFT = Subt.Project(new int2(220, 220), new[] { new float3(0, 0, 0) }, 110); // Image Proj = ProjFT.AsIFFT(); // Proj.RemapFromFT(); // Proj.WriteMRC("d_testproj.mrc"); //} // Projector /*{ Image MapForProjector = StageDataLoad.LoadMap("E:\\youwei\\run36_half1_class001_unfil.mrc", new int2(1, 1), 0, typeof (float)); Projector Proj = new Projector(MapForProjector, 2); Image Projected = Proj.Project(new int2(240, 240), new[] { new float3(0, 0, 0) }, 120); Projected = Projected.AsIFFT(); Projected.RemapFromFT(); Projected.WriteMRC("d_projected.mrc"); }*/ // Backprojector /*{ Image Dot = new Image(new int3(32, 32, 360)); for (int a = 0; a < 360; a++) Dot.GetHost(Intent.Write)[a][0] = 1; Dot = Dot.AsFFT(); Dot.AsAmplitudes().WriteMRC("d_dot.mrc"); Image DotWeights = new Image(new int3(32, 32, 360), true); for (int a = 0; a < 360; a++) for (int i = 0; i < DotWeights.ElementsSliceReal; i++) DotWeights.GetHost(Intent.Write)[a][i] = 1; float3[] Angles = new float3[360]; for (int a = 0; a < 360; a++) Angles[a] = new float3(0, a * Helper.ToRad * 0.05f, 0); Projector Proj = new Projector(new int3(32, 32, 32), 2); Proj.BackProject(Dot, DotWeights, Angles); Proj.Weights.WriteMRC("d_weights.mrc"); //Image Re = Proj.Data.AsImaginary(); //Re.WriteMRC("d_projdata.mrc"); Image Rec = Proj.Reconstruct(true); Rec.WriteMRC("d_rec.mrc"); }*/ //Star Models = new Star("D:\\rado27\\Refine3D\\run1_ct5_it005_half1_model.star", "data_model_group_2"); //Debug.WriteLine(Models.GetRow(0)[0]); /*Image Volume = StageDataLoad.LoadMap("F:\\carragher20s\\ref256.mrc", new int2(1, 1), 0, typeof (float)); Image VolumePadded = Volume.AsPadded(new int3(512, 512, 512)); VolumePadded.WriteMRC("d_padded.mrc"); Volume.Dispose(); VolumePadded.RemapToFT(true); Image VolumeFT = VolumePadded.AsFFT(true); VolumePadded.Dispose(); Image VolumeProjFT = VolumeFT.AsProjections(new[] { new float3(Helper.ToRad * 0, Helper.ToRad * 0, Helper.ToRad * 0) }, new int2(256, 256), 2f); Image VolumeProj = VolumeProjFT.AsIFFT(); VolumeProjFT.Dispose(); VolumeProj.RemapFromFT(); VolumeProj.WriteMRC("d_proj.mrc"); VolumeProj.Dispose();*/ /*Options.Movies.Add(new Movie(@"D:\Dev\warp\May19_21.44.54.mrc")); Options.Movies.Add(new Movie(@"D:\Dev\warp\May19_21.49.06.mrc")); Options.Movies.Add(new Movie(@"D:\Dev\warp\May19_21.50.48.mrc")); Options.Movies.Add(new Movie(@"D:\Dev\warp\May19_21.52.16.mrc")); Options.Movies.Add(new Movie(@"D:\Dev\warp\May19_21.53.43.mrc")); CTFDisplay.PS2D = new BitmapImage();*/ /*float2[] SimCoords = new float2[512 * 512]; for (int y = 0; y < 512; y++) for (int x = 0; x < 512; x++) { int xcoord = x - 512, ycoord = y - 512; SimCoords[y * 512 + x] = new float2((float) Math.Sqrt(xcoord * xcoord + ycoord * ycoord), (float) Math.Atan2(ycoord, xcoord)); } float[] Sim2D = new CTF {Defocus = -2M}.Get2D(SimCoords, 512, true); byte[] Sim2DBytes = new byte[Sim2D.Length]; for (int i = 0; i < 512 * 512; i++) Sim2DBytes[i] = (byte) (Sim2D[i] * 255f); BitmapSource Sim2DSource = BitmapSource.Create(512, 512, 96, 96, PixelFormats.Indexed8, BitmapPalettes.Gray256, Sim2DBytes, 512); CTFDisplay.Simulated2D = Sim2DSource;*/ /*float2[] PointsPS1D = new float2[512]; for (int i = 0; i < PointsPS1D.Length; i++) PointsPS1D[i] = new float2(i, (float) Math.Exp(-i / 300f)); CTFDisplay.PS1D = PointsPS1D; float[] SimCTF = new CTF { Defocus = -2M }.Get1D(512, true); float2[] PointsSim1D = new float2[SimCTF.Length]; for (int i = 0; i < SimCTF.Length; i++) PointsSim1D[i] = new float2(i, SimCTF[i] * (float)Math.Exp(-i / 100f) + (float)Math.Exp(-i / 300f)); CTFDisplay.Simulated1D = PointsSim1D;*/ /*CubicGrid Grid = new CubicGrid(new int3(5, 5, 5), 0, 0, Dimension.X); Grid.Values[2, 2, 2] = 1f; float[] Data = new float[11 * 11 * 11]; int i = 0; for (float z = 0f; z < 1.05f; z += 0.1f) for (float y = 0f; y < 1.05f; y += 0.1f) for (float x = 0f; x < 1.05f; x += 0.1f) Data[i++] = Grid.GetInterpolated(new float3(x, y, z)); Image DataImage = new Image(Data, new int3(11, 11, 11)); DataImage.WriteMRC("bla.mrc"); Image GPUImage = new Image(DataImage.GetDevice(Intent.Read), new int3(11, 11, 11)); GPUImage.WriteMRC("gpu.mrc");*/ /*CubicGrid WiggleGrid = new CubicGrid(new int3(2, 2, 1)); float[][] WiggleWeights = WiggleGrid.GetWiggleWeights(new int3(3, 3, 1));*/ } }
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; }
public static void FitCTF(float2[] data, float[] simulation, float[] zeros, float[] peaks, out Cubic1D background, out Cubic1D scale) { if (zeros.Length < 2 || peaks.Length < 1) { background = new Cubic1D(new[] { new float2(0, 0), new float2(1, 0) }); scale = new Cubic1D(new[] { new float2(0, 1), new float2(1, 1) }); return; } float MinX = MathHelper.Min(data.Select(p => p.X)), MaxX = MathHelper.Max(data.Select(p => p.X)), ScaleX = 1f / (MaxX - MinX); 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(); List <float2> Peaks = new List <float2>(), Zeros = new List <float2>(); foreach (var zero in zeros) { int Pos = (int)((zero - MinX) * ScaleX * data.Length); int First = Math.Max(0, Pos - 1); int Last = Math.Min(data.Length - 1, Pos + 1); float MinVal = data[First].Y; for (int i = First; i < Last; i++) { MinVal = Math.Min(MinVal, data[i].Y); } Zeros.Add(new float2(zero, MinVal)); } float[] Background = (new Cubic1D(Zeros.ToArray())).Interp(data.Select(v => v.X).ToArray()); float2[] DataSubtracted = Helper.ArrayOfFunction(i => new float2(data[i].X, data[i].Y - Background[i]), Background.Length); for (int z = 0; z < Zeros.Count; z++) { float2 Zero = Zeros[z]; int Pos = (int)((Zero.X - MinX) * ScaleX * DataSubtracted.Length); int First = Math.Max(0, Pos - 1); int Last = Math.Min(DataSubtracted.Length, Pos + 1); float MinVal = DataSubtracted[First].Y; for (int i = First; i < Last; i++) { MinVal = Math.Min(MinVal, DataSubtracted[i].Y); } Zeros[z] = new float2(Zero.X, Zero.Y + MinVal); } Background = (new Cubic1D(Zeros.ToArray())).Interp(data.Select(v => v.X).ToArray()); DataSubtracted = Helper.ArrayOfFunction(i => new float2(data[i].X, data[i].Y - Background[i]), Background.Length); float GlobalMax = 0; foreach (var peak in peaks) { int Pos = (int)((peak - MinX) * ScaleX * DataSubtracted.Length); int First = Math.Max(0, Pos - 1); int Last = Math.Min(DataSubtracted.Length, Pos + 1); float MaxVal = GlobalMax * 0.05f;// DataSubtracted[First].Y; for (int i = First; i < Last; i++) { MaxVal = Math.Max(MaxVal, DataSubtracted[i].Y); } Peaks.Add(new float2(peak, MaxVal)); GlobalMax = Math.Max(MaxVal, GlobalMax); } background = Zeros.Count > 1 ? new Cubic1D(Zeros.ToArray()) : new Cubic1D(new[] { new float2(0, 0), new float2(1, 0) }); scale = Peaks.Count > 1 ? new Cubic1D(Peaks.ToArray()) : new Cubic1D(new[] { new float2(0, 1), new float2(1, 1) }); return; int EveryNth = 1; float[] ZerosX = new float[Zeros.Count / EveryNth]; for (int i = 0; i < ZerosX.Length; i++) { ZerosX[i] = Zeros[i * EveryNth].X; } float[] PeaksX = new float[Peaks.Count / EveryNth]; for (int i = 0; i < PeaksX.Length; i++) { PeaksX[i] = Peaks[i * EveryNth].X; } float[] DataX = data.Select(v => v.X).ToArray(); float[] DataY = data.Select(v => v.Y).ToArray(); Func <double[], double> Eval = (input) => { Cubic1D SplineBackground = new Cubic1D(Helper.ArrayOfFunction(i => new float2(ZerosX[i], (float)Math.Exp(input[i])), ZerosX.Length)); Cubic1D SplineScale = new Cubic1D(Helper.ArrayOfFunction(i => new float2(PeaksX[i], (float)Math.Exp(input[i + ZerosX.Length])), PeaksX.Length)); float[] ContinuumBackground = SplineBackground.Interp(DataX); float[] ContinuumScale = SplineScale.Interp(DataX); float[] Diff = Helper.ArrayOfFunction(i => ContinuumBackground[i] + ContinuumScale[i] * simulation[i] - DataY[i], ContinuumBackground.Length); float DiffSq = 0; for (int i = 0; i < Diff.Length; i++) { DiffSq += Diff[i] * Diff[i]; } return(DiffSq); }; Func <double[], double[]> Grad = (input) => { double CurrentValue = Eval(input); double[] Result = new double[input.Length]; Parallel.For(0, input.Length, i => { double Delta = 1e-5; double[] InputPlus = input.ToArray(); InputPlus[i] += Delta; Result[i] = (Eval(InputPlus) - CurrentValue) / Delta; }); return(Result); }; double[] ResampledBackground = background.Interp(ZerosX).Select(v => Math.Log(Math.Max(v, 1e-5))).ToArray(); double[] ResampledScale = scale.Interp(PeaksX).Select(v => Math.Log(Math.Max(v, 1e-5))).ToArray(); double[] StartParams = Helper.Combine(ResampledBackground, ResampledScale); BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(StartParams.Length, Eval, Grad); Optimizer.MaxIterations = 10; Optimizer.Minimize(StartParams); background = new Cubic1D(Helper.ArrayOfFunction(i => new float2(ZerosX[i], (float)Math.Exp(StartParams[i])), ZerosX.Length)); scale = new Cubic1D(Helper.ArrayOfFunction(i => new float2(PeaksX[i], (float)Math.Exp(StartParams[i + ZerosX.Length])), PeaksX.Length)); }
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(); }
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); } }
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); } }
public virtual void ProcessCTF(MapHeader originalHeader, Image originalStack, bool doastigmatism, decimal scaleFactor) { if (!Directory.Exists(PowerSpectrumDir)) Directory.CreateDirectory(PowerSpectrumDir); //CTF = new CTF(); PS1D = null; _SimulatedBackground = null; _SimulatedScale = new Cubic1D(new[] { new float2(0, 1), new float2(1, 1) }); #region Dimensions and grids int NFrames = originalHeader.Dimensions.Z; int2 DimsImage = new int2(originalHeader.Dimensions); 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 / 1, DimsRegion.Y / 1), OverlapFraction, out DimsPositionGrid); int NPositions = (int)DimsPositionGrid.Elements(); int CTFGridX = Math.Min(DimsPositionGrid.X, MainWindow.Options.GridCTFX); int CTFGridY = Math.Min(DimsPositionGrid.Y, MainWindow.Options.GridCTFY); int CTFGridZ = Math.Min(NFrames, MainWindow.Options.GridCTFZ); GridCTF = new CubicGrid(new int3(CTFGridX, CTFGridY, CTFGridZ)); GridCTFPhase = new CubicGrid(new int3(1, 1, CTFGridZ)); bool CTFSpace = CTFGridX * CTFGridY > 1; bool CTFTime = CTFGridZ > 1; int3 CTFSpectraGrid = new int3(CTFSpace ? DimsPositionGrid.X : 1, CTFSpace ? DimsPositionGrid.Y : 1, CTFTime ? CTFGridZ : 1); int MinFreqInclusive = (int)(MainWindow.Options.CTFRangeMin * DimsRegion.X / 2); int MaxFreqExclusive = (int)(MainWindow.Options.CTFRangeMax * DimsRegion.X / 2); int NFreq = MaxFreqExclusive - MinFreqInclusive; float PixelSize = (float)(MainWindow.Options.CTFPixelMin + MainWindow.Options.CTFPixelMax) * 0.5f; float PixelDelta = (float)(MainWindow.Options.CTFPixelMax - MainWindow.Options.CTFPixelMin) * 0.5f; float PixelAngle = (float)MainWindow.Options.CTFPixelAngle / 180f * (float)Math.PI; #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(originalStack.GetDevice(Intent.Read), DimsImage, NFrames, PositionGrid, NPositions, DimsRegion, CTFSpectraGrid, CTFSpectra.GetDevice(Intent.Write), CTFMean.GetDevice(Intent.Write)); originalStack.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)); _PS1D = ForPS1D; CTFAverage1D.Dispose(); } #endregion #region Background fitting methods Action UpdateBackgroundFit = () => { float2[] ForPS1D = PS1D.Skip(Math.Max(5, MinFreqInclusive / 2)).ToArray(); Cubic1D.FitCTF(ForPS1D, v => v.Select(x => CTF.Get1D(x / (float)CTF.PixelSize, true)).ToArray(), CTF.GetZeros(), CTF.GetPeaks(), out _SimulatedBackground, out _SimulatedScale); }; 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] - _SimulatedBackground.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[] { CTF.ToStruct() }, CTF.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[PS1D.Length]; if (keepbackground) for (int i = 0; i < ForPS1D.Length; i++) ForPS1D[i] = new float2((float)i / DimsRegion.X, RotationalAverageData[i] + _SimulatedBackground.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); _PS1D = ForPS1D; CTFMeanCorrected.Dispose(); CTFAverage1D.Dispose(); }; #endregion // Fit background to currently best average (not corrected for astigmatism yet). { float2[] ForPS1D = PS1D.Skip(MinFreqInclusive).Take(Math.Max(2, NFreq / 2)).ToArray(); int NumNodes = Math.Max(3, (int)((MainWindow.Options.CTFRangeMax - MainWindow.Options.CTFRangeMin) * 5M)); _SimulatedBackground = Cubic1D.Fit(ForPS1D, NumNodes); // This won't fit falloff and scale, because approx function is 0 float[] CurrentBackground = _SimulatedBackground.Interp(PS1D.Select(p => p.X).ToArray()).Skip(MinFreqInclusive).Take(NFreq / 2).ToArray(); 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 ZStep = (ZMax - ZMin) / 100f; float BestZ = 0, BestPhase = 0, BestScore = -999; for (float z = ZMin; z <= ZMax + 1e-5f; z += ZStep) { for (float p = 0; p <= (MainWindow.Options.CTFDoPhase ? 1f : 0f); 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(PS1D.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; } } } CTF = 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 { Image CTFMeanPolarTrimmed = CTFMean.AsPolar((uint)MinFreqInclusive, (uint)(MinFreqInclusive + NFreq / 1)); // Subtract current background. Image CurrentBackground = new Image(_SimulatedBackground.Interp(PS1D.Select(p => p.X).ToArray()).Skip(MinFreqInclusive).Take(NFreq / 1).ToArray()); CTFMeanPolarTrimmed.SubtractFromLines(CurrentBackground); CurrentBackground.Dispose(); /*Image WaterMask = new Image(new int3(NFreq, 1, 1)); float[] WaterData = WaterMask.GetHost(Intent.Write)[0]; for (int i = 0; i < NFreq; i++) { float f = (i + MinFreqInclusive) / (float)DimsRegion.X * 2f; WaterData[i] = f > 0.2f && f < 0.6f ? 0f : 1f; } //CTFMeanPolarTrimmed.MultiplyLines(WaterMask); WaterMask.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 = CTF.Defocus,// (MainWindow.Options.CTFZMin + MainWindow.Options.CTFZMax) * 0.5M, DefocusDelta = doastigmatism ? 0 : MainWindow.Options.CTFAstigmatism, DefocusAngle = doastigmatism ? 0 : MainWindow.Options.CTFAstigmatismAngle, Cs = MainWindow.Options.CTFCs, Voltage = MainWindow.Options.CTFVoltage, Amplitude = MainWindow.Options.CTFAmplitude }; CTFFitStruct FitParams = new CTFFitStruct { //Pixelsize = new float3(-0.02e-10f, 0.02e-10f, 0.01e-10f), //Pixeldelta = new float3(0.0f, 0.02e-10f, 0.01e-10f), //Pixelangle = new float3(0, 2 * (float)Math.PI, 1 * (float)Math.PI / 18), //Defocus = new float3((float)(MainWindow.Options.CTFZMin - StartParams.Defocus) * 1e-6f, // (float)(MainWindow.Options.CTFZMax - StartParams.Defocus) * 1e-6f, // 0.025e-6f), Defocus = new float3(-0.4e-6f, 0.4e-6f, 0.025e-6f), Defocusdelta = doastigmatism ? new float3(0, 0.8e-6f, 0.02e-6f) : new float3(0, 0, 0), Astigmatismangle = doastigmatism ? new float3(0, 2 * (float)Math.PI, 1 * (float)Math.PI / 18) : new float3(0, 0, 0), Phaseshift = MainWindow.Options.CTFDoPhase ? new float3(0, (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, doastigmatism); CTF.FromStruct(ResultStruct); CTF.Defocus = Math.Max(CTF.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! } #endregion /*for (int i = 0; i < PS1D.Length; i++) PS1D[i].Y -= SimulatedBackground.Interp(PS1D[i].X); SimulatedBackground = new Cubic1D(SimulatedBackground.Data.Select(v => new float2(v.X, 0f)).ToArray()); OnPropertyChanged("PS1D"); CTFSpectra.Dispose(); CTFMean.Dispose(); CTFCoordsCart.Dispose(); CTFCoordsPolarTrimmed.Dispose(); Simulated1D = GetSimulated1D(); CTFQuality = GetCTFQuality(); return;*/ // 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; GridCTF = new CubicGrid(GridCTF.Dimensions, (float)CTF.Defocus, (float)CTF.Defocus, Dimension.X); GridCTFPhase = new CubicGrid(GridCTFPhase.Dimensions, (float)CTF.PhaseShift, (float)CTF.PhaseShift, Dimension.X); for (int preciseFit = 2; preciseFit < 3; preciseFit++) { NFreq = (MaxFreqExclusive - MinFreqInclusive) * (preciseFit + 1) / 3; //if (preciseFit >= 2) // NFreq = MaxFreqExclusive - MinFreqInclusive; 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 = _SimulatedScale.Interp(PS1D.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 => { float Angle = ((float)y / DimsRegion.X + 0.5f) * (float)Math.PI; 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(_SimulatedBackground.Interp(PS1D.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 = GridCTF.GetWiggleWeights(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 1f / (CTFGridZ + 1))); float[][] WiggleWeightsPhase = GridCTFPhase.GetWiggleWeights(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, 1f / (CTFGridZ + 1))); // Helper method for getting CTFStructs for the entire spectra grid. Func<double[], CTF, float[], float[], CTFStruct[]> EvalGetCTF = (input, ctf, defocusValues, phaseValues) => { decimal AlteredDelta = (decimal)input[input.Length - 2]; decimal AlteredAngle = (decimal)(input[input.Length - 1] * 20 / (Math.PI / 180)); CTF Local = ctf.GetCopy(); 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; LocalParams[i].PhaseShift = phaseValues[i] * (float)Math.PI; } return LocalParams; }; // Simulate with adjusted CTF, compare to originals #region Eval and Gradient methods float BorderZ = 0.5f / CTFGridZ; Func<double[], double> Eval = input => { CubicGrid Altered = new CubicGrid(GridCTF.Dimensions, input.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v).ToArray()); float[] DefocusValues = Altered.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ)); CubicGrid AlteredPhase = new CubicGrid(GridCTFPhase.Dimensions, input.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v).ToArray()); float[] PhaseValues = AlteredPhase.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ)); CTFStruct[] LocalParams = EvalGetCTF(input, CTF, DefocusValues, PhaseValues); 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 - 2; //int StartComponent = 0; for (int i = StartComponent; i < input.Length; i++) { 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 AlteredPhase = new CubicGrid(GridCTFPhase.Dimensions, input.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v).ToArray()); float[] PhaseValues = AlteredPhase.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ)); { CubicGrid AlteredPlus = new CubicGrid(GridCTF.Dimensions, input.Take((int)GridCTF.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, BorderZ)); CTFStruct[] LocalParams = EvalGetCTF(input, CTF, DefocusValues, PhaseValues); 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(GridCTF.Dimensions, input.Take((int)GridCTF.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, BorderZ)); CTFStruct[] LocalParams = EvalGetCTF(input, CTF, DefocusValues, PhaseValues); 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, GridCTF.Dimensions.Elements(), i => Result[i] = MathHelper.ReduceWeighted(LocalGradients, WiggleWeights[i]) / LocalGradients.Length / (2f * Step) * 1000f); } // ..., and take shortcut for phases. if (MainWindow.Options.CTFDoPhase) { CubicGrid AlteredPlus = new CubicGrid(GridCTF.Dimensions, input.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v).ToArray()); float[] DefocusValues = AlteredPlus.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ)); { CubicGrid AlteredPhasePlus = new CubicGrid(GridCTFPhase.Dimensions, input.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v + Step).ToArray()); float[] PhaseValues = AlteredPhasePlus.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ)); CTFStruct[] LocalParams = EvalGetCTF(input, CTF, DefocusValues, PhaseValues); GPU.CTFCompareToSim(CTFSpectraPolarTrimmedHalf.GetDevice(Intent.Read), CTFCoordsPolarTrimmedHalf.GetDevice(Intent.Read), CTFSpectraScaleHalf.GetDevice(Intent.Read), (uint)CTFSpectraPolarTrimmedHalf.ElementsSliceReal, LocalParams, ResultPlus, (uint)LocalParams.Length); } { CubicGrid AlteredPhaseMinus = new CubicGrid(GridCTFPhase.Dimensions, input.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v - Step).ToArray()); float[] PhaseValues = AlteredPhaseMinus.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ)); CTFStruct[] LocalParams = EvalGetCTF(input, CTF, DefocusValues, PhaseValues); 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, GridCTFPhase.Dimensions.Elements(), i => Result[i + GridCTF.Dimensions.Elements()] = MathHelper.ReduceWeighted(LocalGradients, WiggleWeightsPhase[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[GridCTF.Dimensions.Elements() + GridCTFPhase.Dimensions.Elements() + 2]; for (int i = 0; i < GridCTF.Dimensions.Elements(); i++) StartParams[i] = GridCTF.FlatValues[i]; for (int i = 0; i < GridCTFPhase.Dimensions.Elements(); i++) StartParams[i + GridCTF.Dimensions.Elements()] = GridCTFPhase.FlatValues[i]; StartParams[StartParams.Length - 2] = (double)CTF.DefocusDelta; StartParams[StartParams.Length - 1] = (double)CTF.DefocusAngle / 20 * (Math.PI / 180); // Compute correlation for individual spectra, and throw away those that are >.75 sigma worse than mean. #region Discard outliers if (CTFSpace || CTFTime) { CubicGrid Altered = new CubicGrid(GridCTF.Dimensions, StartParams.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v).ToArray()); float[] DefocusValues = Altered.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ)); CubicGrid AlteredPhase = new CubicGrid(GridCTFPhase.Dimensions, StartParams.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v).ToArray()); float[] PhaseValues = AlteredPhase.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ)); CTFStruct[] LocalParams = EvalGetCTF(StartParams, CTF, DefocusValues, PhaseValues); 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 MeanResult = MathHelper.Mean(Result); float StdResult = MathHelper.StdDev(Result); CTFSpectraConsider = new bool[CTFSpectraGrid.Elements()]; Parallel.For(0, CTFSpectraConsider.Length, i => { //if (Result[i] > MeanResult - StdResult * 1.5f) CTFSpectraConsider[i] = true; /*else { CTFSpectraConsider[i] = false; for (int j = 0; j < WiggleWeights.Length; j++) // Make sure the spectrum's gradient doesn't affect the overall gradient. WiggleWeights[j][i] = 0; }*/ }); NCTFSpectraConsider = CTFSpectraConsider.Where(v => v).Count(); } #endregion BroydenFletcherGoldfarbShanno Optimizer = new BroydenFletcherGoldfarbShanno(StartParams.Length, Eval, Gradient) { Past = 1, Delta = 1e-6, MaxLineSearch = 15, Corrections = 20 }; Optimizer.Minimize(StartParams); #endregion #region Retrieve parameters CTF.Defocus = (decimal)MathHelper.Mean(Optimizer.Solution.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v)); CTF.DefocusDelta = (decimal)Optimizer.Solution[StartParams.Length - 2]; CTF.DefocusAngle = (decimal)(Optimizer.Solution[StartParams.Length - 1] * 20 / (Math.PI / 180)); CTF.PhaseShift = (decimal)MathHelper.Mean(Optimizer.Solution.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.Dimensions.Elements()).Select(v => (float)v)); if (CTF.DefocusDelta < 0) { CTF.DefocusAngle += 90; CTF.DefocusDelta *= -1; } CTF.DefocusAngle = ((int)CTF.DefocusAngle + 180 * 99) % 180; GridCTF = new CubicGrid(GridCTF.Dimensions, Optimizer.Solution.Take((int)GridCTF.Dimensions.Elements()).Select(v => (float)v).ToArray()); GridCTFPhase = new CubicGrid(GridCTFPhase.Dimensions, Optimizer.Solution.Skip((int)GridCTF.Dimensions.Elements()).Take((int)GridCTFPhase.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 if (preciseFit >= 2) { if (!CTFSpace && !CTFTime) { UpdateRotationalAverage(true); } else { 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] = _SimulatedBackground.Interp(r / DimsRegion.X); }); CTFSpectra.SubtractFromSlices(CTFSpectraBackground); float[] DefocusValues = GridCTF.GetInterpolatedNative(CTFSpectraGrid, new float3(DimsRegion.X / 2f / DimsImage.X, DimsRegion.Y / 2f / DimsImage.Y, BorderZ)); CTFStruct[] LocalParams = DefocusValues.Select(v => { CTF Local = CTF.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 = CTF.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[PS1D.Length]; for (int i = 0; i < ForPS1D.Length; i++) ForPS1D[i] = new float2((float)i / DimsRegion.X, (float)Math.Round(RotationalAverageData[i], 4) + _SimulatedBackground.Interp((float)i / DimsRegion.X)); MathHelper.UnNaN(ForPS1D); _PS1D = ForPS1D; CTFSpectraBackground.Dispose(); CTFAverage1D.Dispose(); CTFSpectra.FreeDevice(); } CTF.Defocus = Math.Max(CTF.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] - SimulatedBackground.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)] - SimulatedBackground.Interp(r); } } IOHelper.WriteMapFloat(PowerSpectrumPath, new HeaderMRC { Dimensions = DimsAverage, MinValue = MathHelper.Min(Average2DData), MaxValue = MathHelper.Max(Average2DData) }, Average2DData); PS2DTemp = null; OnPropertyChanged("PS2D"); } #endregion for (int i = 0; i < PS1D.Length; i++) PS1D[i].Y -= SimulatedBackground.Interp(PS1D[i].X); SimulatedBackground = new Cubic1D(SimulatedBackground.Data.Select(v => new float2(v.X, 0f)).ToArray()); OnPropertyChanged("PS1D"); CTFSpectra.Dispose(); CTFMean.Dispose(); CTFCoordsCart.Dispose(); CTFCoordsPolarTrimmed.Dispose(); Simulated1D = GetSimulated1D(); CTFQuality = GetCTFQuality(); SaveMeta(); }
public virtual void LoadMeta() { if (!File.Exists(XMLPath)) return; using (Stream SettingsStream = File.OpenRead(XMLPath)) { XPathDocument Doc = new XPathDocument(SettingsStream); XPathNavigator Reader = Doc.CreateNavigator(); Reader.MoveToRoot(); Reader.MoveToFirstChild(); { string StatusString = Reader.GetAttribute("Status", ""); if (StatusString != null && StatusString.Length > 0) { switch (StatusString) { case "Processed": _Status = ProcessingStatus.Processed; break; case "Outdated": _Status = ProcessingStatus.Outdated; break; case "Unprocessed": _Status = ProcessingStatus.Unprocessed; break; case "Skip": _Status = ProcessingStatus.Skip; break; } } } XPathNavigator NavPS1D = Reader.SelectSingleNode("//PS1D"); if (NavPS1D != null) PS1D = NavPS1D.InnerXml.Split(';').Select(v => { string[] Pair = v.Split('|'); return new float2(float.Parse(Pair[0], CultureInfo.InvariantCulture), float.Parse(Pair[1], CultureInfo.InvariantCulture)); }).ToArray(); XPathNavigator NavSimBackground = Reader.SelectSingleNode("//SimulatedBackground"); if (NavSimBackground != null) _SimulatedBackground = new Cubic1D(NavSimBackground.InnerXml.Split(';').Select(v => { string[] Pair = v.Split('|'); return new float2(float.Parse(Pair[0], CultureInfo.InvariantCulture), float.Parse(Pair[1], CultureInfo.InvariantCulture)); }).ToArray()); XPathNavigator NavSimScale = Reader.SelectSingleNode("//SimulatedScale"); if (NavSimScale != null) _SimulatedScale = new Cubic1D(NavSimScale.InnerXml.Split(';').Select(v => { string[] Pair = v.Split('|'); return new float2(float.Parse(Pair[0], CultureInfo.InvariantCulture), float.Parse(Pair[1], CultureInfo.InvariantCulture)); }).ToArray()); XPathNavigator NavCTF = Reader.SelectSingleNode("//CTF"); if (NavCTF != null) CTF.Load(NavCTF); XPathNavigator NavGridCTF = Reader.SelectSingleNode("//GridCTF"); if (NavGridCTF != null) GridCTF = CubicGrid.Load(NavGridCTF); XPathNavigator NavGridCTFPhase = Reader.SelectSingleNode("//GridCTFPhase"); if (NavGridCTFPhase != null) GridCTFPhase = CubicGrid.Load(NavGridCTFPhase); XPathNavigator NavMoveX = Reader.SelectSingleNode("//GridMovementX"); if (NavMoveX != null) GridMovementX = CubicGrid.Load(NavMoveX); XPathNavigator NavMoveY = Reader.SelectSingleNode("//GridMovementY"); if (NavMoveY != null) GridMovementY = CubicGrid.Load(NavMoveY); XPathNavigator NavLocalX = Reader.SelectSingleNode("//GridLocalMovementX"); if (NavLocalX != null) GridLocalX = CubicGrid.Load(NavLocalX); XPathNavigator NavLocalY = Reader.SelectSingleNode("//GridLocalMovementY"); if (NavLocalY != null) GridLocalY = CubicGrid.Load(NavLocalY); PyramidShiftX.Clear(); foreach (XPathNavigator NavShiftX in Reader.Select("//PyramidShiftX")) PyramidShiftX.Add(CubicGrid.Load(NavShiftX)); PyramidShiftY.Clear(); foreach (XPathNavigator NavShiftY in Reader.Select("//PyramidShiftY")) PyramidShiftY.Add(CubicGrid.Load(NavShiftY)); } }