Luca
Luca

Reputation: 10996

Extracting foreground image as mask by thresholding

I'm looking for a robust way to extract the foreground from an image where the background has some noise in it.

So, the image I want to use it on is:

Image with some background noise

My attempt was to use the Otsu thresholding. I did that in Python as follows:

from skimage.filter import threshold_otsu
import os.path as path
import matplotlib.pyplot as plt

img = io.imread(path.expanduser('~/Desktop/62.jpg'))
r_t = threshold_otsu(img[:, :, 0])
g_t = threshold_otsu(img[:, :, 1])
b_t = threshold_otsu(img[:, :, 2])

m = np.zeros((img.shape[0], img.shape[1]), dtype=np.uint8)
mask = (img[:, :, 0] < r_t) & (img[:, :, 1] < g_t) & (img[:, :, 2] < b_t)
m[~mask] = 255

plt.imshow(m)
plt.show()

This gives the R, G, B threshold as (62 67 64), which is a bit high. The result is:

Otsu result

This image is also one of the images where Otsu thresholding worked best. If I use a manual threshold like a value of 30, it works quite well. The result is:

Manual

I was wondering if there are some other approaches that I should try. Segmentation really is not my area of expertise and what I can do out of the box seem limited.

Upvotes: 1

Views: 2953

Answers (1)

NoDataDumpNoContribution
NoDataDumpNoContribution

Reputation: 10859

Your image looks not very colorful. So you can perform the segmentation on the gray values and not on each color separately and then combining three masks.

Looking at the package scikit-image.filter there are several other threshold methods. I tried them all and found threshold_isodata to perform extremely well giving almost the same image as your desired image. Therefore I recommend the isodata algorithm.

Example:

import numpy as np
import skimage.io as io
import skimage.filter as filter
import matplotlib.pyplot as plt

img = io.imread('62.jpg')
gray = np.sum(img, axis=2) # summed up over red, green, blue
#threshold = filter.threshold_otsu(gray) # delivers very high threshold
threshold = filter.threshold_isodata(gray) # works extremely well
#threshold = filter.threshold_yen(gray) # delivers even higher threshold
print(threshold)

plt.imshow(gray > threshold)
plt.show()

gives:

enter image description here

Upvotes: 3

Related Questions