OpenCV Object Detection - Center Point

61,851

Solution 1

There's already an example of how to do rectangle detection in OpenCV (look in samples/squares.c), and it's quite simple, actually.

Here's the rough algorithm they use:

0. rectangles <- {}
1. image <- load image
2. for every channel:
2.1  image_canny <- apply canny edge detector to this channel
2.2  for threshold in bunch_of_increasing_thresholds:
2.2.1   image_thresholds[threshold] <- apply threshold to this channel
2.3  for each contour found in {image_canny} U image_thresholds:
2.3.1   Approximate contour with polygons
2.3.2   if the approximation has four corners and the angles are close to 90 degrees.
2.3.2.1    rectangles <- rectangles U {contour}

Not an exact transliteration of what they are doing, but it should help you.

Solution 2

Hope this helps, uses the moment method to get the centroid of a black and white image.

cv::Point getCentroid(cv::Mat img)
{
    cv::Point Coord;
    cv::Moments mm = cv::moments(img,false);
    double moment10 = mm.m10;
    double moment01 = mm.m01;
    double moment00 = mm.m00;
    Coord.x = int(moment10 / moment00);
    Coord.y = int(moment01 / moment00);
    return Coord;
}

Solution 3

OpenCV has heaps of functions that can help you achieve this. Download Emgu.CV for a C#.NET wrapped to the library if you are programming in that language.

Some methods of getting what you want:

  1. Find the corners as before - e.g. "CornerHarris" OpenCV function

  2. Threshold the image and calculate the centre of gravity - see http://www.roborealm.com/help/Center%20of%20Gravity.php ... this is the method i would use. You can even perform the thresholding in the COG routine. i.e. cog_x += *imagePtr < 128 ? 255 : 0;

  3. Find the moments of the image to give rotation, center of gravity etc - e.g. "Moments" OpenCV function. (I haven't used this)

  4. (edit) The AForge.NET library has corner detection functions as well as an example project (MotionDetector) and libraries to connect to webcams. I think this would be the easiest way to go, assuming you are using Windows and .NET.

Solution 4

Since no one has posted a complete OpenCV solution, here's a simple approach:

  1. Obtain binary image. We load the image, convert to grayscale, and then obtain a binary image using Otsu's threshold

  2. Find outer contour. We find contours using findContours and then extract the bounding box coordinates using boundingRect

  3. Find center coordinate. Since we have the contour, we can find the center coordinate using moments to extract the centroid of the contour


Here's an example with the bounding box and center point highlighted in green

Input image -> Output

Center: (100, 100)

Center: (200, 200)

Center: (300, 300)

So to recap:

Given an object on a plain white background, does anybody know if OpenCV provides functionality to easily detect an object from a captured frame?

First obtain a binary image (Canny edge detection, simple thresholding, Otsu's threshold, or Adaptive threshold) and then find contours using findContours. To obtain the bounding rectangle coordinates, you can use boundingRect which will give you the coordinates in the form of x,y,w,h. To draw the rectangle, you can draw it with rectangle. This will give you the 4 corner points of the contour. If you wanted to obtain the center point, use moments to extract the centroid of the contour

Code

import cv2
import numpy as np

# Load image, convert to grayscale, and Otsu's threshold 
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Find contours and extract the bounding rectangle coordintes
# then find moments to obtain the centroid
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    # Obtain bounding box coordinates and draw rectangle
    x,y,w,h = cv2.boundingRect(c)
    cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 2)

    # Find center coordinate and draw center point
    M = cv2.moments(c)
    cx = int(M['m10']/M['m00'])
    cy = int(M['m01']/M['m00'])
    cv2.circle(image, (cx, cy), 2, (36,255,12), -1)
    print('Center: ({}, {})'.format(cx,cy))

cv2.imshow('image', image)
cv2.waitKey()
Share:
61,851
David McGraw
Author by

David McGraw

Grinding my way to the moon. Startups, product and code are what I do and I'm not here to be average. iOS Expert (Started 2008). USMC Vet/OIF.

Updated on January 29, 2020

Comments

  • David McGraw
    David McGraw about 4 years

    Given an object on a plain white background, does anybody know if OpenCV provides functionality to easily detect an object from a captured frame?

    I'm trying to locate the corner/center points of an object (rectangle). The way I'm currently doing it, is by brute force (scanning the image for the object) and not accurate. I'm wondering if there is functionality under the hood that i'm not aware of.

    Edit Details: The size about the same as a small soda can. The camera is positioned above the object, to give it a 2D/Rectangle feel. The orientation/angle from from the camera is random, which is calculated from the corner points.

    It's just a white background, with the object on it (black). The quality of the shot is about what you'd expect to see from a Logitech webcam.

    Once I get the corner points, I calculate the center. The center point is then converted to centimeters.

    It's refining just 'how' I get those 4 corners is what I'm trying to focus on. You can see my brute force method with this image: Image

  • David McGraw
    David McGraw over 15 years
    In reference to cvCornerHarris, can you elaborate on how it's used? From what I see, you create an image and run cvCornerHarris(image, cornerimg, blockSize(?), apertureSize(?)). And, how are you able to pull information from the corner image?
  • geometrikal
    geometrikal over 15 years
    From what I can tell the way it works is for every pixel it runs a sobel edge detector of size 'apertureSize' over the surrounding 'blockSize' by 'blockSize' group of pixels. It then uses a formula to give a score to the edges detected in this area. A corner will have both horizontal and vertical.
  • geometrikal
    geometrikal over 15 years
    The resulting image is the same size as the original, except the brightest pixels correspond. to strongest corners. Choose block size bigger than the corner to detect - try 5 or 7 for your image. Choose apertureSize and bit smaller - try 3. I haven't used this function myself, so say how it goes
  • cduck
    cduck about 13 years
    I am doing a similar project. I'm new to OpenCV so could you please post the source code to do these steps?
  • user4581301
    user4581301 almost 13 years
    I believe the file you are looking for is now [OpenCV_proj_dir]/samples/cpp/squares.cpp.