I am very new to image processing in Python and need your guidance.
I want to mask my stacked images (sample of notch rock) based on edge detection. I am trying to use cv2.Canny edge detection, cv2.findContours, cv2.drawContours, cv2.threshold, and cv2.floodFill to do this. I then applied it to my image slices. However, when I check the results, some of the slices are not properly masked. I am not sure in which part should I improve my code.
I uploaded my image slices in here: Image slices
And I use this code to do the masking:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import glob
import os
from tqdm import tqdm
import cv2
# =============================================================================
# Working directory
# =============================================================================
workdir = r'filepath to image slices'
# =============================================================================
# List files
# =============================================================================
lsfiles = sorted(glob.glob(workdir+'/*.tif*'))
# =============================================================================
# Function
# =============================================================================
def gauss_canny_mask(img_fname, savfolder):
image3 = cv2.imread(img_fname,0)
blurred_image = cv2.GaussianBlur(image3.copy(),(5,5),0)
def auto_canny(image, sigma=0.33):
# compute the median of the single channel pixel intensities
v = np.median(image)
# apply automatic Canny edge detection using the computed median
lower = int(max(0, (1.0 - sigma) * v))
upper = int(min(255, (1.0 + sigma) * v))
edged = cv2.Canny(image, lower, upper)
# return the edged image
return edged
edges3 = auto_canny(blurred_image)
contours, hierarchy = cv2.findContours(edges3, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
image_copy = image3.copy()
cv2.drawContours(image=image_copy, contours=contours, contourIdx=-1, color=(0, 255, 0), thickness=2, lineType=cv2.LINE_AA)
th, im_th = cv2.threshold(image_copy, 1, 30, cv2.THRESH_BINARY_INV);
# Copy the thresholded image.
im_floodfill = im_th.copy()
# Mask used to flood filling.
# Notice the size needs to be 2 pixels than the image.
h, w = image_copy.shape[:2]
mask = np.zeros((h+2, w+2), np.uint8)
# Floodfill from point (0, 0)
cv2.floodFill(im_floodfill, mask, (0,0), 255);
# Invert floodfilled image
im_floodfill_inv = cv2.bitwise_not(im_floodfill)
# Combine the two images to get the foreground.
im_out = im_th | im_floodfill_inv
masked_img = image3.copy()
img_out = np.array(im_out !=0, dtype=bool)
masked_img[~img_out] = 0
masked_img = Image.fromarray(masked_img)
masked_img.save(savfolder)
# =============================================================================
# Test function
# =============================================================================
for img_file_name in lsfiles:
savfolder = '\\'.join(workdir.split('\\')[:-1])+'\\'+'Mask'+'\\'+'Mask_'+img_file_name.split('\\')[-1]
gauss_canny_mask(img_file_name, savfolder)
Any guidance and help would be very much appreciated!
Thanks so much!
EDIT: This is example of expected masked image that I want to have Masked image