public static double Compute(double[] X, GaussianMixtureModel GMM) { // Initialisations int K = GMM.K; double[,] membership = new double[GMM.K, X.Length]; int count = 0; double?logLikelihood = null; while (count++ < 100) { // Expectation - recalculate membership values for all k,x pairs int XLength = X.Length; for (int x = 0; x < XLength; x++) { double total = 0.0; for (int k = 0; k < K; k++) { membership[k, x] = GMM.MixingWeight[k] * GMM.SingleGaussianProbabilityEM(X[x], k); total += membership[k, x]; } for (int k = 0; k < K; k++) { membership[k, x] /= total; } } // Maximisation - recalculate GMM parameters based on the results of the expectation step for (int k = 0; k < K; k++) { // Mean double sumMembership = 0.0; double sumMembershipX = 0.0; for (int x = 0; x < XLength; x++) { sumMembership += membership[k, x]; sumMembershipX += membership[k, x] * X[x]; } GMM.Mean[k] = sumMembershipX / sumMembership; // Sigma double Mean = GMM.Mean[k]; double sumMembershipXMinusMeanSquared = 0.0; for (int x = 0; x < XLength; x++) { sumMembershipXMinusMeanSquared += membership[k, x] * Math.Pow(X[x] - Mean, 2.0); } // Prevent Sigma from going below 1 to avoid S.Ds of 0 GMM.Sigma[k] = Math.Max(Math.Sqrt(sumMembershipXMinusMeanSquared / sumMembership), 1); // Mixing GMM.MixingWeight[k] = sumMembership / XLength; } // Log likelihood double currentLikelihood = 1.0; for (int x = 0; x < XLength; x++) { double value = X[x]; double total = 0.0; for (int k = 0; k < K; k++) { total += GMM.MixingWeight[k] * GMM.SingleGaussianProbabilityEM(value, k); } currentLikelihood += Math.Log(total); } if (logLikelihood == null) { logLikelihood = currentLikelihood; } else { double ratio = currentLikelihood / (double)logLikelihood; logLikelihood = currentLikelihood; if (1 - ratio < 0.0001) { // Convergence break; } } } return(logLikelihood == null ? Double.NaN : (double)logLikelihood); }
protected override void OnDoWork(DoWorkEventArgs e) { List <Tuple <EMPatch, GaussianMixtureModel> > currentMixtures = new List <Tuple <EMPatch, GaussianMixtureModel> >(); int patchProgressCount = 0; int totalPatches = Patches.Count; foreach (EMPatch patch in Patches) { // Check bounds if (patch.Left < 0 || patch.Right > Width || patch.Top < 0 || patch.Bottom > Height) { throw new EMPatch.EMPatchException("Patch falls outside the bounds of the source image"); } // Copy data int patchWidth = patch.Right - patch.Left; int patchHeight = patch.Bottom - patch.Top; int left = patch.Left, right = patch.Right, top = patch.Top, bottom = patch.Bottom; if (this.data == null || this.data.Length != patchWidth * patchHeight) { this.data = new double[patchWidth * patchHeight]; } int i = 0; double max = Double.MinValue, min = Double.MaxValue; for (int pX = left; pX < right; pX++) { for (int pY = top; pY < bottom; pY++) { data[i] = IntensityBuffer[pY * Width + pX]; if (data[i] < min) { min = data[i]; } if (data[i] > max) { max = data[i]; } i += 1; } } GaussianMixtureModel GMM = null; // Run EM starting at the initial class count and increasing the class count if necessary for (int classCount = Configuration.InitialClassCount; classCount <= Configuration.MaximumClassCount; classCount++) { // KMeans initialisation GMM = GaussianMixtureModel.KMeansInitialisation(data, classCount, (int)min, (int)max); // Expectation Maximisation ExpectationMaximisation.Compute(data, GMM); // Threshold GMM.ThresholdAtPercentage(Configuration.BackgroundPercentage, Configuration.BackgroundExcessSigma); // Check remaining classes for none (empty patch) or the correct number of classes if (GMM.K - (GMM.ThresholdK + 1) >= Configuration.ExpectedRootClassCount || GMM.K - (GMM.ThresholdK + 1) == 0) { break; } } // Add Patch, GMM pair into output array if (GMM == null) { throw new InvalidOperationException("GMM cannot be null"); } currentMixtures.Add(new Tuple <EMPatch, GaussianMixtureModel>(patch, GMM)); // Signal progress changed base.OnProgressChanged(new ProgressChangedEventArgs((int)(++patchProgressCount * 100.0 / totalPatches), null)); } // Assign this.Mixtures this.Mixtures = currentMixtures; }