Here is one way in Python/OpenCV. Threshold the image on white. Then apply some morphology to clean it up a bit. Then invert it to make a mask. Then apply the mask to the input. I note that your pills overlap the ring. So this method does not remove the ring.

Input:

import cv2
import numpy as np

# Read image
img = cv2.imread('pills.jpg')
hh, ww = img.shape[:2]

# threshold on white
# Define lower and uppper limits
lower = np.array([200, 200, 200])
upper = np.array([255, 255, 255])

# Create mask to only select black
thresh = cv2.inRange(img, lower, upper)

# apply morphology
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (20,20))
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)

# invert morp image
mask = 255 - morph

# apply mask to image
result = cv2.bitwise_and(img, img, mask=mask)


# save results
cv2.imwrite('pills_thresh.jpg', thresh)
cv2.imwrite('pills_morph.jpg', morph)
cv2.imwrite('pills_mask.jpg', mask)
cv2.imwrite('pills_result.jpg', result)

cv2.imshow('thresh', thresh)
cv2.imshow('morph', morph)
cv2.imshow('mask', mask)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

Threshold image:

Morphology cleaned image:

Mask image:

Result:

Answer from fmw42 on Stack Overflow
Top answer
1 of 2
14

Here is one way in Python/OpenCV. Threshold the image on white. Then apply some morphology to clean it up a bit. Then invert it to make a mask. Then apply the mask to the input. I note that your pills overlap the ring. So this method does not remove the ring.

Input:

import cv2
import numpy as np

# Read image
img = cv2.imread('pills.jpg')
hh, ww = img.shape[:2]

# threshold on white
# Define lower and uppper limits
lower = np.array([200, 200, 200])
upper = np.array([255, 255, 255])

# Create mask to only select black
thresh = cv2.inRange(img, lower, upper)

# apply morphology
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (20,20))
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)

# invert morp image
mask = 255 - morph

# apply mask to image
result = cv2.bitwise_and(img, img, mask=mask)


# save results
cv2.imwrite('pills_thresh.jpg', thresh)
cv2.imwrite('pills_morph.jpg', morph)
cv2.imwrite('pills_mask.jpg', mask)
cv2.imwrite('pills_result.jpg', result)

cv2.imshow('thresh', thresh)
cv2.imshow('morph', morph)
cv2.imshow('mask', mask)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

Threshold image:

Morphology cleaned image:

Mask image:

Result:

2 of 2
3

Here is another way to do that in Python/OpenCV removing the ring. But it will remove parts of the pills that overlap the ring.

  • Read the input
  • Threshold on white
  • Apply morphology close to remove the center strip
  • Get the contours
  • Draw the contours as white filled on black background
  • Get the convex hull of the white filled contours
  • Fit an ellipse to the convex hull
  • Print the ellipse shape to make sure it is close to a circle
  • Draw the convex hull outline in red on the input to check if fits the white region
  • Draw a circle using the average ellipse radii and center as white filled on black background
  • Erode the circle a little to avoid leaving a partial white ring
  • Combine the inverted morph image and the circle image to make a final mask
  • Apply the final mask to the input
  • Save the results

import cv2
import numpy as np

# Read image
img = cv2.imread('pills.jpg')
hh, ww = img.shape[:2]

# threshold on white
# Define lower and uppper limits
lower = np.array([200, 200, 200])
upper = np.array([255, 255, 255])

# Create mask to only select black
thresh = cv2.inRange(img, lower, upper)

# apply morphology
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (20,20))
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)

# get contours
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]

# draw white contours on black background as mask
mask = np.zeros((hh,ww), dtype=np.uint8)
for cntr in contours:
    cv2.drawContours(mask, [cntr], 0, (255,255,255), -1)

# get convex hull
points = np.column_stack(np.where(thresh.transpose() > 0))
hullpts = cv2.convexHull(points)
((centx,centy), (width,height), angle) = cv2.fitEllipse(hullpts)
print("center x,y:",centx,centy)
print("diameters:",width,height)
print("orientation angle:",angle)

# draw convex hull on image
hull = img.copy()
cv2.polylines(hull, [hullpts], True, (0,0,255), 1)

# create new circle mask from ellipse 
circle = np.zeros((hh,ww), dtype=np.uint8)
cx = int(centx)
cy = int(centy)
radius = (width+height)/4
cv2.circle(circle, (cx,cy), int(radius), 255, -1)

# erode circle a bit to avoid a white ring
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (6,6))
circle = cv2.morphologyEx(circle, cv2.MORPH_ERODE, kernel)

# combine inverted morph and circle
mask2 = cv2.bitwise_and(255-morph, 255-morph, mask=circle)

# apply mask to image
result = cv2.bitwise_and(img, img, mask=mask2)

# save results
cv2.imwrite('pills_thresh2.jpg', thresh)
cv2.imwrite('pills_morph2.jpg', morph)
cv2.imwrite('pills_mask2.jpg', mask)
cv2.imwrite('pills_hull2.jpg', hull)
cv2.imwrite('pills_circle.jpg', circle)
cv2.imwrite('pills_result2.jpg', result)

cv2.imshow('thresh', thresh)
cv2.imshow('morph', morph)
cv2.imshow('mask', mask)
cv2.imshow('hull', hull)
cv2.imshow('circle', circle)
cv2.imshow('mask2', mask2)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

Threshold image:

Morphology image:

Filled contours image:

Convex hull on input:

Circle image:

Final mask image:

Result:

Top answer
1 of 3
24

I solved your problem using the OpenCV's watershed algorithm. You can find the theory and examples of watershed here.

First I selected several points (markers) to dictate where is the object I want to keep, and where is the background. This step is manual, and can vary a lot from image to image. Also, it requires some repetition until you get the desired result. I suggest using a tool to get the pixel coordinates. Then I created an empty integer array of zeros, with the size of the car image. And then I assigned some values (1:background, [255,192,128,64]:car_parts) to pixels at marker positions.

NOTE: When I downloaded your image I had to crop it to get the one with the car. After cropping, the image has size of 400x601. This may not be what the size of the image you have, so the markers will be off.

Afterwards I used the watershed algorithm. The 1st input is your image and 2nd input is the marker image (zero everywhere except at marker positions). The result is shown in the image below.

I set all pixels with value greater than 1 to 255 (the car), and the rest (background) to zero. Then I dilated the obtained image with a 3x3 kernel to avoid losing information on the outline of the car. Finally, I used the dilated image as a mask for the original image, using the cv2.bitwise_and() function, and the result lies in the following image:

Here is my code:

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

# Load the image
img = cv2.imread("/path/to/image.png", 3)

# Create a blank image of zeros (same dimension as img)
# It should be grayscale (1 color channel)
marker = np.zeros_like(img[:,:,0]).astype(np.int32)

# This step is manual. The goal is to find the points
# which create the result we want. I suggest using a
# tool to get the pixel coordinates.

# Dictate the background and set the markers to 1
marker[204][95] = 1
marker[240][137] = 1
marker[245][444] = 1
marker[260][427] = 1
marker[257][378] = 1
marker[217][466] = 1

# Dictate the area of interest
# I used different values for each part of the car (for visibility)
marker[235][370] = 255    # car body
marker[135][294] = 64     # rooftop
marker[190][454] = 64     # rear light
marker[167][458] = 64     # rear wing
marker[205][103] = 128    # front bumper

# rear bumper
marker[225][456] = 128
marker[224][461] = 128
marker[216][461] = 128

# front wheel
marker[225][189] = 192
marker[240][147] = 192

# rear wheel
marker[258][409] = 192
marker[257][391] = 192
marker[254][421] = 192

# Now we have set the markers, we use the watershed
# algorithm to generate a marked image
marked = cv2.watershed(img, marker)

# Plot this one. If it does what we want, proceed;
# otherwise edit your markers and repeat
plt.imshow(marked, cmap='gray')
plt.show()

# Make the background black, and what we want to keep white
marked[marked == 1] = 0
marked[marked > 1] = 255

# Use a kernel to dilate the image, to not lose any detail on the outline
# I used a kernel of 3x3 pixels
kernel = np.ones((3,3),np.uint8)
dilation = cv2.dilate(marked.astype(np.float32), kernel, iterations = 1)

# Plot again to check whether the dilation is according to our needs
# If not, repeat by using a smaller/bigger kernel, or more/less iterations
plt.imshow(dilation, cmap='gray')
plt.show()

# Now apply the mask we created on the initial image
final_img = cv2.bitwise_and(img, img, mask=dilation.astype(np.uint8))

# cv2.imread reads the image as BGR, but matplotlib uses RGB
# BGR to RGB so we can plot the image with accurate colors
b, g, r = cv2.split(final_img)
final_img = cv2.merge([r, g, b])

# Plot the final result
plt.imshow(final_img)
plt.show()

If you have a lot of images you will probably need to create a tool to annotate the markers graphically, or even an algorithm to find markers automatically.

2 of 3
9

The problem is that you're subtracting arrays of unsigned 8 bit integers. This operation can overflow.

To demonstrate

>>> import numpy as np
>>> a = np.array([[10,10]],dtype=np.uint8)
>>> b = np.array([[11,11]],dtype=np.uint8)
>>> a - b
array([[255, 255]], dtype=uint8)

Since you're using OpenCV, the simplest way to achieve your goal is to use cv2.absdiff().

>>> cv2.absdiff(a,b)
array([[1, 1]], dtype=uint8)
Discussions

python - remove background of any image using opencv - Stack Overflow
I have been searching for a technique to remove the background of a any given image. The idea is to detect a face and remove the background of the detected face. I have finished the face part. Now More on stackoverflow.com
🌐 stackoverflow.com
Remove Background Color From Image - Python - OpenCV
Hi, so i was playing around opencv and found a way online on how to detect and crop the image, but how can i remove its Background color, so only the image is saved in Lossy web Compressed PNG format. My Code: import … More on forum.opencv.org
🌐 forum.opencv.org
0
October 16, 2021
python - OpenCV : Remove background of an image - Stack Overflow
I am using Opencv and python to detect shapes and then crop them. I have succeeded to do that, however now I am trying to take the cropped images and remove their backgrounds. The image has a cir... More on stackoverflow.com
🌐 stackoverflow.com
January 21, 2018
Remove Background from Image - Python - Stack Overflow
Have you tried similar techniques ... in OpenCV? The solution is nearly bullet-proof but I will keep troubleshooting on my end, thanks again! 2020-08-24T21:16:29.353Z+00:00 ... Find the answer to your question by asking. Ask question ... See similar questions with these tags. ... New site design and philosophy for Stack Overflow: Starting February 24, 2026... 0 Remove Black Background from Image with Grabcut - Python... More on stackoverflow.com
🌐 stackoverflow.com
🌐
OpenCV Q&A Forum
answers.opencv.org › question › 201115 › removing-image-background-from-image-with-python
Removing image background from image with python - OpenCV Q&A Forum
This is python code : import cv2 import argparse import numpy as np parser = argparse.ArgumentParser() parser.add_argument('file_in', help='Input file') parser.add_argument('file_out', help='Output file') args = parser.parse_args() #== Parameters ======================================================================= BLUR = 19 CANNY_THRESH_1 = 25 CANNY_THRESH_2 = 255 MASK_DILATE_ITER = 5 MASK_ERODE_ITER = 13 MASK_COLOR = (0.0,0.0,1.0) #-- Read image ----------------------------------------------------------------------- img = cv2.imread(args.file_in) gray = cv2.GaussianBlur(img, (5, 5), 1) gra
🌐
DEV Community
dev.to › azure › opencv-10-lines-to-remove-the-background-in-an-image-3m98
OpenCV – 10 lines to remove the background in an image 🖼️ - DEV Community
June 7, 2022 - Hi ! Super quick post today, with a very simple scenario: Load and image and remove the... Tagged with englishpost, codesample, opencv.
🌐
Moonbooks
en.moonbooks.org › Articles › How-to-Remove-the-Background-of-an-Image-Using-Python-
How to Remove the Background of an Image Using Python ?
June 6, 2025 - Use an image editor (GIMP, Photoshop) or OpenCV GUI tools to inspect HSV values of the background. Use: cv2.cvtColor(pixel_bgr_value, cv2.COLOR_BGR2HSV) to test color values interactively. ... Use .png format to preserve transparency (JPG doesn't support it). If you don’t need transparency, you can fill the removed background with a solid color (like gray or white) using cv2.addWeighted() or NumPy.
Find elsewhere
🌐
FreedomVC
freedomvc.com › home › image › opencv › basic background remover with opencv
Basic Background Remover with OpenCV - FreedomVC
January 17, 2022 - # We use TOZERO_INV as we want to keep some details of the foregorund ret,foreground = cv2.threshold(myimage_grey,0,255,cv2.THRESH_TOZERO_INV+cv2.THRESH_OTSU) #Currently foreground is only a mask foreground = cv2.bitwise_and(myimage,myimage, mask=foreground) # Update foreground with bitwise_and to extract real foreground # Combine the background and foreground to obtain our final image finalimage = background+foreground return finalimage · Obviously in method 1, we performed a lot of image processing. As can be seen, Gaussian Blur, and Otsu thresholding require a lot of processing. Additionally, when applying Gaussian Blur and binning, we lost a lot of detail in our image. Hence, we wanted to design an alternative strategy that will hopefully be faster. Balanced against efficiency and knowing OpenCV is a highly optimized library, we opted for a thresholding focused approach:
🌐
Towards Data Science
towardsdatascience.com › home › latest › background removal with python
Background Removal with Python | Towards Data Science
January 21, 2025 - By contrast, very large contours which take up most the screen probably aren’t the foreground, but some visual artefact of the background. Finally a mask is generated from the remaining contours and is blended into the original image. ... Before doing much, two libraries need to be imported. NumPy works to make some the number-crunching more efficient. OpenCV handles the image manipulation.
🌐
Delft Stack
delftstack.com › home › howto › python › opencv background subtraction
How to Subtract Background in OpenCV | Delft Stack
February 2, 2024 - First, we have to install the cvzone and mediapipe to use them with OpenCV. We can install them using pip. We have to import the selfie segmentation module from the cvzone library. To use its removeBG() method, we can remove the background from ...
🌐
DataFlair
data-flair.training › blogs › python-remove-image-background
How to Remove Background of Images in Python? - DataFlair
July 29, 2025 - Please download the source code of image background removal with opencv: Image Background Removal Project Code · Below are the steps to develop remove image background project in python · 1. Import necessary packages. 2. Initialize selfie-segmentation object. 3. Read frames from a webcam.
🌐
Analytics Vidhya
analyticsvidhya.com › home › learn how to do real-time background replacement using opencv and cvzone
Learn How To Do Real-Time Background Replacement using OpenCV and CVzone
July 6, 2021 - Here we are going to implement something similar, but using OpenCV and CVzone. Install the required modules. -- pip install OpenCV-python -- pip install cvzone -- pip install mediapipe
🌐
Better Programming
betterprogramming.pub › automating-white-or-any-other-color-background-removal-with-python-and-opencv-4be4addb6c99
Automating Background Color Removal with Python and OpenCV | by Pierre Chehwan | Better Programming
November 26, 2019 - We need the numpy array since OpenCVs' inRange function will use those. lower = np.array([hMin, sMin, vMin]) upper = np.array([hMax, sMax, vMax]) # Create HSV Image and threshold it into the proper range. hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # Converting color space from BGR to HSV mask = cv2.inRange(hsv, lower, upper) # Create a mask based on the lower and upper range, using the new HSV image # Create the output image, using the mask created above. This will perform the removal of all unneeded colors, but will keep a black background.
🌐
HowDev
how.dev › answers › removing-background-using-opencv
Removing background using OpenCV
Line 42: Invert the thresholded image by subtracting it from 255 to create a binary mask, where white pixels (255) in threshold_img become black (0), and vice versa. Store the resulting mask in the variable mask. Line 44: Perform a bitwise AND operation between the resized_image and itself, but use mask to mask out the background storing the resulting image in the variable result.
🌐
DEV Community
dev.to › stokry › remove-background-from-images-with-python-dkj
Remove background from images with Python - DEV Community
April 9, 2021 - Today I will show you how you can remove background from your images. I'll use numpy, cv2, matpl...
🌐
OpenCV
forum.opencv.org › python
Remove Background Color From Image - Python - OpenCV
October 16, 2021 - Hi, so i was playing around opencv and found a way online on how to detect and crop the image, but how can i remove its Background color, so only the image is saved in Lossy web Compressed PNG format. My Code: import cv2 import numpy as np import os o = os.getcwd() for s in os.listdir(): if ".jpg" in s: T = os.path.splitext(s)[0] img = cv2.imread(s) blurred = cv2.blur(img, (3,3)) canny = cv2.Canny(blurred, 50, 200) ## find the non-zero min-max ...
🌐
Code Pasta
codepasta.com › 2016 › 11 › 06 › background-segmentation-removal-with-opencv
Background removal with OpenCV (AKA segmentation)
November 6, 2016 - So in other words, this approach works only if background is relatively plain, contrasting in color (to the object) and the object stands separated from other objects and image edges. Now I’ll dive into more details and the code. Step 0: First begin with preprocessing the image with a slight Gaussian blur to reduce noise from the original image before doing an edge detection. import numpy as np import cv2 img = cv2.imread('078.jpg') blurred = cv2.GaussianBlur(img, (5, 5), 0) # Remove noise
Top answer
1 of 1
3

Here is one approach to make your background transparent in Python/OpenCV.

  • Read the input
  • Convert to gray
  • Threshold
  • Apply morphology to clean extraneous spots
  • Get external contours
  • Find the largest contour
  • Draw the contour as white filled on a black background as a mask
  • Antialias the mask
  • Put that into the alpha channel of the input image
  • Save the result

Input:

import cv2
import numpy as np
import skimage.exposure

# load image
img = cv2.imread('aerial_image.jpg')

# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# threshold
thresh = cv2.threshold(gray, 11, 255, cv2.THRESH_BINARY)[1]

# apply morphology to clean small spots
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, borderType=cv2.BORDER_CONSTANT, borderValue=0)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel, borderType=cv2.BORDER_CONSTANT, borderValue=0)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
morph = cv2.morphologyEx(morph, cv2.MORPH_ERODE, kernel, borderType=cv2.BORDER_CONSTANT, borderValue=0)

# get external contour
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)

# draw white filled contour on black background as mas
contour = np.zeros_like(gray)
cv2.drawContours(contour, [big_contour], 0, 255, -1)

# blur dilate image
blur = cv2.GaussianBlur(contour, (5,5), sigmaX=0, sigmaY=0, borderType = cv2.BORDER_DEFAULT)

# stretch so that 255 -> 255 and 127.5 -> 0
mask = skimage.exposure.rescale_intensity(blur, in_range=(127.5,255), out_range=(0,255))

# put mask into alpha channel of input
result = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
result[:,:,3] = mask

# save output
cv2.imwrite('aerial_image_thresh.png', thresh)
cv2.imwrite('aerial_image_morph.png', morph)
cv2.imwrite('aerial_image_contour.png', contour)
cv2.imwrite('aerial_image_mask.png', mask)
cv2.imwrite('aerial_image_antialiased.png', result)


# Display various images to see the steps
cv2.imshow('thresh', thresh)
cv2.imshow('morph', morph)
cv2.imshow('contour', contour)
cv2.imshow('mask', mask)
cv2.imshow('result', result)

cv2.waitKey(0)
cv2.destroyAllWindows()

Threshold image:

Morphology cleaned image:

Contour image:

Mask image:

Result with transparent background: