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:
Blue image:
Red image:
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)
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.