Example #1
0
        public static MapHeader ReadFromFile(BinaryReader reader, FileInfo info, int2 headerlessSliceDims, long headerlessOffset, Type headerlessType)
        {
            MapHeader Header = null;

            if (info.Extension.ToLower() == ".mrc" || info.Extension.ToLower() == ".mrcs" || info.Extension.ToLower() == ".ali" || info.Extension.ToLower() == ".rec" || info.Extension.ToLower() == ".st")
            {
                Header = new HeaderMRC(reader);
            }
            else if (info.Extension.ToLower() == ".em")
            {
                Header = new HeaderEM(reader);
            }
            else if (info.Extension.ToLower() == ".tif" || info.Extension.ToLower() == ".tiff")
            {
                Header = new HeaderTiff(info.FullName);
            }
            else if (info.Extension.ToLower() == ".dat")
            {
                long SliceElements = headerlessSliceDims.Elements() * ImageFormatsHelper.SizeOf(headerlessType);
                long Slices        = (info.Length - headerlessOffset) / SliceElements;
                int3 Dims3         = new int3(headerlessSliceDims.X, headerlessSliceDims.Y, (int)Slices);
                Header = new HeaderRaw(Dims3, headerlessOffset, headerlessType);
            }
            else
            {
                throw new Exception("File type not supported.");
            }

            return(Header);
        }
Example #2
0
        public static MapHeader ReadFromFile(BinaryReader reader, string path, int2 headerlessSliceDims, long headerlessOffset, Type headerlessType, Stream stream = null)
        {
            MapHeader Header = null;
            string Extension = Helper.PathToExtension(path).ToLower();

            if (Extension == ".mrc" || Extension == ".mrcs" || Extension == ".rec" || Extension == ".st")
                Header = new HeaderMRC(reader);
            else if (Extension == ".em")
                Header = new HeaderEM(reader);
            else if (Extension == ".dm4" || Extension == ".dm3")
                Header = new HeaderDM4(reader);
            else if (Extension == ".tif" || Extension == ".tiff")
                Header = new HeaderTiff(path, stream);
            else if (Extension == ".eer")
                Header = new HeaderEER(path, stream);
            else if (Extension == ".dat")
            {
                FileInfo info = new FileInfo(path);

                long SliceElements = headerlessSliceDims.Elements() * ImageFormatsHelper.SizeOf(headerlessType);
                long Slices = (info.Length - headerlessOffset) / SliceElements;
                int3 Dims3 = new int3(headerlessSliceDims.X, headerlessSliceDims.Y, (int)Slices);
                Header = new HeaderRaw(Dims3, headerlessOffset, headerlessType);
            }
            else
                throw new Exception("File type not supported.");

            return Header;
        }
Example #3
0
        public static long GetHeaderSize(string path)
        {
            long Position;

            using (BinaryReader Reader = new BinaryReader(File.OpenRead(path)))
            {
                HeaderMRC Header = new HeaderMRC(Reader);
                Position = Reader.BaseStream.Position;
            }

            return(Position);
        }
Example #4
0
        public static int3 GetMapDimensions(string path)
        {
            int3 Dims = new int3(1, 1, 1);
            FileInfo Info = new FileInfo(path);

            using (BinaryReader Reader = new BinaryReader(OpenWithBigBuffer(path)))
            {
                if (Info.Extension.ToLower() == ".mrc" || Info.Extension.ToLower() == ".mrcs")
                {
                    HeaderMRC Header = new HeaderMRC(Reader);
                    Dims = Header.Dimensions;
                }
                else if (Info.Extension.ToLower() == ".em")
                {
                    HeaderEM Header = new HeaderEM(Reader);
                    Dims = Header.Dimensions;
                }
                else
                    throw new Exception("Format not supported.");
            }

            return Dims;
        }
Example #5
0
        public void GrabScreenshot(string path)
        {
            MakeCurrent();

            bool IsMRC = path.ToLower().Substring(path.Length - 3) == "mrc";
            Color ScreenshotBackground = IsMRC ? Colors.Black : Colors.White;

            Color OldBackground = BackgroundColor;
            if (IsMRC)
            {
                BackgroundColor = ScreenshotBackground;
                Redraw();
            }

            Bitmap bmp = new Bitmap(GLControl.ClientSize.Width, GLControl.ClientSize.Height);
            System.Drawing.Imaging.BitmapData data = bmp.LockBits(GLControl.ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            GL.ReadPixels(0, 0, GLControl.ClientSize.Width, GLControl.ClientSize.Height, PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
            bmp.UnlockBits(data);

            if (!IsMRC)
            {
                bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
                using (Stream ImageStream = File.Create(path))
                {
                    bmp.Save(ImageStream, System.Drawing.Imaging.ImageFormat.Png);
                }
            }
            else
            {
                using (BinaryWriter Writer = new BinaryWriter(File.Create(path)))
                {
                    HeaderMRC Header = new HeaderMRC
                    {
                        Dimensions = new int3(bmp.Width, bmp.Height, 1),
                        Mode = MRCDataType.Byte
                    };
                    Header.Write(Writer);

                    int Elements = bmp.Width * bmp.Height;
                    byte[] Data = new byte[Elements];
                    System.Drawing.Imaging.BitmapData BitmapData = bmp.LockBits(GLControl.ClientRectangle, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                    unsafe
                    {
                        fixed (byte* DataPtr = Data)
                        {
                            byte* BitmapDataP = (byte*)BitmapData.Scan0;

                            for (int i = 0; i < Elements; i++)
                                DataPtr[i] = BitmapDataP[i * 4 + 1];
                        }
                    }
                    bmp.UnlockBits(BitmapData);

                    Writer.Write(Data);
                }

                BackgroundColor = OldBackground;
            }
        }
Example #6
0
        public TiltSeries(string path)
            : base(path)
        {
            if (Angles.Length < Dimensions.Z)   // In case angles and dose haven't been read and stored in .xml yet.
            {
                if (File.Exists(DirectoryName + RootName + ".star"))
                {
                    Star Table = new Star(DirectoryName + RootName + ".star");

                    if (!Table.HasColumn("wrpDose") || !Table.HasColumn("wrpAngleTilt"))
                        throw new Exception("STAR file has no wrpDose or wrpTilt column.");

                    List<float> TempAngles = new List<float>();
                    List<float> TempDose = new List<float>();

                    for (int i = 0; i < Table.RowCount; i++)
                    {
                        TempAngles.Add(float.Parse(Table.GetRowValue(i, "wrpAngleTilt")));
                        TempDose.Add(float.Parse(Table.GetRowValue(i, "wrpDose")));
                    }

                    if (TempAngles.Count == 0)
                        throw new Exception("Metadata must contain 2 values per tilt: angle, dose.");
                    if (TempAngles.Count != Dimensions.Z)
                        throw new Exception("Metadata must contain one line for each image in tilt series.");

                    Angles = TempAngles.ToArray();
                    Dose = TempDose.ToArray();
                }
                else
                {
                    HeaderMRC Header = new HeaderMRC(new BinaryReader(File.OpenRead(Path)));
                    if (Header.ImodTilt != null)
                    {
                        Angles = Header.ImodTilt;
                        Dose = new float[Angles.Length];
                    }
                    else
                        throw new Exception("A .meta file with angles and accumulated dose, or at least tilt angle data in the .ali's header are needed to work with tilt series.");
                }
            }
        }
Example #7
0
        public static MapHeader ReadFromFile(BinaryReader reader, FileInfo info, int2 headerlessSliceDims, long headerlessOffset, Type headerlessType)
        {
            MapHeader Header = null;

            if (info.Extension.ToLower() == ".mrc" || info.Extension.ToLower() == ".mrcs" || info.Extension.ToLower() == ".ali" || info.Extension.ToLower() == ".rec" || info.Extension.ToLower() == ".st")
                Header = new HeaderMRC(reader);
            else if (info.Extension.ToLower() == ".em")
                Header = new HeaderEM(reader);
            else if (info.Extension.ToLower() == ".tif" || info.Extension.ToLower() == ".tiff")
                Header = new HeaderTiff(info.FullName);
            else if (info.Extension.ToLower() == ".dat")
            {
                long SliceElements = headerlessSliceDims.Elements() * ImageFormatsHelper.SizeOf(headerlessType);
                long Slices = (info.Length - headerlessOffset) / SliceElements;
                int3 Dims3 = new int3(headerlessSliceDims.X, headerlessSliceDims.Y, (int)Slices);
                Header = new HeaderRaw(Dims3, headerlessOffset, headerlessType);
            }
            else
                throw new Exception("File type not supported.");

            return Header;
        }
Example #8
0
        public void ExportParticlesMovieOld(Star table, int size)
        {
            List<int> RowIndices = new List<int>();
            string[] ColumnMicrographName = table.GetColumn("rlnMicrographName");
            for (int i = 0; i < ColumnMicrographName.Length; i++)
                if (ColumnMicrographName[i].Contains(RootName))
                    RowIndices.Add(i);

            if (RowIndices.Count == 0)
                return;

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

            MapHeader OriginalHeader = MapHeader.ReadFromFile(Path,
                                                              new int2(MainWindow.Options.InputDatWidth, MainWindow.Options.InputDatHeight),
                                                              MainWindow.Options.InputDatOffset,
                                                              ImageFormatsHelper.StringToType(MainWindow.Options.InputDatType));
            /*Image OriginalStack = StageDataLoad.LoadMap(Path,
                                                        new int2(MainWindow.Options.InputDatWidth, MainWindow.Options.InputDatHeight),
                                                        MainWindow.Options.InputDatOffset,
                                                        ImageFormatsHelper.StringToType(MainWindow.Options.InputDatType));*/

            //OriginalStack.Xray(20f);

            int3 Dims = OriginalHeader.Dimensions;
            int3 DimsRegion = new int3(size, size, 1);
            int NParticles = RowIndices.Count / Dimensions.Z;

            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[DimsRegion.ElementsSlice()];
                //Helper.ForEachElementFT(new int2(DimsRegion), (x, y, xx, yy) =>
                for (int y = 0; y < DimsRegion.Y; y++)
                    for (int x = 0; x < DimsRegion.X / 2 + 1; x++)
                    {
                        int xx = x;
                        int yy = y < DimsRegion.Y / 2 + 1 ? y : y - DimsRegion.Y;

                        float xs = xx / (float)DimsRegion.X;
                        float ys = yy / (float)DimsRegion.Y;
                        float r = (float)Math.Sqrt(xs * xs + ys * ys);
                        float angle = (float)(Math.Atan2(yy, xx));
                        float CurrentPixelSize = PixelSize + PixelDelta * (float)Math.Cos(2f * (angle - PixelAngle));

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

                CTFCoords = new Image(CTFCoordsData, DimsRegion.Slice(), true);
                //CTFCoords.RemapToFT();
            }
            Image CTFFreq = CTFCoords.AsReal();

            Image CTFStack = new Image(new int3(size, size, NParticles * Dimensions.Z), true);
            int CTFStackIndex = 0;

            string[] ColumnPosX = table.GetColumn("rlnCoordinateX");
            string[] ColumnPosY = table.GetColumn("rlnCoordinateY");
            int3[] Origins = new int3[NParticles];
            for (int i = 0; i < NParticles; i++)
                Origins[i] = new int3((int)double.Parse(ColumnPosX[RowIndices[i]]) - DimsRegion.X * 2 / 2,
                                      (int)double.Parse(ColumnPosY[RowIndices[i]]) - DimsRegion.Y * 2 / 2,
                                      0);

            int IndexOffset = RowIndices[0];

            string[] ColumnOriginX = table.GetColumn("rlnOriginX");
            string[] ColumnOriginY = table.GetColumn("rlnOriginY");
            string[] ColumnPriorX = table.GetColumn("rlnOriginXPrior");
            string[] ColumnPriorY = table.GetColumn("rlnOriginYPrior");
            float2[] ShiftPriors = new float2[NParticles];
            for (int i = 0; i < NParticles; i++)
                ShiftPriors[i] = new float2(float.Parse(ColumnPriorX[IndexOffset + i]),
                                            float.Parse(ColumnPriorY[IndexOffset + i]));

            float2[][] ParticleTracks = new float2[NParticles][];
            for (int i = 0; i < NParticles; i++)
            {
                ParticleTracks[i] = new float2[Dimensions.Z];
                for (int z = 0; z < Dimensions.Z; z++)
                    ParticleTracks[i][z] = new float2(float.Parse(ColumnOriginX[IndexOffset + z * NParticles + i]) - ShiftPriors[i].X,
                                                      float.Parse(ColumnOriginY[IndexOffset + z * NParticles + i]) - ShiftPriors[i].Y);
            }

            Image AverageFT = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles), true, true);
            Image AveragePS = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles), true);
            Image Weights = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles), true);
            Weights.Fill(1e-6f);
            Image FrameParticles = new Image(IntPtr.Zero, new int3(DimsRegion.X * 2, DimsRegion.Y * 2, NParticles));

            float StepZ = 1f / Math.Max(Dims.Z - 1, 1);
            for (int z = 0; z < Dims.Z; z++)
            {
                float CoordZ = z * StepZ;

                /*GPU.Extract(OriginalStack.GetDeviceSlice(z, Intent.Read),
                            FrameParticles.GetDevice(Intent.Write),
                            Dims.Slice(),
                            new int3(DimsRegion.X * 2, DimsRegion.Y * 2, 1),
                            Helper.ToInterleaved(Origins),
                            (uint)NParticles);*/

                // Shift particles
                {
                    float3[] Shifts = new float3[NParticles];

                    for (int i = 0; i < NParticles; i++)
                    {
                        float NormX = Math.Max(0.15f, Math.Min((float)Origins[i].X / Dims.X, 0.85f));
                        float NormY = Math.Max(0.15f, Math.Min((float)Origins[i].Y / Dims.Y, 0.85f));
                        float3 Coords = new float3(NormX, NormY, CoordZ);
                        Shifts[i] = new float3(GridMovementX.GetInterpolated(Coords) + ParticleTracks[i][z].X,
                                               GridMovementY.GetInterpolated(Coords) + ParticleTracks[i][z].Y,
                                               0f);
                    }
                    FrameParticles.ShiftSlices(Shifts);
                }

                Image FrameParticlesCropped = FrameParticles.AsPadded(new int2(DimsRegion));
                Image FrameParticlesFT = FrameParticlesCropped.AsFFT();
                FrameParticlesCropped.Dispose();

                Image PS = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles), true);
                PS.Fill(1f);

                // Apply motion blur filter.
                {
                    const int Samples = 11;
                    float StartZ = (z - 0.75f) * StepZ;
                    float StopZ = (z + 0.75f) * StepZ;

                    float2[] Shifts = new float2[Samples * NParticles];
                    for (int p = 0; p < NParticles; p++)
                    {
                        float NormX = Math.Max(0.15f, Math.Min((float)Origins[p].X / Dims.X, 0.85f));
                        float NormY = Math.Max(0.15f, Math.Min((float)Origins[p].Y / Dims.Y, 0.85f));

                        for (int zz = 0; zz < Samples; zz++)
                        {
                            float zp = StartZ + (StopZ - StartZ) / (Samples - 1) * zz;
                            float3 Coords = new float3(NormX, NormY, zp);
                            Shifts[p * Samples + zz] = new float2(GridMovementX.GetInterpolated(Coords),
                                                                  GridMovementY.GetInterpolated(Coords));
                        }
                    }

                    Image MotionFilter = new Image(IntPtr.Zero, new int3(DimsRegion.X, DimsRegion.Y, NParticles), true);
                    GPU.CreateMotionBlur(MotionFilter.GetDevice(Intent.Write),
                                         DimsRegion,
                                         Helper.ToInterleaved(Shifts.Select(v => new float3(v.X, v.Y, 0)).ToArray()),
                                         Samples,
                                         (uint)NParticles);
                    PS.Multiply(MotionFilter);
                    //MotionFilter.WriteMRC("motion.mrc");
                    MotionFilter.Dispose();
                }

                // Apply CTF.
                if (CTF != null)
                {
                    CTFStruct[] Structs = new CTFStruct[NParticles];
                    for (int p = 0; p < NParticles; p++)
                    {
                        CTF Altered = CTF.GetCopy();
                        Altered.Defocus = (decimal)GridCTF.GetInterpolated(new float3(float.Parse(ColumnPosX[RowIndices[p]]) / Dims.X,
                                                                                      float.Parse(ColumnPosY[RowIndices[p]]) / Dims.Y,
                                                                                      z * StepZ));

                        Structs[p] = Altered.ToStruct();
                    }

                    Image CTFImage = new Image(IntPtr.Zero, new int3(DimsRegion.X, DimsRegion.Y, NParticles), true);
                    GPU.CreateCTF(CTFImage.GetDevice(Intent.Write),
                                  CTFCoords.GetDevice(Intent.Read),
                                  (uint)CTFCoords.ElementsSliceComplex,
                                  Structs,
                                  false,
                                  (uint)NParticles);

                    //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 * (z + 0.5f) / (PixelSize * PixelSize);

                    Image DoseImage = new Image(IntPtr.Zero, DimsRegion, true);
                    GPU.DoseWeighting(CTFFreq.GetDevice(Intent.Read),
                                      DoseImage.GetDevice(Intent.Write),
                                      (uint)DoseImage.ElementsSliceComplex,
                                      new[] { FrameDose },
                                      NikoConst,
                                      1);
                    PS.MultiplySlices(DoseImage);
                    //DoseImage.WriteMRC("dose.mrc");
                    DoseImage.Dispose();
                }*/

                // Copy custom CTF into the CTF stack
                GPU.CopyDeviceToDevice(PS.GetDevice(Intent.Read),
                                       new IntPtr((long)CTFStack.GetDevice(Intent.Write) + CTFStack.ElementsSliceReal * NParticles * z * sizeof (float)),
                                       CTFStack.ElementsSliceReal * NParticles);

                Image PSAbs = new Image(PS.GetDevice(Intent.Read), new int3(DimsRegion.X, DimsRegion.Y, NParticles), true);
                PSAbs.Abs();

                FrameParticlesFT.Multiply(PSAbs);
                AverageFT.Add(FrameParticlesFT);

                Weights.Add(PSAbs);

                PS.Multiply(PSAbs);
                AveragePS.Add(PS);

                PS.Dispose();
                FrameParticlesFT.Dispose();
                PSAbs.Dispose();

                // Write paths to custom CTFs into the .star
                for (int i = 0; i < NParticles; i++)
                {
                    string ParticleCTFPath = (CTFStackIndex + 1).ToString("D6") + "@particlectf/" + RootName + ".mrcs";
                    table.SetRowValue(RowIndices[NParticles * z + i], "rlnCtfImage", ParticleCTFPath);
                    CTFStackIndex++;
                }
            }
            FrameParticles.Dispose();
            CTFCoords.Dispose();
            CTFFreq.Dispose();

            AverageFT.Divide(Weights);
            AveragePS.Divide(Weights);
            Weights.Dispose();

            Image AverageParticles = AverageFT.AsIFFT();
            AverageFT.Dispose();

            GPU.NormParticles(AverageParticles.GetDevice(Intent.Read), AverageParticles.GetDevice(Intent.Write), DimsRegion, (uint)(90f / PixelSize), true, (uint)NParticles);

            HeaderMRC ParticlesHeader = new HeaderMRC
            {
                Pixelsize = new float3(PixelSize, PixelSize, PixelSize)
            };

            AverageParticles.WriteMRC(ParticlesPath, ParticlesHeader);
            AverageParticles.Dispose();

            //OriginalStack.Dispose();

            CTFStack.WriteMRC(DirectoryName + "particlectf/" + RootName + ".mrcs");
            CTFStack.Dispose();

            /*for (int i = 0; i < NParticles; i++)
            {
                string ParticlePath = (i + 1).ToString("D6") + "@particles/" + RootName + "_particles.mrcs";
                table.SetRowValue(RowIndices[i], "rlnImageName", ParticlePath);
            }*/

            AveragePS.Dispose();

            //table.RemoveRows(RowIndices.Skip(NParticles).ToArray());
        }
Example #9
0
        public void ExportParticlesMovie(Star tableIn, Star tableOut, MapHeader originalHeader, Image originalStack, int size, float particleradius, decimal scaleFactor)
        {
            int CurrentDevice = GPU.GetDevice();

            #region Make sure directories exist.
            lock (tableIn)
            {
                if (!Directory.Exists(ParticleMoviesDir))
                    Directory.CreateDirectory(ParticleMoviesDir);
                if (!Directory.Exists(ParticleCTFMoviesDir))
                    Directory.CreateDirectory(ParticleCTFMoviesDir);
            }
            #endregion

            #region Get row indices for all, and individual halves

            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);

            //RowIndices = RowIndices.Take(13).ToList();

            List<int> RowIndices1 = new List<int>();
            List<int> RowIndices2 = new List<int>();
            for (int i = 0; i < RowIndices.Count; i++)
                if (tableIn.GetRowValue(RowIndices[i], "rlnRandomSubset") == "1")
                    RowIndices1.Add(RowIndices[i]);
                else
                    RowIndices2.Add(RowIndices[i]);

            #endregion

            if (RowIndices.Count == 0)
                return;

            #region Auxiliary variables

            List<int> TableOutIndices = new List<int>();

            int3 Dims = originalHeader.Dimensions;
            Dims.Z = 36;
            int3 DimsRegion = new int3(size, size, 1);
            int3 DimsPadded = new int3(size * 2, size * 2, 1);
            int NParticles = RowIndices.Count;
            int NParticles1 = RowIndices1.Count;
            int NParticles2 = RowIndices2.Count;

            float PixelSize = (float)CTF.PixelSize / 1.00f;
            float PixelDelta = (float)CTF.PixelSizeDelta / 1.00f;
            float PixelAngle = (float)CTF.PixelSizeAngle * Helper.ToRad;

            #endregion

            #region Prepare initial coordinates and shifts

            string[] ColumnPosX = tableIn.GetColumn("rlnCoordinateX");
            string[] ColumnPosY = tableIn.GetColumn("rlnCoordinateY");
            string[] ColumnOriginX = tableIn.GetColumn("rlnOriginX");
            string[] ColumnOriginY = tableIn.GetColumn("rlnOriginY");

            int3[] Origins1 = new int3[NParticles1];
            int3[] Origins2 = new int3[NParticles2];
            float3[] ResidualShifts1 = new float3[NParticles1];
            float3[] ResidualShifts2 = new float3[NParticles2];

            lock (tableIn)  // Writing to the table, better be on the safe side
            {
                // Half1: Add translational shifts to coordinates, sans the fractional part
                for (int i = 0; i < NParticles1; i++)
                {
                    float2 Pos = new float2(float.Parse(ColumnPosX[RowIndices1[i]], CultureInfo.InvariantCulture),
                                            float.Parse(ColumnPosY[RowIndices1[i]], CultureInfo.InvariantCulture)) * 1.00f;
                    float2 Shift = new float2(float.Parse(ColumnOriginX[RowIndices1[i]], CultureInfo.InvariantCulture),
                                              float.Parse(ColumnOriginY[RowIndices1[i]], CultureInfo.InvariantCulture)) * 1.00f;

                    Origins1[i] = new int3((int)(Pos.X - Shift.X),
                                           (int)(Pos.Y - Shift.Y),
                                           0);
                    ResidualShifts1[i] = new float3(-MathHelper.ResidualFraction(Pos.X - Shift.X),
                                                    -MathHelper.ResidualFraction(Pos.Y - Shift.Y),
                                                    0f);

                    tableIn.SetRowValue(RowIndices1[i], "rlnCoordinateX", Origins1[i].X.ToString());
                    tableIn.SetRowValue(RowIndices1[i], "rlnCoordinateY", Origins1[i].Y.ToString());
                    tableIn.SetRowValue(RowIndices1[i], "rlnOriginX", "0.0");
                    tableIn.SetRowValue(RowIndices1[i], "rlnOriginY", "0.0");
                }

                // Half2: Add translational shifts to coordinates, sans the fractional part
                for (int i = 0; i < NParticles2; i++)
                {
                    float2 Pos = new float2(float.Parse(ColumnPosX[RowIndices2[i]], CultureInfo.InvariantCulture),
                                            float.Parse(ColumnPosY[RowIndices2[i]], CultureInfo.InvariantCulture)) * 1.00f;
                    float2 Shift = new float2(float.Parse(ColumnOriginX[RowIndices2[i]], CultureInfo.InvariantCulture),
                                              float.Parse(ColumnOriginY[RowIndices2[i]], CultureInfo.InvariantCulture)) * 1.00f;

                    Origins2[i] = new int3((int)(Pos.X - Shift.X),
                                           (int)(Pos.Y - Shift.Y),
                                           0);
                    ResidualShifts2[i] = new float3(-MathHelper.ResidualFraction(Pos.X - Shift.X),
                                                    -MathHelper.ResidualFraction(Pos.Y - Shift.Y),
                                                    0f);

                    tableIn.SetRowValue(RowIndices2[i], "rlnCoordinateX", Origins2[i].X.ToString());
                    tableIn.SetRowValue(RowIndices2[i], "rlnCoordinateY", Origins2[i].Y.ToString());
                    tableIn.SetRowValue(RowIndices2[i], "rlnOriginX", "0.0");
                    tableIn.SetRowValue(RowIndices2[i], "rlnOriginY", "0.0");
                }
            }

            #endregion

            #region Allocate memory for particle and PS stacks

            Image ParticleStackAll = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles * Dims.Z));
            Image ParticleStack1 = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles1 * Dims.Z));
            Image ParticleStack2 = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles2 * Dims.Z));
            Image PSStackAll = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles * Dims.Z), true);
            Image PSStack1 = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles1 * Dims.Z), true);
            Image PSStack2 = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles2 * Dims.Z), true);

            Image FrameParticles1 = new Image(IntPtr.Zero, new int3(DimsPadded.X, DimsPadded.Y, NParticles1));
            Image FrameParticles2 = new Image(IntPtr.Zero, new int3(DimsPadded.X, DimsPadded.Y, NParticles2));

            float[][] ParticleStackData = ParticleStackAll.GetHost(Intent.Write);
            float[][] ParticleStackData1 = ParticleStack1.GetHost(Intent.Write);
            float[][] ParticleStackData2 = ParticleStack2.GetHost(Intent.Write);
            float[][] PSStackData = PSStackAll.GetHost(Intent.Write);
            float[][] PSStackData1 = PSStack1.GetHost(Intent.Write);
            float[][] PSStackData2 = PSStack2.GetHost(Intent.Write);

            #endregion

            #region Create rows in outTable

            lock (tableOut)  // Creating rows in outTable, this absolutely needs to be staged sequentially
            {
                for (int z = 0; z < Dims.Z; z++)
                {
                    for (int i = 0; i < NParticles; i++)
                    {
                        int Index = i < NParticles1 ? RowIndices1[i] : RowIndices2[i - NParticles1];

                        string OriParticlePath = (i + 1).ToString("D6") + "@particles/" + RootName + "_particles.mrcs";
                        string ParticleName = (z * NParticles + i + 1).ToString("D6") + "@particlemovies/" + RootName + "_particles.mrcs";
                        string ParticleCTFName = (z * NParticles + i + 1).ToString("D6") + "@particlectfmovies/" + RootName + "_particlectf.mrcs";

                        List<string> NewRow = tableIn.GetRow(Index).Select(v => v).ToList(); // Get copy of original row.
                        NewRow[tableOut.GetColumnIndex("rlnOriginalParticleName")] = OriParticlePath;
                        NewRow[tableOut.GetColumnIndex("rlnAngleRotPrior")] = tableIn.GetRowValue(Index, "rlnAngleRot");
                        NewRow[tableOut.GetColumnIndex("rlnAngleTiltPrior")] = tableIn.GetRowValue(Index, "rlnAngleTilt");
                        NewRow[tableOut.GetColumnIndex("rlnAnglePsiPrior")] = tableIn.GetRowValue(Index, "rlnAnglePsi");
                        NewRow[tableOut.GetColumnIndex("rlnOriginXPrior")] = "0.0";
                        NewRow[tableOut.GetColumnIndex("rlnOriginYPrior")] = "0.0";

                        NewRow[tableOut.GetColumnIndex("rlnImageName")] = ParticleName;
                        NewRow[tableOut.GetColumnIndex("rlnCtfImage")] = ParticleCTFName;
                        NewRow[tableOut.GetColumnIndex("rlnMicrographName")] = (z + 1).ToString("D6") + "@stack/" + RootName + "_movie.mrcs";

                        TableOutIndices.Add(tableOut.RowCount);
                        tableOut.AddRow(NewRow);
                    }
                }
            }

            #endregion

            #region For every frame, extract particles from each half; shift, correct, and norm them

            float StepZ = 1f / Math.Max(Dims.Z - 1, 1);
            for (int z = 0; z < Dims.Z; z++)
            {
                float CoordZ = z * StepZ;

                #region Extract, correct, and norm particles

                #region Half 1
                {
                    if (originalStack != null)
                        GPU.Extract(originalStack.GetDeviceSlice(z, Intent.Read),
                                    FrameParticles1.GetDevice(Intent.Write),
                                    Dims.Slice(),
                                    DimsPadded,
                                    Helper.ToInterleaved(Origins1.Select(v => new int3(v.X - DimsPadded.X / 2, v.Y - DimsPadded.Y / 2, 0)).ToArray()),
                                    (uint)NParticles1);

                    // Shift particles
                    {
                        float3[] Shifts = new float3[NParticles1];

                        for (int i = 0; i < NParticles1; i++)
                        {
                            float3 Coords = new float3((float)Origins1[i].X / Dims.X, (float)Origins1[i].Y / Dims.Y, CoordZ);
                            Shifts[i] = ResidualShifts1[i] + new float3(GetShiftFromPyramid(Coords)) * 1.00f;
                        }
                        FrameParticles1.ShiftSlices(Shifts);
                    }

                    Image FrameParticlesCropped = FrameParticles1.AsPadded(new int2(DimsRegion));
                    Image FrameParticlesCorrected = FrameParticlesCropped.AsAnisotropyCorrected(new int2(DimsRegion),
                                                                                                PixelSize + PixelDelta / 2f,
                                                                                                PixelSize - PixelDelta / 2f,
                                                                                                PixelAngle,
                                                                                                6);
                    FrameParticlesCropped.Dispose();

                    GPU.NormParticles(FrameParticlesCorrected.GetDevice(Intent.Read),
                                      FrameParticlesCorrected.GetDevice(Intent.Write),
                                      DimsRegion,
                                      (uint)(particleradius / PixelSize),
                                      true,
                                      (uint)NParticles1);

                    float[][] FrameParticlesCorrectedData = FrameParticlesCorrected.GetHost(Intent.Read);
                    for (int n = 0; n < NParticles1; n++)
                    {
                        ParticleStackData[z * NParticles + n] = FrameParticlesCorrectedData[n];
                        ParticleStackData1[z * NParticles1 + n] = FrameParticlesCorrectedData[n];
                    }

                    //FrameParticlesCorrected.WriteMRC("intermediate_particles1.mrc");

                    FrameParticlesCorrected.Dispose();
                }
                #endregion

                #region Half 2
                {
                    if (originalStack != null)
                        GPU.Extract(originalStack.GetDeviceSlice(z, Intent.Read),
                                    FrameParticles2.GetDevice(Intent.Write),
                                    Dims.Slice(),
                                    DimsPadded,
                                    Helper.ToInterleaved(Origins2.Select(v => new int3(v.X - DimsPadded.X / 2, v.Y - DimsPadded.Y / 2, 0)).ToArray()),
                                    (uint)NParticles2);

                    // Shift particles
                    {
                        float3[] Shifts = new float3[NParticles2];

                        for (int i = 0; i < NParticles2; i++)
                        {
                            float3 Coords = new float3((float)Origins2[i].X / Dims.X, (float)Origins2[i].Y / Dims.Y, CoordZ);
                            Shifts[i] = ResidualShifts2[i] + new float3(GetShiftFromPyramid(Coords)) * 1.00f;
                        }
                        FrameParticles2.ShiftSlices(Shifts);
                    }

                    Image FrameParticlesCropped = FrameParticles2.AsPadded(new int2(DimsRegion));
                    Image FrameParticlesCorrected = FrameParticlesCropped.AsAnisotropyCorrected(new int2(DimsRegion),
                                                                                                PixelSize + PixelDelta / 2f,
                                                                                                PixelSize - PixelDelta / 2f,
                                                                                                PixelAngle,
                                                                                                6);
                    FrameParticlesCropped.Dispose();

                    GPU.NormParticles(FrameParticlesCorrected.GetDevice(Intent.Read),
                                      FrameParticlesCorrected.GetDevice(Intent.Write),
                                      DimsRegion,
                                      (uint)(particleradius / PixelSize),
                                      true,
                                      (uint)NParticles2);

                    float[][] FrameParticlesCorrectedData = FrameParticlesCorrected.GetHost(Intent.Read);
                    for (int n = 0; n < NParticles2; n++)
                    {
                        ParticleStackData[z * NParticles + NParticles1 + n] = FrameParticlesCorrectedData[n];
                        ParticleStackData2[z * NParticles2 + n] = FrameParticlesCorrectedData[n];
                    }

                    //FrameParticlesCorrected.WriteMRC("intermediate_particles2.mrc");

                    FrameParticlesCorrected.Dispose();
                }
                #endregion

                #endregion
                
                #region PS Half 1
                {
                    Image PS = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles1), true);
                    PS.Fill(1f);

                    // Apply motion blur filter.

                    #region Motion blur weighting

                    {
                        const int Samples = 11;
                        float StartZ = (z - 0.5f) * StepZ;
                        float StopZ = (z + 0.5f) * StepZ;

                        float2[] Shifts = new float2[Samples * NParticles1];
                        for (int p = 0; p < NParticles1; p++)
                        {
                            float NormX = (float)Origins1[p].X / Dims.X;
                            float NormY = (float)Origins1[p].Y / Dims.Y;

                            for (int zz = 0; zz < Samples; zz++)
                            {
                                float zp = StartZ + (StopZ - StartZ) / (Samples - 1) * zz;
                                float3 Coords = new float3(NormX, NormY, zp);
                                Shifts[p * Samples + zz] = GetShiftFromPyramid(Coords) * 1.00f;
                            }
                        }

                        Image MotionFilter = new Image(IntPtr.Zero, new int3(DimsRegion.X, DimsRegion.Y, NParticles1), true);
                        GPU.CreateMotionBlur(MotionFilter.GetDevice(Intent.Write),
                                             DimsRegion,
                                             Helper.ToInterleaved(Shifts.Select(v => new float3(v.X, v.Y, 0)).ToArray()),
                                             Samples,
                                             (uint)NParticles1);
                        PS.Multiply(MotionFilter);
                        //MotionFilter.WriteMRC("motion.mrc");
                        MotionFilter.Dispose();
                    }

                    #endregion

                    float[][] PSData = PS.GetHost(Intent.Read);
                    for (int n = 0; n < NParticles1; n++)
                        PSStackData[z * NParticles + n] = PSData[n];

                    //PS.WriteMRC("intermediate_ps1.mrc");

                    PS.Dispose();
                }
                #endregion

                #region PS Half 2
                {
                    Image PS = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles2), true);
                    PS.Fill(1f);

                    // Apply motion blur filter.

                    #region Motion blur weighting

                    {
                        const int Samples = 11;
                        float StartZ = (z - 0.5f) * StepZ;
                        float StopZ = (z + 0.5f) * StepZ;

                        float2[] Shifts = new float2[Samples * NParticles2];
                        for (int p = 0; p < NParticles2; p++)
                        {
                            float NormX = (float)Origins2[p].X / Dims.X;
                            float NormY = (float)Origins2[p].Y / Dims.Y;

                            for (int zz = 0; zz < Samples; zz++)
                            {
                                float zp = StartZ + (StopZ - StartZ) / (Samples - 1) * zz;
                                float3 Coords = new float3(NormX, NormY, zp);
                                Shifts[p * Samples + zz] = GetShiftFromPyramid(Coords) * 1.00f;
                            }
                        }

                        Image MotionFilter = new Image(IntPtr.Zero, new int3(DimsRegion.X, DimsRegion.Y, NParticles2), true);
                        GPU.CreateMotionBlur(MotionFilter.GetDevice(Intent.Write),
                                             DimsRegion,
                                             Helper.ToInterleaved(Shifts.Select(v => new float3(v.X, v.Y, 0)).ToArray()),
                                             Samples,
                                             (uint)NParticles2);
                        PS.Multiply(MotionFilter);
                        //MotionFilter.WriteMRC("motion.mrc");
                        MotionFilter.Dispose();
                    }

                    #endregion

                    float[][] PSData = PS.GetHost(Intent.Read);
                    for (int n = 0; n < NParticles2; n++)
                        PSStackData[z * NParticles + NParticles1 + n] = PSData[n];

                    //PS.WriteMRC("intermediate_ps2.mrc");

                    PS.Dispose();
                }
                #endregion
            }
            FrameParticles1.Dispose();
            FrameParticles2.Dispose();
            originalStack.FreeDevice();

            #endregion

            HeaderMRC ParticlesHeader = new HeaderMRC
            {
                Pixelsize = new float3(PixelSize, PixelSize, PixelSize)
            };

            // Do translation and rotation BFGS per particle
            {
                float MaxHigh = 2.6f;

                CubicGrid GridX = new CubicGrid(new int3(NParticles1, 1, 2));
                CubicGrid GridY = new CubicGrid(new int3(NParticles1, 1, 2));
                CubicGrid GridRot = new CubicGrid(new int3(NParticles1, 1, 2));
                CubicGrid GridTilt = new CubicGrid(new int3(NParticles1, 1, 2));
                CubicGrid GridPsi = new CubicGrid(new int3(NParticles1, 1, 2));

                int2 DimsCropped = new int2(DimsRegion / (MaxHigh / PixelSize / 2f)) / 2 * 2;

                #region Get coordinates for CTF and Fourier-space shifts
                Image CTFCoords;
                Image ShiftFactors;
                {
                    float2[] CTFCoordsData = new float2[(DimsCropped.X / 2 + 1) * DimsCropped.Y];
                    float2[] ShiftFactorsData = new float2[(DimsCropped.X / 2 + 1) * DimsCropped.Y];
                    for (int y = 0; y < DimsCropped.Y; y++)
                        for (int x = 0; x < DimsCropped.X / 2 + 1; x++)
                        {
                            int xx = x;
                            int yy = y < DimsCropped.Y / 2 + 1 ? y : y - DimsCropped.Y;

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

                            CTFCoordsData[y * (DimsCropped.X / 2 + 1) + x] = new float2(r / PixelSize, angle);
                            ShiftFactorsData[y * (DimsCropped.X / 2 + 1) + x] = new float2((float)-xx / DimsRegion.X * 2f * (float)Math.PI,
                                                                                          (float)-yy / DimsRegion.X * 2f * (float)Math.PI);
                        }

                    CTFCoords = new Image(CTFCoordsData, new int3(DimsCropped), true);
                    ShiftFactors = new Image(ShiftFactorsData, new int3(DimsCropped), true);
                }
                #endregion

                #region Get inverse sigma2 spectrum for this micrograph from Relion's model.star
                Image Sigma2Noise = new Image(new int3(DimsCropped), true);
                {
                    int GroupNumber = int.Parse(tableIn.GetRowValue(RowIndices[0], "rlnGroupNumber"));
                    //Star SigmaTable = new Star("D:\\rado27\\Refine3D\\run1_ct5_it009_half1_model.star", "data_model_group_" + GroupNumber);
                    Star SigmaTable = new Star(MainWindow.Options.ModelStarPath, "data_model_group_" + GroupNumber);
                    float[] SigmaValues = SigmaTable.GetColumn("rlnSigma2Noise").Select(v => float.Parse(v)).ToArray();

                    float[] Sigma2NoiseData = Sigma2Noise.GetHost(Intent.Write)[0];
                    Helper.ForEachElementFT(DimsCropped, (x, y, xx, yy, r, angle) =>
                    {
                        int ir = (int)r;
                        float val = 0;
                        if (ir < SigmaValues.Length && ir >= size / (50f / PixelSize) && ir < DimsCropped.X / 2)
                        {
                            if (SigmaValues[ir] != 0f)
                                val = 1f / SigmaValues[ir];
                        }
                        Sigma2NoiseData[y * (DimsCropped.X / 2 + 1) + x] = val;
                    });
                    float MaxSigma = MathHelper.Max(Sigma2NoiseData);
                    for (int i = 0; i < Sigma2NoiseData.Length; i++)
                        Sigma2NoiseData[i] /= MaxSigma;

                    Sigma2Noise.RemapToFT();
                }
                //Sigma2Noise.WriteMRC("d_sigma2noise.mrc");
                #endregion

                #region Initialize particle angles for both halves

                float3[] ParticleAngles1 = new float3[NParticles1];
                float3[] ParticleAngles2 = new float3[NParticles2];
                for (int p = 0; p < NParticles1; p++)
                    ParticleAngles1[p] = new float3(float.Parse(tableIn.GetRowValue(RowIndices1[p], "rlnAngleRot")),
                                                    float.Parse(tableIn.GetRowValue(RowIndices1[p], "rlnAngleTilt")),
                                                    float.Parse(tableIn.GetRowValue(RowIndices1[p], "rlnAnglePsi")));
                for (int p = 0; p < NParticles2; p++)
                    ParticleAngles2[p] = new float3(float.Parse(tableIn.GetRowValue(RowIndices2[p], "rlnAngleRot")),
                                                    float.Parse(tableIn.GetRowValue(RowIndices2[p], "rlnAngleTilt")),
                                                    float.Parse(tableIn.GetRowValue(RowIndices2[p], "rlnAnglePsi")));
                #endregion

                #region Prepare masks
                Image Masks1, Masks2;
                {
                    // Half 1
                    {
                        Image Volume = StageDataLoad.LoadMap(MainWindow.Options.MaskPath, new int2(1, 1), 0, typeof (float));
                        Image VolumePadded = Volume.AsPadded(Volume.Dims * MainWindow.Options.ProjectionOversample);
                        Volume.Dispose();
                        VolumePadded.RemapToFT(true);
                        Image VolMaskFT = VolumePadded.AsFFT(true);
                        VolumePadded.Dispose();

                        Image MasksFT = VolMaskFT.AsProjections(ParticleAngles1.Select(v => new float3(v.X * Helper.ToRad, v.Y * Helper.ToRad, v.Z * Helper.ToRad)).ToArray(),
                                                                new int2(DimsRegion),
                                                                MainWindow.Options.ProjectionOversample);
                        VolMaskFT.Dispose();

                        Masks1 = MasksFT.AsIFFT();
                        MasksFT.Dispose();

                        Masks1.RemapFromFT();

                        Parallel.ForEach(Masks1.GetHost(Intent.ReadWrite), slice =>
                        {
                            for (int i = 0; i < slice.Length; i++)
                                slice[i] = (Math.Max(2f, Math.Min(50f, slice[i])) - 2) / 48f;
                        });
                    }

                    // Half 2
                    {
                        Image Volume = StageDataLoad.LoadMap(MainWindow.Options.MaskPath, new int2(1, 1), 0, typeof(float));
                        Image VolumePadded = Volume.AsPadded(Volume.Dims * MainWindow.Options.ProjectionOversample);
                        Volume.Dispose();
                        VolumePadded.RemapToFT(true);
                        Image VolMaskFT = VolumePadded.AsFFT(true);
                        VolumePadded.Dispose();

                        Image MasksFT = VolMaskFT.AsProjections(ParticleAngles2.Select(v => new float3(v.X * Helper.ToRad, v.Y * Helper.ToRad, v.Z * Helper.ToRad)).ToArray(),
                                                                new int2(DimsRegion),
                                                                MainWindow.Options.ProjectionOversample);
                        VolMaskFT.Dispose();

                        Masks2 = MasksFT.AsIFFT();
                        MasksFT.Dispose();

                        Masks2.RemapFromFT();

                        Parallel.ForEach(Masks2.GetHost(Intent.ReadWrite), slice =>
                        {
                            for (int i = 0; i < slice.Length; i++)
                                slice[i] = (Math.Max(2f, Math.Min(50f, slice[i])) - 2) / 48f;
                        });
                    }
                }
                //Masks1.WriteMRC("d_masks1.mrc");
                //Masks2.WriteMRC("d_masks2.mrc");
                #endregion

                #region Load and prepare references for both halves
                Image VolRefFT1;
                {
                    Image Volume = StageDataLoad.LoadMap(MainWindow.Options.ReferencePath, new int2(1, 1), 0, typeof(float));
                    //GPU.Normalize(Volume.GetDevice(Intent.Read), Volume.GetDevice(Intent.Write), (uint)Volume.ElementsReal, 1);
                    Image VolumePadded = Volume.AsPadded(Volume.Dims * MainWindow.Options.ProjectionOversample);
                    Volume.Dispose();
                    VolumePadded.RemapToFT(true);
                    VolRefFT1 = VolumePadded.AsFFT(true);
                    VolumePadded.Dispose();
                }
                VolRefFT1.FreeDevice();

                Image VolRefFT2;
                {
                    // Can't assume there is a second half, but certainly hope so
                    string Half2Path = MainWindow.Options.ReferencePath;
                    if (Half2Path.Contains("half1"))
                        Half2Path = Half2Path.Replace("half1", "half2");

                    Image Volume = StageDataLoad.LoadMap(Half2Path, new int2(1, 1), 0, typeof(float));
                    //GPU.Normalize(Volume.GetDevice(Intent.Read), Volume.GetDevice(Intent.Write), (uint)Volume.ElementsReal, 1);
                    Image VolumePadded = Volume.AsPadded(Volume.Dims * MainWindow.Options.ProjectionOversample);
                    Volume.Dispose();
                    VolumePadded.RemapToFT(true);
                    VolRefFT2 = VolumePadded.AsFFT(true);
                    VolumePadded.Dispose();
                }
                VolRefFT2.FreeDevice();
                #endregion

                #region Prepare particles: group and resize to DimsCropped

                Image ParticleStackFT1 = new Image(IntPtr.Zero, new int3(DimsCropped.X, DimsCropped.Y, NParticles1 * Dims.Z / 3), true, true);
                {
                    GPU.CreatePolishing(ParticleStack1.GetDevice(Intent.Read),
                                        ParticleStackFT1.GetDevice(Intent.Write),
                                        Masks1.GetDevice(Intent.Read),
                                        new int2(DimsRegion),
                                        DimsCropped,
                                        NParticles1,
                                        Dims.Z);

                    ParticleStack1.FreeDevice();
                    Masks1.Dispose();

                    /*Image Amps = ParticleStackFT1.AsIFFT();
                    Amps.RemapFromFT();
                    Amps.WriteMRC("d_particlestackft1.mrc");
                    Amps.Dispose();*/
                }

                Image ParticleStackFT2 = new Image(IntPtr.Zero, new int3(DimsCropped.X, DimsCropped.Y, NParticles2 * Dims.Z / 3), true, true);
                {
                    GPU.CreatePolishing(ParticleStack2.GetDevice(Intent.Read),
                                        ParticleStackFT2.GetDevice(Intent.Write),
                                        Masks2.GetDevice(Intent.Read),
                                        new int2(DimsRegion),
                                        DimsCropped,
                                        NParticles2,
                                        Dims.Z);

                    ParticleStack1.FreeDevice();
                    Masks2.Dispose();

                    /*Image Amps = ParticleStackFT2.AsIFFT();
                    Amps.RemapFromFT();
                    Amps.WriteMRC("d_particlestackft2.mrc");
                    Amps.Dispose();*/
                }
                #endregion

                Image Projections1 = new Image(IntPtr.Zero, new int3(DimsCropped.X, DimsCropped.Y, NParticles1 * Dims.Z / 3), true, true);
                Image Projections2 = new Image(IntPtr.Zero, new int3(DimsCropped.X, DimsCropped.Y, NParticles2 * Dims.Z / 3), true, true);

                Image Shifts1 = new Image(new int3(NParticles1, Dims.Z / 3, 1), false, true);
                float3[] Angles1 = new float3[NParticles1 * Dims.Z / 3];
                CTFStruct[] CTFParams1 = new CTFStruct[NParticles1 * Dims.Z / 3];

                Image Shifts2 = new Image(new int3(NParticles2, Dims.Z / 3, 1), false, true);
                float3[] Angles2 = new float3[NParticles2 * Dims.Z / 3];
                CTFStruct[] CTFParams2 = new CTFStruct[NParticles2 * Dims.Z / 3];

                float[] BFacs =
                {
                    -3.86f,
                    0.00f,
                    -17.60f,
                    -35.24f,
                    -57.48f,
                    -93.51f,
                    -139.57f,
                    -139.16f
                };

                #region Initialize defocus and phase shift values
                float[] InitialDefoci1 = new float[NParticles1 * (Dims.Z / 3)];
                float[] InitialPhaseShifts1 = new float[NParticles1 * (Dims.Z / 3)];
                float[] InitialDefoci2 = new float[NParticles2 * (Dims.Z / 3)];
                float[] InitialPhaseShifts2 = new float[NParticles2 * (Dims.Z / 3)];
                for (int z = 0, i = 0; z < Dims.Z / 3; z++)
                {
                    for (int p = 0; p < NParticles1; p++, i++)
                    {
                        InitialDefoci1[i] = GridCTF.GetInterpolated(new float3((float)Origins1[p].X / Dims.X,
                                                                               (float)Origins1[p].Y / Dims.Y,
                                                                               (float)(z * 3 + 1) / (Dims.Z - 1)));
                        InitialPhaseShifts1[i] = GridCTFPhase.GetInterpolated(new float3((float)Origins1[p].X / Dims.X,
                                                                                         (float)Origins1[p].Y / Dims.Y,
                                                                                         (float)(z * 3 + 1) / (Dims.Z - 1)));

                        CTF Alt = CTF.GetCopy();
                        Alt.PixelSize = (decimal)PixelSize;
                        Alt.PixelSizeDelta = 0;
                        Alt.Defocus = (decimal)InitialDefoci1[i];
                        Alt.PhaseShift = (decimal)InitialPhaseShifts1[i];
                        //Alt.Bfactor = (decimal)BFacs[z];

                        CTFParams1[i] = Alt.ToStruct();
                    }
                }
                for (int z = 0, i = 0; z < Dims.Z / 3; z++)
                {
                    for (int p = 0; p < NParticles2; p++, i++)
                    {
                        InitialDefoci2[i] = GridCTF.GetInterpolated(new float3((float)Origins2[p].X / Dims.X,
                                                                               (float)Origins2[p].Y / Dims.Y,
                                                                               (float)(z * 3 + 1) / (Dims.Z - 1)));
                        InitialPhaseShifts2[i] = GridCTFPhase.GetInterpolated(new float3((float)Origins2[p].X / Dims.X,
                                                                                         (float)Origins2[p].Y / Dims.Y,
                                                                                         (float)(z * 3 + 1) / (Dims.Z - 1)));

                        CTF Alt = CTF.GetCopy();
                        Alt.PixelSize = (decimal)PixelSize;
                        Alt.PixelSizeDelta = 0;
                        Alt.Defocus = (decimal)InitialDefoci2[i];
                        Alt.PhaseShift = (decimal)InitialPhaseShifts2[i];
                        //Alt.Bfactor = (decimal)BFacs[z];

                        CTFParams2[i] = Alt.ToStruct();
                    }
                }
                #endregion

                #region SetPositions lambda
                Action<double[]> SetPositions = input =>
                {
                    float BorderZ = 0.5f / (Dims.Z / 3);

                    GridX = new CubicGrid(new int3(NParticles, 1, 2), input.Take(NParticles * 2).Select(v => (float)v).ToArray());
                    GridY = new CubicGrid(new int3(NParticles, 1, 2), input.Skip(NParticles * 2 * 1).Take(NParticles * 2).Select(v => (float)v).ToArray());

                    float[] AlteredX = GridX.GetInterpolatedNative(new int3(NParticles, 1, Dims.Z / 3), new float3(0, 0, BorderZ));
                    float[] AlteredY = GridY.GetInterpolatedNative(new int3(NParticles, 1, Dims.Z / 3), new float3(0, 0, BorderZ));

                    GridRot = new CubicGrid(new int3(NParticles, 1, 2), input.Skip(NParticles * 2 * 2).Take(NParticles * 2).Select(v => (float)v).ToArray());
                    GridTilt = new CubicGrid(new int3(NParticles, 1, 2), input.Skip(NParticles * 2 * 3).Take(NParticles * 2).Select(v => (float)v).ToArray());
                    GridPsi = new CubicGrid(new int3(NParticles, 1, 2), input.Skip(NParticles * 2 * 4).Take(NParticles * 2).Select(v => (float)v).ToArray());

                    float[] AlteredRot = GridRot.GetInterpolatedNative(new int3(NParticles, 1, Dims.Z / 3), new float3(0, 0, BorderZ));
                    float[] AlteredTilt = GridTilt.GetInterpolatedNative(new int3(NParticles, 1, Dims.Z / 3), new float3(0, 0, BorderZ));
                    float[] AlteredPsi = GridPsi.GetInterpolatedNative(new int3(NParticles, 1, Dims.Z / 3), new float3(0, 0, BorderZ));

                    float[] ShiftData1 = Shifts1.GetHost(Intent.Write)[0];
                    float[] ShiftData2 = Shifts2.GetHost(Intent.Write)[0];

                    for (int z = 0; z < Dims.Z / 3; z++)
                    {
                        // Half 1
                        for (int p = 0; p < NParticles1; p++)
                        {
                            int i1 = z * NParticles1 + p;
                            int i = z * NParticles + p;
                            ShiftData1[i1 * 2] = AlteredX[i];
                            ShiftData1[i1 * 2 + 1] = AlteredY[i];

                            Angles1[i1] = new float3(AlteredRot[i] * 1f * Helper.ToRad, AlteredTilt[i] * 1f * Helper.ToRad, AlteredPsi[i] * 1f * Helper.ToRad);
                        }

                        // Half 2
                        for (int p = 0; p < NParticles2; p++)
                        {
                            int i2 = z * NParticles2 + p;
                            int i = z * NParticles + NParticles1 + p;
                            ShiftData2[i2 * 2] = AlteredX[i];
                            ShiftData2[i2 * 2 + 1] = AlteredY[i];

                            Angles2[i2] = new float3(AlteredRot[i] * 1f * Helper.ToRad, AlteredTilt[i] * 1f * Helper.ToRad, AlteredPsi[i] * 1f * Helper.ToRad);
                        }
                    }
                };
                #endregion

                #region EvalIndividuals lambda
                Func<double[], bool, double[]> EvalIndividuals = (input, redoProj) =>
                {
                    SetPositions(input);

                    if (redoProj)
                    {
                        GPU.ProjectForward(VolRefFT1.GetDevice(Intent.Read),
                                           Projections1.GetDevice(Intent.Write),
                                           VolRefFT1.Dims,
                                           DimsCropped,
                                           Helper.ToInterleaved(Angles1),
                                           MainWindow.Options.ProjectionOversample,
                                           (uint)(NParticles1 * Dims.Z / 3));

                        GPU.ProjectForward(VolRefFT2.GetDevice(Intent.Read),
                                           Projections2.GetDevice(Intent.Write),
                                           VolRefFT2.Dims,
                                           DimsCropped,
                                           Helper.ToInterleaved(Angles2),
                                           MainWindow.Options.ProjectionOversample,
                                           (uint)(NParticles2 * Dims.Z / 3));
                    }

                    /*{
                        Image ProjectionsAmps = Projections1.AsIFFT();
                        ProjectionsAmps.RemapFromFT();
                        ProjectionsAmps.WriteMRC("d_projectionsamps1.mrc");
                        ProjectionsAmps.Dispose();
                    }
                    {
                        Image ProjectionsAmps = Projections2.AsIFFT();
                        ProjectionsAmps.RemapFromFT();
                        ProjectionsAmps.WriteMRC("d_projectionsamps2.mrc");
                        ProjectionsAmps.Dispose();
                    }*/

                    float[] Diff1 = new float[NParticles1];
                    float[] DiffAll1 = new float[NParticles1 * (Dims.Z / 3)];
                    GPU.PolishingGetDiff(ParticleStackFT1.GetDevice(Intent.Read),
                                         Projections1.GetDevice(Intent.Read),
                                         ShiftFactors.GetDevice(Intent.Read),
                                         CTFCoords.GetDevice(Intent.Read),
                                         CTFParams1,
                                         Sigma2Noise.GetDevice(Intent.Read),
                                         DimsCropped,
                                         Shifts1.GetDevice(Intent.Read),
                                         Diff1,
                                         DiffAll1,
                                         (uint)NParticles1,
                                         (uint)Dims.Z / 3);

                    float[] Diff2 = new float[NParticles2];
                    float[] DiffAll2 = new float[NParticles2 * (Dims.Z / 3)];
                    GPU.PolishingGetDiff(ParticleStackFT2.GetDevice(Intent.Read),
                                         Projections2.GetDevice(Intent.Read),
                                         ShiftFactors.GetDevice(Intent.Read),
                                         CTFCoords.GetDevice(Intent.Read),
                                         CTFParams2,
                                         Sigma2Noise.GetDevice(Intent.Read),
                                         DimsCropped,
                                         Shifts2.GetDevice(Intent.Read),
                                         Diff2,
                                         DiffAll2,
                                         (uint)NParticles2,
                                         (uint)Dims.Z / 3);

                    double[] DiffBoth = new double[NParticles];
                    for (int p = 0; p < NParticles1; p++)
                        DiffBoth[p] = Diff1[p];
                    for (int p = 0; p < NParticles2; p++)
                        DiffBoth[NParticles1 + p] = Diff2[p];

                    return DiffBoth;
                };
                #endregion

                Func<double[], double> Eval = input =>
                {
                    float Result = MathHelper.Mean(EvalIndividuals(input, true).Select(v => (float)v)) * NParticles;
                    Debug.WriteLine(Result);
                    return Result;
                };

                Func<double[], double[]> Grad = input =>
                {
                    SetPositions(input);

                    GPU.ProjectForward(VolRefFT1.GetDevice(Intent.Read),
                                       Projections1.GetDevice(Intent.Write),
                                       VolRefFT1.Dims,
                                       DimsCropped,
                                       Helper.ToInterleaved(Angles1),
                                       MainWindow.Options.ProjectionOversample,
                                       (uint)(NParticles1 * Dims.Z / 3));

                    GPU.ProjectForward(VolRefFT2.GetDevice(Intent.Read),
                                       Projections2.GetDevice(Intent.Write),
                                       VolRefFT2.Dims,
                                       DimsCropped,
                                       Helper.ToInterleaved(Angles2),
                                       MainWindow.Options.ProjectionOversample,
                                       (uint)(NParticles2 * Dims.Z / 3));

                    double[] Result = new double[input.Length];

                    double Step = 0.1;
                    int NVariables = 10;    // (Shift + Euler) * 2
                    for (int v = 0; v < NVariables; v++)
                    {
                        double[] InputPlus = new double[input.Length];
                        for (int i = 0; i < input.Length; i++)
                        {
                            int iv = i / NParticles;

                            if (iv == v)
                                InputPlus[i] = input[i] + Step;
                            else
                                InputPlus[i] = input[i];
                        }
                        double[] ScorePlus = EvalIndividuals(InputPlus, v >= 4);

                        double[] InputMinus = new double[input.Length];
                        for (int i = 0; i < input.Length; i++)
                        {
                            int iv = i / NParticles;

                            if (iv == v)
                                InputMinus[i] = input[i] - Step;
                            else
                                InputMinus[i] = input[i];
                        }
                        double[] ScoreMinus = EvalIndividuals(InputMinus, v >= 4);

                        for (int i = 0; i < NParticles; i++)
                            Result[v * NParticles + i] = (ScorePlus[i] - ScoreMinus[i]) / (Step * 2.0);
                    }

                    return Result;
                };

                double[] StartParams = new double[NParticles * 2 * 5];
                
                for (int i = 0; i < NParticles * 2; i++)
                {
                    int p = i % NParticles;
                    StartParams[NParticles * 2 * 0 + i] = 0;
                    StartParams[NParticles * 2 * 1 + i] = 0;

                    if (p < NParticles1)
                    {
                        StartParams[NParticles * 2 * 2 + i] = ParticleAngles1[p].X / 1.0;
                        StartParams[NParticles * 2 * 3 + i] = ParticleAngles1[p].Y / 1.0;
                        StartParams[NParticles * 2 * 4 + i] = ParticleAngles1[p].Z / 1.0;
                    }
                    else
                    {
                        p -= NParticles1;
                        StartParams[NParticles * 2 * 2 + i] = ParticleAngles2[p].X / 1.0;
                        StartParams[NParticles * 2 * 3 + i] = ParticleAngles2[p].Y / 1.0;
                        StartParams[NParticles * 2 * 4 + i] = ParticleAngles2[p].Z / 1.0;
                    }
                }

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

                #region Calculate particle quality for high frequencies
                float[] ParticleQuality = new float[NParticles * (Dims.Z / 3)];
                {
                    Sigma2Noise.Dispose();
                    Sigma2Noise = new Image(new int3(DimsCropped), true);
                    {
                        int GroupNumber = int.Parse(tableIn.GetRowValue(RowIndices[0], "rlnGroupNumber"));
                        //Star SigmaTable = new Star("D:\\rado27\\Refine3D\\run1_ct5_it009_half1_model.star", "data_model_group_" + GroupNumber);
                        Star SigmaTable = new Star(MainWindow.Options.ModelStarPath, "data_model_group_" + GroupNumber);
                        float[] SigmaValues = SigmaTable.GetColumn("rlnSigma2Noise").Select(v => float.Parse(v)).ToArray();

                        float[] Sigma2NoiseData = Sigma2Noise.GetHost(Intent.Write)[0];
                        Helper.ForEachElementFT(DimsCropped, (x, y, xx, yy, r, angle) =>
                        {
                            int ir = (int)r;
                            float val = 0;
                            if (ir < SigmaValues.Length && ir >= size / (4.0f / PixelSize) && ir < DimsCropped.X / 2)
                            {
                                if (SigmaValues[ir] != 0f)
                                    val = 1f / SigmaValues[ir] / (ir * 3.14f);
                            }
                            Sigma2NoiseData[y * (DimsCropped.X / 2 + 1) + x] = val;
                        });
                        float MaxSigma = MathHelper.Max(Sigma2NoiseData);
                        for (int i = 0; i < Sigma2NoiseData.Length; i++)
                            Sigma2NoiseData[i] /= MaxSigma;

                        Sigma2Noise.RemapToFT();
                    }
                    //Sigma2Noise.WriteMRC("d_sigma2noiseScore.mrc");

                    SetPositions(StartParams);

                    GPU.ProjectForward(VolRefFT1.GetDevice(Intent.Read),
                                       Projections1.GetDevice(Intent.Write),
                                       VolRefFT1.Dims,
                                       DimsCropped,
                                       Helper.ToInterleaved(Angles1),
                                       MainWindow.Options.ProjectionOversample,
                                       (uint)(NParticles1 * Dims.Z / 3));

                    GPU.ProjectForward(VolRefFT2.GetDevice(Intent.Read),
                                       Projections2.GetDevice(Intent.Write),
                                       VolRefFT2.Dims,
                                       DimsCropped,
                                       Helper.ToInterleaved(Angles2),
                                       MainWindow.Options.ProjectionOversample,
                                       (uint)(NParticles2 * Dims.Z / 3));

                    float[] Diff1 = new float[NParticles1];
                    float[] ParticleQuality1 = new float[NParticles1 * (Dims.Z / 3)];
                    GPU.PolishingGetDiff(ParticleStackFT1.GetDevice(Intent.Read),
                                         Projections1.GetDevice(Intent.Read),
                                         ShiftFactors.GetDevice(Intent.Read),
                                         CTFCoords.GetDevice(Intent.Read),
                                         CTFParams1,
                                         Sigma2Noise.GetDevice(Intent.Read),
                                         DimsCropped,
                                         Shifts1.GetDevice(Intent.Read),
                                         Diff1,
                                         ParticleQuality1,
                                         (uint)NParticles1,
                                         (uint)Dims.Z / 3);

                    float[] Diff2 = new float[NParticles2];
                    float[] ParticleQuality2 = new float[NParticles2 * (Dims.Z / 3)];
                    GPU.PolishingGetDiff(ParticleStackFT2.GetDevice(Intent.Read),
                                         Projections2.GetDevice(Intent.Read),
                                         ShiftFactors.GetDevice(Intent.Read),
                                         CTFCoords.GetDevice(Intent.Read),
                                         CTFParams2,
                                         Sigma2Noise.GetDevice(Intent.Read),
                                         DimsCropped,
                                         Shifts2.GetDevice(Intent.Read),
                                         Diff2,
                                         ParticleQuality2,
                                         (uint)NParticles2,
                                         (uint)Dims.Z / 3);

                    for (int z = 0; z < Dims.Z / 3; z++)
                    {
                        for (int p = 0; p < NParticles1; p++)
                            ParticleQuality[z * NParticles + p] = ParticleQuality1[z * NParticles1 + p];

                        for (int p = 0; p < NParticles2; p++)
                            ParticleQuality[z * NParticles + NParticles1 + p] = ParticleQuality2[z * NParticles2 + p];
                    }
                }
                #endregion

                lock (tableOut)     // Only changing cell values, but better be safe in case table implementation changes later
                {
                    GridX = new CubicGrid(new int3(NParticles, 1, 2), Optimizer.Solution.Take(NParticles * 2).Select(v => (float)v).ToArray());
                    GridY = new CubicGrid(new int3(NParticles, 1, 2), Optimizer.Solution.Skip(NParticles * 2 * 1).Take(NParticles * 2).Select(v => (float)v).ToArray());
                    float[] AlteredX = GridX.GetInterpolated(new int3(NParticles, 1, Dims.Z), new float3(0, 0, 0));
                    float[] AlteredY = GridY.GetInterpolated(new int3(NParticles, 1, Dims.Z), new float3(0, 0, 0));

                    GridRot = new CubicGrid(new int3(NParticles, 1, 2), Optimizer.Solution.Skip(NParticles * 2 * 2).Take(NParticles * 2).Select(v => (float)v).ToArray());
                    GridTilt = new CubicGrid(new int3(NParticles, 1, 2), Optimizer.Solution.Skip(NParticles * 2 * 3).Take(NParticles * 2).Select(v => (float)v).ToArray());
                    GridPsi = new CubicGrid(new int3(NParticles, 1, 2), Optimizer.Solution.Skip(NParticles * 2 * 4).Take(NParticles * 2).Select(v => (float)v).ToArray());
                    float[] AlteredRot = GridRot.GetInterpolated(new int3(NParticles, 1, Dims.Z), new float3(0, 0, 0));
                    float[] AlteredTilt = GridTilt.GetInterpolated(new int3(NParticles, 1, Dims.Z), new float3(0, 0, 0));
                    float[] AlteredPsi = GridPsi.GetInterpolated(new int3(NParticles, 1, Dims.Z), new float3(0, 0, 0));
                    
                    for (int i = 0; i < TableOutIndices.Count; i++)
                    {
                        int p = i % NParticles;
                        int z = i / NParticles;
                        float Defocus = 0, PhaseShift = 0;

                        if (p < NParticles1)
                        {
                            Defocus = GridCTF.GetInterpolated(new float3((float)Origins1[p].X / Dims.X,
                                                                         (float)Origins1[p].Y / Dims.Y,
                                                                         (float)z / (Dims.Z - 1)));
                            PhaseShift = GridCTFPhase.GetInterpolated(new float3((float)Origins1[p].X / Dims.X,
                                                                                 (float)Origins1[p].Y / Dims.Y,
                                                                                 (float)z / (Dims.Z - 1)));
                        }
                        else
                        {
                            p -= NParticles1;
                            Defocus = GridCTF.GetInterpolated(new float3((float)Origins2[p].X / Dims.X,
                                                                         (float)Origins2[p].Y / Dims.Y,
                                                                         (float)z / (Dims.Z - 1)));
                            PhaseShift = GridCTFPhase.GetInterpolated(new float3((float)Origins2[p].X / Dims.X,
                                                                                 (float)Origins2[p].Y / Dims.Y,
                                                                                 (float)z / (Dims.Z - 1)));
                        }

                        tableOut.SetRowValue(TableOutIndices[i], "rlnOriginX", AlteredX[i].ToString(CultureInfo.InvariantCulture));
                        tableOut.SetRowValue(TableOutIndices[i], "rlnOriginY", AlteredY[i].ToString(CultureInfo.InvariantCulture));
                        tableOut.SetRowValue(TableOutIndices[i], "rlnAngleRot", (-AlteredRot[i]).ToString(CultureInfo.InvariantCulture));
                        tableOut.SetRowValue(TableOutIndices[i], "rlnAngleTilt", (-AlteredTilt[i]).ToString(CultureInfo.InvariantCulture));
                        tableOut.SetRowValue(TableOutIndices[i], "rlnAnglePsi", (-AlteredPsi[i]).ToString(CultureInfo.InvariantCulture));
                        tableOut.SetRowValue(TableOutIndices[i], "rlnDefocusU", ((Defocus + (float)CTF.DefocusDelta / 2f) * 1e4f).ToString(CultureInfo.InvariantCulture));
                        tableOut.SetRowValue(TableOutIndices[i], "rlnDefocusV", ((Defocus - (float)CTF.DefocusDelta / 2f) * 1e4f).ToString(CultureInfo.InvariantCulture));
                        tableOut.SetRowValue(TableOutIndices[i], "rlnPhaseShift", (PhaseShift * 180f).ToString(CultureInfo.InvariantCulture));
                        tableOut.SetRowValue(TableOutIndices[i], "rlnCtfFigureOfMerit", (ParticleQuality[(z / 3) * NParticles + (i % NParticles)]).ToString(CultureInfo.InvariantCulture));

                        tableOut.SetRowValue(TableOutIndices[i], "rlnMagnification", ((float)MainWindow.Options.CTFDetectorPixel * 10000f / PixelSize).ToString());
                    }
                }

                VolRefFT1.Dispose();
                VolRefFT2.Dispose();
                Projections1.Dispose();
                Projections2.Dispose();
                Sigma2Noise.Dispose();
                ParticleStackFT1.Dispose();
                ParticleStackFT2.Dispose();
                Shifts1.Dispose();
                Shifts2.Dispose();
                CTFCoords.Dispose();
                ShiftFactors.Dispose();

                ParticleStack1.Dispose();
                ParticleStack2.Dispose();
                PSStack1.Dispose();
                PSStack2.Dispose();
            }
            
            // Write movies to disk asynchronously, so the next micrograph can load.
            Thread SaveThread = new Thread(() =>
            {
                GPU.SetDevice(CurrentDevice);   // It's a separate thread, make sure it's using the same device

                ParticleStackAll.WriteMRC(ParticleMoviesPath, ParticlesHeader);
                //ParticleStackAll.WriteMRC("D:\\gala\\particlemovies\\" + RootName + "_particles.mrcs", ParticlesHeader);
                ParticleStackAll.Dispose();

                PSStackAll.WriteMRC(ParticleCTFMoviesPath);
                //PSStackAll.WriteMRC("D:\\rado27\\particlectfmovies\\" + RootName + "_particlectf.mrcs");
                PSStackAll.Dispose();
            });
            SaveThread.Start();
        }
Example #10
0
        public void ExportParticles(Star tableIn, Star tableOut, MapHeader originalHeader, Image originalStack, int size, float particleradius, decimal scaleFactor)
        {
            if (!tableIn.HasColumn("rlnAutopickFigureOfMerit"))
                tableIn.AddColumn("rlnAutopickFigureOfMerit");

            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;

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

            int3 Dims = originalHeader.Dimensions;
            int3 DimsRegion = new int3(size, size, 1);
            int3 DimsPadded = new int3(size * 2, size * 2, 1);
            int NParticles = RowIndices.Count;

            float PixelSize = (float)CTF.PixelSize;
            float PixelDelta = (float)CTF.PixelSizeDelta;
            float PixelAngle = (float)CTF.PixelSizeAngle * Helper.ToRad;
            /*Image CTFCoords;
            Image CTFFreq;
            {
                float2[] CTFCoordsData = new float2[(DimsRegion.X / 2 + 1) * DimsRegion.Y];
                float[] CTFFreqData = new float[(DimsRegion.X / 2 + 1) * DimsRegion.Y];
                for (int y = 0; y < DimsRegion.Y; y++)
                    for (int x = 0; x < DimsRegion.X / 2 + 1; x++)
                    {
                        int xx = x;
                        int yy = y < DimsRegion.Y / 2 + 1 ? y : y - DimsRegion.Y;

                        float xs = xx / (float)DimsRegion.X;
                        float ys = yy / (float)DimsRegion.Y;
                        float r = (float)Math.Sqrt(xs * xs + ys * ys);
                        float angle = (float)(Math.Atan2(yy, xx));
                        float CurrentPixelSize = PixelSize + PixelDelta * (float)Math.Cos(2f * (angle - PixelAngle));

                        CTFCoordsData[y * (DimsRegion.X / 2 + 1) + x] = new float2(r / DimsRegion.X, angle);
                        CTFFreqData[y * (DimsRegion.X / 2 + 1) + x] = r / CurrentPixelSize;
                    }

                CTFCoords = new Image(CTFCoordsData, DimsRegion.Slice(), true);
                CTFFreq = new Image(CTFFreqData, DimsRegion.Slice(), true);
            }*/

            string[] ColumnPosX = tableIn.GetColumn("rlnCoordinateX");
            string[] ColumnPosY = tableIn.GetColumn("rlnCoordinateY");
            string[] ColumnOriginX = tableIn.GetColumn("rlnOriginX");
            string[] ColumnOriginY = tableIn.GetColumn("rlnOriginY");
            int3[] Origins = new int3[NParticles];
            float3[] ResidualShifts = new float3[NParticles];

            for (int i = 0; i < NParticles; i++)
            {
                float2 Pos = new float2(float.Parse(ColumnPosX[RowIndices[i]], CultureInfo.InvariantCulture),
                                        float.Parse(ColumnPosY[RowIndices[i]], CultureInfo.InvariantCulture)) * 1.00f;
                float2 Shift = new float2(float.Parse(ColumnOriginX[RowIndices[i]], CultureInfo.InvariantCulture),
                                          float.Parse(ColumnOriginY[RowIndices[i]], CultureInfo.InvariantCulture)) * 1.00f;

                Origins[i] = new int3((int)(Pos.X - Shift.X),
                                      (int)(Pos.Y - Shift.Y),
                                      0);
                ResidualShifts[i] = new float3(-MathHelper.ResidualFraction(Pos.X - Shift.X),
                                               -MathHelper.ResidualFraction(Pos.Y - Shift.Y),
                                               0f);

                tableIn.SetRowValue(RowIndices[i], "rlnCoordinateX", Origins[i].X.ToString());
                tableIn.SetRowValue(RowIndices[i], "rlnCoordinateY", Origins[i].Y.ToString());
                tableIn.SetRowValue(RowIndices[i], "rlnOriginX", "0.0");
                tableIn.SetRowValue(RowIndices[i], "rlnOriginY", "0.0");
            }

            Image AverageFT = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles), true, true);
            Image AveragePS = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles), true);
            Image Weights = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles), true);
            Weights.Fill(1e-6f);
            Image FrameParticles = new Image(IntPtr.Zero, new int3(DimsPadded.X, DimsPadded.Y, NParticles));

            float StepZ = 1f / Math.Max(Dims.Z - 1, 1);
            for (int z = 0; z < Dims.Z; z++)
            {
                float CoordZ = z * StepZ;

                if (originalStack != null)
                    GPU.Extract(originalStack.GetDeviceSlice(z, Intent.Read),
                                FrameParticles.GetDevice(Intent.Write),
                                Dims.Slice(),
                                DimsPadded,
                                Helper.ToInterleaved(Origins.Select(v => new int3(v.X - DimsPadded.X / 2, v.Y - DimsPadded.Y / 2, 0)).ToArray()),
                                (uint)NParticles);

                // Shift particles
                {
                    float3[] Shifts = new float3[NParticles];

                    for (int i = 0; i < NParticles; i++)
                    {
                        float3 Coords = new float3((float)Origins[i].X / Dims.X, (float)Origins[i].Y / Dims.Y, CoordZ);
                        Shifts[i] = ResidualShifts[i] + new float3(GetShiftFromPyramid(Coords)) * 1.00f;
                    }
                    FrameParticles.ShiftSlices(Shifts);
                }

                Image FrameParticlesCropped = FrameParticles.AsPadded(new int2(DimsRegion));
                Image FrameParticlesFT = FrameParticlesCropped.AsFFT();
                FrameParticlesCropped.Dispose();

                //Image PS = new Image(new int3(DimsRegion.X, DimsRegion.Y, NParticles), true);
                //PS.Fill(1f);

                // Apply motion blur filter.

                #region Motion blur weighting

                /*{
                    const int Samples = 11;
                    float StartZ = (z - 0.5f) * StepZ;
                    float StopZ = (z + 0.5f) * StepZ;

                    float2[] Shifts = new float2[Samples * NParticles];
                    for (int p = 0; p < NParticles; p++)
                    {
                        float NormX = (float)Origins[p].X / Dims.X;
                        float NormY = (float)Origins[p].Y / Dims.Y;

                        for (int zz = 0; zz < Samples; zz++)
                        {
                            float zp = StartZ + (StopZ - StartZ) / (Samples - 1) * zz;
                            float3 Coords = new float3(NormX, NormY, zp);
                            Shifts[p * Samples + zz] = GetShiftFromPyramid(Coords);
                        }
                    }

                    Image MotionFilter = new Image(IntPtr.Zero, new int3(DimsRegion.X, DimsRegion.Y, NParticles), true);
                    GPU.CreateMotionBlur(MotionFilter.GetDevice(Intent.Write),
                                         DimsRegion,
                                         Helper.ToInterleaved(Shifts.Select(v => new float3(v.X, v.Y, 0)).ToArray()),
                                         Samples,
                                         (uint)NParticles);
                    PS.Multiply(MotionFilter);
                    //MotionFilter.WriteMRC("motion.mrc");
                    MotionFilter.Dispose();
                }*/

                #endregion

                // Apply CTF.

                #region CTF weighting

                /*if (CTF != null)
                {
                    CTFStruct[] Structs = new CTFStruct[NParticles];
                    for (int p = 0; p < NParticles; p++)
                    {
                        CTF Altered = CTF.GetCopy();
                        Altered.Defocus = (decimal)GridCTF.GetInterpolated(new float3(Origins[p].X / Dims.X,
                                                                                      Origins[p].Y / Dims.Y,
                                                                                      z * StepZ));

                        Structs[p] = Altered.ToStruct();
                    }

                    Image CTFImage = new Image(IntPtr.Zero, new int3(DimsRegion.X, DimsRegion.Y, NParticles), true);
                    GPU.CreateCTF(CTFImage.GetDevice(Intent.Write),
                                  CTFCoords.GetDevice(Intent.Read),
                                  (uint)CTFCoords.ElementsSliceComplex,
                                  Structs,
                                  false,
                                  (uint)NParticles);

                    //CTFImage.Abs();
                    PS.Multiply(CTFImage);
                    //CTFImage.WriteMRC("ctf.mrc");
                    CTFImage.Dispose();
                }*/

                #endregion

                // Apply dose.

                #region 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 * (z + 0.5f) / (PixelSize * PixelSize);

                    Image DoseImage = new Image(IntPtr.Zero, DimsRegion, true);
                    GPU.DoseWeighting(CTFFreq.GetDevice(Intent.Read),
                                      DoseImage.GetDevice(Intent.Write),
                                      (uint)DoseImage.ElementsSliceComplex,
                                      new[] { FrameDose },
                                      NikoConst,
                                      1);
                    PS.MultiplySlices(DoseImage);
                    //DoseImage.WriteMRC("dose.mrc");
                    DoseImage.Dispose();
                }*/
                #endregion

                //Image PSAbs = new Image(PS.GetDevice(Intent.Read), new int3(DimsRegion.X, DimsRegion.Y, NParticles), true);
                //PSAbs.Abs();

                //FrameParticlesFT.Multiply(PS);
                AverageFT.Add(FrameParticlesFT);

                //Weights.Add(PSAbs);

                //PS.Multiply(PS);
                //AveragePS.Add(PS);

                //PS.Dispose();
                FrameParticlesFT.Dispose();
                //PSAbs.Dispose();
            }
            FrameParticles.Dispose();
            //CTFCoords.Dispose();

            //AverageFT.Divide(Weights);
            //AveragePS.Divide(Weights);
            //AverageFT.Multiply(AveragePS);
            Weights.Dispose();

            Image AverageParticlesUncorrected = AverageFT.AsIFFT();
            AverageFT.Dispose();

            Image AverageParticles = AverageParticlesUncorrected.AsAnisotropyCorrected(new int2(DimsRegion),
                                                                                       (float)(CTF.PixelSize + CTF.PixelSizeDelta / 2M),
                                                                                       (float)(CTF.PixelSize - CTF.PixelSizeDelta / 2M),
                                                                                       (float)CTF.PixelSizeAngle * Helper.ToRad,
                                                                                       8);
            AverageParticlesUncorrected.Dispose();

            GPU.NormParticles(AverageParticles.GetDevice(Intent.Read),
                              AverageParticles.GetDevice(Intent.Write),
                              DimsRegion,
                              (uint)(particleradius / (PixelSize / 1.00f)),
                              true,
                              (uint)NParticles);

            HeaderMRC ParticlesHeader = new HeaderMRC
            {
                Pixelsize = new float3(PixelSize, PixelSize, PixelSize)
            };

            AverageParticles.WriteMRC(ParticlesPath, ParticlesHeader);
            AverageParticles.Dispose();

            //AveragePS.WriteMRC(ParticleCTFPath, ParticlesHeader);
            AveragePS.Dispose();

            float[] DistanceWeights = new float[NParticles];
            for (int p1 = 0; p1 < NParticles - 1; p1++)
            {
                float2 Pos1 = new float2(Origins[p1].X, Origins[p1].Y);

                for (int p2 = p1 + 1; p2 < NParticles; p2++)
                {
                    float2 Pos2 = new float2(Origins[p2].X, Origins[p2].Y);
                    float2 Diff = Pos2 - Pos1;
                    float Dist = Diff.X * Diff.X + Diff.Y * Diff.Y;
                    Dist = 1f / Dist;

                    DistanceWeights[p1] += Dist;
                    DistanceWeights[p2] += Dist;
                }
            }

            for (int i = 0; i < NParticles; i++)
            {
                string ParticlePath = (i + 1).ToString("D6") + "@particles/" + RootName + "_particles.mrcs";
                tableIn.SetRowValue(RowIndices[i], "rlnImageName", ParticlePath);

                //string ParticleCTFsPath = (i + 1).ToString("D6") + "@particlectf/" + RootName + "_particlectf.mrcs";
                //tableIn.SetRowValue(RowIndices[i], "rlnCtfImage", ParticleCTFsPath);

                tableIn.SetRowValue(RowIndices[i], "rlnAutopickFigureOfMerit", DistanceWeights[i].ToString(CultureInfo.InvariantCulture));
            }
        }
Example #11
0
        public void WriteMRC(string path, HeaderMRC header = null)
        {
            if (header == null)
                header = new HeaderMRC();
            header.Dimensions = IsFT ? DimsFT : Dims;
            header.Dimensions.X *= IsComplex ? 2 : 1;

            float[][] Data = GetHost(Intent.Read);
            float Min = float.MaxValue, Max = -float.MaxValue;
            Parallel.For(0, Data.Length, z =>
            {
                float LocalMin = MathHelper.Min(Data[z]);
                float LocalMax = MathHelper.Max(Data[z]);
                lock (Data)
                {
                    Min = Math.Min(LocalMin, Min);
                    Max = Math.Max(LocalMax, Max);
                }
            });
            header.MinValue = Min;
            header.MaxValue = Max;

            IOHelper.WriteMapFloat(path, header, GetHost(Intent.Read));
        }