/// <summary> /// Computes the pixel variances and their average /// </summary> public override void OnEndIteration(int curIteration) { // Update the mean and moment based on the buffered image of the current iteration Parallel.For(0, momentImage.Height, row => { for (int col = 0; col < momentImage.Width; ++col) { float val = bufferImage.GetPixel(col, row); momentImage.AtomicAdd(col, row, val * val / curIteration); meanImage.AtomicAdd(col, row, val / curIteration); } }); // Blur both buffers to get a more stable estimate. // TODO this could be done in-place by directly splatting in multiple pixels above BoxFilter filter = new(1); MonochromeImage blurredMean = new(meanImage.Width, meanImage.Height); MonochromeImage blurredMoment = new(meanImage.Width, meanImage.Height); filter.Apply(meanImage, blurredMean); filter.Apply(momentImage, blurredMoment); // Compute the final variance and update the main image Average = 0; Parallel.For(0, momentImage.Height, row => { for (int col = 0; col < momentImage.Width; ++col) { float mean = blurredMean.GetPixel(col, row); float variance = blurredMoment.GetPixel(col, row) - mean * mean; variance /= (mean * mean + 0.001f); Image.SetPixelChannel(col, row, 0, variance); Atomic.AddFloat(ref Average, variance); } }); Average /= momentImage.Height * momentImage.Width; }