Search code examples
imageopencvimage-processingcomputer-visionobject-detection

Image processing how to detect specific custom shape in image


I want to detect a specific shape which is combination of 2 shapes Pentagon and a square. However side of a square should be around 3 times of a pentagon to match a valid shape as shown in the image.

enter image description here

I'm learning different image processing techniques for my requirement.

  1. Contour Detection: Issue is knowing there are 7 corners is not enough as x : 3x need to be validated.

  2. Haar features: Issue is images consist of background and there are text inside these object. So haar will not optimal method in my opinion.

My idea

I think may be I can detect lines corners and using side lengths I can do some math and identify the image.Is there any image processing technique that use mathematical distance, scale, location to identify object?

Original image

matching

enter image description here

not matching

enter image description here


Solution

  • Here's a simple approach:

    1. Obtain binary image. Load image, convert to grayscale, Gaussian blur, then Otsu's threshold.

    2. Filter for desired contour. We find contours then filter using a combination of contour approximation + contour area. With the observation that the shapes have a large area difference, we can use a predetermined threshold area to identify between the two.


    Input -> Binary image -> Detected shape

    Result

    Match
    

    Input -> Binary image -> Detected shape

    Result

    No Match
    

    Since you didn't specify a language, I implemented in Python

    import cv2
    import numpy as np
    
    # Load image, grayscale, Gaussian blur, Otsus threshold
    image = cv2.imread('1.png')
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray, (3,3), 0)
    thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
    
    # Find contours
    cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    for c in cnts:
        peri = cv2.arcLength(c, True)
        approx = cv2.approxPolyDP(c, 0.04 * peri, True)
        area = cv2.contourArea(c)
        # Filter using contour approximation and area filtering (Remove small noise)
        if len(approx) > 4 and len(approx) < 8 and area > 200:
            # This is the larger version
            if area >= 5500:
                print('Match')
            # Smaller version
            elif area < 5500:
                print('No Match')
            cv2.drawContours(image, [c], -1, (255,0,12), 3)
    
    cv2.imshow('thresh', thresh)
    cv2.imshow('image', image)
    cv2.waitKey()