Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EmguCV: Draw contour on object in Motion using Optical Flow?

I would like to do motion detection in C# (using EmguCV 3.0) to remove object in motion or in foreground to draw an overlay.

Here is a sample test I done with a Kinect (because It's a depth camera) Démo with Kinect

How can I get started with EmguCV 3.0 ?

  • I tried many background removal code that do not work
  • It seems OpticalFlow is a good start but there si no example in EmguCV 3.0
  • If I find the largest blob how can I find its contours ?

Can someone help me to get started ?

EDIT: 17/06/2015

In EmguCV3.0.0 RC I don't see OpticalFlow in the package and documentation: http://www.emgu.com/wiki/files/3.0.0-rc1/document/html/b72c032d-59ae-c36f-5e00-12f8d621dfb8.htm

There is only : DenseOpticalFlow, OpticalFlowDualTVL1 ???

This is a AbsDiff Code:

var grayFrame = frame.Convert<Gray, Byte>();
var motionFrame = grayFrame.AbsDiff(backFrame)
                           .ThresholdBinary(new Gray(20), new Gray(255))
                           .Erode(2) 
                           .Dilate(2);

Result: Demo Diff

I don't know how to get the motion in white ?

This is the Blob Code:

Image<Bgr, Byte> smoothedFrame = new Image<Bgr, byte>(frame.Size);
CvInvoke.GaussianBlur(frame, smoothedFrame, new Size(3, 3), 1); //filter out noises

Mat forgroundMask = new Mat();
fgDetector.Apply(smoothedFrame, forgroundMask);

CvBlobs blobs = new CvBlobs();
blobDetector.Detect(forgroundMask.ToImage<Gray, byte>(), blobs);
blobs.FilterByArea(400, int.MaxValue);
blobTracker.Update(blobs, 1.0, 0, 1);

foreach (var pair in blobs) {
  CvBlob b = pair.Value;
  CvInvoke.Rectangle(frame, b.BoundingBox, new MCvScalar(255.0, 255.0, 255.0), 2);
}

Result: Blob Demo

Why so much false positive ?

This is a MOG2 Code:

forgroundDetector.Apply(frame, forgroundMask);
motionHistory.Update(forgroundMask);
var motionMask = GetMotionMask();
Image<Bgr, Byte> motionImage = new Image<Bgr, byte>(motionMask.Size);
CvInvoke.InsertChannel(motionMask, motionImage, 0);

Rectangle[] rects;
using (VectorOfRect boundingRect = new VectorOfRect()) {
  motionHistory.GetMotionComponents(segMask, boundingRect);
  rects = boundingRect.ToArray();
}

foreach (Rectangle comp in rects) { ...

Result: MOG2 Demo

If I select the biggest Area how can I get the contour of the object ?

like image 631
Jean-Philippe Encausse Avatar asked Jun 09 '15 23:06

Jean-Philippe Encausse


1 Answers

First, I can give you some example Optical Flow code.

Let oldImage and newImage be variables that hold the previous and current frame. In my code, it's of type Image<Gray, Byte>.

// prep containers for x and y vectors
Image<Gray, float> velx = new Image<Gray, float>(newImage.Size);
Image<Gray, float> vely = new Image<Gray, float>(newImage.Size);

// use the Horn and Schunck dense optical flow algorithm.
OpticalFlow.HS(oldImage, newImage, true, velx, vely, 0.1d, new MCvTermCriteria(100));

// color each pixel
Image<Hsv, Byte> coloredMotion = new Image<Hsv, Byte>(newImage.Size);
for (int i = 0; i < coloredMotion.Width; i++)
{
    for (int j = 0; j < coloredMotion.Height; j++)
    {
        // Pull the relevant intensities from the velx and vely matrices
        double velxHere = velx[j, i].Intensity;
        double velyHere = vely[j, i].Intensity;

        // Determine the color (i.e, the angle)
        double degrees = Math.Atan(velyHere / velxHere) / Math.PI * 90 + 45;
        if (velxHere < 0)
        {
            degrees += 90;
        }
        coloredMotion.Data[j, i, 0] = (Byte) degrees;
        coloredMotion.Data[j, i, 1] = 255;

        // Determine the intensity (i.e, the distance)
        double intensity = Math.Sqrt(velxHere * velxHere + velyHere * velyHere) * 10;
        coloredMotion.Data[j, i, 2] = (intensity > 255) ? 255 : intensity;
    }
}
// coloredMotion is now an image that shows intensity of motion by lightness
// and direction by color.

Regarding the larger question of how to remove the foreground:

If I had a way to get a static background image, that's the best way to start. Then, the foreground would be detected by the AbsDiff method and using Erode and Dilate or Gaussian to smooth the image, then use blob detection.

For simple foreground detection, I found Optical Flow to be way too much processing (8fps max), whereas the AbsDiff method was just as accurate but had no effect on framerate.

Regarding contours, if you're merely looking to find the size, position, and other moments, then the blob detection in the AbsDiff tutorial above seems to be sufficient, which uses Image.FindContours(...).

If not, I would start looking at the CvBlobDetector class as used in this tutorial. There's a built-in DrawBlob function that might come in handy.

like image 102
Mark Miller Avatar answered Nov 03 '22 22:11

Mark Miller