I have problem with this function -

def click_on_portal(debug=False, roi=(561, 66, 1350, 850)): import cv2 """ Clicks on the blue portal within a specified region of interest (ROI).

Args:
    debug (bool): If True, shows debug images.
    roi (tuple): A tuple (x, y, w, h) defining the region of interest.
                 If None, the entire screen is searched.
"""
from core.img_recognition import find_blue_portal
# Capture the screen
screen_img = grab_screen_cv(debug=debug)

if screen_img is None:
    log.error("[ACTION] Failed to capture the screen.")
    return False

# Define the region of interest if not provided
if roi is None:
    # Default ROI: search the entire screen
    height, width = screen_img.shape[:2]
    roi = (0, 0, width, height)
else:
    x, y, w, h = roi
    # Ensure the ROI is within the bounds of the screen image
    height, width = screen_img.shape[:2]
    if x < 0 or y < 0 or x + w > width or y + h > height:
        log.error("[ACTION] ROI is out of bounds.")
        return False

# Extract the ROI from the screen image
x, y, w, h = roi
roi_img = screen_img[y:y+h, x:x+w]

if debug:
    # Create a copy of the screen image for drawing the ROI
    debug_img = screen_img.copy()
    cv2.rectangle(debug_img, (x, y), (x + w, y + h), (0, 255, 0), 2)
    cv2.imshow("Debug - ROI", debug_img)
    cv2.waitKey(0)

# Find the blue portal within the ROI
position = find_blue_portal(roi_img, debug=debug)

if position:
    # Adjust the position to be relative to the full screen
    roi_x, roi_y = position
    screen_x = x + roi_x
    screen_y = y + roi_y

    # Ensure the click position is within the ROI
    if (x <= screen_x <= x + w) and (y <= screen_y <= y + h):
        # Click in the middle of the portal
        mouse.click((screen_x, screen_y))
        log.info(f"[ACTION] Clicked on portal at ({screen_x}, {screen_y})")
        return True
    else:
        log.error(f"[ACTION] Portal found at ({screen_x}, {screen_y}) is outside the ROI.")
        return False
else:
    log.error("[ACTION] Blue portal not found on screen.")
    return False
def find_blue_portal(image, debug=False): """ Finds the blue/purple portal in the given image and returns the center (x, y). Returns None if not found. """ if image is None: log.error("Input image for find_blue_portal is None.") return None

hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

# --- Adjusted HSV Range for Blue-Purple/Magenta ---
lower_purple_blue = np.array([100, 50, 50])  # Start from typical blue, but lower saturation/value
upper_purple_blue = np.array([170, 255, 255]) # Extend to purple/magenta

mask = cv2.inRange(hsv, lower_purple_blue, upper_purple_blue)

# Apply morphological operations to remove noise and connect adjacent regions
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7)) # Increased kernel size
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)

if debug:
    cv2.imshow("Debug - HSV Mask", mask)
    cv2.waitKey(0)

contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

if not contours:
    log.error("No contours found for the specified color range.")
    return None

valid_contours = []
for contour in contours:
    area = cv2.contourArea(contour)

    # Increased minimum area threshold to filter out small noise particles.
    if area < 300: # Adjust this value if needed
        continue

    valid_contours.append(contour)

if not valid_contours:
    log.error("No valid contours after filtering by area.")
    return None

# Sort contours by area in descending order and pick the largest one
largest_contour = max(valid_contours, key=cv2.contourArea)

# Calculate the center of the bounding box of the largest contour
x, y, w, h = cv2.boundingRect(largest_contour)
center_x = x + w // 2
center_y = y + h // 2

if debug:
    debug_image = image.copy()
    cv2.rectangle(debug_image, (x, y), (x + w, y + h), (0, 255, 0), 2)
    cv2.circle(debug_image, (center_x, center_y), 5, (0, 0, 255), -1)
    cv2.imshow("Debug - Portal Detected", debug_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

return (center_x, center_y)
Whenever it finds the portal, it sometimes clicking outside of it.