pythonopencvcomputer-visionhsvcolor-detection

Choosing the correct upper and lower HSV boundaries for color detection with`cv::inRange` (OpenCV)


I have an image of a coffee can with an orange lid position of which I want to find. Here is it image.

gcolor2 utility shows HSV at the center of the lid to be (22, 59, 100). The question is how to choose the limits of the color then? I tried min = (18, 40, 90) and max = (27, 255, 255), but have got unexpected result

Here is the Python code:

import cv

in_image = 'kaffee.png'
out_image = 'kaffee_out.png'
out_image_thr = 'kaffee_thr.png'

ORANGE_MIN = cv.Scalar(18, 40, 90)
ORANGE_MAX = cv.Scalar(27, 255, 255)
COLOR_MIN = ORANGE_MIN
COLOR_MAX = ORANGE_MAX

def test1():
    frame = cv.LoadImage(in_image)
    frameHSV = cv.CreateImage(cv.GetSize(frame), 8, 3)
    cv.CvtColor(frame, frameHSV, cv.CV_RGB2HSV)
    frame_threshed = cv.CreateImage(cv.GetSize(frameHSV), 8, 1)
    cv.InRangeS(frameHSV, COLOR_MIN, COLOR_MAX, frame_threshed)
    cv.SaveImage(out_image_thr, frame_threshed)

if __name__ == '__main__':
    test1()

Solution

  • Problem 1 : Different applications use different scales for HSV. For example gimp uses H = 0-360, S = 0-100 and V = 0-100. But OpenCV uses H: 0-179, S: 0-255, V: 0-255. Here i got a hue value of 22 in gimp. So I took half of it, 11, and defined range for that. ie (5,50,50) - (15,255,255).

    Problem 2: And also, OpenCV uses BGR format, not RGB. So change your code which converts RGB to HSV as follows:

    cv.CvtColor(frame, frameHSV, cv.CV_BGR2HSV)
    

    Now run it. I got an output as follows:

    enter image description here

    Hope that is what you wanted. There are some false detections, but they are small, so you can choose biggest contour which is your lid.

    EDIT:

    As Karl Philip told in his comment, it would be good to add new code. But there is change of only a single line. So, I would like to add the same code implemented in new cv2 module, so users can compare the easiness and flexibility of new cv2 module.

    import cv2
    import numpy as np
    
    img = cv2.imread('sof.jpg')
    
    ORANGE_MIN = np.array([5, 50, 50],np.uint8)
    ORANGE_MAX = np.array([15, 255, 255],np.uint8)
    
    hsv_img = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
    
    frame_threshed = cv2.inRange(hsv_img, ORANGE_MIN, ORANGE_MAX)
    cv2.imwrite('output2.jpg', frame_threshed)
    

    It gives the same result as above. But code is much more simpler.