Home > Enterprise >  Python: use OpenCV to extract object inside a shape
Python: use OpenCV to extract object inside a shape

Time:10-08

I need to extract the image from inside this rectangular box using python OpenCV.

enter image description here

I've seen and tried several ways of extracting content from inside a shape, but each has a limitation. Most examples have a shape with a high contrast, solid color such as green (0,255,0) or red(255,0,0). However, my problem is that the red box has some transparency/opaqueness making the identification of the rectangle much more difficult. Red pixel color for the rectangle ranges from 175-220, but the rest of the image also has similar tones of red making the extraction even more difficult.

enter image description here

I'm fairly new to OpenCV, so any assistance is greatly appreciated. I've tried both of the solutions on this thread, but neither produces the desired result.

CodePudding user response:

Here's a possible solution:

  1. Convert image to HSV
  2. Threshold on red to segment the rectangle
  3. Apply some morphology to close the contour as much as possible
  4. Detect external contours
  5. Filter noisy blobs based on area; You are looking for the largest contour
  6. Get the contour's bounding rectangle
  7. Slice the image using the bounding rectangle

Let's see the code:

# imports:
import cv2
import numpy as np

# image path
path = "D://opencvImages//"
fileName = "uHNzL.jpg"

# Reading an image in default mode:
inputImage = cv2.imread(path   fileName)
# Deep copy for results:
inputImageCopy = inputImage.copy()

# Convert the image to the HSV color space:
hsvImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2HSV)

# Set the HSV values:
lowRange = np.array([144, 105, 0])
uppRange = np.array([179, 255, 255])

# Create the HSV mask
binaryMask = cv2.inRange(hsvImage, lowRange, uppRange)

This is the mask:

As you see the rectangle is (mostly) there. Let's try to improve the contour with some morphology. Some dilations followed by erosions should do the trick:

# Apply Dilate   Erode:
kernel = np.ones((3, 3), np.uint8)
binaryMask = cv2.morphologyEx(binaryMask, cv2.MORPH_DILATE, kernel, iterations=3)
binaryMask = cv2.morphologyEx(binaryMask, cv2.MORPH_ERODE, kernel, iterations=3)

This is the result:

The rectangle is a little bit bigger, but so does the noise. However, we are looking for the biggest blob, so we can filter by area. Let's crop the largest blob:

# Find the target blobs on the binary mask:
contours, hierarchy = cv2.findContours(binaryMask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# Alright, just look for the outer bounding boxes:
for _, c in enumerate(contours):

    # Get blob area:
    blobArea = cv2.contourArea(c)

    # Set minimum area:
    minArea = 1000

    if blobArea > minArea:
        # Get the bounding rectangle:
        boundRect = cv2.boundingRect(c)

        # Bondary offset:
        offset = 15

        # Draw the rectangle on the input image:
        # Get the dimensions of the bounding rect:
        rectX = int(boundRect[0]   offset)
        rectY = int(boundRect[1]   offset)
        rectWidth = int(boundRect[2] - 2 * offset)
        rectHeight = int(boundRect[3] - 2 * offset)

        #  Green Color, detected blobs:
        color = (0, 255, 0)
        cv2.rectangle(inputImageCopy, (int(rectX), int(rectY)),
                      (int(rectX   rectWidth), int(rectY   rectHeight)), color, 2)

        # Crop contour:
        croppedImage = inputImage[rectY:rectY   rectHeight, rectX:rectX   rectWidth]
        cv2.imshow("croppedImage", croppedImage)
        cv2.waitKey(0)

    cv2.imshow("Rectangles", inputImageCopy)
    cv2.waitKey(0)

Now, because we are processing a morphed image, the rectangle binary outline is a little bit bigger than the actual red rectangle, so I introduce an offset to crop the image a little bit tighter.

This is the bounding rectangle with an offset of 15 pixels:

This is the cropped image:

  • Related