diff --git a/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs b/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs
index 99959e24..1c4b20db 100644
--- a/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs
+++ b/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs
@@ -34,6 +34,17 @@ public class FrameDiffAnalyser : FrameAnalyser
private byte[] _mask;
private Stopwatch _testFrameAge;
+ private int[] _cellDiff;
+ private Rectangle[] _cellRect;
+ private byte[] _workingData;
+
+ ///
+ /// Controls how many cells the frames are divided into. The result is a power of two of this
+ /// value (so the default of 32 yields 1024 cells). These cells are processed in parallel. This
+ /// should be a value that divides evenly into the X and Y resolutions of the motion stream.
+ ///
+ public int CellDivisor { get; set; } = 32;
+
internal Action OnDetect { get; set; }
///
@@ -121,9 +132,30 @@ private void PrepareTestFrame()
_frameBpp = this.GetBpp() / 8;
_frameStride = this.ImageContext.Stride;
+ // one-time setup of the diff cell parameters and arrays
+ int indices = (int)Math.Pow(CellDivisor, 2);
+ int cellWidth = _frameWidth / CellDivisor;
+ int cellHeight = _frameHeight / CellDivisor;
+ int i = 0;
+
+ _cellRect = new Rectangle[indices];
+ _cellDiff = new int[indices];
+
+ for (int row = 0; row < CellDivisor; row++)
+ {
+ int y = row * cellHeight;
+
+ for (int col = 0; col < CellDivisor; col++)
+ {
+ int x = col * cellWidth;
+ _cellRect[i] = new Rectangle(x, y, cellWidth, cellHeight);
+ i++;
+ }
+ }
+
this.TestFrame = this.WorkingData.ToArray();
- if(!string.IsNullOrWhiteSpace(this.MotionConfig.MotionMaskPathname))
+ if (!string.IsNullOrWhiteSpace(this.MotionConfig.MotionMaskPathname))
{
this.PrepareMask();
}
@@ -218,48 +250,40 @@ private void CheckForChanges(Action onDetect)
private int Analyse()
{
- var quadA = new Rectangle(0, 0, _frameWidth / 2, _frameHeight / 2);
- var quadB = new Rectangle(_frameWidth / 2, 0, _frameWidth / 2, _frameHeight / 2);
- var quadC = new Rectangle(0, _frameHeight / 2, _frameWidth / 2, _frameHeight / 2);
- var quadD = new Rectangle(_frameWidth / 2, _frameHeight / 2, _frameWidth / 2, _frameHeight / 2);
+ _workingData = this.WorkingData.ToArray();
- var currentBytes = this.WorkingData.ToArray();
+ var result = Parallel.ForEach(_cellDiff, (cell, loopState, loopIndex) => CheckDiff(loopIndex, loopState));
- int diff = 0;
-
- var t1 = Task.Run(() =>
- {
- diff += this.CheckDiff(quadA, currentBytes);
- });
- var t2 = Task.Run(() =>
+ // How Parallel Stop works: https://docs.microsoft.com/en-us/previous-versions/msp-n-p/ff963552(v=pandp.10)#parallel-stop
+ if (!result.IsCompleted && !result.LowestBreakIteration.HasValue)
{
- diff += this.CheckDiff(quadB, currentBytes);
- });
- var t3 = Task.Run(() =>
- {
- diff += this.CheckDiff(quadC, currentBytes);
- });
- var t4 = Task.Run(() =>
+ return int.MaxValue; // loop was stopped, so return a large diff
+ }
+ else
{
- diff += this.CheckDiff(quadD, currentBytes);
- });
+ int diff = 0;
- Task.WaitAll(t1, t2, t3, t4);
-
- return diff;
+ foreach (var cellDiff in _cellDiff)
+ {
+ diff += cellDiff;
+ }
+
+ return diff;
+ }
}
- private int CheckDiff(Rectangle quad, byte[] currentFrame)
+ private void CheckDiff(long cellIndex, ParallelLoopState loopState)
{
int diff = 0;
+ var rect = _cellRect[cellIndex];
- for (int column = quad.X; column < quad.X + quad.Width; column++)
+ for (int col = rect.X; col < rect.X + rect.Width; col++)
{
- for (int row = quad.Y; row < quad.Y + quad.Height; row++)
+ for (int row = rect.Y; row < rect.Y + rect.Height; row++)
{
- var index = (column * _frameBpp) + (row * _frameStride);
+ var index = (col * _frameBpp) + (row * _frameStride);
- if(_mask != null)
+ if (_mask != null)
{
var rgbMask = _mask[index] + _mask[index + 1] + _mask[index + 2];
@@ -270,28 +294,31 @@ private int CheckDiff(Rectangle quad, byte[] currentFrame)
}
var rgb1 = TestFrame[index] + TestFrame[index + 1] + TestFrame[index + 2];
-
- var rgb2 = currentFrame[index] + currentFrame[index + 1] + currentFrame[index + 2];
+ var rgb2 = _workingData[index] + _workingData[index + 1] + _workingData[index + 2];
if (rgb2 - rgb1 > MotionConfig.Threshold)
{
diff++;
}
- // If the threshold has been exceeded, we want to exit from this method immediately for performance reasons.
+ // If the threshold has been exceeded, exit immediately and preempt any CheckDiff calls not yet started.
if (diff > MotionConfig.Threshold)
{
- return diff;
+ _cellDiff[cellIndex] = diff;
+ loopState.Stop();
+ return;
}
}
if (diff > MotionConfig.Threshold)
{
- return diff;
+ _cellDiff[cellIndex] = diff;
+ loopState.Stop();
+ return;
}
}
- return diff;
+ _cellDiff[cellIndex] = diff;
}
}
}