The code of my solution got longer than expected, but it offers some fancy manipulation possibilities. First of all, let's the see the actual window:

There are sliders for
- the morphological operation (dilate, erode, close, open),
- the structuring element (rectangle, ellipse, cross), and
- the kernel size (here: limited to the range
1 ... 21
).
The window name reflects the current settings for the first two sliders:

When pressing s, the image is saved incorporating the current settings:
Saved image as Erosion_Ellipsoid_SLEM_11.png.
When pressing n, the next image from a list is selected.
At any time, when pressing q, the application is exited. It ends automatically after processing all images from the list.
Before and after the interactive part, you can add any operation you want, cf. the code.
And, here's the full code:
import cv2
# Collect morphological operations
morphs = [cv2.MORPH_DILATE, cv2.MORPH_ERODE, cv2.MORPH_CLOSE, cv2.MORPH_OPEN]
# Collect some texts for later
morph_texts = {
cv2.MORPH_DILATE: 'Dilation',
cv2.MORPH_ERODE: 'Erosion',
cv2.MORPH_CLOSE: 'Closing',
cv2.MORPH_OPEN: 'Opening'
}
# Collect structuring elements
slems = [cv2.MORPH_RECT, cv2.MORPH_ELLIPSE, cv2.MORPH_CROSS]
# Collect some texts for later
slem_texts = {
cv2.MORPH_RECT: 'Rectangular SLEM',
cv2.MORPH_ELLIPSE: 'Ellipsoid SLEM',
cv2.MORPH_CROSS: 'Cross SLEM'
}
# Collect images
images = [...]
# Set up maximum values for each slider
max_morph = len(morphs) - 1
max_slem = len(slems) - 1
max_ks = 21
# Set up initial values for each slider
morph = 0
slem = 0
ks = 1
# Set up initial working image
temp = None
# Set up initial window name
title_window = 'Interactive {} with {}'.format(morph_texts[morphs[morph]],
slem_texts[slems[slem]])
# Triggered when any slider is manipulated
def on_trackbar(unused):
global image, ks, morph, slem, temp, title_window
# Get current slider values
morph = cv2.getTrackbarPos('Operation', title_window)
slem = cv2.getTrackbarPos('SLEM', title_window)
ks = cv2.getTrackbarPos('Kernel size', title_window)
# Reset window name
cv2.setWindowTitle(title_window, 'Interactive {} with {}'.
format(morph_texts[morphs[morph]],
slem_texts[slems[slem]]))
# Get current morphological operation and structuring element
op = morphs[morph]
sl = cv2.getStructuringElement(slems[slem], (ks, ks))
# Actual morphological operation
temp = cv2.morphologyEx(image.copy(), op, sl)
# Show manipulated image with current settings
cv2.imshow(title_window, temp)
# Iterate images
for image in images:
# Here go your steps before the interactive part
# ...
image = cv2.threshold(cv2.cvtColor(image.copy(), cv2.COLOR_BGR2GRAY),
0, 255, cv2.THRESH_OTSU)[1]
# Here starts the interactive part
cv2.namedWindow(title_window)
cv2.createTrackbar('Operation', title_window, morph, max_morph, on_trackbar)
cv2.createTrackbar('SLEM', title_window, slem, max_slem, on_trackbar)
cv2.createTrackbar('Kernel size', title_window, ks, max_ks, on_trackbar)
cv2.setTrackbarMin('Kernel size', title_window, 1)
on_trackbar(0)
k = cv2.waitKey(0)
# Exit everytime on pressing q
while k != ord('q'):
# Save image on pressing s
if k == ord('s'):
# Here go your steps after the interactive part, but before saving
# ...
filename = '{} {} {}.png'.\
format(morph_texts[morphs[morph]],
slem_texts[slems[slem]],
ks).replace(' ', '_')
cv2.imwrite(filename, temp)
print('Saved image as {}.'.format(filename))
# Go to next image on pressing n
elif k == ord('n'):
print('Next image')
break
# Continue if any other key was pressed
k = cv2.waitKey(0)
# Actual exiting
if k == ord('q'):
break
Hopefully, the code is self-explanatory. If not, don't hesitate to ask questions. You should be able to easily add every slider you additionally need by yourself, e.g. for the filters.unsharp_mask
stuff.
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.16299-SP0
Python: 3.9.1
OpenCV: 4.5.1
----------------------------------------