Detect the outer boundary of gray color inside an image and draw contour


i have attached my original image and then i attached way I want to detect the contour. I have tried many ways (canny edge, sobel filter, shape recognition) but nothing worked for me. I have tried to make tracker bars to set the value of thresholding and canny edge detection but no combination works.
‘’’

import cv2 as cv
import numpy as np


# load the image
def load_image():
    """load the image in rgb format"""
    img = cv.imread("images/test_pillar.png", cv.IMREAD_COLOR)
    return img


# resize the image to 100 microns for marker length
def reshape_image(frame):
    """resize the image to 100 microns for marker length"""
    scale_percent = 80  # percent of original size
    width = int(frame.shape[1] * scale_percent / 100)
    height = int(frame.shape[0] * scale_percent / 100)

    dim = (width, height)

    # resize image
    frame = cv.resize(frame, dim, interpolation=cv.INTER_AREA)
    return frame


def image_processing(img, lower_gray, upper_gray, lower_canny, upper_canny):
    """take an rgb image and returns a canny image"""

    # convert image to rgb
    img = cv.cvtColor(img, cv.COLOR_BGR2RGB)

    gray_image = cv.cvtColor(img, cv.COLOR_RGB2GRAY)  # gray image

    blur_image = cv.GaussianBlur(gray_image, (5, 5), 0)

    # canny edge detection
    canny_image = cv.Canny(blur_image, lower_canny, upper_canny)

    # # setting threshold of gray image

    # _, threshold = cv.threshold(canny_image, lower_gray, upper_gray, cv.THRESH_BINARY)

    eroded = cv.erode(canny_image, (5, 5), iterations=1)  # type: ignore
    dilated = cv.dilate(eroded, (5, 5), iterations=1)  # type: ignore

    thresh = cv.adaptiveThreshold(dilated, 255,
                                  cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY_INV, 11, 11)

    return thresh


def nothing(x):
    pass


def add_tracker_bars(window_name):
    """add four tracker bars to the window, two for thresholding and two for canny edge detection"""

    cv.namedWindow(window_name)
    cv.createTrackbar("lower_gray", window_name, 0, 255, nothing)
    cv.createTrackbar("upper_gray", window_name, 0, 255, nothing)
    cv.createTrackbar("lower_canny", window_name, 0, 255, nothing)
    cv.createTrackbar("upper_canny", window_name, 0, 255, nothing)

    # set initial values for tracker bar
    cv.setTrackbarPos("lower_gray", window_name, 1)
    cv.setTrackbarPos("upper_gray", window_name, 10)
    cv.setTrackbarPos("lower_canny", window_name, 10)
    cv.setTrackbarPos("upper_canny", window_name, 30)


# find contours and properties of contours like area, perimeter and approx
def find_contours_and_its_properties(canny_image):
    # using a findContours() function
    contours, _ = cv.findContours(
        canny_image, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)

    return contours


# main function
def main():
    # load the image
    img = load_image()

    # reshape the image
    img = reshape_image(img)

    # add tracker bars
    window_name = "image"
    add_tracker_bars(window_name)

    while True:
        lower_gray = cv.getTrackbarPos("lower_gray", window_name)
        upper_gray = cv.getTrackbarPos("upper_gray", window_name)
        lower_canny = cv.getTrackbarPos("lower_canny", window_name)
        upper_canny = cv.getTrackbarPos("upper_canny", window_name)

        # apply image processing
        canny_image = image_processing(img, lower_gray, upper_gray, lower_canny, upper_canny)

        # find contours and its properties
        contours = find_contours_and_its_properties(canny_image)

        # # draw the contours
        img_copy = img.copy()
        cv.drawContours(img_copy, contours, -1, (255, 0, 255), 5)

        # display the image
        cv.imshow(window_name, img_copy)
        # cv.imshow("canny", canny_image)

        if cv.waitKey(1) & 0xFF == ord("q"):
            break


if __name__ == "__main__":
    main()


here is the way I want to detect the boundary of the inside shape.