A much faster 2 frame motion detection

Got any feature requests or suggestions?
leszekm1
Posts: 8
Joined: Fri May 05, 2017 5:14 pm

A much faster 2 frame motion detection

Postby leszekm1 » Fri May 05, 2017 5:21 pm

Change the code to use bitwise operations. When I did that on my system the CPU usage went down by 30%:

UInt64* prevFrame = (UInt64*) _previousFrame.ImageData.ToPointer( );
UInt64* currFrame = (UInt64*) _motionFrame.ImageData.ToPointer( );
// difference value

// 1 - get difference between frames
// 2 - threshold the difference
// 3 - copy current frame to previous frame
for (int i = 0; i < _frameSize / sizeof(UInt64); i++, prevFrame++, currFrame++ )
{
// difference
var diff = (*currFrame ^ *prevFrame) & _differenceThresholMask;
// copy current frame to previous
*prevFrame = *currFrame;
// treshold
*currFrame = 0;
if ((diff & 0xFF00000000000000) != 0) // take care of the 1st byte
*currFrame |= 0xFF00000000000000;
if ((diff & 0x00FF000000000000) != 0) // take care of the 2nd byte
*currFrame |= 0x00FF000000000000;
if ((diff & 0x0000FF0000000000) != 0) // take care of the 3rd byte
*currFrame |= 0x0000FF0000000000;
if ((diff & 0x000000FF00000000) != 0) // take care of the 4th byte
*currFrame |= 0x000000FF00000000;
if ((diff & 0x00000000FF000000) != 0) // take care of the 5th byte
*currFrame |= 0x00000000FF000000;
if ((diff & 0x0000000000FF0000) != 0) // take care of the 6th byte
*currFrame |= 0x0000000000FF0000;
if ((diff & 0x000000000000FF00) != 0) // take care of the 7th byte
*currFrame |= 0x000000000000FF00;
if ((diff & 0x00000000000000FF) != 0) // take care of the 8th byte
*currFrame |= 0x00000000000000FF;
}

if ( _suppressNoise )
{
// suppress noise and calculate motion amount
AForge.SystemTools.CopyUnmanagedMemory( _tempFrame.ImageData, _motionFrame.ImageData, _frameSize );
_erosionFilter.Apply( _tempFrame, _motionFrame );
}

// calculate amount of motion pixels
_pixelsChanged = 0;
UInt64* motion = (UInt64*) _motionFrame.ImageData.ToPointer( );

for ( int i = 0; i < _frameSize / sizeof(UInt64); i++, motion++ )
{
if ((*motion & 0xFF00000000000000) != 0) // take care of the 1st byte
_pixelsChanged ++;
if ((*motion & 0x00FF000000000000) != 0) // take care of the 2nd byte
_pixelsChanged++;
if ((*motion & 0x0000FF0000000000) != 0) // take care of the 3rd byte
_pixelsChanged++;
if ((*motion & 0x000000FF00000000) != 0) // take care of the 4th byte
_pixelsChanged++;
if ((*motion & 0x00000000FF000000) != 0) // take care of the 5th byte
_pixelsChanged++;
if ((*motion & 0x0000000000FF0000) != 0) // take care of the 6th byte
_pixelsChanged++;
if ((*motion & 0x000000000000FF00) != 0) // take care of the 7th byte
_pixelsChanged++;
if ((*motion & 0x00000000000000FF) != 0) // take care of the 8th byte
_pixelsChanged++;
}

leszekm1
Posts: 8
Joined: Fri May 05, 2017 5:14 pm

Re: A much faster 2 frame motion detection

Postby leszekm1 » Sat May 06, 2017 3:36 am

And the mask which gives you 8 levels (but is never set in the code, nor exposed in the UI anyway)

private int _differenceLevel = 16;
private UInt64 _differenceThresholMask = 0xF0F0F0F0F0F0F0F0;

/// <summary>
/// Difference threshold value, [1, 255].
/// </summary>
///
/// <remarks><para>The value specifies the amount off difference between pixels, which is treated
/// as motion pixel.</para>
///
/// <para>Default value is set to <b>16</b>.</para>
/// </remarks>
///
public int DifferenceThreshold
{
get { return _differenceLevel; }
set
{
{
_differenceLevel = Math.Max( 1, Math.Min( 255, value ) );
if ((_differenceLevel & 0x80) != 0)
_differenceThresholMask = 0x8080808080808080; // difference >= 128 per byte
else if ((_differenceLevel & 0x40) != 0)
_differenceThresholMask = 0xC0C0C0C0C0C0C0C0; // difference >= 64 per byte
else if ((_differenceLevel & 0x20) != 0)
_differenceThresholMask = 0xE0E0E0E0E0E0E0E0; // difference >= 32 per byte
else if ((_differenceLevel & 0x10) != 0)
_differenceThresholMask = 0xF0F0F0F0F0F0F0F0; // difference >= 16 per byte
else if ((_differenceLevel & 0x08) != 0)
_differenceThresholMask = 0xF8F8F8F8F8F8F8F8; // difference >= 8 per byte
else if ((_differenceLevel & 0x04) != 0)
_differenceThresholMask = 0xFCFCFCFCFCFCFCFC; // difference >= 4 per byte
else if ((_differenceLevel & 0x02) != 0)
_differenceThresholMask = 0xFEFEFEFEFEFEFEFE; // difference >= 2 per byte
else
_differenceThresholMask = 0xFFFFFFFFFFFFFFFF; // difference >= 1 per byte
}
}
}

ramby
Posts: 8
Joined: Mon Jun 12, 2017 12:36 am

Re: A much faster 2 frame motion detection

Postby ramby » Tue Jun 27, 2017 8:44 am

This implementation is being used in the current version, and unfortunately does not work correctly (see the post "http://community.ispyconnect.com/viewtopic.php?f=3&t=6070 "Noise Supression Broken?").

I have compared running both the existing code and your implementation, and found your implementation produces many false motion detection areas.

The flaw exists in this line

var diff = (*currFrame ^ *prevFrame) & _differenceThresholMask;

where the two frames are XOR'd and then the difference mask is applied.

This technique won't work, since the difference mask will ignore the least significant bits of the current and previous values, and only look at the higher order bits. The least significant bits are still needed in order to calculate the absolute difference between the two values.

The best way to see that it doesn't work is with an example (for simplicity I've used single byte sized values here, rather than 8 bytes at a time).

If the current frame value (curr) = 45, and the previous frame value (prev) = 50.

curr = 45, hex 0x2d, binary 0010 1101
prev = 50, hex 0x32, binary 0011 0010

In the old code, the difference was calculated by the following

diff = curr - prev
= -5

and then checked if it's within the difference threshold (default _differenceThreshold = 15 and _differenceThresholdNeg = -15)

( ( diff >= _differenceThreshold ) || ( diff <= _differenceThresholdNeg ) ) ? (byte) 255 : (byte) 0;

which returns 0 (i.e. no motion 5 < 15)

With the current (leszekm1) implementation the two values are XOR'd and difference mask applied
if we use a difference mask of 0xF0 (16) or (1111 0000)

diff = (curr XOR prev) AND differencemask
= (0x2d XOR 0x32) AND (0xF0)
= (0010 1101 XOR 0011 0010) AND (1111 0000)
= (0001 1111) AND 1111 0000
= 0001 0000

which then causes the current frame to be set as motion detected, since the result is non-zero

if ((diff & 0x00000000000000FF) != 0) // take care of the 8th byte
*currFrame |= 0x00000000000000FF;

The code needs to looks at all the bits to determine the diff. A higher order bit change does not necessarily mean a difference of > 16 occurred, and can occur when the actual value change is only small, as this example illustrates.

I think you're on the right track leszekm1, as your implementation is much more efficient, performance wise. Just need to fix the difference calculation part.

I'm sure there's an efficient way of computing the difference across 8 bytes at a time, just can't think how at the moment.

ebaziuk
Posts: 3
Joined: Fri Jul 28, 2017 5:55 pm

Re: A much faster 2 frame motion detection

Postby ebaziuk » Fri Nov 03, 2017 1:13 am

Is there any way of passing motion detection work to the GPU?


Return to “Suggestions”

Who is online

Users browsing this forum: No registered users and 2 guests