From 25adf0874cb53416a43256714ab024a1855111b1 Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Thu, 20 Aug 2020 19:19:35 -0400 Subject: [PATCH 1/4] Parallel processing improvements --- .../Processors/Motion/FrameDiffAnalyser.cs | 102 +++++++++++------- 1 file changed, 61 insertions(+), 41 deletions(-) diff --git a/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs b/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs index 99959e24..f8842998 100644 --- a/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs +++ b/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs @@ -34,6 +34,22 @@ public class FrameDiffAnalyser : FrameAnalyser private byte[] _mask; private Stopwatch _testFrameAge; + private struct DiffRect + { + internal int diff; + internal Rectangle rect; + } + + private DiffRect[] _cell; + 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 8 yields 64 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; } = 8; + internal Action OnDetect { get; set; } /// @@ -121,9 +137,25 @@ private void PrepareTestFrame() _frameBpp = this.GetBpp() / 8; _frameStride = this.ImageContext.Stride; + // one-time setup of the diff cell parameters and arrays + _cell = new DiffRect[(int)Math.Pow(CellDivisor, 2)]; + int cellWidth = _frameWidth / CellDivisor; + int cellHeight = _frameHeight / CellDivisor; + int i = 0; + for (int row = 0; row < CellDivisor; row++) + { + int y = row * cellHeight; + for (int col = 0; col < CellDivisor; col++) + { + int x = col * cellWidth; + _cell[i].rect = 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,36 @@ 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); - - var currentBytes = this.WorkingData.ToArray(); + _workingData = this.WorkingData.ToArray(); - int diff = 0; + var result = Parallel.ForEach(_cell, (src, loopState) => CheckDiff(src, loopState)); - 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); - }); - - Task.WaitAll(t1, t2, t3, t4); - - return diff; + int diff = 0; + foreach (var cell in _cell) + diff += cell.diff; + return diff; + } } - private int CheckDiff(Rectangle quad, byte[] currentFrame) + private void CheckDiff(DiffRect cell, ParallelLoopState loopState) { - int diff = 0; + cell.diff = 0; + var rect = cell.rect; - 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]; @@ -271,27 +291,27 @@ 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++; + cell.diff++; } - // If the threshold has been exceeded, we want to exit from this method immediately for performance reasons. - if (diff > MotionConfig.Threshold) + // If the threshold has been exceeded, exit immediately and preempt any CheckDiff calls not yet started. + if (cell.diff > MotionConfig.Threshold) { - return diff; + loopState.Stop(); + return; } } - if (diff > MotionConfig.Threshold) + if (cell.diff > MotionConfig.Threshold) { - return diff; + loopState.Stop(); + return; } } - - return diff; } } } From 2c525d84b54e9e7ad5bfe0ea09b38aab0b69fafc Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Fri, 21 Aug 2020 15:15:27 -0400 Subject: [PATCH 2/4] prefer atomic array write vs mutable struct --- .../Processors/Motion/FrameDiffAnalyser.cs | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs b/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs index f8842998..a3c7eddb 100644 --- a/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs +++ b/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs @@ -34,13 +34,8 @@ public class FrameDiffAnalyser : FrameAnalyser private byte[] _mask; private Stopwatch _testFrameAge; - private struct DiffRect - { - internal int diff; - internal Rectangle rect; - } - - private DiffRect[] _cell; + private int[] _cellDiff; + private Rectangle[] _cellRect; private byte[] _workingData; /// @@ -138,7 +133,9 @@ private void PrepareTestFrame() _frameStride = this.ImageContext.Stride; // one-time setup of the diff cell parameters and arrays - _cell = new DiffRect[(int)Math.Pow(CellDivisor, 2)]; + int indices = (int)Math.Pow(CellDivisor, 2); + _cellRect = new Rectangle[indices]; + _cellDiff = new int[indices]; int cellWidth = _frameWidth / CellDivisor; int cellHeight = _frameHeight / CellDivisor; int i = 0; @@ -148,7 +145,7 @@ private void PrepareTestFrame() for (int col = 0; col < CellDivisor; col++) { int x = col * cellWidth; - _cell[i].rect = new Rectangle(x, y, cellWidth, cellHeight); + _cellRect[i] = new Rectangle(x, y, cellWidth, cellHeight); i++; } } @@ -252,7 +249,7 @@ private int Analyse() { _workingData = this.WorkingData.ToArray(); - var result = Parallel.ForEach(_cell, (src, loopState) => CheckDiff(src, loopState)); + var result = Parallel.ForEach(_cellDiff, (cell, loopState, loopIndex) => CheckDiff(loopIndex, loopState)); // 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) @@ -262,16 +259,16 @@ private int Analyse() else { int diff = 0; - foreach (var cell in _cell) - diff += cell.diff; + foreach (var cellDiff in _cellDiff) + diff += cellDiff; return diff; } } - private void CheckDiff(DiffRect cell, ParallelLoopState loopState) + private void CheckDiff(long cellIndex, ParallelLoopState loopState) { - cell.diff = 0; - var rect = cell.rect; + int diff = 0; + var rect = _cellRect[cellIndex]; for (int col = rect.X; col < rect.X + rect.Width; col++) { @@ -295,23 +292,27 @@ private void CheckDiff(DiffRect cell, ParallelLoopState loopState) if (rgb2 - rgb1 > MotionConfig.Threshold) { - cell.diff++; + diff++; } // If the threshold has been exceeded, exit immediately and preempt any CheckDiff calls not yet started. - if (cell.diff > MotionConfig.Threshold) + if (diff > MotionConfig.Threshold) { + _cellDiff[cellIndex] = diff; loopState.Stop(); return; } } - if (cell.diff > MotionConfig.Threshold) + if (diff > MotionConfig.Threshold) { + _cellDiff[cellIndex] = diff; loopState.Stop(); return; } } + + _cellDiff[cellIndex] = diff; } } } From cdd0b5b107f4885dbb3f3b69b6f50882b6cac57e Mon Sep 17 00:00:00 2001 From: Jon McGuire Date: Fri, 21 Aug 2020 15:17:01 -0400 Subject: [PATCH 3/4] increase cell divisor to 32 (1024 cells) --- src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs b/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs index a3c7eddb..30960178 100644 --- a/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs +++ b/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs @@ -43,7 +43,7 @@ public class FrameDiffAnalyser : FrameAnalyser /// value (so the default of 8 yields 64 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; } = 8; + public int CellDivisor { get; set; } = 32; internal Action OnDetect { get; set; } From afe2ffe6986640b474505e4883c25e5ab3b46a34 Mon Sep 17 00:00:00 2001 From: techyian Date: Sat, 22 Aug 2020 14:28:19 +0100 Subject: [PATCH 4/4] Styling changes. --- .../Processors/Motion/FrameDiffAnalyser.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs b/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs index 30960178..1c4b20db 100644 --- a/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs +++ b/src/MMALSharp.Processing/Processors/Motion/FrameDiffAnalyser.cs @@ -40,7 +40,7 @@ public class FrameDiffAnalyser : FrameAnalyser /// /// Controls how many cells the frames are divided into. The result is a power of two of this - /// value (so the default of 8 yields 64 cells). These cells are processed in parallel. 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; @@ -134,14 +134,17 @@ private void PrepareTestFrame() // one-time setup of the diff cell parameters and arrays int indices = (int)Math.Pow(CellDivisor, 2); - _cellRect = new Rectangle[indices]; - _cellDiff = new int[indices]; 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; @@ -259,8 +262,12 @@ private int Analyse() else { int diff = 0; + foreach (var cellDiff in _cellDiff) + { diff += cellDiff; + } + return diff; } } @@ -287,7 +294,6 @@ private void CheckDiff(long cellIndex, ParallelLoopState loopState) } var rgb1 = TestFrame[index] + TestFrame[index + 1] + TestFrame[index + 2]; - var rgb2 = _workingData[index] + _workingData[index + 1] + _workingData[index + 2]; if (rgb2 - rgb1 > MotionConfig.Threshold)