Reputation: 10996
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:
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:
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:
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
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:
Upvotes: 3