I need to extract the image from inside this rectangular box using python OpenCV.
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.
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:
- Convert image to
HSV
- Threshold on red to segment the rectangle
- Apply some morphology to close the contour as much as possible
- Detect external contours
- Filter noisy blobs based on area; You are looking for the largest contour
- Get the contour's bounding rectangle
- 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: