// В качестве радиальной функции чаще всего применяется функция Гаусса. При размещении ее центра в точке она может быть определена в сокращенной форме как: public double Gaussian(double[] input) { Func <int, double> func = j => { var numerator = input[j] - Centroids[j]; numerator *= numerator; var denominator = Widths[j] * Widths[j]; return(numerator / denominator); }; //для каждого центра считаем функцию гаусса, потом суммируем результаты var sum = Centroids.Select((c, j) => func(j)).Sum(); return(Math.Pow(Math.E, -0.5 * sum)); }
public float4[] Match(Image volume, float diameterAngstrom, float threshold, Func <int3, int, string, bool> progressCallback) { float PixelSizeBN = PixelSize; int3 DimsRegionBN = CubeNet.BoxDimensionsPredict; int3 DimsRegionValidBN = CubeNet.BoxDimensionsValidPredict; int BorderBN = (DimsRegionBN.X - DimsRegionValidBN.X) / 2; int3 DimsBN = volume.Dims; GPU.Normalize(volume.GetDevice(Intent.Read), volume.GetDevice(Intent.Write), (uint)volume.ElementsReal, 1); volume.FreeDevice(); float[] Predictions = new float[DimsBN.Elements()]; float[] Mask = new float[DimsBN.Elements()]; { int3 DimsPositions = (DimsBN + DimsRegionValidBN - 1) / DimsRegionValidBN; float3 PositionStep = new float3(DimsBN - DimsRegionBN) / new float3(Math.Max(DimsPositions.X - 1, 1), Math.Max(DimsPositions.Y - 1, 1), Math.Max(DimsPositions.Z - 1, 1)); int NPositions = (int)DimsPositions.Elements(); int3[] Positions = new int3[NPositions]; for (int p = 0; p < NPositions; p++) { int Z = p / (DimsPositions.X * DimsPositions.Y); int Y = (p - Z * DimsPositions.X * DimsPositions.Y) / DimsPositions.X; int X = p % DimsPositions.X; Positions[p] = new int3((int)(X * PositionStep.X + DimsRegionBN.X / 2), (int)(Y * PositionStep.Y + DimsRegionBN.Y / 2), (int)(Z * PositionStep.Z + DimsRegionBN.Z / 2)); } float[][] PredictionTiles = Helper.ArrayOfFunction(i => new float[DimsRegionBN.Elements()], NPositions); int NGPUThreads = MaxThreads; Image[] Extracted = new Image[NGPUThreads]; int DeviceID = GPU.GetDevice(); int BatchesDone = 0; Helper.ForCPU(0, NPositions, NGPUThreads, threadID => { GPU.SetDevice(DeviceID); Extracted[threadID] = new Image(IntPtr.Zero, DimsRegionBN); }, (b, threadID) => { int GPUID = threadID / NGPUThreads; int GPUThreadID = threadID % NGPUThreads; #region Extract and normalize windows GPU.Extract(volume.GetDevice(Intent.Read), Extracted[threadID].GetDevice(Intent.Write), volume.Dims, DimsRegionBN, new int[] { Positions[b].X - DimsRegionBN.X / 2, Positions[b].Y - DimsRegionBN.Y / 2, Positions[b].Z - DimsRegionBN.Z / 2 }, 1); //GPU.Normalize(Extracted[threadID].GetDevice(Intent.Read), // Extracted[threadID].GetDevice(Intent.Write), // (uint)Extracted[threadID].ElementsReal, // 1); #endregion //Extracted[threadID].WriteMRC("d_extracted.mrc", true); #region Predict long[] BatchArgMax; float[] BatchProbability; Predict(Extracted[threadID].GetDevice(Intent.Read), GPUThreadID, out BatchArgMax, out BatchProbability); //new Image(BatchArgMax.Select(v => (float)v).ToArray(), new int3(DimsRegionBN)).WriteMRC("d_labels.mrc", true); for (int i = 0; i < BatchArgMax.Length; i++) { int Label = (int)BatchArgMax[i]; float Probability = BatchProbability[i * NClasses + Label]; PredictionTiles[b][i] = (Label >= 1 && Probability >= threshold ? Probability : 0); } #endregion lock (volume) progressCallback?.Invoke(new int3(NPositions, 1, 1), ++BatchesDone, ""); }, threadID => { int GPUID = threadID / NGPUThreads; int GPUThreadID = threadID % NGPUThreads; Extracted[threadID].Dispose(); }); for (int z = 0; z < DimsBN.Z; z++) { for (int y = 0; y < DimsBN.Y; y++) { for (int x = 0; x < DimsBN.X; x++) { int ClosestX = (int)Math.Max(0, Math.Min(DimsPositions.X - 1, (int)(((float)x - DimsRegionBN.X / 2) / PositionStep.X + 0.5f))); int ClosestY = (int)Math.Max(0, Math.Min(DimsPositions.Y - 1, (int)(((float)y - DimsRegionBN.Y / 2) / PositionStep.Y + 0.5f))); int ClosestZ = (int)Math.Max(0, Math.Min(DimsPositions.Z - 1, (int)(((float)z - DimsRegionBN.Z / 2) / PositionStep.Z + 0.5f))); int ClosestID = (ClosestZ * DimsPositions.Y + ClosestY) * DimsPositions.X + ClosestX; int3 Position = Positions[ClosestID]; int LocalX = Math.Max(0, Math.Min(DimsRegionBN.X - 1, x - Position.X + DimsRegionBN.X / 2)); int LocalY = Math.Max(0, Math.Min(DimsRegionBN.Y - 1, y - Position.Y + DimsRegionBN.Y / 2)); int LocalZ = Math.Max(0, Math.Min(DimsRegionBN.Z - 1, z - Position.Z + DimsRegionBN.Z / 2)); Predictions[(z * DimsBN.Y + y) * DimsBN.X + x] = PredictionTiles[ClosestID][(LocalZ * DimsRegionBN.Y + LocalY) * DimsRegionBN.X + LocalX]; } } } volume.FreeDevice(); } #region Apply Gaussian and find peaks Image PredictionsImage = new Image(Predictions, new int3(DimsBN)); PredictionsImage.WriteMRC("d_predictions.mrc", true); //Image PredictionsConvolved = PredictionsImage.AsConvolvedGaussian((float)options.ExpectedDiameter / PixelSizeBN / 6); //PredictionsConvolved.Multiply(PredictionsImage); //PredictionsImage.Dispose(); //PredictionsImage.WriteMRC(MatchingDir + RootName + "_boxnet.mrc", PixelSizeBN, true); int3[] Peaks = PredictionsImage.GetLocalPeaks((int)(diameterAngstrom / PixelSizeBN / 4 + 0.5f), 1e-6f); PredictionsImage.Dispose(); int BorderDist = (int)(diameterAngstrom / PixelSizeBN * 0.8f + 0.5f); Peaks = Peaks.Where(p => p.X > BorderDist && p.Y > BorderDist && p.Z > BorderDist && p.X < DimsBN.X - BorderDist && p.Y < DimsBN.Y - BorderDist && p.Z < DimsBN.Z - BorderDist).ToArray(); #endregion #region Label connected components and get centroids List <float3> Centroids; List <int> Extents; { List <List <int3> > Components = new List <List <int3> >(); int[] PixelLabels = Helper.ArrayOfConstant(-1, Predictions.Length); foreach (var peak in Peaks) { if (PixelLabels[DimsBN.ElementFromPosition(peak)] >= 0) { continue; } List <int3> Component = new List <int3>() { peak }; int CN = Components.Count; PixelLabels[DimsBN.ElementFromPosition(peak)] = CN; Queue <int3> Expansion = new Queue <int3>(100); Expansion.Enqueue(peak); while (Expansion.Count > 0) { int3 pos = Expansion.Dequeue(); int PosElement = (int)DimsBN.ElementFromPosition(pos); if (pos.X > 0 && Predictions[PosElement - 1] > 0 && PixelLabels[PosElement - 1] < 0) { PixelLabels[PosElement - 1] = CN; Component.Add(pos + new int3(-1, 0, 0)); Expansion.Enqueue(pos + new int3(-1, 0, 0)); } if (pos.X < DimsBN.X - 1 && Predictions[PosElement + 1] > 0 && PixelLabels[PosElement + 1] < 0) { PixelLabels[PosElement + 1] = CN; Component.Add(pos + new int3(1, 0, 0)); Expansion.Enqueue(pos + new int3(1, 0, 0)); } if (pos.Y > 0 && Predictions[PosElement - DimsBN.X] > 0 && PixelLabels[PosElement - DimsBN.X] < 0) { PixelLabels[PosElement - DimsBN.X] = CN; Component.Add(pos + new int3(0, -1, 0)); Expansion.Enqueue(pos + new int3(0, -1, 0)); } if (pos.Y < DimsBN.Y - 1 && Predictions[PosElement + DimsBN.X] > 0 && PixelLabels[PosElement + DimsBN.X] < 0) { PixelLabels[PosElement + DimsBN.X] = CN; Component.Add(pos + new int3(0, 1, 0)); Expansion.Enqueue(pos + new int3(0, 1, 0)); } if (pos.Z > 0 && Predictions[PosElement - DimsBN.X * DimsBN.Y] > 0 && PixelLabels[PosElement - DimsBN.X * DimsBN.Y] < 0) { PixelLabels[PosElement - DimsBN.X * DimsBN.Y] = CN; Component.Add(pos + new int3(0, 0, -1)); Expansion.Enqueue(pos + new int3(0, 0, -1)); } if (pos.Z < DimsBN.Z - 1 && Predictions[PosElement + DimsBN.X * DimsBN.Y] > 0 && PixelLabels[PosElement + DimsBN.X * DimsBN.Y] < 0) { PixelLabels[PosElement + DimsBN.X * DimsBN.Y] = CN; Component.Add(pos + new int3(0, 0, 1)); Expansion.Enqueue(pos + new int3(0, 0, 1)); } } Components.Add(Component); } Centroids = Components.Select(c => MathHelper.Mean(c.Select(v => new float3(v)))).ToList(); Extents = Components.Select(c => c.Count).ToList(); } List <int> ToDelete = new List <int>(); // Hit test with crap mask //for (int c1 = 0; c1 < Centroids.Count; c1++) //{ // float2 P1 = Centroids[c1]; // if (MaskHitTest[(int)P1.Y * DimsBN.X + (int)P1.X] == 0) // { // ToDelete.Add(c1); // continue; // } //} for (int c1 = 0; c1 < Centroids.Count - 1; c1++) { float3 P1 = Centroids[c1]; for (int c2 = c1 + 1; c2 < Centroids.Count; c2++) { if ((P1 - Centroids[c2]).Length() < diameterAngstrom / PixelSizeBN / 1.5f) { int D = Extents[c1] < Extents[c2] ? c1 : c2; if (!ToDelete.Contains(D)) { ToDelete.Add(D); } } } } ToDelete.Sort(); for (int i = ToDelete.Count - 1; i >= 0; i--) { Centroids.RemoveAt(ToDelete[i]); Extents.RemoveAt(ToDelete[i]); } #endregion //new Image(Predictions, DimsBN).WriteMRC("d_predictions.mrc", true); #region Write peak positions and angles into table return(Centroids.Select((c, i) => new float4(c.X, c.Y, c.Z, Extents[i])).ToArray()); #endregion }