Cristian Desivo
Cristian Desivo

Reputation: 305

Is there a way to thresholding an image such that it ignores as much as possible shadows with opencv?

I want to be able to find the bounding boxes of digits in images that may or may not have shadows in it.

enter image description here

To do that I convert the image to grayscale, then to black and white and then I find the digits with cv2.findCountours()

img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img = cv2.bitwise_not(img)
img = cv2.GaussianBlur(img,(3,3),0)
cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU,img)
contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)

But in the last example I get this black and white image:

enter image description here

Which doesn't allow the find contours function to work well.

Is there a way to solve this problem?

Upvotes: 0

Views: 346

Answers (1)

Andreas
Andreas

Reputation: 485

Otsu's threshold is not the right option. It is correct given a histogram with bimodal distribution. Read more here.

Among many alternatives is adaptive threshold.

import cv2

img = cv2.imread("path/to/image")
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img = cv2.bitwise_not(img)
img = cv2.GaussianBlur(img, (3, 3), 0)
# _, img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 401)
contours, _, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

You here have to provide a value of the size of the block size of the adaptive kernel. I think 401 worked fine here, but it might not work on your other images.

For a little simpler solution, here is one using the OpenCV Wrapper library:

import cv2
import opencv_wrapper as cvw
import numpy as np

img = cv2.imread("masterproject/numbers.jpg")
img = cvw.bgr2gray(img)
img = ~img.astype(np.uint8)  # Not part of the library, this is numpy. Only works with uint8
img = cvw.blur_gaussian(img, 3)
img = cvw.threshold_adaptive(img, 401)
contours = cvw.find_external_contours(img)
cvw.draw_contours(img, contours, cvw.Color.GREEN)

Upvotes: 1

Related Questions