I analyze images with particles, as e.g.
Particles - raw image:
As visible, some of the particles are broken and the program does not always recognize them correctly, as visible in images with contours and colour marks:
Image with contours:
Image with colour marks
My code for analysis is as follows:
import cv2
import numpy as np
from IPython.display import Image, display
from matplotlib import pyplot as plt
from skimage import io, img_as_float, color, measure, img_as_ubyte
from skimage.segmentation import clear_border
from skimage.restoration import denoise_nl_means, estimate_sigma
path = 'xxx'
# import image and sigma factor for denoising
img_sigma = img_as_float(io.imread(path))
img = img_sigma[0:1024, :]
sigma_est = np.mean(estimate_sigma(img_sigma))
img_ori = cv2.imread(path)
img_ori = img_ori[:1024, :]
sigma_est
# denoise the image
sigma_fak = 2
denoise_img = denoise_nl_means(img, h = sigma_fak*sigma_est, fast_mode = True, patch_size = 5, patch_distance = 5)
denoise_img_ubyte = img_as_ubyte(denoise_img)
denoise_img = cv2.medianBlur(denoise_img_ubyte, 5)
#denoise_img = denoise_nl_means(denoise_img, fast_mode = True, patch_size = 3, patch_distance = 3)
#denoise_img_ubyte = img_as_ubyte(denoise_img)
# turn into gray scale
gray = denoise_img_ubyte
# tresholding
ret, bin_img = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# smoothing the image
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
#kernel = np.ones((5,5), np.uint8)
#bin_img = cv2.morphologyEx(bin_img, cv2.MORPH_CLOSE, kernel, iterations=3)
bin_img = cv2.morphologyEx(bin_img, cv2.MORPH_OPEN, kernel, iterations=2)
# removal of the objects that touch the edge of the image
bin_img = clear_border(bin_img)
# sure background area
sure_bg = cv2.dilate(bin_img, kernel, iterations=15)
# Distance transform
dist = cv2.distanceTransform(bin_img, cv2.DIST_L2, 5)
# Make the distance transform normal.
dist = cv2.normalize(dist, None, 0, 1.0, cv2.NORM_MINMAX)
# foreground area
ret, sure_fg = cv2.threshold(dist, 0.15 * dist.max(), 255, cv2.THRESH_BINARY)
sure_fg = sure_fg.astype(np.uint8)
# unknown area
unknown = cv2.subtract(sure_bg, sure_fg)
# Marker labelling sure foreground
ret, markers = cv2.connectedComponents(sure_fg)
markers += 255
markers[unknown == 255] = 0
# watershed Algorithm
gray = cv2.cvtColor(gray, cv2.COLOR_GRAY2RGB)
markers = cv2.watershed(gray, markers)
# drawing contours from color marks
labels = np.unique(markers)
coins = []
for label in labels[3:]:
target = np.where(markers == label, 255, 0).astype(np.uint8)
contours, hierarchy = cv2.findContours(target, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
coins.append(contours[0])
#img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.drawContours(img_ori, coins, -1, color=(0, 255, 0), thickness=2)
img2 = color.label2rgb(markers, bg_label=255)
img2[img2 == 0] = 0`
For every image I play with factors in order to adjust the results such as:
adding additional blur method (Median) and/or changing patch_size and patch_distance of denoise_nl_means function
denoise_img = denoise_nl_means(img, h = sigma_fak*sigma_est, fast_mode = True, patch_size = 5, patch_distance = 5)
denoise_img = cv2.medianBlur(denoise_img_ubyte, 5)
number of iterations for the sure background area
sure_bg = cv2.dilate(bin_img, kernel, iterations=15)
fraction for distance transform for sure foreground:
ret, sure_fg = cv2.threshold(dist, 0.15 * dist.max(), 255, cv2.THRESH_BINARY) sure_fg = sure_fg.astype(np.uint8)
number of iterations for opening:
bin_img = cv2.morphologyEx(bin_img, cv2.MORPH_OPEN, kernel, iterations=2)
What can I do in order to be sure, that the cracks of the partciles are blurred/ignored, but not the borders between the partciles? And is there any way to automate it, in the way, that parameters are chosen by the program based on the image?
You could try a pre-processing step to fill in the cracks beforehand - since the cracks seem to consistently be of a lower intensity than the actual background something like this should work:
This might also create some white noise in your background depending on how high you choose
CRACK_INTENSITY_TH
, this should however by removed during the processing steps of your WST. If not you could also add a small opening operation as part of the pre-processing which will take care of this as well.