We may assume that the color of the rectangle is close to bright green, and there are very few scattered bright green pixels in the background.
We may assume that the bright green pixels are approximately connected in wide or long lines.
We may also assume there are at least two good quality lines in the rectangle (the sample image has a poor line in the left side).
We may use the following stages for finding the green rectangle in the day image:
- Use cv2.inRange for finding bright green pixels.
- Apply closing morphological operation for connecting small gaps in the lines.
The closing operation may be required if the quality of the lines is poor.
- Find contours, and erase contours with small height and width (assumed to be part of the background).
Note: there are no such pixels in the current sample image.
- Find the coordinates of the most top-left and bottom right white pixel in the mask.
There are the corners of the rectangle we are looking for.
Note:
Using cv2.inRange finding the green pixels supposed to be better than converting to HSV because it is less likely that pixels in the background be in that range.
In your solution you are using only H that is one channel, and the probability for finding "false green" pixels to be in the range is higher compared to testing in three channels.
Adjusting the gamma is also problematic because it makes the background brighter, and closer to the values of the green rectangle.
Code sample:
import cv2
import numpy as np
img = cv2.imread('example.png')
green = cv2.inRange(img, (0, 180, 0), (90, 255, 90)) # Deliberately set the range to be inaccurate - for testing
# Apply closing morphological operation for connecting small gaps in the lines (we don't really need it here).
mask = cv2.morphologyEx(green, cv2.MORPH_CLOSE, np.ones((3, 3), np.uint8))
# Find contours
cnts = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0]
for c in cnts:
size_tresh = 20
x, y, w, h = cv2.boundingRect(c)
if w < size_tresh and h < size_tresh: # Ignore contours with small height and width
cv2.drawContours(mask, [c], 0, 0, -1) # Erase the small contour
# Find the coordinates of the most top-left and bottom right white pixel in the mask
y, x = np.where(mask == 255)
x0, y0 = x.min(), y.min()
x1, y1 = x.max(), y.max()
out = img[y0:y1, x0:x1] # Crop the ROI.
# Draw rectangle for testing
cv2.rectangle(img, (x0, y0), (x1, y1), (255, 0, 255), 2)
# Show images for testing
cv2.imshow('green', green)
cv2.imshow('mask', mask)
cv2.imshow('img', img)
cv2.waitKey()
cv2.destroyAllWindows()
Results:
Output img (with magenta rectangle):

mask:

green:

As you can see, the left line is not found accurately, but we don't need 4 lines for finding the rectangle.