Skip to content
This repository has been archived by the owner on Feb 22, 2024. It is now read-only.

Commit

Permalink
Improved motion perf (#171)
Browse files Browse the repository at this point in the history
* Parallel processing improvements

* prefer atomic array write vs mutable struct

* increase cell divisor to 32 (1024 cells)

* Styling changes.

Co-authored-by: techyian <[email protected]>
  • Loading branch information
MV10 and techyian authored Aug 23, 2020
1 parent e8d3ed8 commit 1f33ad0
Showing 1 changed file with 63 additions and 36 deletions.
99 changes: 63 additions & 36 deletions src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ public class FrameDiffAnalyser : FrameAnalyser
private byte[] _mask;
private Stopwatch _testFrameAge;

private int[] _cellDiff;
private Rectangle[] _cellRect;
private byte[] _workingData;

/// <summary>
/// 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.
/// </summary>
public int CellDivisor { get; set; } = 32;

internal Action OnDetect { get; set; }

/// <summary>
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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];

Expand All @@ -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;
}
}
}

0 comments on commit 1f33ad0

Please sign in to comment.