Image processing - ECE-180D-WS-2024/Wiki-Knowledge-Base GitHub Wiki

Introduction

Images have a powerful way of capturing the essence of our world, telling their own unique story through a wealth of information. This treasure trove of data can be unlocked and explored through image processing, a technique that stands at the intersection of technology and creativity. In our digital age, computers are not just passive viewers but active interpreters of imagery, employing a blend of computer vision and image processing to 'see' and understand much like we do. This tutorial will guide you through image analysis with Python, ensuring your images are not just seen but truly understood. We'll walk you through image preprocessing techniques - from basic resizing and cropping to more advanced methods like noise reduction and edge detection. With libraries such as OpenCV and Pillow, you'll enhance images easily. It's time to roll up your sleeves and immerse yourself in a comprehensive guide to mastering image processing techniques. Welcome to a world where images are not just viewed but truly comprehended.

What is an image?

An image is a way to capture and display visuals using pixels, tiny dots that together form a complete picture. You can think of an image as a map, where each pixel's location is determined by coordinates (x,y) in a two-dimensional space. When these pixels have specific, finite values, we're talking about a digital image, neatly organized into a grid of rows and columns. These pixels aren't just placeholders; they carry vital information about the intensity and color of the image. Beyond just flat, images can also be expanded into three dimensions, adding a z-coordinate to offer depth, making the pixels part of a larger matrix. Pixel values, which range from 0 to 255, indicate the intensity or brightness of that spot in the image. For example, a simple 20 x 20 image is made up of a matrix with 400 of these pixel values. Images come in various types, with RGB images being one of the most common. These images use three layers (red, green, and blue) to create a full-color picture. Grayscale images, on the other hand, simplify things by using shades of gray, ranging from black to white, to depict the visual content, relying on a single layer for information.

What is image processing and why is it important?

Image processing plays a crucial role in transforming raw image data into a format that's both usable and meaningful. This transformation is key to the field of computer vision, enabling the removal of unwanted distortions and the enhancement of specific features critical for various applications. It's important to distinguish between image processing and computer vision, though they are closely related. Both fields use images as input, but the distinction lies in the output: image processing outputs another image, refined or adjusted based on specific criteria, while computer vision seeks to extract features or information from the image for further analysis or decision-making. This distinction underscores the symbiotic relationship between the two, showcasing how image processing serves as a foundational step in the broader scope of computer vision tasks. Image processing encompasses a variety of techniques that are essential for preparing images for analysis, especially for machine learning and computer vision. Here are some key methods:

Resizing: To ensure consistency across your dataset, images must often be adjusted to a standard size. This is crucial for machine learning models to process the data efficiently. OpenCV offers a resize() function for this purpose.

Grayscaling: Simplifying images by converting them from color to grayscale can make them easier to process and reduce the computational load. OpenCV's cvtColor() method is perfect for transforming RGB images into grayscale.

Noise Reduction: Images often come with a certain amount of noise or distortion. Applying smoothing, blurring, and filtering techniques helps clean up these images, making them clearer and more suitable for analysis. These techniques are readily available in OpenCV.

Binarization: For some applications, converting grayscale images into a stark contrast of black and white can be very useful. This is done through a process called binarization, which applies a threshold to determine which pixels should be black and which should be white. OpenCV's threshold() method is used for binarization. To begin working with image processing in Python, the first step is loading your images into a format easily manipulated by libraries such as OpenCV and Pillow.

Loading Images with OpenCV OpenCV is a library that supports a range of image formats, including PNG, JPG, TIFF, and BMP. To load an image using OpenCV, you'll start by importing the library and then using the cv2.imread() function. Here's how:

import cv2
image = cv2.imread('path/to/image.jpg')

This command loads the image into a NumPy array, with the colors represented in the BGR (Blue, Green, Red) color space, which is the default for OpenCV.

Loading Images with Pillow Pillow, an accessible fork of the Python Image Library (PIL), expands the range of supported image formats even further, including PSD, ICO, and WEBP, among others. Loading an image with Pillow is straightforward:

from PIL import Image
image = Image.open('path/to/image.jpg')

This method loads the image in the RGB (Red, Green, Blue) color space, making it ready for further processing.

Converting Between Color Spaces

Depending on your project's needs, you might find it necessary to convert images between different color spaces, such as from RGB to Grayscale, BGR to HSV, etc. Both OpenCV and Pillow offer easy-to-use functions for these conversions. Converting with OpenCV: To convert an image from BGR to Grayscale:

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

Converting with Pillow: To change an RGB image to HSV:

image = image.convert('HSV')

In machine learning, consistency is key, especially when it comes to the size of your input images. Algorithms typically work best with images of uniform dimensions, making it necessary to resize and crop images to common square sizes, such as 224x224 or 256x256 pixels. Fortunately, this can be achieved easily using either PIL or OpenCV.

Resizing Images with OpenCV

OpenCV offers a straightforward approach to resizing images through its resize() function. Here's a quick example:

import cv2
image = cv2.imread('original.jpg')
resized = cv2.resize(image, (224, 224))

This snippet resizes the image to a 224x224 pixel square, ensuring that it meets the standard dimension requirement.

Cropping Images to a Square with OpenCV

To crop an image to a square, first, calculate the dimensions of the center square, then use slicing to crop it:

height, width = image.shape[:2]
size = min(height, width)
x = (width - size)//2
y = (height - size)//2
cropped = image[y: y + size, x: x + size]

This method focuses on the central part of the image, cropping it into a square based on the shortest dimension.

Resizing and Cropping with PIL PIL (Python Imaging Library), through its friendly fork Pillow, also supports resizing and cropping operations:

from PIL import Image
image = Image.open('original.jpg)
resized = image.resize(224, 224)
width, height, = image.size
size = min(width, height)
left = (width, height)/2
top = (height - size)/2
right = (width + size)/2
bottom = (height + size)/2
cropped = image.crop(left, top, right, bottom))

This process first resizes the image to the desired dimensions, then calculates the coordinates for a center square crop, and finally crops the image to these dimensions. Whether using PIL or OpenCV, these methods allow for the preparation of images in a uniform size and shape, a crucial step for successful machine learning model training.

Applying Filters to Reduce Noise and Sharpen Images

It's common to encounter raw data that contains noise, or undesirable elements, which can obscure the true information in an image. Using such images for feature extraction often results in diminished algorithm accuracy. Noise in images can manifest in various forms, such as Gaussian noise, which gives a grainy appearance, or salt and pepper noise, characterized by random white and black pixels scattered across the image. We can combat these visual intruders by applying specific filters designed to either eliminate or significantly reduce their presence. The effectiveness of a filter largely depends on the type of noise it's up against, meaning that different filters excel in handling different kinds of noise. To illustrate, We'll start with a grayscale image of a rose and intentionally add salt and pepper noise to it. Following this, we'll apply various filters to this deliberately noised image and observe which filter performs best in clearing up the noise. You can check for the code in the appendix.

Reducing Noise and Enhancing Image Clarity with Filters

After successfully loading images into Python, the next step is to refine them for analysis. Image filters play a crucial role in this phase, helping to diminish noise, accentuate details, and generally uplift the visual quality of images. Let's delve into some notable filters and their effects.

Gaussian Blur The Gaussian blur filter is renowned for its ability to soften images. It works by applying a Gaussian function across each pixel and its neighbors, effectively blurring the image. This process not only reduces noise and detail but also smooths out edges, making it an excellent preparatory step for subsequent image processing tasks like edge detection.

Median Blur

Among the various filters at our disposal, the Median Blur stands out for its effectiveness in cleaning up images. This filter excels at removing the pesky "salt and pepper" noise, which is characterized by random occurrences of black and white pixels. The Median Blur operates by taking each pixel and setting its value to the median of the values in its surrounding neighborhood. This method is particularly adept at smoothing out the noise of isolated pixels without blurring the edges of the image, thus maintaining the clarity and integrity of its borders.

When comparing the Median Blur Filter in Figure 3 to the original grayscale version, the filtered image remarkably mirrors the initial one in both appearance and quality. The intensity and brightness levels are preserved, and even the luminous areas on the rose are distinctly emphasized. This demonstrates the Median Blur Filter's exceptional capability in mitigating the effects of salt and pepper noise, effectively restoring the image's original clarity and detail.

Laplacian Filter

The Laplacian filter is a powerful tool for edge detection within images. It identifies regions where there's a significant shift in intensity, effectively bringing the edges to the forefront. The result is an image where edges are prominently outlined, making it particularly useful for edge detection applications. This capability is invaluable for feature identification and extraction, enhancing the process of analyzing and interpreting images by emphasizing structural details.

We can conclude that the Laplacian Filter is not a good option for salt and paper noise reduction because it enhances edges, making this random, sharp noise more pronounced instead of removing it.

Detecting and Removing Backgrounds With Segmentation Removing backgrounds from images is a crucial preprocessing step for many computer vision tasks, enhancing the focus on the primary subject. One effective technique for this is image segmentation, which isolates the subject from its background, creating a cleaner image for analysis. Python, with the help of OpenCV, offers several methods for performing segmentation.

Thresholding

Thresholding is a simple yet powerful method for separating an image into a binary format, distinguishing between black and white pixels. This method is particularly effective for images with strong contrast and consistent lighting. By setting a specific threshold value, pixels darker than this threshold are turned black, while those lighter are turned white, resulting in a clear distinction between the subject and its background. In practice, thresholding transforms the image into a binary format where each pixel is assigned either a value of 0 (black) or 1 (white). For example, with a threshold (T) value of 125, pixels with values above 125 are set to 1, and those with values at or below 125 are set to 0. This can be achieved in OpenCV using the threshold() function, which allows for the precise manipulation of images to achieve the desired binary outcome. Let's explore how this is implemented in code for a clearer understanding.

Edge Detection

Edge detection focuses on identifying the boundaries of objects within an image. By pinpointing and connecting these edges, it becomes possible to distinguish the main subjects from the background. The Canny edge detector is one of the most renowned algorithms for this purpose. A common approach to edge detection is to use the Canny edge detector, which is available in OpenCV as the cv2.Canny() function. To optimize edge detection, you can adjust parameters like low_threshold and high_threshold, which fine-tune the sensitivity of the detection process. Adjusting these thresholds allows for more precise control over which edges are detected, enabling clearer separation and isolation of foreground elements.

Here are three key steps to discuss. Firstly, it reduces noise in the image, which is a common preliminary step in image processing. Secondly, it identifies edges by finding points where the first derivative of the image intensity sharply increases, indicating a potential edge. Finally, it applies thresholding to determine the actual edges. This technique uses two thresholds - a high and a low one. Points above the high threshold are immediately marked as edges. Points between the two thresholds are only considered edges if they are adjacent to points already identified as edges. This method helps to retain true edges while reducing false positives and negatives, making edge detection more reliable.

K-Means Clustering

K-Means clustering is a powerful and widely used unsupervised machine learning algorithm that partitions data into K distinct clusters based on feature similarity. In image processing, K-Means clustering can be applied for color quantization, which reduces the number of colors in an image, and for image segmentation, which involves partitioning an image into meaningful segments. This technique is valuable for various applications, including object detection, image compression, and enhancing image aesthetics. By reducing the number of colors or segmenting images into distinct regions, K-Means clustering helps in simplifying complex images, which is particularly useful in reducing file sizes and enhancing visual quality.

K-Means clustering works by iteratively partitioning the dataset into K clusters. The algorithm begins by randomly selecting K initial cluster centroids. Each data point is then assigned to the nearest centroid. Next, the centroids are recalculated by averaging the data points within each cluster. This process is repeated until the data points converge, and the centroids stabilize. Once complete, the clusters segment the image into regions with similar colors or textures. This iterative process ensures that the clustering is optimized and that the segmented regions are as accurate as possible.

To process an image with K-Means clustering, it must first be loaded and converted into a format suitable for clustering. Typically, the image is reshaped into a two-dimensional array where each pixel is represented by its color values (RGB). The criteria for the K-Means algorithm, including the maximum number of iterations and the desired accuracy, are then defined. The K-Means algorithm is applied to segment the image. After clustering, the image is reshaped back to its original dimensions and visualized as the segmented image. This visualization helps in assessing the effectiveness of the clustering and making any necessary adjustments. K-Means clustering is also effective for extracting specific segments by focusing on a particular region, such as isolating an object from the background. This involves creating a mask for the desired cluster and applying it to the original image. This step enhances the focus on specific regions of interest, allowing further processing or analysis. For instance, in medical imaging, K-Means clustering can help isolate tumors or other anomalies from surrounding tissues, aiding in diagnosis and treatment planning.

[Figure 7]

Refining K-Means Clustered Images with Bilateral Filtering

Once the image has been segmented using K-Means clustering, bilateral filtering can be applied to refine the isolated segments. Bilateral filtering is a powerful noise reduction technique that preserves edges while smoothing images. It achieves this by combining gray levels or colors based on both their geometric closeness and their photometric similarity. This approach helps maintain sharp edges, which are typically blurred by other smoothing techniques like Gaussian Blur. By applying bilateral filtering to the segmented images, you can enhance the visual quality of the isolated regions, making them clearer and more defined.

Overall, K-Means clustering is a versatile and effective method for image segmentation. By grouping similar pixels together, this technique simplifies the image, making it easier to analyze and process further. Whether used for color quantization or isolating objects of interest, K-Means clustering enhances the ability to interpret and manipulate images. This deeper dive into K-Means clustering builds on the foundation provided in the original article and offers practical examples that readers can implement in their image processing projects. By understanding and utilizing advanced techniques like K-Means clustering and bilateral filtering, you can significantly improve the quality and efficiency of image segmentation tasks, leading to better outcomes in computer vision applications. Furthermore, its simplicity and efficiency make K-Means clustering an accessible yet powerful tool for anyone working with image data.

Conclusion

In this guide, we've explored how to set up OpenCV, the leading library for image processing with Python, across various operating systems including Windows, MacOS, and Linux. We delved into the basics of images and image processing, highlighting its significance in computer vision We examined common types of image noise and outlined methods to eliminate such noise using a range of filters, preparing images for further use in applications. Moreover, we introduced several key image processing techniques crucial for analyzing images, particularly for applications in machine learning. This guide merely scratches the surface of digital image processing, a field brimming with additional concepts and techniques awaiting exploration.

References

[1] Patel, Maahi. “The Complete Guide to Image Preprocessing Techniques in Python.” Medium, 23 Oct. 2023, medium.com/@maahip1304/the-complete-guide-to-image-preprocessing-techniques-in-python-dca30804550c.

[2] “Introduction to Image Processing in Python with OpenCV.” Stack Abuse, 24 Feb. 2020, stackabuse.com/introduction-to-image-processing-in-python-with-opencv/. Accessed 13 Mar. 2024.

‌[3] “Image Processing in Python: Algorithms, Tools, and Methods You Should Know.” Neptune.ai, 9 Nov. 2020, neptune.ai/blog/image-processing-python. Accessed 18 Jan. 2022.

‌[4] Mwiti, Derrick. “Essential Pil (Pillow) Image Tutorial (for Machine Learning People).” Neptune.ai, 21 July 2022, neptune.ai/blog/pil-image-tutorial-for-machine-learning. Accessed 13 Mar. 2024.

[5] Chauhan, Nagesh Singh. "Introduction to Image Segmentation with K-Means Clustering." KG Nuggets, 9 August 2019, kdnuggets.com/2019/08/introduction-image-segmentation-k-means-clustering.html#:~:text=K%2DMeans%20clustering%20algorithm%20is,without%20defined%20categories%20or%20groups).

word count: 2231

--Appendix--

Installation Guide

Before diving into the world of OpenCV through Python, ensure that Python (version 3) is set up on your computer. This is a crucial step to get everything running smoothly. For different operating systems, the installation commands vary slightly.

Windows Users:

Type pip install opencv-python in your command prompt to install OpenCV.

MacOS Users:

Use the command brew install opencv3 --with-contrib --with-python3 in your terminal. This installs OpenCV along with additional contributions and Python 3 support.

Linux Users:

Enter sudo apt-get install libopencv-dev python-opencv in your terminal to get OpenCV ready for use. After installation, you might want to verify everything is set up correctly. To do this, open up a Python shell or your command prompt/terminal and run the command import cv2. If no errors appear, congratulations! You've successfully installed OpenCV on your workstation.

Figure 1 code:

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

def add_salt_and_pepper_noise(image, noise_ratio=0.02):
    """
    Add salt and pepper noise to a grayscale image.
    :param image: Input grayscale image.
    :param noise_ratio: Probability of the noise.
    :return: Noisy image.
    """
    output = np.copy(image)
    
    # Calculate the number of pixels to affect
    num_salt = np.ceil(noise_ratio * image.size * 0.5).astype(int)
    num_pepper = np.ceil(noise_ratio * image.size * 0.5).astype(int)

    # Apply salt noise
    coords = [np.random.randint(0, i - 1, num_salt) for i in image.shape[:2]]
    output[coords[0], coords[1]] = 255

    # Apply pepper noise
    coords = [np.random.randint(0, i - 1, num_pepper) for i in image.shape[:2]]
    output[coords[0], coords[1]] = 0

    return output

# Load the image
image_path = r"C:\Users\\OneDrive\\rose.png" # Replace with your image path
img = cv2.imread(image_path)

# Convert the image to grayscale
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Add salt and pepper noise to the grayscale image
noisy_img = add_salt_and_pepper_noise(img_gray, noise_ratio=0.02)

# Plotting the original grayscale and noisy images side by side
plt.figure(figsize=(10, 5))

# Plot original grayscale image
plt.subplot(1, 2, 1)
plt.imshow(img_gray, cmap='gray')
plt.title('Original Grayscale Image')
plt.axis('off')

# Plot noisy image
plt.subplot(1, 2, 2)
plt.imshow(noisy_img, cmap='gray')
plt.title('Grayscale Image with Salt and Pepper Noise')
plt.axis('off')

plt.show()

Figure 2 code:

import cv2
from matplotlib import pyplot as plt

def apply_gaussian_filter(image_path, kernel_size=(5, 5), sigmaX=0):
    """
    Apply Gaussian filter to an image.

    :param image_path: Path to the input image.
    :param kernel_size: Size of the kernel. Must be odd numbers.
    :param sigmaX: Standard deviation in X direction; if 0, it is calculated from the kernel size.
    :return: None
    """
    # Read the image
    img = cv2.imread(image_path)

    # Check if image is loaded properly
    if img is None:
        print("Error: Image could not be read.")
        return

    # Convert image to RGB (OpenCV loads images in BGR)
    img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

    # Apply Gaussian Blur
    blurred_img = cv2.GaussianBlur(img_rgb, kernel_size, sigmaX)

    # Plotting the original and blurred images side by side
    plt.figure(figsize=(10, 5))

    # Plot original image
    plt.subplot(1, 2, 1)
    plt.imshow(img_rgb)
    plt.title('Salt and Pepper Noise Image')
    plt.axis('off')

    # Plot Gaussian blurred image
    plt.subplot(1, 2, 2)
    plt.imshow(blurred_img)
    plt.title('Gaussian Blurred Image')
    plt.axis('off')

    plt.show()

# Example usage
image_path = r"C:\Users\\OneDrive\\noisef.png"  # Replace with your image path
apply_gaussian_filter(image_path, kernel_size=(5, 5), sigmaX=0)

Figure 3 code:

import cv2
from matplotlib import pyplot as plt

def apply_median_blur(image_path, kernel_size=5):
   
    # Read the image
    img = cv2.imread(image_path)

    # Check if image is loaded properly
    if img is None:
        print("Error: Image could not be read.")
        return

    # Convert image to RGB (OpenCV loads images in BGR)
    img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

    # Apply Median Blur
    blurred_img = cv2.medianBlur(img_rgb, kernel_size)

    # Plotting the original and blurred images side by side
    plt.figure(figsize=(10, 5))

    # Plot original image
    plt.subplot(1, 2, 1)
    plt.imshow(img_rgb)
    plt.title('Paper and Salt Noise Image')
    plt.axis('off')

    # Plot Median blurred image
    plt.subplot(1, 2, 2)
    plt.imshow(blurred_img)
    plt.title('Median Blurred Image')
    plt.axis('off')

    plt.show()

# Example usage
image_path = r"C:\Users\\OneDrive\\noisef.png" # Replace with your image path
apply_median_blur(image_path, kernel_size=5)


Figure 4 code:

import cv2
from matplotlib import pyplot as plt

def apply_laplacian_filter(image_path, ddepth=cv2.CV_64F):
    """
    Apply Laplacian filter to an image for edge detection.

    :param image_path: Path to the input image.
    :param ddepth: Desired depth of the destination image. cv2.CV_64F is used to handle negative numbers.
    :return: None
    """
    # Read the image
    img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)  # Convert to grayscale upon loading

    # Check if image is loaded properly
    if img is None:
        print("Error: Image could not be read.")
        return

    # Apply Laplacian filter
    laplacian_img = cv2.Laplacian(img, ddepth)

    # Convert the output back to uint8
    laplacian_img = cv2.convertScaleAbs(laplacian_img)

    # Plotting the original and Laplacian filtered images side by side
    plt.figure(figsize=(10, 5))

    # Plot original image
    plt.subplot(1, 2, 1)
    plt.imshow(img, cmap='gray')
    plt.title('Papper and Slat noise Image')
    plt.axis('off')

    # Plot Laplacian filtered image
    plt.subplot(1, 2, 2)
    plt.imshow(laplacian_img, cmap='gray')
    plt.title('Laplacian Filtered Image')
    plt.axis('off')

    plt.show()

# Example usage
image_path = r"C:\Users\\OneDrive\\noisef.png"  # Replace with your image path
apply_laplacian_filter(image_path)

Figure 5 code:

import cv2
from matplotlib import pyplot as plt

def convert_to_binary(image_path, threshold_value=120):
   
    # Read the image
    img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)  # Convert to grayscale upon loading

    # Check if image is loaded properly
    if img is None:
        print("Error: Image could not be read.")
        return

    # Apply threshold to convert image to binary
    _, binary_img = cv2.threshold(img, threshold_value, 255, cv2.THRESH_BINARY)

    # Plotting the original grayscale and binary images side by side
    plt.figure(figsize=(10, 5))

    # Plot original grayscale image
    plt.subplot(1, 2, 1)
    plt.imshow(img, cmap='gray')
    plt.title('Original Grayscale Image')
    plt.axis('off')

    # Plot binary image
    plt.subplot(1, 2, 2)
    plt.imshow(binary_img, cmap='gray')
    plt.title('Binary Image')
    plt.axis('off')

    plt.show()

# Example usage
image_path = r"C:\Users\\OneDrive\\binaryp.png"  # Replace with your image path
convert_to_binary(image_path, threshold_value=120)

Figure 6 code:

import cv2
import matplotlib.pyplot as plt

def canny_edge_detection(image_path, blur_ksize=5, threshold1=100, threshold2=200):
    """
    Perform Canny edge detection on an image.

    :param image_path: Path to the input image.
    :param blur_ksize: Kernel size for Gaussian blur to reduce noise before edge detection.
    :param threshold1: First threshold for the hysteresis procedure in Canny edge detection.
    :param threshold2: Second threshold for the hysteresis procedure in Canny edge detection.
    :return: None
    """
    # Read the image
    img = cv2.imread(image_path)
    
    # Check if image is loaded properly
    if img is None:
        print("Error: Image could not be read.")
        return
    
    # Convert to grayscale
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # Apply Gaussian blur
    blurred = cv2.GaussianBlur(gray, (blur_ksize, blur_ksize), 0)
    
    # Perform Canny edge detection
    edges = cv2.Canny(blurred, threshold1, threshold2)
    
    # Plotting the original image and the edge detected image
    plt.figure(figsize=(12, 5))
    
    # Plot original image
    plt.subplot(1, 2, 1)
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))  # Convert to RGB for plotting
    plt.title('Original Image')
    plt.axis('off')
    
    # Plot edge detected image
    plt.subplot(1, 2, 2)
    plt.imshow(edges, cmap='gray')
    plt.title('Edge Detection')
    plt.axis('off')
    
    plt.show()

# Example usage
image_path = r"C:\Users\\OneDrive\\cat.png" # Replace with your image path
canny_edge_detection(image_path)

Figure 7 code:


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

# Load the image
image = cv2.imread('path/to/image.jpg')
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# Reshape the image to a 2D array of pixels
pixel_values = image_rgb.reshape((-1, 3))
pixel_values = np.float32(pixel_values)

# Define criteria and apply KMeans
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.2)
k = 4  # Number of clusters
_, labels, centers = cv2.kmeans(pixel_values, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)

# Convert centers to uint8
centers = np.uint8(centers)
labels = labels.flatten()

# Assume cluster 0 represents the object of interest
mask = labels == 0

# Reshape the mask to the original image dimensions
mask = mask.reshape(image_rgb.shape[:2])

# Create an all-black background with the same dimensions as the original image
segmented_image = np.zeros_like(image_rgb)

# Place the object on the black background
segmented_image[mask] = image_rgb[mask]

# Plot the segmented image with the isolated object
plt.imshow(segmented_image)
plt.title('Isolated Object')
plt.axis('off')
plt.show()

# Load the new background image
background = cv2.imread('path/to/background.jpg')
background_rgb = cv2.cvtColor(background, cv2.COLOR_BGR2RGB)

# Ensure the background image is the same size as the original image
background_resized = cv2.resize(background_rgb, (image_rgb.shape[1], image_rgb.shape[0]))

# Create a mask for placing the object onto the new background
object_mask = mask.astype(bool)

# Place the isolated object onto the new background
combined_image = background_resized.copy()
combined_image[object_mask] = segmented_image[object_mask]

# Plot the final image with the isolated object on the new background
plt.imshow(combined_image)
plt.title('Object on New Background')
plt.axis('off')
plt.show()