keep everything same, and add the mexican hat profile to enhance the edges, so it is easier make the circle around it and then crop ir

## Crop the image around detected red circle ##

import cv2
import numpy as np
import matplotlib.pyplot as plt

# Read the image    
src = cv2.imread('/Users/abhi/Downloads/Zerodur/Sample 8_Fine.tif')
imgray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)

# Blur the image to suppress artefacts
# in the binary image while preserving edges
median = cv2.medianBlur(imgray, 15)

# Do a global thresholding
ret, thresh = cv2.threshold(median, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# Morphological operations to reduce noise
kernel = np.ones((5, 5), np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=1)
closing = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, kernel, iterations=1)

# Copy image to show binary image (findContours manipulates source)
copy = closing.copy()

# Detect just external contours
contours, hierarchy = cv2.findContours(copy, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
height, width, channels = src.shape

# Variable to store the largest circle contour
largest_circle_contour = None
largest_circle_area = 0

# Loop through contours to find the largest one
for contour in contours:
    # Check if the contour has at least 5 points
    if len(contour) >= 10:
        # Calculate the area of the contour
        area = cv2.contourArea(contour)
        
        # Calculate the circularity of the contour
        perimeter = cv2.arcLength(contour, True)
        circularity = 4 * np.pi * (area / (perimeter ** 2)) if perimeter > 0 else 0
        
        # Check if the contour is circular enough and has a larger area
        if circularity > 0.01 and area > largest_circle_area:  ########### Adjust circularity threshold as needed #############
            largest_circle_contour = contour
            largest_circle_area = area

# Create a copy of the original image to draw the contour on
src_with_contour = src.copy()

# Draw the largest circle contour if found
if largest_circle_contour is not None:
    # Draw the contour in blue
    cv2.drawContours(src_with_contour, [largest_circle_contour], 0, (255, 0, 0), 2)
    
    # Find the smallest enclosing circle
    (x, y), radius = cv2.minEnclosingCircle(largest_circle_contour)
    center = (int(x), int(y))
    
    # Draw the enclosing circle in red
    cv2.circle(src_with_contour, center, int(radius), (0, 0, 255), 2)
    
    # Optionally draw the fitted ellipse in green
    try:
        ellipse = cv2.fitEllipse(largest_circle_contour)
        # Check if the ellipse parameters are valid
        if ellipse[1][0] > 0 and ellipse[1][1] > 0:
            cv2.ellipse(src_with_contour, ellipse, (0, 255, 0), 2)
        else:
            print("Invalid ellipse parameters:", ellipse)
    except Exception as e:
        print("Error fitting ellipse:", e)
    
    # Draw a blue dot at the center of the smallest enclosing circle
    cv2.circle(src_with_contour, center, 5, (255, 0, 0), -1)  # Blue dot with a thickness of -1 (filled)

    # Create a mask with the same size as the image, initialized to zero
    mask = np.zeros_like(src)

    # Draw a white circle on the mask
    cv2.circle(mask, center, int(radius), (255, 255, 255), -1)

    # Convert the mask to a float and normalize to [0, 1]
    mask = mask.astype(np.float32) / 255.0

    # Apply the mask to the original image
    cropped_image = src * mask

    # Convert the cropped image to uint8
    cropped_image = cropped_image.astype(np.uint8)

    # Create a circular mask for cropping
    y, x = np.ogrid[:height, :width]
    mask_circle = np.zeros((height, width), dtype=np.uint8)
    cv2.circle(mask_circle, center, int(radius), 255, -1)

    # Use the mask to crop the circular region
    cropped_circular_image = cv2.bitwise_and(cropped_image, cropped_image, mask=mask_circle)

    # Convert images from BGR to RGB for matplotlib
    src_rgb = cv2.cvtColor(src, cv2.COLOR_BGR2RGB)
    median_rgb = cv2.cvtColor(median, cv2.COLOR_GRAY2RGB)
    thresh_rgb = cv2.cvtColor(thresh, cv2.COLOR_GRAY2RGB)
    opening_rgb = cv2.cvtColor(opening, cv2.COLOR_GRAY2RGB)
    closing_rgb = cv2.cvtColor(closing, cv2.COLOR_GRAY2RGB)
    src_with_contour_rgb = cv2.cvtColor(src_with_contour, cv2.COLOR_BGR2RGB)
    cropped_circular_image_rgb = cv2.cvtColor(cropped_circular_image, cv2.COLOR_BGR2RGB)

    print("Threshold value (Otsu):", ret)

    # Plot the images using matplotlib
    plt.figure(figsize=(15, 7.5))

    plt.subplot(2, 4, 1)
    plt.title('Original Image')
    plt.imshow(src_rgb)
    plt.axis('off')

    plt.subplot(2, 4, 2)
    plt.title('Median Blurred Image')
    plt.imshow(median_rgb)
    plt.axis('off')

    plt.subplot(2, 4, 3)
    plt.title('Binary Image')
    plt.imshow(thresh_rgb, cmap='gray')
    plt.axis('off')

    plt.subplot(2, 4, 4)
    plt.title('Largest Circle Contour on Original Image')
    plt.imshow(src_with_contour_rgb)
    plt.axis('off')

    plt.subplot(2, 4, 5)
    plt.title('Cropped Circular Image')
    plt.imshow(cropped_circular_image_rgb)
    plt.axis('off')

    plt.tight_layout()
    plt.show()