How to ouput image containing only specified color range?

Viewed 80

I only want the purple lane in the output image.

import cv2
import numpy as np

img = cv2.imread("minimap_example.png")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) #hsv image

lower_purple = np.array([154,135,160])
upper_purple = np.array([167,90,235])

mask = cv2.inRange(hsv, lower_purple, upper_purple)

lane = cv2.bitwise_and(img, img, mask=mask)
cv2.imwrite("laneOnly.png", lane)

But the output image is not correct at all : enter image description here

How to resolve this error?

3 Answers

You are doing 2 things wrong:

  • You are using RGB ranges/values but you are converting the image to HSV. For simplicity leave it in RGB (even though HSV would be the proper way).
  • You are expressing them in RGB order but the image is in BGR, so switch the order of values. Moreover make the range a little bit wider... otherwise you get nothing.

I propose you do something like this:

import cv2

img = cv2.imread("minimap_example.png")

lower_purple = (100, 45, 120) # you might want to refine these
upper_purple = (260, 105, 200) # you might want to refine these

mask = cv2.inRange(img, lower_purple, upper_purple)

lane = cv2.bitwise_and(img, img, mask=mask)
cv2.imwrite("laneOnly.png", lane)

you will get this output: enter image description here

There is something wrong with the range.

If you check (print) at your image, zooming into the lane to this slice hsv[40:60, 80:90], you'll find the values of the hsv color are around [136 167 243].

So trying with those values gives a better result:

lower_purple = (136,167,155)
upper_purple = (136,167,243)

So, dig deeper and find the better option for you.

try:

%%capture
%matplotlib inline
from skimage import io
from ipywidgets import interact, widgets
from IPython.display import display
import cv2
from scipy import misc
import matplotlib.pyplot as plt
import numpy as np

def f(lower_H, lower_S, lower_V, upper_H, upper_S, upper_V, **kwargs):
    hsv = cv2.cvtColor(racoon, cv2.COLOR_BGR2HSV)

    lower_range = np.array([lower_H, lower_S, lower_V])
    upper_range = np.array([upper_H, upper_S, upper_V])

    mask = cv2.inRange(hsv, lower_range, upper_range)

    ax_neu.imshow(mask, cmap="gray")
    ax_neu.set_title(f"Lower Range: H = {lower_H}, S = {lower_S}, V = {lower_V}\
                     \nUpper Range: H = {upper_H}, S = {upper_S}, V = {upper_V}")
    fig.canvas.draw()
    display(fig)

    

img = io.imread('https://i.stack.imgur.com/6UnnM.png/')
# racoon = cv2.imread('')
print(img.shape)

fig = plt.figure(figsize=(10, 8))
ax_orig = fig.add_subplot(121) 
ax_neu = fig.add_subplot(122) 

ax_orig.imshow(img, cmap="gray")
ax_neu.imshow(img, cmap="gray")

print("Setting Lower and Upper Range for HSV")
interact(f, lower_H=widgets.IntSlider(min=1,max=255,step=1,value=105),
            lower_S=widgets.IntSlider(min=1,max=255,step=1,value=130),
            lower_V=widgets.IntSlider(min=1,max=255,step=1,value=110),
            upper_H=widgets.IntSlider(min=1,max=255,step=1,value=225),
            upper_S=widgets.IntSlider(min=1,max=255,step=1,value=205),
            upper_V=widgets.IntSlider(min=1,max=255,step=1,value=255))

enter image description here

Values comes out to be:

lower_purple = (105, 130, 110)
upper_purple = (225, 205, 255)
Related