OpenCV Image processing pipeline to segment a flower from a plant image

116 Views Asked by At

I am asked to develop an image processing pipeline using OpenCV python to segment a flower from plant images. But I still have problems getting the accurate binary image. I'm given a data set of images of different flower colours to segment, so removing solely certain colours of flowers won't work for all images.

This is my current pipeline code:

def process_image(image_path):
    # Read the image
    image = cv2.imread(image_path)

    # Apply bilateral filter for noise reduction
    noise_reduced_image = cv2.bilateralFilter(image, d=9, sigmaColor=75, sigmaSpace=75)

    # Convert to grayscale
    grayscale_image = cv2.cvtColor(noise_reduced_image, cv2.COLOR_BGR2GRAY)

    # Apply Gaussian blur to reduce noise
    blurred_image = cv2.GaussianBlur(grayscale_image, (5, 5), 0)

    # Threshold the image - this value may need adjustment for your images
    ret, thresholded_image = cv2.threshold(blurred_image, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

    # Find contours from the binary image
    contours, hierarchy = cv2.findContours(thresholded_image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    # Create an empty image for contours which is the same size as the original image
    contour_image = np.zeros_like(thresholded_image)

    # Draw the contours on the contour image
    cv2.drawContours(contour_image, contours, -1, (255), thickness=cv2.FILLED)

    # Perform morphological operations to further clean up the image
    kernel = np.ones((5, 5), np.uint8)
    contour_image = cv2.morphologyEx(contour_image, cv2.MORPH_OPEN, kernel, iterations=7)  # Remove noise
    dilated_image = cv2.dilate(contour_image, kernel, iterations=1)  # Fill in the gaps

    final_image = cv2.bitwise_not(dilated_image)
    return final_image

Input Image:

enter image description here

Ground truth:

enter image description here

What I got: enter image description here

3

There are 3 best solutions below

2
KRG On

I tried by filtering the yellow color part of the image.

import cv2
import numpy as np

def process_image(image_path):
    image = cv2.imread(image_path)
    image = cv2.GaussianBlur(image, (11, 11), 0) #Noise removal
    image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) #Convert to HSV
    lower = np.array([22, 93, 0], dtype="uint8") #Lower value for yellow
    upper = np.array([45, 255, 255], dtype="uint8") #Upper value for yellow
    mask = cv2.inRange(image, lower, upper) #filter only areas in YELLOW colour.
    mask = cv2.erode(mask, None, iterations=2) #Erode to remove noise
    mask = cv2.dilate(mask, None, iterations=2) #dilate to close gaps
    contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) #get contour

    selected_contours = []
    for ct in contours:
        if cv2.contourArea(ct) > 10000: #Select contours greater than a particular area value.
            selected_contours.append(ct)
    contour_image = np.zeros_like(mask)
    
    cv2.drawContours(contour_image, selected_contours, -1, (255), thickness=cv2.FILLED) # draw contour of selected contours
    return contour_image


result = process_image("flower.jpg")
cv2.imwrite("result.jpg",result)

Output:

enter image description here

1
r-parra On

You can filter by threshold (look the value that is good for you) and then for each contour you can calculate the area of the object.

With this countours you can filter and the object with high area will be the desired one.

It's an idea, hope it helps!

5
Tino D On

You can use the remove function from the very nice rembg module. First install it with pip install rembg. Then use this:

from PIL import Image 
from rembg import remove 
input = Image.open("flower.jpg") 
output = remove(input) 
output.save("BackgroundRemovedFlower.png")

It will take some time to load the modules and to remove the background, don't get discouraged. The result:

Flower

Might not work on all, but maybe most.