1

I have a code which gives me binary images using Otsu thresholding. I am making a dataset for a U-Net, and I want to try different algorithms (global as well as local) for the same, so that I can save the "best" image. Below is the code for my image binarization.

import cv2
import numpy as np
import skimage.filters as filters

img = cv2.imread('math.png') # read the image
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) # convert to gray
smooth = cv2.GaussianBlur(gray, (95,95), 0) # blur
division = cv2.divide(gray, smooth, scale=255) # divide gray by morphology image

# Add Morphology here. Dilation, Erosion, Opening, Closing or so.

sharp = filters.unsharp_mask(division, radius=1.5, amount=1.5, multichannel=False, preserve_range=False) # sharpen using unsharp masking
sharp = (255*sharp).clip(0,255).astype(np.uint8)
thresh = cv2.threshold(sharp, 0, 255, cv2.THRESH_OTSU )[1]  # threshold

I am getting pretty decent results with a broader area, but I want to use cv2.namedWindow, cv2.createTrackbar, cv2.getTrackbarPos, so that I can set the values of radius, amount, kernel, dilation, erosion, etc. by using below functions.

cv2.namedWindow('Tracking Window')
cv2.createTrackbar('param1','Tracking Window',0,255,dummy) # dummy is just a dummy function which returns None
param1 = cv2.getTrackbarPos('param1','Tracking Window')

How can I get all those. Also, how can I save the image when I press s, open next image by pressing n?

Original Question was posted by me 6 months ago

HansHirse
  • 18,010
  • 10
  • 38
  • 67
Deshwal
  • 3,436
  • 4
  • 35
  • 94

1 Answers1

2

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:

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:

Other settings

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
----------------------------------------
HansHirse
  • 18,010
  • 10
  • 38
  • 67
  • Thanks a lot ! Actually I did it all by myself. Too much work. But yours is too fancy man. Need you rcode for revision. Just one question, If I want to apply erosion or dilation or so, where should I use it in my code? Between `sharpen` and `Threshold` or between `division` and `sharpen`? – Deshwal Mar 05 '21 at 11:48
  • That's more like a semantic question to your actual problem, cf. your linked original question. To be honest, I haven't reviewed that, so I'm afraid, I can't give any advice here. – HansHirse Mar 05 '21 at 11:50
  • Oh Okay! One more thing, How can I change the size of `namedWindow`. I used different flags but when I used the full screen, I am not able to resize to smaller size. Also, When I use the same Window for showing images and tracking bar, I get weird sizes as my images are of different dimensions and can not fit in window. – Deshwal Mar 05 '21 at 12:30
  • Yeah, that's quite annoying, when using trackbars, but the High-Level GUI API is just for fast prototyping. You can try [`cv2.resizeWindow`](https://docs.opencv.org/master/d7/dfc/group__highgui.html#ga9e80e080f7ef33f897e415358aee7f7e), but depending on your system, some flags might work and other not. It's like that, unfortunately. – HansHirse Mar 05 '21 at 12:32
  • So you mean to say there is no way to bring back that smaller size panel? It'll be full size no matter which flag I use? Can you please give an example? – Deshwal Mar 05 '21 at 12:54
  • For example, in my code, use something like `cv2.namedWindow(title_window, cv2.WINDOW_NORMAL)`, `cv2.resizeWindow(title_window, 200, 300)`. Attention: Width and height define the size of the whole window including image and trackbars! Since that call is inside the loop, you can adjust the window size w.r.t. each image. – HansHirse Mar 05 '21 at 13:00
  • But the problem is that I have around 10+ variables to adjust and that is why my image becomes so small. Any solution? – Deshwal Mar 05 '21 at 13:05
  • You could set up two windows, one for the actual image, one for the many sliders. But, to be honest, I don't want to rework that now... – HansHirse Mar 05 '21 at 13:11
  • No worries. Actually even before you submitted answers, I had implemented my own. Just asking for idea. Got a few ideas from yours too. Two windows is what I am using. Thanks a lot – Deshwal Mar 05 '21 at 13:29