Find and draw the largest contour in opencv on a specific color (Python)
You can start by defining a mask in the range of the red tones of the book you are looking for.
Then you can just find the contour with the biggest area and draw the rectangular shape of the book.
import numpy as npimport cv2# load the imageimage = cv2.imread("path_to_your_image.png", 1)# red color boundaries [B, G, R]lower = [1, 0, 20]upper = [60, 40, 220]# create NumPy arrays from the boundarieslower = np.array(lower, dtype="uint8")upper = np.array(upper, dtype="uint8")# find the colors within the specified boundaries and apply# the maskmask = cv2.inRange(image, lower, upper)output = cv2.bitwise_and(image, image, mask=mask)ret,thresh = cv2.threshold(mask, 40, 255, 0)if (cv2.__version__[0] > 3): contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)else: im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)if len(contours) != 0: # draw in blue the contours that were founded cv2.drawContours(output, contours, -1, 255, 3) # find the biggest countour (c) by the area c = max(contours, key = cv2.contourArea) x,y,w,h = cv2.boundingRect(c) # draw the biggest contour (c) in green cv2.rectangle(output,(x,y),(x+w,y+h),(0,255,0),2)# show the imagescv2.imshow("Result", np.hstack([image, output]))cv2.waitKey(0)
Using your image:
If you want the book to rotate you can use rect = cv2.minAreaRect(cnt)
as you can find it here.
Edit:
You should also consider other colour spaces beside the RGB, as the HSV or HLS. Usually, people use the HSV since the H channel stays fairly consistent in shadow or excessive brightness. In other words, you should get better results if you use the HSV colourspace.
In specific, in OpenCV the Hue range is [0,179]
. In the following figure (made by @Knight), you can find a 2D slice of that cylinder, in V = 255
, where the horizontal axis is the H
and the vertical axis the S
. As you can see from that figure to capture the red you need both to include the lower (e.g., H=0 to H=10) and upper region (e.g., H=170 to H=179) of the Hue values.
Use this to convert Grayscale masks to Rectangles
def mask_to_rect(image): ''' Give rectangle cordinates according to the mask image Params: image : (numpy.array) Gray Scale Image Returns: Cordinates : (list) List of cordinates [x, y, w h] ''' # Getting the Thresholds and ret ret,thresh = cv2.threshold(image, 0, 1, 0) # Checking the version of open cv I tried for (version 4) # Getting contours on the bases of thresh if (int(cv2.__version__[0]) > 3): contours, hierarchy = cv2.findContours(thresh.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) else: im2, contours, hierarchy = cv2.findContours(thresh.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) # Getting the biggest contour if len(contours) != 0: # draw in blue the contours that were founded cv2.drawContours(output, contours, -1, 255, 3) # find the biggest countour (c) by the area c = max(contours, key = cv2.contourArea) x,y,w,h = cv2.boundingRect(c) return [x, y, w, h]
Result