Search code examples
python-3.xopencvimage-thresholding

Otsu's method thresholding making a 'shroud'


I'm trying to threshold an image using Otsu's method in Opencv:

input-image

Although when I threshold it, some parts of the picture are completely surrounded by white and creates and ends up in Opencv not detecting all the contours in the image. This is what I get when I do Otsu's method thresholding usingret,thresh=cv2.threshold(blurred,0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU):

thresholding-the-image

EDIT: Some people have asked for the code I am using so here it is:

gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)

cv2.imshow('Input Image', image)
cv2.waitKey(0)

blurred = cv2.GaussianBlur(gray, (5, 5), 0)
thresh = cv2.adaptiveThreshold(blurred,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
                               cv2.THRESH_BINARY_INV,81,2)
#ret, thresh = cv2.threshold(blurred,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
kernel = np.ones((5,5),np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
closing = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
#thresh_value = 70
#ret,thresh= cv2.threshold(blurred,thresh_value,255,cv2.THRESH_BINARY)

Now it makes some checkered noise: WOAH


Solution

  • You do not need to manually find a sweet spot! Let OpenCV do it for you!

    OpenCV has an adaptive thresholding algorithm exactly from problems like this, called adaptiveThreshold

    This function divides the image into multiple sub-images, and thresholds each one individually. This means that it will find a nice threshold value for each part of the image and give you a nice and uniformly lit image. See this example.

    Try this:

    th3 = cv.adaptiveThreshold(blurred,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,\
            cv.THRESH_BINARY,11,2)
    

    Update: Functions like these do not work perfectly out of the box. If it still creates artefacts like salt and pepper noise, you can try:

    • Significantly increasing the blockSize. This can ensure that each block has a letter inside, which will hopefully mean the threshold will be chosen better. (e.g. Dividing the image into 25 blocks instead of 100. A blocksize of 11 pixels is very small.)
    • First apply a blurring filter to ease out the bad spots creating the seasoning noise. (With the image name blurry I imagine that you've done this already.
    • First the simple threshold function to just removes some noise. For example setting all pixels above 5 and below 100 equal to zero. Then after that apply the adaptiveThreshold.
    • Follow @Mark`s advice by subtracting a blurred image from the original image. (See this thread)

    I hope this helps!