Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
287 views
in Technique[技术] by (71.8m points)

opencv - HSV Range for Line Follower for various light conditions

I have the following problem: when detecting a while line under various lighting conditions, a mask (based on HSV) results in good performance in only one scenario (very bright or very shaded areas). As seen below.

My code is as follows, I am using HSV. The threshold for upper and lower is a constant value (+x/-x)

    ## SHADE
    shadeLower1 = np.array([127,30,117] , dtype=np.uint8) 
    shadeUpper1 = np.array([147,51,138], dtype=np.uint8) 
    
    ## SUN
    sunLower2 = np.array([4,0,184], dtype=np.uint8) 
    sunUpper2 = np.array([104,57,255], dtype=np.uint8) 

    mask1 = cv2.inRange(hsv, shadeLower1, shadeUpper1)
    mask2 = cv2.inRange(hsv, sunLower2, sunUpper2)

    mask = cv2.max(mask1, mask2)      

For instance, it will be fine in the shaded region (the white tape is perfect) and once it reaches the sunny area, the mask window is just saturated with white and I loose my white object.

Any help would be appreciated in what to do!

Shaded Area

Sunny Area

question from:https://stackoverflow.com/questions/66053374/hsv-range-for-line-follower-for-various-light-conditions

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I mostly did the same thing you did for thresholding, but I used bitwise_and instead of bitwise_or (bitwise_or is the same as cv2.max). The lines are a little messy, but hopefully good enough for you to use. You might be able to clean them up more if you take the hue channel into account to exclude the red (I avoided it since white is technically all hues).

enter image description here

enter image description here

It might even be worth it to try and filter across multiple color spaces and combine the masks.

import cv2
import numpy as np

# find path and return its contour
def findPath(hsv):
    # threshold on s an v channel
    h,s,v = cv2.split(hsv);
    mask1 = cv2.inRange(s, 0, 45);
    mask2 = cv2.inRange(v, 115, 255);
    mask3 = cv2.bitwise_and(mask1, mask2, mask = None);

    # close
    kernel = np.ones((5,5),np.uint8)
    mask3 = cv2.dilate(mask3,kernel,iterations = 1);
    mask3 = cv2.erode(mask3,kernel, iterations = 1);

    # open
    mask3 = cv2.erode(mask3,kernel,iterations = 1);
    mask3 = cv2.dilate(mask3,kernel, iterations = 1);

    # find contours
    _, contours, _ = cv2.findContours(mask3, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);

    # find biggest contour
    biggest = None;
    biggest_size = -1;
    for contour in contours:
        area = cv2.contourArea(contour);
        if area > biggest_size:
            biggest = contour;
            biggest_size = area;
    return biggest;

# skeletonize the mask
def skeleton(mask):
    # get structure
    img = mask.copy();
    size = np.size(img);
    skel = np.zeros_like(mask);
    elem = cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3));
    while True:
        # skeleton iteration
        eroded = cv2.erode(img,elem);
        temp = cv2.dilate(eroded,elem);
        temp = cv2.subtract(img,temp);
        skel = cv2.bitwise_or(skel,temp);

        # check for end condition
        img = eroded.copy() ;
        zeros = size - cv2.countNonZero(img);
        if zeros == size:
            break;

    # connect small gaps
    kernel = np.ones((2,2), np.uint8);
    skel = cv2.dilate(skel, kernel, iterations = 1);

    # filter out little lines
    _, contours, _ = cv2.findContours(skel, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);

    # filter contours by size
    big_cntrs = [];
    for contour in contours:
        perimeter = cv2.arcLength(contour, True);
        if perimeter > 50:
            big_cntrs.append(contour);
    thin_lines = np.zeros_like(skel);
    thin_lines = cv2.drawContours(thin_lines, big_cntrs, -1, 255, -1);
    skel = thin_lines;

    # dilate and close to connect lines
    kernel = np.ones((3,3), np.uint8)
    skel = cv2.dilate(skel, kernel, iterations = 5);
    skel = cv2.erode(skel, kernel, iterations = 4);

    # show
    return skel;

# load image
imgs = [];
l1 = cv2.imread("line1.png");
l2 = cv2.imread("line2.png");
imgs.append(l1);
imgs.append(l2);

# convert
hsvs = [];
for img in imgs:
    hsvs.append(cv2.cvtColor(img, cv2.COLOR_BGR2HSV));

# draw contours
masks = [];
for a in range(len(imgs)):
    # get contour
    contour = findPath(hsvs[a]);

    # create mask
    mask = np.zeros_like(hsvs[a][:,:,0]);
    cv2.drawContours(mask, [contour], -1, (255), -1);
    mask = cv2.medianBlur(mask, 5);
    masks.append(mask);

# skeleton
skelly_masks = [];
for mask in masks:
    skelly = skeleton(mask.copy());
    skelly_masks.append(skelly);

# draw on original
for a in range(len(imgs)):
    imgs[a][np.where(masks[a] == 255)] = (155,0,0); # 155 to avoid blinding people
    imgs[a][np.where(skelly_masks[a] == 255)] = (0,0,155);
    cv2.imshow(str(a), imgs[a]);
    cv2.imwrite("img" + str(a) + ".png", imgs[a]);
cv2.waitKey(0);

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...