How to apply threshold to segment grey region out from the image?

Viewed 72

Sample Image:

1

Here's my code

import cv2 
import numpy as np 


image = cv2.imread("1.bmp")

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

ret, thresh1 = cv2.threshold(img_grey,120,255,cv2.THRESH_BINARY)
ret, thresh = cv2.threshold(img_grey,120,255,cv2.THRESH_BINARY_INV)

cv2.imshow("image 1", thresh1)
cv2.imshow("image 2", thresh)

cv2.waitKey(0)

Any idea to segment the grey region ? I thought some sort of subtracting the white and black region using raw image but not working.

2 Answers

Option 1:

You need to place different threshold values but the same binary operation. Then try subtracting the result:

ret, thresh1 = cv2.threshold(img_grey,20,255,cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img_grey,235,255,cv2.THRESH_BINARY)

result = thresh1 - thresh2

cv2.imshow("Result", result)
cv2.waitKey(0)

See the images of thresh1 and thresh2 to understand what's going on.

And if you are worried about how to subtract use cv2.subtract(), it handles pixel range:

result2 = cv2.subtract(thresh1, thresh2)
cv2.imshow("Result2", result2)
cv2.waitKey(0)

The output does not provide the desired result.

Option 2:

You can also use multi-Otsu threshold module available from skimage. This module provides threshold values based on the number of regions you want to segment the image.

  • If you want to segment the image into 3 regions (A, B, C), the function gives you 2 threshold values (t1, t2).
    • 0 - t1 --> region A
    • t1 - t2 --> region B
    • t2 - 255 --> region C

The same can be extended for any number of regions, but the function takes more time to calculate threshold values for regions greater than 5.

Number of Threshold values = Number of regions - 1

Code:

# import the module
from skimage.filters import threshold_multiotsu

im = cv2.imread('cells.png', 0)
# create black screen of same image shape
b = np.zeros(im.shape, np.uint8)

# find threshold values for 3 regions
thresholds = threshold_multiotsu(im,3)
print(thresholds)
>>> [ 65 189]     # 2 threshold values

# finding average value 
mean = int(np.mean(thresholds))
b[im == mean] = 255

enter image description here

Option 2 would be a more statistical way of determining threshold value for any image

Here are two ways to do that in Python/OpenCV/Numpy. The first method uses cv2.inRange() color thresholding. The second method use Numpy. Both threshold so that the gray region becomes white and the rest black.

Input:

enter image description here

import cv2
import numpy as np

# read input
img = cv2.imread("white_gray_black.png")

# Method 1 Use cv2.inRange
low = (127,127,127)
high = (127,127,127)
mask1 = cv2.inRange(img, low, high)
cv2.imwrite("white_gray_black_mask1.png",mask1)

# Method 2 Use Numpy
mask2 = img.copy()
mask2[img==127] = 255
mask2[img!=127 ] = 0
cv2.imwrite("white_gray_black_mask2.png",mask2)

cv2.imshow('mask1', mask1)
cv2.imshow('mask2', mask2)
cv2.waitKey(0)
cv2.destroyAllWindows()

Result 1:

enter image description here

Result 2:

enter image description here

If you need to preserve the gray, then the second method is easier.

# Method 3 Use Numpy
mask3 = img.copy()
mask3[img!=127] = 0
cv2.imwrite("white_gray_black_mask3.png",mask3)

cv2.imshow('mask3', mask3)
cv2.waitKey(0)
cv2.destroyAllWindows()
Related