Yousef Hesham
Yousef Hesham

Reputation: 33

How to detect White region in an image with Opencv & Python?

I'm trying to extract the coordinates of a big white region in an image as follows: Here's the original image:

Original Image

Using a small square kernel, I applied a closing operation to fill small holes and help identify larger structures in the image as follows:

import cv2
import numpy as np
import imutils 

original = cv2.imread("Plates\\24.png")
original = cv2.resize(original, None, fx=3, fy=3, interpolation=cv2.INTER_CUBIC)
gray = cv2.cvtColor(original, cv2.COLOR_BGR2GRAY)
    
# next, find regions in the image that are light
squareKern = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
light = cv2.morphologyEx(gray, cv2.MORPH_CLOSE, squareKern)
light = cv2.threshold(light, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]

the resulting image is as follows:

Resulting Image 1

Here's another example:

Resulting Image 2

What I wish to be able to do is to detect the large white region in the plate as follows:

Needed output

Keeping in mind that contours will not work well with many examples

Upvotes: 3

Views: 6114

Answers (1)

Red
Red

Reputation: 27547

With the one image you provided:

enter image description here

I came up with 2 approaches as to how this problem can be solved:

Approach 1

Contour Area Comparison

As you can see there are 3 large contours in the image; the top rectangle and the two rectangles below it, of which you want to detect as a whole.

So I used a threshold on your image, detected the contours of the thresholded image, and indexed the second largest contour and the third largest contour (the largest is the top rectangle which you want to ignore).

Here is the thresholded image:

enter image description here

I stacked the two contours together and detected the bounding box of the two contours:

import cv2
import numpy as np

img = cv2.imread("image.png")

def process(img):
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    _, thresh = cv2.threshold(img_gray, 128, 255, cv2.THRESH_BINARY)
    img_blur = cv2.GaussianBlur(thresh, (5, 5), 2)
    img_canny = cv2.Canny(img_blur, 0, 0)
    return img_canny

def get_contours(img):
    contours, _ = cv2.findContours(process(img), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
    r1, r2 = sorted(contours, key=cv2.contourArea)[-3:-1]
    x, y, w, h = cv2.boundingRect(np.r_[r1, r2])
    cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)

get_contours(img)
cv2.imshow("img_processed", img)
cv2.waitKey(0)

Output:

enter image description here


Approach 2

Threshold Masking

As the 2 bottom rectangles are whiter than the top rectangle of the plate, I used a threshold to mask out the top of the plate:

enter image description here

I used the canny edge detector on the mask shown above.

import cv2

def process(img):
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    _, thresh = cv2.threshold(img_gray, 163, 255, cv2.THRESH_BINARY)
    img_canny = cv2.Canny(thresh, 0, 0)
    img_dilate = cv2.dilate(img_canny, None, iterations=7)
    return cv2.erode(img_dilate, None, iterations=7)

def get_contours(img):
    contours, _ = cv2.findContours(process(img), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
    x, y, w, h = cv2.boundingRect(max(contours, key=cv2.contourArea))
    cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)

img = cv2.imread("egypt.png")
get_contours(img)
cv2.imshow("img_processed", img)
cv2.waitKey(0)

Output:

enter image description here

Of course, this method may not work properly if the top of the plate isn't brighter than the bottom.

Upvotes: 2

Related Questions