pythonimageopencvimage-processingstraight-line-detection

How to perform image segmentation on white lines in a green field/grassy field


As the title states I'm attempting to do image segmentation in hopes of doing 'lane' detection. Here is a sample image I want to test on.

Here's my first coding attempt for what I tried that I found online essentially.

from matplotlib import pyplot as plt
import os
import cv2
def image_seg_watershed():
    img = cv2.imread(os.path.join(img_file,img_file_list[0]))
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
    plt.subplot(121), plt.imshow(thresh)
    plt.show()

This is the output.

Kinda close, but not what I wanted. Any tips or helpful advice?


Solution

  • A potential approach is color segmentation using cv2.inRange(). With the assumption that the desired lines are white, we can isolate pixels in this color range. Here's the main idea


    We convert the image to HSV format since its easier to represent color than RBG or BGR format. Then we create a lower/upper threshold to detect white pixels and create a mask using cv2.inRange()

    import numpy as np
    import cv2
    
    image = cv2.imread('1.jpg')
    result = image.copy()
    image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    lower = np.array([0,0,200])
    upper = np.array([179, 77, 255])
    mask = cv2.inRange(image, lower, upper)
    result = cv2.bitwise_and(result,result, mask=mask)
    

    Note there are small particles of noise so the next step is to remove some of it. There are several approaches we can take here. One is to use morphological operations to erode/dilate the image. Another approach is to find contours and filter using contour area to ignore the small particles. I'll use the latter approach. We use a minimum threshold area to filter out particles and fill them in with black using cv2.drawContours(). Here's the result

    cnts = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    
    for c in cnts:
        area = cv2.contourArea(c)
        if area < 1:
            cv2.drawContours(result, [c], -1, (0,0,0), -1)
    

    Full code

    import numpy as np
    import cv2
    
    image = cv2.imread('1.jpg')
    result = image.copy()
    image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    lower = np.array([0,0,200])
    upper = np.array([179, 77, 255])
    mask = cv2.inRange(image, lower, upper)
    result = cv2.bitwise_and(result,result, mask=mask)
    
    cnts = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    
    for c in cnts:
        area = cv2.contourArea(c)
        if area < 1:
            cv2.drawContours(result, [c], -1, (0,0,0), -1)
    
    cv2.imshow('mask', mask)
    cv2.imshow('result', result)
    cv2.waitKey()
    

    You can use a color threshold script to find the lower/upper HSV boundaries

    import cv2
    import sys
    import numpy as np
    
    def nothing(x):
        pass
    
    # Create a window
    cv2.namedWindow('image')
    
    # create trackbars for color change
    cv2.createTrackbar('HMin','image',0,179,nothing) # Hue is from 0-179 for Opencv
    cv2.createTrackbar('SMin','image',0,255,nothing)
    cv2.createTrackbar('VMin','image',0,255,nothing)
    cv2.createTrackbar('HMax','image',0,179,nothing)
    cv2.createTrackbar('SMax','image',0,255,nothing)
    cv2.createTrackbar('VMax','image',0,255,nothing)
    
    # Set default value for MAX HSV trackbars.
    cv2.setTrackbarPos('HMax', 'image', 179)
    cv2.setTrackbarPos('SMax', 'image', 255)
    cv2.setTrackbarPos('VMax', 'image', 255)
    
    # Initialize to check if HSV min/max value changes
    hMin = sMin = vMin = hMax = sMax = vMax = 0
    phMin = psMin = pvMin = phMax = psMax = pvMax = 0
    
    img = cv2.imread('1.jpg')
    output = img
    waitTime = 33
    
    while(1):
    
        # get current positions of all trackbars
        hMin = cv2.getTrackbarPos('HMin','image')
        sMin = cv2.getTrackbarPos('SMin','image')
        vMin = cv2.getTrackbarPos('VMin','image')
    
        hMax = cv2.getTrackbarPos('HMax','image')
        sMax = cv2.getTrackbarPos('SMax','image')
        vMax = cv2.getTrackbarPos('VMax','image')
    
        # Set minimum and max HSV values to display
        lower = np.array([hMin, sMin, vMin])
        upper = np.array([hMax, sMax, vMax])
    
        # Create HSV Image and threshold into a range.
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
        mask = cv2.inRange(hsv, lower, upper)
        output = cv2.bitwise_and(img,img, mask= mask)
    
        # Print if there is a change in HSV value
        if( (phMin != hMin) | (psMin != sMin) | (pvMin != vMin) | (phMax != hMax) | (psMax != sMax) | (pvMax != vMax) ):
            print("(hMin = %d , sMin = %d, vMin = %d), (hMax = %d , sMax = %d, vMax = %d)" % (hMin , sMin , vMin, hMax, sMax , vMax))
            phMin = hMin
            psMin = sMin
            pvMin = vMin
            phMax = hMax
            psMax = sMax
            pvMax = vMax
    
        # Display output image
        cv2.imshow('image',output)
    
        # Wait longer to prevent freeze for videos.
        if cv2.waitKey(waitTime) & 0xFF == ord('q'):
            break
    
    cv2.destroyAllWindows()