Search code examples
pythonopencvhsv

How to fix Black digit detection in hsv?


I am trying to detect the digits and am unable to do for digits written with black pen. My code works perfectly for digits written in other color apart from black.

Black image:

Need To detect this

Blue image:

e

Red image:

enter image description here

img = cv2.imread("blue.jpg")
image = cv2.resize(img, (660, 600))

hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, (0, 65, 0), (179, 255, 255))
mask_inv = cv2.bitwise_not(mask)
ret, thresh = cv2.threshold(mask_inv, 127, 255, cv2.THRESH_BINARY_INV)
kernel = np.ones((15, 15), np.uint8)
img_dilation = cv2.dilate(thresh, kernel, iterations=1)
ctrs, hier = cv2.findContours(img_dilation.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
sorted_ctrs = sorted(ctrs, key=lambda ctr: cv2.boundingRect(ctr)[0])

for i, ctr in enumerate(sorted_ctrs):
    x, y, w, h = cv2.boundingRect(ctr)
    roi = mask_inv[y:y + h, x:x + w]
    if h > 30 and w < 150:

        cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow('ROIs', image)

Solution

  • In the cases when we need to segment two contrasting unknown colors(blue or black) from a relatively consistent background color, we can use cv2.threshold() with OTSU or cv2.adaptiveThreshold().

    Since the ink color is not known beforehand, so defining a HSV range won't work for all the cases. I would prefer cv2.adaptiveThreshold() over OTSU due to it's adaptive nature. Expected output can be achieved as:

    def get_mask(img):
        img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        return cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 3, 4)
    

    You can tweak the parameters for different image sizes but these would work for most of them. You can read more about cv2.adaptiveThreshold() in docs.

    Outputs:

    enter image description here

    enter image description here