// -------------- Average and Maximum -------------- // This method that takes YData instead of YDataFloat is by far the fastest method. public static void CalculateAverageAndMaximumCPU(YData imageData, out float average, out float maximum) { const float LOW_ACCURACY_THRESHOLD = 64.0f; const int LOW_ACCURACY_DIVISOR = 8; int width = imageData.Width; int height = imageData.Height; // Calculate average and maximum average = 0.0f; maximum = 0.0f; var averageSlices = new int[height]; var averageCounts = new int[height]; var maximumSlices = new byte[height]; int loopCount = height / LOW_ACCURACY_DIVISOR; Parallel.For(0, loopCount, (loop, state) => { for (int y = loop * LOW_ACCURACY_DIVISOR; y < (loop + 1) * LOW_ACCURACY_DIVISOR && (y < height); y++) { unsafe { fixed(byte *pDataByte = imageData.Data) { var pData = (int *)pDataByte; // Tried long* instead of int* and it made little difference int ySrcOffset = (y * imageData.Stride) / sizeof(int); int xMax = width / sizeof(int); int avg = 0; int count = 0; byte max = 0; for (int x = 0; x < xMax; x++) { var color = pData[ySrcOffset++]; byte value; value = (byte)(color & 0xFF); avg += value; if (max < value) { max = value; } value = (byte)((color >> 8) & 0xFF); avg += value; if (max < value) { max = value; } value = (byte)((color >> 16) & 0xFF); avg += value; if (max < value) { max = value; } value = (byte)((color >> 24) & 0xFF); avg += value; if (max < value) { max = value; } count += 4; if (max > LOW_ACCURACY_THRESHOLD) { x += 7; ySrcOffset += 7; } } averageSlices[y] = avg; averageCounts[y] = count; maximumSlices[y] = max; if (max > LOW_ACCURACY_THRESHOLD) { break; } } } } }); average = averageSlices.AsParallel().Sum(); var totalCount = averageCounts.AsParallel().Sum(); average /= (float)totalCount; maximum = maximumSlices.AsParallel().Max(); }
// -------------- Standard Deviation -------------- public static void CalculateStandardDeviationCPU(YData imageData, float average, out float stdDev) { const float LOW_ACCURACY_THRESHOLD = 48.0f; const int LOW_ACCURACY_DIVISOR = 16; int width = imageData.Width; int height = imageData.Height; // Calculate standard deviation stdDev = 0.0f; float[] totalSlices = new float[height]; int[] countSlices = new int[height]; int loopCount = height; if (average > LOW_ACCURACY_THRESHOLD) { loopCount = height / LOW_ACCURACY_DIVISOR; } //for (int y = 0; y < height; y++) Parallel.For(0, loopCount, (y) => { if (average > LOW_ACCURACY_THRESHOLD) { y *= LOW_ACCURACY_DIVISOR; } unsafe { fixed(byte *pDataByte = imageData.Data) { var pData = (int *)pDataByte; // Tried long* instead of int* and it was far worse performance int ySrcOffset = (y * imageData.Stride) / sizeof(int); int xMax = width / sizeof(int); float total = 0.0f; int count = 0; for (int x = 0; x < xMax; x++) { var color = pData[ySrcOffset++]; float value; value = (float)(color & 0xFF); value = (value - average) * (value - average); total += value; value = (float)((color >> 8) & 0xFF); value = (value - average) * (value - average); total += value; value = (float)((color >> 16) & 0xFF); value = (value - average) * (value - average); total += value; value = (float)((color >> 24) & 0xFF); value = (value - average) * (value - average); total += value; count += 4; if (average > LOW_ACCURACY_THRESHOLD) { x += (LOW_ACCURACY_DIVISOR - 1); ySrcOffset += (LOW_ACCURACY_DIVISOR - 1); } } totalSlices[y] = total; countSlices[y] = count; } } }); stdDev = totalSlices.AsParallel().Sum(); var totalCount = countSlices.AsParallel().Sum(); stdDev /= (float)totalCount; stdDev = (float)Math.Sqrt(stdDev); }