3

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?

nathancy
  • 42,661
  • 14
  • 115
  • 137
Bob Stoops
  • 151
  • 2
  • 12
  • why do you call this function watershed if you're just applying a global threshold? – Piglet Aug 28 '19 at 17:38
  • Forgot to change the name. Tried watershed and it didn't work lol, so I just rewrote the method and never changed the name – Bob Stoops Aug 28 '19 at 17:49

1 Answers1

3

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

  • Convert image to HSV format since its easier to represent color
  • Perform color segmentation using lower/upper threshold
  • Filter using contour area to remove small particles

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()
nathancy
  • 42,661
  • 14
  • 115
  • 137
  • damn that's awesome. Thanks for the advice. I'll def try this out when I have the time tomorrow. Thank you! – Bob Stoops Aug 29 '19 at 02:55