13
13
using Microsoft . Extensions . Logging ;
14
14
using MMALSharp . Common ;
15
15
using MMALSharp . Common . Utility ;
16
- using MMALSharp . Processors . Effects ;
17
16
18
17
namespace MMALSharp . Processors . Motion
19
18
{
@@ -75,12 +74,7 @@ public override void Apply(ImageContext context)
75
74
if ( context . Eos )
76
75
{
77
76
this . FullTestFrame = true ;
78
- MMALLog . Logger . LogDebug ( "EOS reached for test frame. Applying edge detection." ) ;
79
-
80
- // We want to apply Edge Detection to the test frame to make it easier to detect changes.
81
- var edgeDetection = new EdgeDetection ( this . MotionConfig . Sensitivity ) ;
82
- this . ImageContext . Data = this . TestFrame . ToArray ( ) ;
83
- edgeDetection . ApplyConvolution ( edgeDetection . Kernel , EdgeDetection . KernelWidth , EdgeDetection . KernelHeight , this . ImageContext ) ;
77
+ MMALLog . Logger . LogDebug ( "EOS reached for test frame." ) ;
84
78
}
85
79
}
86
80
@@ -92,19 +86,27 @@ public override void Apply(ImageContext context)
92
86
this . CheckForChanges ( this . OnDetect ) ;
93
87
}
94
88
}
95
-
96
- private void CheckForChanges ( Action onDetect )
89
+
90
+ /// <summary>
91
+ /// Resets the test and working frames this analyser is using.
92
+ /// </summary>
93
+ public void ResetAnalyser ( )
97
94
{
98
- var edgeDetection = new EdgeDetection ( EDStrength . Medium ) ;
99
- this . ImageContext . Data = this . WorkingData . ToArray ( ) ;
100
- edgeDetection . ApplyConvolution ( EdgeDetection . MediumStrengthKernel , 3 , 3 , this . ImageContext ) ;
101
- var diff = this . Analyse ( ) ;
95
+ this . TestFrame = new List < byte > ( ) ;
96
+ this . WorkingData = new List < byte > ( ) ;
97
+ this . FullFrame = false ;
98
+ this . FullTestFrame = false ;
99
+ }
102
100
103
- MMALLog . Logger . LogDebug ( $ "Diff size: { diff } ") ;
101
+ private void CheckForChanges ( Action onDetect )
102
+ {
103
+ this . PrepareDifferenceImage ( this . ImageContext , this . MotionConfig . Threshold ) ;
104
104
105
+ var diff = this . Analyse ( ) ;
106
+
105
107
if ( diff >= this . MotionConfig . Threshold )
106
108
{
107
- MMALLog . Logger . LogInformation ( "Motion detected!" ) ;
109
+ MMALLog . Logger . LogInformation ( $ "Motion detected! Frame difference { diff } . ") ;
108
110
onDetect ( ) ;
109
111
}
110
112
}
@@ -115,11 +117,7 @@ private Bitmap LoadBitmap(MemoryStream stream)
115
117
{
116
118
PixelFormat format = default ;
117
119
118
- if ( this . ImageContext . PixelFormat == MMALEncoding . RGB16 )
119
- {
120
- format = PixelFormat . Format16bppRgb565 ;
121
- }
122
-
120
+ // RGB16 doesn't appear to be supported by GDI?
123
121
if ( this . ImageContext . PixelFormat == MMALEncoding . RGB24 )
124
122
{
125
123
format = PixelFormat . Format24bppRgb ;
@@ -198,11 +196,114 @@ private int Analyse()
198
196
199
197
testBmp . UnlockBits ( testBmpData ) ;
200
198
currentBmp . UnlockBits ( currentBmpData ) ;
201
-
199
+
202
200
return diff ;
203
201
}
204
202
}
205
203
204
+ private void PrepareDifferenceImage ( ImageContext context , int threshold )
205
+ {
206
+ BitmapData bmpData = null ;
207
+ IntPtr pNative = IntPtr . Zero ;
208
+ int bytes ;
209
+ byte [ ] store = null ;
210
+
211
+ using ( var ms = new MemoryStream ( context . Data ) )
212
+ using ( var bmp = this . LoadBitmap ( ms ) )
213
+ {
214
+ bmpData = bmp . LockBits ( new Rectangle ( 0 , 0 ,
215
+ bmp . Width ,
216
+ bmp . Height ) ,
217
+ ImageLockMode . ReadWrite ,
218
+ bmp . PixelFormat ) ;
219
+
220
+ if ( context . Raw )
221
+ {
222
+ this . InitBitmapData ( bmpData , ms . ToArray ( ) ) ;
223
+ }
224
+
225
+ pNative = bmpData . Scan0 ;
226
+
227
+ // Split image into 4 quadrants and process individually.
228
+ var quadA = new Rectangle ( 0 , 0 , bmpData . Width / 2 , bmpData . Height / 2 ) ;
229
+ var quadB = new Rectangle ( bmpData . Width / 2 , 0 , bmpData . Width / 2 , bmpData . Height / 2 ) ;
230
+ var quadC = new Rectangle ( 0 , bmpData . Height / 2 , bmpData . Width / 2 , bmpData . Height / 2 ) ;
231
+ var quadD = new Rectangle ( bmpData . Width / 2 , bmpData . Height / 2 , bmpData . Width / 2 , bmpData . Height / 2 ) ;
232
+
233
+ bytes = bmpData . Stride * bmp . Height ;
234
+
235
+ var rgbValues = new byte [ bytes ] ;
236
+
237
+ // Copy the RGB values into the array.
238
+ Marshal . Copy ( pNative , rgbValues , 0 , bytes ) ;
239
+
240
+ var bpp = Image . GetPixelFormatSize ( bmp . PixelFormat ) / 8 ;
241
+
242
+ var t1 = Task . Run ( ( ) =>
243
+ {
244
+ this . ApplyThreshold ( quadA , bmpData , bpp , threshold ) ;
245
+ } ) ;
246
+ var t2 = Task . Run ( ( ) =>
247
+ {
248
+ this . ApplyThreshold ( quadB , bmpData , bpp , threshold ) ;
249
+ } ) ;
250
+ var t3 = Task . Run ( ( ) =>
251
+ {
252
+ this . ApplyThreshold ( quadC , bmpData , bpp , threshold ) ;
253
+ } ) ;
254
+ var t4 = Task . Run ( ( ) =>
255
+ {
256
+ this . ApplyThreshold ( quadD , bmpData , bpp , threshold ) ;
257
+ } ) ;
258
+
259
+ Task . WaitAll ( t1 , t2 , t3 , t4 ) ;
260
+
261
+ if ( context . Raw )
262
+ {
263
+ store = new byte [ bytes ] ;
264
+ Marshal . Copy ( pNative , store , 0 , bytes ) ;
265
+ }
266
+
267
+ bmp . UnlockBits ( bmpData ) ;
268
+ }
269
+
270
+ context . Data = store ;
271
+ }
272
+
273
+ private void ApplyThreshold ( Rectangle quad , BitmapData bmpData , int pixelDepth , int threshold )
274
+ {
275
+ unsafe
276
+ {
277
+ // Declare an array to hold the bytes of the bitmap.
278
+ var stride = bmpData . Stride ;
279
+
280
+ byte * ptr1 = ( byte * ) bmpData . Scan0 ;
281
+
282
+ for ( int column = quad . X ; column < quad . X + quad . Width ; column ++ )
283
+ {
284
+ for ( int row = quad . Y ; row < quad . Y + quad . Height ; row ++ )
285
+ {
286
+ var rgb1 = ptr1 [ ( column * pixelDepth ) + ( row * stride ) ] +
287
+ ptr1 [ ( column * pixelDepth ) + ( row * stride ) + 1 ] +
288
+ ptr1 [ ( column * pixelDepth ) + ( row * stride ) + 2 ] ;
289
+
290
+ if ( rgb1 > threshold )
291
+ {
292
+ ptr1 [ ( column * pixelDepth ) + ( row * stride ) ] = 255 ;
293
+ ptr1 [ ( column * pixelDepth ) + ( row * stride ) + 1 ] = 255 ;
294
+ ptr1 [ ( column * pixelDepth ) + ( row * stride ) + 2 ] = 255 ;
295
+ }
296
+ else
297
+ {
298
+ ptr1 [ ( column * pixelDepth ) + ( row * stride ) ] = 0 ;
299
+ ptr1 [ ( column * pixelDepth ) + ( row * stride ) + 1 ] = 0 ;
300
+ ptr1 [ ( column * pixelDepth ) + ( row * stride ) + 2 ] = 0 ;
301
+ }
302
+ }
303
+ }
304
+ }
305
+ }
306
+
206
307
private int CheckDiff ( Rectangle quad , BitmapData bmpData , BitmapData bmpData2 , int pixelDepth , int threshold )
207
308
{
208
309
unsafe
@@ -228,7 +329,7 @@ private int CheckDiff(Rectangle quad, BitmapData bmpData, BitmapData bmpData2, i
228
329
ptr2 [ ( column * pixelDepth ) + ( row * stride2 ) + 1 ] +
229
330
ptr2 [ ( column * pixelDepth ) + ( row * stride2 ) + 2 ] ;
230
331
231
- if ( rgb2 > rgb1 + threshold )
332
+ if ( rgb2 - rgb1 > threshold )
232
333
{
233
334
diff ++ ;
234
335
@@ -252,6 +353,17 @@ private int CheckDiff(Rectangle quad, BitmapData bmpData, BitmapData bmpData2, i
252
353
highestX = column ;
253
354
}
254
355
}
356
+
357
+ // If the threshold has been exceeded, we want to exit from this method immediately for performance reasons.
358
+ if ( diff > threshold )
359
+ {
360
+ break ;
361
+ }
362
+ }
363
+
364
+ if ( diff > threshold )
365
+ {
366
+ break ;
255
367
}
256
368
}
257
369
0 commit comments