6

I m working on small image processing assignment where I need to track 4 red color object. I got how to track single one. I want to know what is the best approach to track more than one point.

There are 4 points which are positioned to form a rectangle so can I use shape detection or corner detection to detect and track the points Please see image below..

enter image description here

  • have a look at probabilistic tracking methods like particle filtering – Micka Sep 11 '14 at 08:39
  • depends on the red dots movements and whether you have to continue tracking the right red dot if they overlap. – Micka Sep 11 '14 at 14:24
  • so your idea is not to track multiple objects but to track a single pattern? – Micka Sep 11 '14 at 16:15
  • Yes, indeed! But I need to also get the x, y position of 4 red dots. –  Sep 11 '14 at 17:36
  • If there is nothing else red in the image and if you know how many red dots belong to one pattern the easiest way would be to extract/threshold red areas. Then cluster them. For example with k-means. – Micka Sep 12 '14 at 05:39
  • 1
    Can I use Quadrangle algorithm? –  Sep 12 '14 at 14:18
  • http://www.youtube.com/watch?v=bVATrWZHOcU check this one. I m typically trying to do the same, detect small dots and draw a classifier box around it. Can anyone explain me how he did this. –  Sep 14 '14 at 13:12
  • @aaa What I demonstrated in my answer does exactly that, except it draws a small circle in the middle of the detected red blob. – karlphillip Sep 17 '14 at 23:02

3 Answers3

6

Here is my implementation on GitHub: https://github.com/Smorodov/Multitarget-tracker video on youtube: http://www.youtube.com/watch?v=2fW5TmAtAXM&list=UUhlR5ON5Uqhi_3RXRu-pdVw

In short:

  1. Detect objects. This step provides a set of points (detected objects coordinates).
  2. Solve an assignment problem (rectangular Hungarian algorithm). this step assigns detected objects to existing tracks.
  3. Manage unassigned/lost tracks. This step deletes tracks with too many missed detections in a row and adds tracks for new detections.
  4. Apply statistical filter for each track (Kalman filter in this case), for predict missing detections and smooth tracks using objects objects dynamics information (defined in Kalman filter matrices).

BTW, to get coordinates of 4 points, you need to know coordinates only for 3 points, because your pattern is rectangular, you can compute 4-th point.

Andrey Smorodov
  • 10,649
  • 2
  • 35
  • 42
4

My naive implementation uses a technique described at OpenCV bounding boxes to do the tracking of red blobs.

The following is a helper function used to retrieve the center of all the red objects that were detected:

/* get_positions: a function to retrieve the center of the detected blobs.
 * largely based on OpenCV's "Creating Bounding boxes and circles for contours" tutorial.
 */
std::vector<cv::Point2f> get_positions(cv::Mat& image)
{
    if (image.channels() > 1)
    {
        std::cout << "get_positions: !!! Input image must have a single channel" << std::endl;
        return std::vector<cv::Point2f>();
    }

    std::vector<std::vector<cv::Point> > contours;
    cv::findContours(image, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);

    // Approximate contours to polygons and then get the center of the objects
    std::vector<std::vector<cv::Point> > contours_poly(contours.size());
    std::vector<cv::Point2f> center(contours.size());
    std::vector<float> radius(contours.size());
    for (unsigned int i = 0; i < contours.size(); i++ )
    {
        cv::approxPolyDP(cv::Mat(contours[i]), contours_poly[i], 5, true );
        cv::minEnclosingCircle((cv::Mat)contours_poly[i], center[i], radius[i]);
    }

    return center;
}   

I wrote the code to test my approach in real-time by capturing frames from a webcam. The overall procedure is quite similar to what @Dennis described (sorry, I was already coding when you submitted your answer).

OK, so this is where the fun actually begins.

int main()
{
    // Open the capture device. My webcam ID is 0:
    cv::VideoCapture cap(0);
    if (!cap.isOpened())
    {
        std::cout << "!!! Failed to open webcam" << std::endl;
        return -1;
    }

    // Let's create a few window titles for debugging purposes
    std::string wnd1 = "Input", wnd2 = "Red Objs", wnd3 = "Output";   

    // These are the HSV values used later to isolate RED-ish colors
    int low_h = 160, low_s = 140, low_v = 50;
    int high_h = 179, high_s = 255, high_v = 255;

    cv::Mat frame, hsv_frame, red_objs;
    while (true)
    {
        // Retrieve a new frame from the camera
        if (!cap.read(frame))
            break;

        cv::Mat orig_frame = frame.clone();
        cv::imshow(wnd1, orig_frame);

orig_frame:

enter image description here

        // Convert BGR frame to HSV to be easier to separate the colors
        cv::cvtColor(frame, hsv_frame, CV_BGR2HSV);

        // Isolate red colored objects and save them in a binary image
        cv::inRange(hsv_frame,
                    cv::Scalar(low_h,  low_s,  low_v),
                    cv::Scalar(high_h, high_s, high_v),
                    red_objs);

        // Remove really small objects (mostly noises)
        cv::erode(red_objs, red_objs, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)));
        cv::dilate(red_objs, red_objs, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(7, 7)));

        cv::Mat objs = red_objs.clone();
        cv::imshow(wnd2, objs);

objs:

enter image description here

        // Retrieve a vector of points with the (x,y) location of the objects
        std::vector<cv::Point2f> points = get_positions(objs);

        // Draw a small green circle at those locations for educational purposes
        for (unsigned int i = 0; i < points.size(); i++)
            cv::circle(frame, points[i], 3, cv::Scalar(0, 255, 0), -1, 8, 0);

        cv::imshow(wnd3, frame);

enter image description here

        char key = cv::waitKey(33);
        if (key == 27) {   /* ESC was pressed */
            //cv::imwrite("out1.png", orig_frame);
            //cv::imwrite("out2.png", red_objs);
            //cv::imwrite("out3.png", frame);
            break;
        }
    }

    cap.release();

    return 0;
}
karlphillip
  • 92,053
  • 36
  • 243
  • 426
  • Promising solution! Thanks But, How did you get only one green circle for red object? My focus is on getting near perfect center x, y coordinate of red object! I guess blob isnt smooth here! –  Sep 15 '14 at 16:48
  • That part is performed by `cv::approxPolyDP()` and `cv::minEnclosingCircle()` in the code. It's a good alternative to find the center of things that are not a perfect square or a circle. – karlphillip Sep 15 '14 at 18:52
  • @aaa I think I now understand what you were expecting. You wanted to place all the small detected squares inside a single big rectangle, right? **However**, that's not what that youtube video does. In [that video](http://www.youtube.com/watch?v=bVATrWZHOcU), it's very clear that every red blob ends up having it's own rectangle around it. I did the exact same thing, except I placed a green circle on the center of the blob. You are under the impression what I did is different because I showed an object that displayed 6 small red squares, and the video guy showed 2 bigger red squares. – karlphillip Sep 18 '14 at 01:11
  • @aaa I'll just run my code again and record a youtube video of me showing 2 big red squares to convince you. That way you would see the same demonstration of the video you were impressed with. I'm positively absolutely sure that if the video guy showed a rubik' s cube, his application would draw 6 rectangles around each red blob detected. – karlphillip Sep 18 '14 at 01:14
  • 1
    [Here is the link to my video](https://www.youtube.com/watch?v=D_YYcern0B4) (btw, I know it sucks) ;D – karlphillip Sep 18 '14 at 02:40
  • 1
    [Here is my result](http://i58.tinypic.com/svknjp.png) I dont understand why I m getting multiple green circles on my red object? –  Sep 18 '14 at 08:39
  • Display `objs` in a window (as my code is doing) and you'll see *over-segmentation*. Each segment ends ups becoming a single blob. To reduce the amount of segmentation, After `cv::dilate()`, invoke: `cv::morphologyEx(red_objs, red_objs, cv::MORPH_CLOSE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(15, 15)));` This will join/connect blobs that are close. I need to move on to other challenges, so don't be upset if I don't reply back anymore. This is as far as I go. See you around. – karlphillip Sep 18 '14 at 12:25
1

Here are the steps for multiple colored object tracking:
For each frame do the following steps:

  1. Convert your input image (BGR) to HSV color space using cv::cvtColor()
  2. Segment red colored objects using cv::inRange()
  3. Extract each blob from the image using cv::findContours()
  4. For each blob calculate its center using cv::boundingRect()
  5. Match the blobs from the previous frame plus some movement to the current blobs comparing the distances between their centers --> Match them if they have the smallest distance

This is the basis of the algorithm. Then you have to handle situations, when blobs enter the image (this is the case when there is a blob in the current frame, but no close blob from the previous frame) or leave the image (there is a blob in the previous frame, but no close blob in the current frame).

Dennis
  • 521
  • 3
  • 10