Removing background color from image opencv python

Ratchainant Thammasudjarit :

I have many images of specimen which have uncontrollable background color. Some of them have black background. Some of them have white background. Some of them have green background, etc.

I would like to remove these background color of a given image where the object in the image is just only one specimen. I try this code but it does not work as i expect.

def get_holes(image, thresh):
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

        im_bw = cv2.threshold(gray, thresh, 255, cv2.THRESH_BINARY)[1]
        im_bw_inv = cv2.bitwise_not(im_bw)

        _, contour, _ = cv2.findContours(im_bw_inv, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
        for cnt in contour:
            cv2.drawContours(im_bw_inv, [cnt], 0, 255, -1)

        nt = cv2.bitwise_not(im_bw)
        im_bw_inv = cv2.bitwise_or(im_bw_inv, nt)
        return im_bw_inv


    def remove_background(image, thresh, scale_factor=.25, kernel_range=range(1, 15), border=None):
        border = border or kernel_range[-1]

        holes = get_holes(image, thresh)
        small = cv2.resize(holes, None, fx=scale_factor, fy=scale_factor)
        bordered = cv2.copyMakeBorder(small, border, border, border, border, cv2.BORDER_CONSTANT)

        for i in kernel_range:
            kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2*i+1, 2*i+1))
            bordered = cv2.morphologyEx(bordered, cv2.MORPH_CLOSE, kernel)

        unbordered = bordered[border: -border, border: -border]
        mask = cv2.resize(unbordered, (image.shape[1], image.shape[0]))
        fg = cv2.bitwise_and(image, image, mask=mask)
        return fg

file = your_file_location
img = cv2.imread(file)
nb_img = dm.remove_background(img, 255)

These are some example images

sample 1

sample 2

May i have your suggestions?

nathancy :

Here's a simple approach with the assumption that there is only one specimen per image.

  1. Kmeans color quantization. We load the image then perform Kmeans color quantization to segment the image into a specified cluster of colors. For instance with clusters=4, the image will be labeled into four colors.

  2. Obtain binary image. Convert to grayscale, Gaussian blur, adaptive threshold.

  3. Draw largest enclosing circle onto mask. Find contours, sort for largest contour using contour area filtering then draw the largest enclosing circle onto a mask using cv2.minEnclosingCircle.

  4. Bitwise-and. Since we have isolated the desired sections to extract, we simply bitwise-and the mask and input image


Input image -> Kmeans -> Binary image

Detected largest enclosing circle -> Mask -> Result

Here's the output for the second image

Input image -> Kmeans -> Binary image

Detected largest enclosing circle -> Mask -> Result

Code

import cv2
import numpy as np

# Kmeans color segmentation
def kmeans_color_quantization(image, clusters=8, rounds=1):
    h, w = image.shape[:2]
    samples = np.zeros([h*w,3], dtype=np.float32)
    count = 0

    for x in range(h):
        for y in range(w):
            samples[count] = image[x][y]
            count += 1

    compactness, labels, centers = cv2.kmeans(samples,
            clusters, 
            None,
            (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10000, 0.0001), 
            rounds, 
            cv2.KMEANS_RANDOM_CENTERS)

    centers = np.uint8(centers)
    res = centers[labels.flatten()]
    return res.reshape((image.shape))

# Load image and perform kmeans
image = cv2.imread('2.jpg')
original = image.copy()
kmeans = kmeans_color_quantization(image, clusters=4)

# Convert to grayscale, Gaussian blur, adaptive threshold
gray = cv2.cvtColor(kmeans, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,21,2)

# Draw largest enclosing circle onto a mask
mask = np.zeros(original.shape[:2], dtype=np.uint8)
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
for c in cnts:
    ((x, y), r) = cv2.minEnclosingCircle(c)
    cv2.circle(image, (int(x), int(y)), int(r), (36, 255, 12), 2)
    cv2.circle(mask, (int(x), int(y)), int(r), 255, -1)
    break

# Bitwise-and for result
result = cv2.bitwise_and(original, original, mask=mask)
result[mask==0] = (255,255,255)

cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.imshow('mask', mask)
cv2.imshow('kmeans', kmeans)
cv2.imshow('image', image)
cv2.waitKey()

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=11725&siteId=1