Search code examples
pythonopencvimage-processingcolorspython-imaging-library

Remove everything of a specific color (with a color variation tolerance) from an image with Python


I have some text in blue #00a2e8, and some text in black on a PNG image (white background).

How to remove everything in blue (including text in blue) on an image with Python PIL or OpenCV, with a certain tolerance for the variations of color?

Indeed, every pixel of the text is not perfectly of the same color, there are variations, shades of blue.

Here is what I was thinking:

  • convert from RGB to HSV
  • find the Hue h0 for the blue
  • do a Numpy mask for Hue in the interval [h0-10, h0+10]
  • set these pixels to white

Before coding this, is there a more standard way to do this with PIL or OpenCV Python?

Example PNG file: foo and bar blocks should be removed

enter image description here


Solution

  • Your image has some issues. Firstly, it has a completely superfluous alpha channel which can be ignored. Secondly, the colours around your blues are quite a long way from blue!

    I used your planned approach and found the removal was pretty poor:

    #!/usr/bin/env python3
    
    import cv2
    import numpy as np
    
    # Load image
    im = cv2.imread('nwP8M.png')
    
    # Define lower and upper limits of our blue
    BlueMin = np.array([90,  200, 200],np.uint8)
    BlueMax = np.array([100, 255, 255],np.uint8)
    
    # Go to HSV colourspace and get mask of blue pixels
    HSV  = cv2.cvtColor(im,cv2.COLOR_BGR2HSV)
    mask = cv2.inRange(HSV, BlueMin, BlueMax)
    
    # Make all pixels in mask white
    im[mask>0] = [255,255,255]
    cv2.imwrite('DEBUG-plainMask.png', im)
    

    That gives this:

    enter image description here

    If you broaden the range, to get the rough edges, you start to affect the green letters, so instead I dilated the mask so that pixels spatially near the blues are made white as well as pixels chromatically near the blues:

    # Try dilating (enlarging) mask with 3x3 structuring element
    SE   = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
    mask = cv2.dilate(mask, kernel, iterations=1)
    
    # Make all pixels in mask white
    im[mask>0] = [255,255,255]
    cv2.imwrite('result.png', im)
    

    That gets you this:

    enter image description here

    You may wish to diddle with the actual values for your other images, but the principle is the same.