public void CreateCorrected(MapHeader originalHeader, Image originalStack) { if (!Directory.Exists(AverageDir)) Directory.CreateDirectory(AverageDir); if (!Directory.Exists(CTFDir)) Directory.CreateDirectory(CTFDir); if (MainWindow.Options.PostStack && !Directory.Exists(ShiftedStackDir)) Directory.CreateDirectory(ShiftedStackDir); int3 Dims = originalStack.Dims; Image ShiftedStack = null; if (MainWindow.Options.PostStack) ShiftedStack = new Image(Dims); 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 / (float)(180.0 / Math.PI); Image CTFCoords; { float2[] CTFCoordsData = new float2[Dims.ElementsSlice()]; Helper.ForEachElementFT(new int2(Dims), (x, y, xx, yy) => { float xs = xx / (float)Dims.X; float ys = yy / (float)Dims.Y; float r = (float)Math.Sqrt(xs * xs + ys * ys); float angle = (float)(Math.Atan2(yy, xx) + Math.PI / 2.0); float CurrentPixelSize = PixelSize + PixelDelta * (float)Math.Cos(2f * (angle - PixelAngle)); CTFCoordsData[y * (Dims.X / 2 + 1) + x] = new float2(r / CurrentPixelSize, angle); }); CTFCoords = new Image(CTFCoordsData, Dims.Slice(), true); CTFCoords.RemapToFT(); } Image CTFFreq = CTFCoords.AsReal(); CubicGrid CollapsedMovementX = GridMovementX.CollapseXY(); CubicGrid CollapsedMovementY = GridMovementY.CollapseXY(); CubicGrid CollapsedCTF = GridCTF.CollapseXY(); Image AverageFT = new Image(Dims.Slice(), true, true); Image AveragePS = new Image(Dims.Slice(), true, false); Image Weights = new Image(Dims.Slice(), true, false); Weights.Fill(1e-6f); float StepZ = 1f / Math.Max(Dims.Z - 1, 1); for (int nframe = 0; nframe < Dims.Z; nframe++) { Image PS = new Image(Dims.Slice(), true); PS.Fill(1f); // Apply motion blur filter. /*{ float StartZ = (nframe - 0.5f) * StepZ; float StopZ = (nframe + 0.5f) * StepZ; float2[] Shifts = new float2[21]; for (int z = 0; z < Shifts.Length; z++) { float zp = StartZ + (StopZ - StartZ) / (Shifts.Length - 1) * z; Shifts[z] = new float2(CollapsedMovementX.GetInterpolated(new float3(0.5f, 0.5f, zp)), CollapsedMovementY.GetInterpolated(new float3(0.5f, 0.5f, zp))); } // Center the shifts around 0 float2 ShiftMean = MathHelper.Mean(Shifts); Shifts = Shifts.Select(v => v - ShiftMean).ToArray(); Image MotionFilter = new Image(IntPtr.Zero, Dims.Slice(), true); GPU.CreateMotionBlur(MotionFilter.GetDevice(Intent.Write), MotionFilter.Dims, Helper.ToInterleaved(Shifts.Select(v => new float3(v.X, v.Y, 0)).ToArray()), (uint)Shifts.Length, 1); PS.Multiply(MotionFilter); //MotionFilter.WriteMRC("motion.mrc"); MotionFilter.Dispose(); }*/ // Apply CTF. /*if (CTF != null) { CTF Altered = CTF.GetCopy(); Altered.Defocus = (decimal)CollapsedCTF.GetInterpolated(new float3(0.5f, 0.5f, nframe * StepZ)); Image CTFImage = new Image(IntPtr.Zero, Dims.Slice(), true); GPU.CreateCTF(CTFImage.GetDevice(Intent.Write), CTFCoords.GetDevice(Intent.Read), (uint)CTFCoords.ElementsSliceComplex, new[] { Altered.ToStruct() }, false, 1); CTFImage.Abs(); PS.Multiply(CTFImage); //CTFImage.WriteMRC("ctf.mrc"); CTFImage.Dispose(); }*/ // Apply dose weighting. /*{ float3 NikoConst = new float3(0.245f, -1.665f, 2.81f); // Niko's formula expects e-/A2/frame, we've got e-/px/frame - convert! float FrameDose = (float)MainWindow.Options.CorrectDosePerFrame * (nframe + 0.5f) / (PixelSize * PixelSize); Image DoseImage = new Image(IntPtr.Zero, Dims.Slice(), true); GPU.DoseWeighting(CTFFreq.GetDevice(Intent.Read), DoseImage.GetDevice(Intent.Write), (uint)DoseImage.ElementsSliceComplex, new[] { FrameDose }, NikoConst, 1); PS.Multiply(DoseImage); //DoseImage.WriteMRC("dose.mrc"); DoseImage.Dispose(); }*/ Image Frame = new Image(originalStack.GetHost(Intent.Read)[nframe], Dims.Slice()); Frame.ShiftSlicesMassive(new[] { new float3(CollapsedMovementX.GetInterpolated(new float3(0.5f, 0.5f, nframe * StepZ)), CollapsedMovementY.GetInterpolated(new float3(0.5f, 0.5f, nframe * StepZ)), 0f) }); if (MainWindow.Options.PostStack) ShiftedStack.GetHost(Intent.Write)[nframe] = Frame.GetHost(Intent.Read)[0]; Image FrameFT = Frame.AsFFT(); Frame.Dispose(); //Image PSSign = new Image(PS.GetDevice(Intent.Read), Dims.Slice(), true); //Image PSSign = new Image(Dims.Slice(), true); //PSSign.Fill(1f); //PSSign.Sign(); // Do phase flipping before averaging. //FrameFT.Multiply(PSSign); //PS.Multiply(PSSign); //PSSign.Dispose(); //FrameFT.Multiply(PS); AverageFT.Add(FrameFT); Weights.Add(PS); //PS.WriteMRC("ps.mrc"); PS.Multiply(PS); AveragePS.Add(PS); PS.Dispose(); FrameFT.Dispose(); } CTFCoords.Dispose(); CTFFreq.Dispose(); //AverageFT.Divide(Weights); //AverageFT.WriteMRC("averageft.mrc"); //Weights.WriteMRC("weights.mrc"); AveragePS.Divide(Weights); Weights.Dispose(); Image Average = AverageFT.AsIFFT(); AverageFT.Dispose(); MapHeader Header = originalHeader; Header.Dimensions = Dims.Slice(); Average.WriteMRC(AveragePath); Average.Dispose(); AveragePS.WriteMRC(CTFPath); AveragePS.Dispose(); TempAverageImage = null; OnPropertyChanged("AverageImage"); using (TextWriter Writer = File.CreateText(AverageDir + RootName + "_ctffind3.log")) { decimal Mag = (MainWindow.Options.CTFDetectorPixel * 10000M / CTF.PixelSize); Writer.WriteLine("CS[mm], HT[kV], AmpCnst, XMAG, DStep[um]"); Writer.WriteLine($"{CTF.Cs} {CTF.Voltage} {CTF.Amplitude} {Mag} {MainWindow.Options.CTFDetectorPixel}"); float BestQ = 0; float2[] Q = CTFQuality; if (Q != null) foreach (var q in Q) { if (q.Y < 0.3f) break; BestQ = q.X * 2f; } Writer.WriteLine($"{(CTF.Defocus + CTF.DefocusDelta / 2M) * 1e4M} {(CTF.Defocus - CTF.DefocusDelta / 2M) * 1e4M} {CTF.DefocusAngle} {BestQ} {CTF.PhaseShift * 180M} Final Values"); } if (MainWindow.Options.PostStack) ShiftedStack.WriteMRC(ShiftedStackPath); }