CV2 error: window: 'Plate Identification' in function 'cvGetTrackbarPos

I’ve been trying to test a bacteria colony identifier code I’ve found on Github for some reference. Whenever I run the code, it shows me this:


Then when I close the window, it gives me this error on the terminal:
Traceback (most recent call last): File "c:\Users\UserPC\OneDrive\Documentos\bacteria\colony-counter-main\counter.py", line 548, in <module> main(['counter.py', 'plate2.jpg', 'w']) File "c:\Users\UserPC\OneDrive\Documentos\bacteria\colony-counter-main\counter.py", line 520, in main img_ori, img_pro = preprocess_image(img_ori) ^^^^^^^^^^^^^^^^^^^^^^^^^ File "c:\Users\UserPC\OneDrive\Documentos\bacteria\colony-counter-main\counter.py", line 94, in preprocess_image plate_mask, circle = find_plate(img_ori, img_bin) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "c:\Users\UserPC\OneDrive\Documentos\bacteria\colony-counter-main\counter.py", line 188, in find_plate radius_scale = cv2.getTrackbarPos('Plate Radius', window_name) / 100 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cv2.error: OpenCV(4.10.0) D:\a\opencv-python\opencv-python\opencv\modules\highgui\src\window_w32.cpp:2561: error: (-27:Null pointer) NULL window: 'Plate Identification' in function 'cvGetTrackbarPos'

Am I missing something? The main code counter.py itself seems to be intact:


__author__ = "Kayle Ransby - 34043590"
__credits__ = ["Kayle Ransby", "Richard Green"]
__version__ = "3.1.1"
__license__ = "???"

# Imports

import sys
import time
from csv import writer
import cv2
import numpy as np
from screeninfo import get_monitors
import os

# "Globals"

# Delay when keypress is not detected (milliseconds)
waitKeyDelay = 33

# Colour used to identify colonies
houghColor = (0, 0, 255)

# Function for outputing images
output_image = (lambda n, v: cv2.imwrite('outputs/' + n + '.png', v))

# Debugging and output controls
DEBUG = False
OUTPUTDATA = False


def preprocess_image(img_ori):
    """
    Preprocesses the input image so that it can be used in the different algorithms.
    @param img_ori: Original input image.
    @Returns: (original image - cropped, preprocessed image).
    """

    # Kernal to be used for strong laplace filtering
    kernal_strong = np.array([
        [1, 1, 1],
        [1, -8, 1],
        [1, 1, 1]],
        dtype=np.float32)

    # Kernal to be used for weak laplace filtering
    kernal_weak = np.array([
        [0, 1, 0],
        [1, -4, 1],
        [0, 1, 0]],
        dtype=np.float32)

    # Perform laplace filtering
    img_lap = cv2.filter2D(img_ori, cv2.CV_32F, kernal_weak)
    img_sharp = np.float32(img_ori) - img_lap

    # Save the sharpened image
    if DEBUG:
        output_image("sharpened.png", img_sharp)

    # Convert to 8bits gray scale
    img_sharp = np.clip(img_sharp, 0, 255).astype('uint8')
    img_gray = cv2.cvtColor(img_sharp, cv2.COLOR_BGR2GRAY)

    # Save the greyscale image
    if DEBUG:
        output_image("greyscale.png", img_gray)

    # Binarize the greyscale image
    _, img_bin = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

    # Save the binary image
    if DEBUG:
        output_image("binary.png", img_bin)

    # Remove noise from the binary image
    img_bin = cv2.morphologyEx(img_bin, cv2.MORPH_OPEN, np.ones((3, 3), dtype=int))

    # Save the noise reduced binary image
    if DEBUG:
        output_image("noise_reduction.png", img_bin)

    # Find the circular plate mask in the image
    plate_mask, circle = find_plate(img_ori, img_bin)

    cv2.circle(img_ori, (int(circle[0]), int(circle[1])), int(circle[2]), houghColor, 2)

    # Crop the original image if needed
    img_ori = crop_image(img_ori, circle)

    # If the number of white pixels is greater than the number of black pixels
    # i.e. attempt to automatically detect the mask colour
    inv = 0
    if np.sum(img_bin == 255) > np.sum(img_bin == 0):
        inv = 1

    # Create the GUI elements
    window_name = 'Colour adjustment'
    cv2.namedWindow(window_name)
    cv2.createTrackbar("Invert Plate", window_name, 0, 1, nothing)
    cv2.createTrackbar("Invert Mask", window_name, 0, 1, nothing)
    cv2.createTrackbar("Process more", window_name, 0, 1, nothing)

    # Set default parameters
    cv2.setTrackbarPos("Invert Plate", window_name, inv)
    cv2.setTrackbarPos("Invert Mask", window_name, inv)
    cv2.setTrackbarPos("Process more", window_name, 0)

    # Loop to keep the window open
    while True:
        # Read the parameters from the GUI
        inv = cv2.getTrackbarPos('Invert Plate', window_name)
        mask_inv = cv2.getTrackbarPos('Invert Mask', window_name)
        extra_processing = cv2.getTrackbarPos('Process more', window_name)

        img_pro = np.copy(img_bin)

        # Apply circular mask
        img_pro[(plate_mask == False)] = 255 * mask_inv

        # Crop the processed image if needed
        img_pro = crop_image(img_pro, circle)

        # Apply extra processing if needed
        if extra_processing == 1:
            img_pro = cv2.erode(img_pro, None)
            img_pro = cv2.dilate(img_pro, None)
            img_pro = cv2.erode(img_pro, None)

        # Invert the colours of the image, or not
        if inv == 0:
            img_show = img_pro
        elif inv == 1:
            img_show = cv2.bitwise_not(img_pro)

        # Display the image in the window
        cv2.imshow(window_name, img_show)

        # close the window
        if cv2.waitKey(waitKeyDelay) != -1:
            cv2.destroyAllWindows()
            break

    # Save the preprocessed image
    if DEBUG:
        output_image("preprocessed.png", img_show)

    # Return the (cropped) original image and processed image
    return img_ori, img_show


def find_plate(img_ori, img_bin):
    """
    Identifies the plate with input from the user using the Hough Circle Transform.
    @param img_ori: Original image.
    @param img_bin: Binary thresholded image.
    @Returns: (Plate mask, circle).
    """

    # Define the max possible plate radius as
    # half the image size
    max_possible_radius = int(min(img_bin.shape) / 2)
    circle = 0

    # Create the GUI elements
    window_name = 'Plate Identification'
    cv2.namedWindow(window_name)
    cv2.createTrackbar("Plate Radius", window_name, 0, 50, nothing)
    cv2.createTrackbar("Radius offset", window_name, 0, 200, nothing)

    # Set default parameters
    cv2.setTrackbarPos("Plate Radius", window_name, 25)
    cv2.setTrackbarPos("Radius offset", window_name, 100)

    # Loop to keep the window open
    while True:
        # Read the parameters from the GUI
        radius_scale = cv2.getTrackbarPos('Plate Radius', window_name) / 100
        max_radius = int((max_possible_radius * radius_scale) + (max_possible_radius * 0.5))
        min_radius = max_radius - 10

        radius_offset = cv2.getTrackbarPos('Radius offset', window_name) / 100

        # Find plate in the image with Hough Circle Transform
        circles = cv2.HoughCircles(img_bin, cv2.HOUGH_GRADIENT, 1, 20, param1=100,
                                    param2=10, minRadius=min_radius, maxRadius=max_radius)

        img_show = img_ori.copy()

        if circles is not None:

            # Return data of the smallest circle found
            circles = (circles[0, :]).astype("float")
            max_c = np.argmax(circles, axis=0)
            indx = max_c[2]
            circle = circles[indx]
            circle = (int(circle[0]), int(circle[1]), int(radius_offset * circle[2]))

            # Draw the outer circle
            cv2.circle(img_show, (circle[0], circle[1]), circle[2], houghColor, 2)

            # Draw the center of the circle
            cv2.circle(img_show, (circle[0], circle[1]), 2, houghColor, 3)

        cv2.imshow(window_name, img_show)

        # Close the window
        if cv2.waitKey(waitKeyDelay) != -1:
            cv2.destroyAllWindows()
            break

    # Create plate mask:
    plate_mask = np.zeros(img_bin.shape, np.uint8)
    plate_mask = cv2.circle(plate_mask, (circle[0], circle[1]), circle[2], (255, 255, 255),
                            thickness=-1)

    return plate_mask, circle


def watershed_method(img_ori, img_pro):
    """
    Colony identification using watershed transform.
    @param img_ori: Original image.
    @param img_pro: Processed image.
    @Returns: (output image, no. colonies).
    """

    # Create the border of the components
    border = cv2.dilate(img_pro, None)
    border = border - cv2.erode(border, None)

    # Create the seed(s) for the watershed transform
    dist = cv2.distanceTransform(img_pro, 2, 3)
    dist = ((dist - dist.min()) / (dist.max() - dist.min()) * 255).astype(np.uint8)
    _, dist = cv2.threshold(dist, 110, 255, cv2.THRESH_BINARY)

    # Find the markers for the watershed transform
    num_components, markers = cv2.connectedComponents(dist)

    # Completing the markers
    markers = markers * (255 / (num_components + 1))
    markers[border == 255] = 255
    markers = markers.astype(np.int32)

    # Perfoming the watershead transform
    cv2.watershed(img_ori, markers)

    # Make the markers pretty and apply them to the input image
    markers[markers == -1] = 0
    markers = markers.astype(np.uint8)
    result = 255 - markers
    result[result != 255] = 0
    result = cv2.dilate(result, None)
    img_ori[result == 255] = houghColor

    return img_ori, num_components - 1


def hough_circle_method(img_ori, img_pro):
    """
    Colony identification using Hough Circle transform.
    @param img_ori: Original image.
    @param img_pro: Processed image.
    @Returns: (output image, no. colonies).
    """

    # Create the GUI elements
    window_name = 'Hough Circle Transform'
    cv2.namedWindow(window_name)
    cv2.createTrackbar('Sensitivity', window_name, 0, 10, nothing)
    cv2.createTrackbar('Neighborhood', window_name, 0, 30, nothing)
    cv2.createTrackbar('Accumulator', window_name, 1, 50, nothing)
    cv2.createTrackbar("Min Radius", window_name, 0, 30, nothing)
    cv2.createTrackbar("Max Radius", window_name, 0, 30, nothing)

    # Set some default parameters
    cv2.setTrackbarPos("Sensitivity", window_name, 0)
    cv2.setTrackbarPos("Neighborhood", window_name, 20)
    cv2.setTrackbarPos("Accumulator", window_name, 20)
    cv2.setTrackbarPos("Min Radius", window_name, 15)
    cv2.setTrackbarPos("Max Radius", window_name, 15)

    # Loop to keep the window open
    while True:
        # Read the parameters from the GUI
        sensitivity = cv2.getTrackbarPos('Sensitivity', window_name)
        nhood = cv2.getTrackbarPos('Neighborhood', window_name)
        param2 = cv2.getTrackbarPos('Accumulator', window_name)
        min_radius = cv2.getTrackbarPos('Min Radius', window_name)
        max_radius = cv2.getTrackbarPos('Max Radius', window_name)

        # Find circles is the image with Hough Circle Transform
        circles = cv2.HoughCircles(img_pro, cv2.HOUGH_GRADIENT, sensitivity+1, nhood+1, param1=100,
                                    param2=param2+1, minRadius=min_radius+1, maxRadius=max_radius+1)

        img_show = img_ori.copy()

        if circles is not None:
            circles = np.uint16(np.around(circles))

            for i in circles[0,:]:
                # Draw the outer circle
                cv2.circle(img_show, (i[0], i[1]), i[2], houghColor, 2)


        cv2.imshow(window_name, img_show)

        # close the window
        if cv2.waitKey(waitKeyDelay) != -1:
            cv2.destroyAllWindows()
            break

    return img_show, len(circles[0])


def nothing(_):
    """
    Callback function for the createTrackbar function;
    this does nothing.
    @param _: no purpose.
    """

    pass # Nothing


def error(message, code=0):
    """
    function for throwing cleaner error messages + codes;
    preferable to throwing exceptions.
    @param message: String to be printed on error.
    @param code: scrypt exit code (default = 0).
    """

    print("Error:", message)
    sys.exit(int(code))


def load_image(filename, desired_res):
    """
    Function to load in an input image and set the width to
    the value specified in the parameter "desired_res".
    @param filename: String, file to be opened within the 'images/' directory.
    @param desired_res: Int., desired resolution of the input image (horizontal).
    @Returns: Scaled input image.
    """

    # Load the image
    img_ori = cv2.imread('images/' + filename)

    # Error checking
    if img_ori is None:
        error('Could not open or find the image: "{}".'.format(filename), 1)

    # Resize the image to the specified size
    # ONLY IF the desired resolution is smaller than our image (we don't want to upscale)
    if desired_res < min(img_ori.shape[:2]):
        scale = (desired_res / img_ori.shape[1])
        img_ori = cv2.resize(img_ori, (int(img_ori.shape[1] * scale), int(img_ori.shape[0] * scale)))

    return img_ori


def crop_image(img, mask):
    """
    Crops the given image by fitting a bounding box using
    the given circular mask; This makes the output more
    presentable.
    @param img: Image to be cropped.
    @param mask: Circular mask retrieved from Hough Transform.
    @Returns: Cropped (hopefully square) input image.
    """

    output = img

    # if the height is greater than the width;
    # i.e. portrait image
    if img.shape[0] > img.shape[1]:

        # Retrieve the coordinates & radius from circular mask
        x_pos, y_pos, radius = mask

        # Find the coordinates for the bottom left & top right of box
        x_bot = int(x_pos - radius)    # Bottom Left X
        y_bot = int(y_pos - radius)    # Bottom Left Y
        x_top = int(x_pos + radius)    # Top Right X
        y_top = int(y_pos + radius)    # Top Right Y

        # Find min distance from the edge of the box to the image border
        min_x_dist = min((img.shape[1] - x_top), (img.shape[1] - (img.shape[1] - x_bot)))
        min_y_dist = min((img.shape[0] - y_top), (img.shape[0] - (img.shape[0] - y_bot)))
        min_dist = min(min_x_dist, min_y_dist)

        # Apply remainder
        x_bot = (x_bot - min_dist)    # Bottom Left X
        y_bot = (y_bot - min_dist)    # Bottom Left Y
        x_top = (x_top + min_dist)    # Top Right X
        y_top = (y_top + min_dist)    # Top Right Y

        # Crop image using the new mask
        output = output[y_bot:y_top, x_bot:x_top]

    return output


def generate_output(img, count, method, filename, period):
    """
    Creates a fancy output image containing the processed image,
    the filename, the method used and the number of colonies
    found.
    @param img: Processed image.
    @param count: Number of colonies identified.
    @param method: Transform used to generate output.
    @param filename: Name of the file we just processed.
    @param period: Time taken (in seconds).
    @Returns: Finalized output image.
    """

    # For converting argument to text
    methods = {
        'h': 'Hough Circle Transform',
        'w': 'Watershed Transform'
    }

    # Font to use in output image
    font = cv2.FONT_HERSHEY_SIMPLEX
    font_scl = 0.5
    font_col = (0, 0, 0)

    # Create black border:
    height, width, chan = img.shape
    border_size = 20
    image_height = height + (border_size + 100)
    image_width = width + border_size
    border = np.zeros((image_height, image_width, chan), np.uint8)
    border[:,0:image_width] = houghColor

    # Process filename:
    name = filename.split("/")[-1].split(".")[0]

    # Generate text for image
    # Filename string
    file_str = 'File: "{}"'.format(name)
    cv2.putText(border, file_str, (10, image_height-80), font, font_scl, font_col, 1, cv2.LINE_AA)

    # Method used string
    method_str = 'Method: {}'.format(methods[method.lower()])
    cv2.putText(border, method_str, (10, image_height-60), font, font_scl, font_col, 1, cv2.LINE_AA)

    # Number of colonies string
    count_str = 'Colonies: {}'.format(count)
    cv2.putText(border, count_str, (10, image_height-40), font, font_scl, font_col, 1, cv2.LINE_AA)

    # Amount of time string
    time_str = 'Time: {0:.2f} s'.format(period)
    cv2.putText(border, time_str, (10, image_height-20), font, font_scl, font_col, 1, cv2.LINE_AA)

    # Place output image into the border
    border[10:10+height, 10:10+width] = img

    # Save the output file
    output_image('{}_{}_C{}'.format(name, method, count), border)

    # Save data to .csv file
    if OUTPUTDATA:
        # csv file format
        data = [name, period, count, method]

        # Open our existing CSV file in append mode
        # Create a file object for this file
        with open('data_UC_water.csv', 'a') as f_object:

            # Pass this file object to csv.writer()
            # and get a writer object
            writer_object = writer(f_object)

            # Pass the list as an argument into
            # the writerow()
            writer_object.writerow(data)

            # Close the file object
            f_object.close()

    return border


def main(args):
    """
    Main execution function
    @Param args: Command line arguments.
    """

    # Check if we have the correct number of arguments
    if len(args) != 3:
        error('Incorrect number of arguments. Usage: counter.py <file> <method>', 1)

    # Get screen resolution(s)
    res = [980]
    for mon in get_monitors():
        res.append(mon.width)
        res.append(mon.height)
    imageRes = min(set(res))

    # Load the specified image
    img_ori = load_image(args[1], imageRes)

    # Start counting the time spent counting
    start_time = time.time()

    # Preprocess the input image
    img_ori, img_pro = preprocess_image(img_ori)

    # Run specified transform method
    if args[2].lower() == 'w':
        output, colonies = watershed_method(img_ori, img_pro)

    elif args[2].lower() == 'h':
        output, colonies = hough_circle_method(img_ori, img_pro)

    else:
        error('Undefined detection method: "{}"'.format(args[2]), 1)

    # Create the "outputs" directory if it does not exist
    if not os.path.exists('outputs'):
        os.makedirs('outputs')

    # Generate the output
    output = generate_output(output, colonies, args[2], args[1], (time.time() - start_time))

    # Visualize the final image
    cv2.imshow('Final Result: {} Colonies found'.format(colonies), output)
    cv2.waitKey()


if __name__ == '__main__':

    # Uncomment this line if not running through terminal,
    # only change the second and third elements
    main(['counter.py', 'plate2.jpg', 'w'])

    #main(sys.argv)