首页 > 解决方案 > How to identify contours associated with my objects and find their geometric centroid

问题描述

Problem Statement & Background Info:

EDIT: Constraints: The red coloring on the flange changes over time, so I'm not trying to use color recognition to identify my object at this moment unless it can be robust. Addition,ally external illumination my be a factor since this will be in an outdoor area in the future.

I have RGB-Depth camera and with it, I'm able to capture this scene. Where each pixel (x,y) has a depth value.

enter image description here

Applying a gradient magnitude filter to the depth map associated with my image I'm able to get the following edge map.

enter image description here

The gradient magnitudes are given the value of 0 if they had a magnitude that wasn't zero. Black (255) is for magnitude values associated with 0 (homogenous depth or a flat surface).

From this edge map I dialated the edges so picking up the contours would be easier. enter image description here

Then I found the contours in the image and tried to plot just the 5 biggest contours.

enter image description here

PROBLEM

Is there a way to reliably find the contours associated with my objects (the red box and metal fixture) and then find their geometric centroid? I keep running into the issue that I can find contours in the image, but I have no way of selectively screening for the contours that are my objects and not noise.

I have provided the image I used for the image processing, but for some reason, OpenCV saves the image as a black image, and when you read it in using...

gray = cv2.imread('GRAYTEST.jpeg', cv2.IMREAD_GRAYSCALE)

it appears blue-ish and not a binary white/black image as I show. So sorry about that.

Here is the image: enter image description here

Sorry, I don't know why it saved just as a black image, but if you read it in OpenCV it should show up with the same lines as "magnitude of gradients" plot.

MY CODE

    gray = cv2.imread('GRAYTEST.jpeg', cv2.IMREAD_GRAYSCALE)
    plt.imshow(gray)
    plt.title('gray start image')
    plt.show()

    blurred = cv2.bilateralFilter(gray, 8, 25, 25)  # blurr image while preserving edges
    kernel = np.ones((3, 3), np.uint8)  # define a kernel (block) to apply filters to

    dialated = cv2.dilate(blurred, kernel, iterations=1)
    plt.title('dialated')
    plt.imshow(dialated)
    plt.show()

    #Just performs a canny edge dectection on an image
    edges_empty = self.Commons.CannyE_Auto(dialated)  # Canny edge image for some sigma
    #makes an empty image using the same diemensions of the given image
    empty2 = self.Commons.make_empty(gray)

    _, contours, _ = cv2.findContours(edges_empty, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    cnts = sorted(contours, key=cv2.contourArea, reverse=True)[:5]  # get largest five contour area
    cv2.drawContours(empty2, cnts, -1, (255, 0, 0), thickness=1)

    plt.title('contours')
    plt.imshow(empty2)
    plt.show()

标签: pythonopencvimage-processingcomputer-visionobject-recognition

解决方案


  1. Instead of performing the blurring operation, dialation, canny edge detection on my already thresholded image, I just performed the contour detection on my original image.

  2. Then I was able to find a decent contour for the outline of my image by modifying findContour command.

    _, contours, _ = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

replacing cv2.RETR_TREE with cv2.RETR_EXTERNAL I was able to get only the contours that were associated with an object's outline, rather than trying to get contours within the object. Switching to cv2.CHAIN_APPROX_NONE didn't show any noticeable improvements, but it may provide better contours for more complex geometries.

        for c in cnts:
        # compute the center of the contour
        M = cv2.moments(c)
        cX = int(M["m10"] / M["m00"])
        cY = int(M["m01"] / M["m00"])

        # draw the contour and center of the shape on the image
        cv2.drawContours(empty2, [c], -1, (255, 0, 0), thickness=1)
        perimeter = np.around(cv2.arcLength(c, True), decimals=3)
        area = np.around(cv2.contourArea(c), decimals=3)

        cv2.circle(empty2, (cX, cY), 7, (255, 255, 255), -1)
        cv2.putText(empty2, "center", (cX - 20, cY - 20),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)

        cv2.putText(empty2, "P:{}".format(perimeter), (cX - 50, cY - 50),
                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)

        cv2.putText(empty2, "A:{}".format(area), (cX - 100, cY - 100),
                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)

Using the above code I was able to label centroid of each contour as well as information about each contours perimeter and area.

enter image description here

However, I was unable to perform a test that would select which contour was my desired contour. I have an idea to capture my object in a more ideal setting and find its centroid, perimeter, and associated area. This way when I find a new contour I can compare it with how close it is to my known values.

I think this method could work to remove contours that are too large or too small.

If anyone knows of a better solution that would be fantastic!


推荐阅读