0

I've created the program that picks up the pixel from the video and convert it from RGB to HSV. Then using range function it filter that colour on the video.

The problem is:

When I look at the video, the sample I'm picking the pixel from looks flat - in term of colour. But computer sees it as a huge variety of colours. Is it the low quality video from laptop camera, some kind of compression that mix colours on the level invisible for the eye?

Possible solution:

take a sample not from single pixel, but rather from few - 10 -20 pixels around cursor?

Samples taken literally 5mm from each other and results:

Photo 1

Photo 2

Here is the code if you want to test it on your own:

PS. Picking work only in first 480x480 pixels square. I'm working on making it work everywhere.

import cv2 as cv
import numpy as np

#variable with HSV value of measured pixel
px_hsv = []

#default variable of HSV range values, 
#nothing special just anything to define new variable
min_h = 10
min_s = 10
min_v = 10
max_h = 10
max_s = 10
max_v = 10

#trackbars need some function to operate correctly, 
#so I generated empty one
def nothing(_):
    pass

#mouse function operate all events from mouse:
#- Move of cursor
#- LMB Double click
def mouse(event,x,y,flags,param):
    global px_hsv, min_h, min_s, min_v, max_h, max_s, max_v
    if event == cv.EVENT_MOUSEMOVE:
        #showing converted HSV value of pixel under cursor
        px = frame[x,y]
        px_array = np.uint8([[px]])
        px_hsv = cv.cvtColor(px_array,cv.COLOR_BGR2HSV)
    elif event == cv.EVENT_LBUTTONDBLCLK:
        #get x,y position of cursor and convert its value to HSV
        px = frame[x,y]
        px_array = np.uint8([[px]])
        px_hsv = cv.cvtColor(px_array,cv.COLOR_BGR2HSV)
        #set minimum and maximum values of range, of the selected colour,
        #represented by pixel I double clicked on
        #range is set to -20/+20. Is it enough?
        min_h = (px_hsv[0][0][0]-20 if px_hsv[0][0][0]-20 > 0 else 0)
        min_s = (px_hsv[0][0][1]-40 if px_hsv[0][0][1]-20 > 0 else 0)
        min_v = (px_hsv[0][0][2]-40 if px_hsv[0][0][2]-20 > 0 else 0)
        max_h = (px_hsv[0][0][0]+20 if px_hsv[0][0][0]+20 < 180 else 180)
        max_s = (px_hsv[0][0][1]+20 if px_hsv[0][0][1]+20 < 255 else 255)
        max_v = (px_hsv[0][0][2]+40 if px_hsv[0][0][2]+20 < 255 else 255)

#start recording
cap = cv.VideoCapture(0)

#create new window, video with main view directly
#from the camera and attach mouse operations to it
cv.namedWindow('video')
cv.setMouseCallback("video",mouse)

while True:
    # Take each frame
    _, frame = cap.read()

    # Convert BGR to HSV
    hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)

    # define range of color in HSV

    cv.createTrackbar("H1", "video", min_h, 180, nothing)
    cv.createTrackbar("S1", "video", min_s, 255, nothing)
    cv.createTrackbar("V1", "video", min_v, 255, nothing)
    cv.createTrackbar("H2", "video", max_h, 180, nothing)
    cv.createTrackbar("S2", "video", max_s, 255, nothing)
    cv.createTrackbar("V2", "video", max_v, 255, nothing)

    h1 = cv.getTrackbarPos('H1', 'video')
    s1 = cv.getTrackbarPos('S1', 'video')
    v1 = cv.getTrackbarPos('V1', 'video')
    h2 = cv.getTrackbarPos('H2', 'video')
    s2 = cv.getTrackbarPos('S2', 'video')
    v2 = cv.getTrackbarPos('V2', 'video')

    lower = np.array([h1,s1,v1])
    upper = np.array([h2,s2,v2])

    # Threshold the HSV video to get only colour in defined range
    mask = cv.inRange(hsv, lower, upper)

    #text with HSV value on the main screen
    pixel_hsv = " ".join(str(values) for values in px_hsv)
    font = cv.FONT_HERSHEY_SIMPLEX
    cv.putText(frame, "px HSV: "+pixel_hsv, (10, 260), 
               font, 1, (255, 255, 255), 1, cv.LINE_AA)

    # Bitwise-AND mask and original video
    cv.imshow('video',frame)
    cv.imshow('mask',mask)

    #click ESC to exit the program
    key = cv.waitKey(5) & 0xFF
    if key == 27:
        break
cv.destroyAllWindows()
Filip Matyja
  • 137
  • 1
  • 2
  • 11
  • 2
    You can use `cv2.floodFill()`. You can set a threshold that, for each pixel, it will look at neighboring pixels and see if it's within +/- some value. In other words, it will allow you to grow your mask even on a piece that has some gradient. – alkasm Oct 16 '18 at 18:27
  • 2
    Also, `px = frame[x,y]` is not showing you the color you selected; it should be `[y, x]` (row, column) when you're indexing an array. Though some open cv functions expect *points*, i.e., (x, y), indexing an array expects (row, column), i.e., (y, x). – alkasm Oct 16 '18 at 18:29
  • @AlexanderReynolds, your second comment with shifting "y" and "x" solved the problem. If you want to copy that as answer I will mark it as so. Thanks for picking up this silly error. – Filip Matyja Oct 16 '18 at 18:36
  • Cheers! Happens to all of us! I'll actually just flag the question as caused by a simple typographical error. Note that flagging for closing isn't a bad thing, just helps us weed out questions that aren't likely to be found by searching. – alkasm Oct 16 '18 at 18:53

0 Answers0