SA-Kenan/04_Spurerkennung/dev/Lanedetection_development_V01.py
2022-05-09 20:30:26 +02:00

254 lines
14 KiB
Python

# Creation Date: 01.03.2022
# Author: Kenan Gömek
# This program is for the development of the lane detection algorithm on a windows machine
# It uses images, which were captured previously
import cv2 as cv
import numpy as np
import os
# input
folder_path=r"U:\bwsyncshare\images_input"
# Parameters
pixels_per_mm = 71/24.25 #[px/mm] for 120 mm camera height for resolution: 416x320
# pixels_per_mm = 107/24.25 #[px/mm] for 120 mm camera height for resolution: 640x480
cut_off_brightness_grayscale = 45 # 15 pixels with a value less than this will bet set to zero (black)
# Parameters for HoughCircles
dp = 1 # Inverse ratio of the accumulator resolution to the image resolution. For example, if dp=1 , the accumulator has the same resolution as the input image. If dp=2 , the accumulator has half as big width and height.
minDist_mm = 10 # [mm] minimal distance between two circles
minDist_px = int(minDist_mm*pixels_per_mm) # in [px] Minimum distance in px between the centers of the detected circles. If the parameter is too small, multiple neighbor circles may be falsely detected in addition to a true one. If it is too large, some circles may be missed.
minRadius_mm = 5 # [mm] minimum radius of a circle
minRadius_px = int(minRadius_mm*pixels_per_mm) # [px] Minimum circle radius.
maxRadius_mm = 7 # [mm] maximum radius of a circle
maxRadius_px = int(maxRadius_mm*pixels_per_mm) # [px] Maximum circle radius. If <= 0, uses the maximum image dimension. If < 0, returns centers without finding the radius.
param1 = 100 # 30 First method-specific parameter. In case of HOUGH_GRADIENT , it is the higher threshold of the two passed to the Canny edge detector (the lower one is twice smaller).
# If circles/LEDs should be detected at low shutter speeds, than lower this value
# Upper threshold for the internal Canny edge detector.
# "Gradient value between dark and white"
param2 = 5 # 12 Second method-specific parameter. In case of HOUGH_GRADIENT , it is the accumulator threshold for the circle centers at the detection stage. The smaller it is, the more false circles may be detected. Circles, corresponding to the larger accumulator values, will be returned first.
# By increasing this threshold value, we can ensure that only the best circles, corresponding to larger accumulator values, are returned.
# Define color numbers to identify the color channels in the matrix with all detected LEDs. No string, because numpy array should stay uint16
color_number_red = 1
color_number_green = 2
color_number_blue = 3
def preprocess_grayscale_image_1(image_gray_copy, color_channel):
# Set all pixels with a value less than "cut_off_brightness_grayscale" to zero
image_gray_cutoff = image_gray_copy.copy()
image_gray_cutoff[image_gray_cutoff < cut_off_brightness_grayscale]=0
cv.imshow(f"{color_channel} channel cutoff", image_gray_cutoff[150:,:])
preprocessed_image = image_gray_cutoff
return preprocessed_image
def preprocess_grayscale_image_2(image_gray_copy, color_channel):
# Set all pixels with a value less than "cut_off_brightness_grayscale" to zero
# This prevents the false detection of color-channels which are not intended to be detected
image_gray_cutoff = image_gray_copy.copy()
image_gray_cutoff[image_gray_cutoff < cut_off_brightness_grayscale]=0
# cv.imshow(f"{color_channel} channel grayscale cutoff", image_gray_cutoff[150:,:])
# For better accuracy, binary images are used before finding contours --> apply treshold
image_gray_binary = image_gray_cutoff
perc = np.percentile(image_gray_binary, 95) # calculate 95 % percentile
ret, tresh=cv.threshold(image_gray_binary, perc, 255, cv.THRESH_BINARY) # set pixels above specified percentile to 255, the resto to 0
preprocessed_image = tresh
return preprocessed_image
def preprocess_grayscale_image_3(image_gray_copy, color_channel):
# Set all pixels with a value less than "cut_off_brightness_grayscale" to zero
# This prevents the false detection of color-channels which are not intended to be detected
image_gray_cutoff = image_gray_copy.copy()
image_gray_cutoff[image_gray_cutoff < cut_off_brightness_grayscale]=0
perc = np.percentile(image_gray_cutoff, 95) # calculate 95 % percentile
image_gray_cutoff[image_gray_cutoff < perc]=0
cv.imshow(f"{color_channel} channel grayscale cutoff", image_gray_cutoff[150:,:])
# For better accuracy, binary images are used before finding contours --> apply treshold
image_gray_binary = image_gray_cutoff
perc = np.percentile(image_gray_binary, 95) # calculate 95 % percentile
ret, tresh=cv.threshold(image_gray_binary, perc, 255, cv.THRESH_BINARY) # set pixels above specified percentile to 255, the resto to 0
preprocessed_image = image_gray_cutoff
return preprocessed_image
def preprocess_grayscale_image_4_cannyedge(image_gray_copy, color_channel):
# Set all pixels with a value less than "cut_off_brightness_grayscale" to zero
# This prevents the false detection of color-channels which are not intended to be detected
t_lower = 1
t_upper = 5
edge = cv.Canny(image_gray_copy, t_lower, t_upper)
cv.imshow(f"{color_channel} channel edge", edge[150:,:])
preprocessed_image = edge
return preprocessed_image
def detect_LEDs_in_color_channel(image_gray, color_channel, image_gray_copy, image_bgr_copy):
# preprocessed_gray_image = preprocess_grayscale_image_1(image_gray_copy=image_gray_copy, color_channel=color_channel)
preprocessed_gray_image = preprocess_grayscale_image_2(image_gray_copy=image_gray_copy, color_channel=color_channel)
# preprocessed_gray_image = preprocess_grayscale_image_3(image_gray_copy=image_gray_copy, color_channel=color_channel)
# preprocessed_gray_image = preprocess_grayscale_image_4_cannyedge(image_gray_copy=image_gray_copy, color_channel=color_channel)
# preprocessed_gray_image = image_gray_copy
detected_LEDs = cv.HoughCircles(preprocessed_gray_image, cv.HOUGH_GRADIENT, dp=dp, minDist = minDist_px
, param1=param1, param2=param2, minRadius=minRadius_px, maxRadius=maxRadius_px)
# specify color number, for adding to matrix of detected LEDs
if color_channel == "blue":
color_number = color_number_blue
elif color_channel == "green":
color_number = color_number_green
elif color_channel == "red":
color_number = color_number_red
# check if at least one circle was found in the image
if detected_LEDs is not None:
detected_LEDs = np.uint16(np.round(detected_LEDs)) # convert the (x, y) coordinates and radius of the circles to integers
detected_LEDs = detected_LEDs[0,:]
detected_LEDs=np.hstack((detected_LEDs, np.full((detected_LEDs.shape[0],1), color_number, dtype=np.uint16)))
# matrix with columns: x, y, r
number_of_detected_LEDs = detected_LEDs.shape[0]
print(f"detected {color_channel} LEDs: {number_of_detected_LEDs}")
# paramters for drawing
line_thickness = 1
circle_color = (0,255,0)
vertex_offset = 2
rectangle_color = (0,128,255) # R G B
for (x, y, r, cn) in detected_LEDs:
print(f"x:{x} px, y:{y} px, r:{r} px, r:{round(r*1/(pixels_per_mm),2)} mm, D: {round(2*r*1/(pixels_per_mm),2)} mm, color: {color_channel}")
cv.circle(image_bgr_copy, (x, y), r, circle_color, thickness=line_thickness) # draw detected circumference of the cirle
cv.circle(image_gray_copy, (x, y), r, circle_color, thickness=line_thickness) # draw detected circumference of the cirle
cv.circle(preprocessed_gray_image, (x, y), r, circle_color, thickness=1) # draw detected circumference of the cirle
cv.rectangle(img=image_bgr_copy, pt1=(x-vertex_offset, y-vertex_offset), pt2=(x+vertex_offset, y+vertex_offset), \
color=rectangle_color, thickness=cv.FILLED)
cv.rectangle(img=image_gray_copy, pt1=(x-vertex_offset, y-vertex_offset), pt2=(x+vertex_offset, y+vertex_offset), \
color=rectangle_color, thickness=cv.FILLED)
cv.rectangle(img=preprocessed_gray_image, pt1=(x-vertex_offset, y-vertex_offset), pt2=(x+vertex_offset, y+vertex_offset), \
color=rectangle_color, thickness=cv.FILLED)
cv.imshow(f"{color_channel} channel binary", preprocessed_gray_image[150:,:])
return detected_LEDs
else:
print(f"No {color_channel} LEDs were found in the image")
return None
def detect_blue_LEDs(image_colorchannel_gray, image_colorchannel_gray_copy, image_bgr_copy):
color_channel = "blue"
detected_LEDs_blue = detect_LEDs_in_color_channel(image_gray=image_colorchannel_gray, color_channel=color_channel, \
image_gray_copy=image_colorchannel_gray_copy, image_bgr_copy=image_bgr_copy)
if detected_LEDs_blue is not None:
return detected_LEDs_blue
else:
return None
def detect_green_LEDs(image_colorchannel_gray, image_colorchannel_gray_copy, image_bgr_copy):
color_channel = "green"
detected_LEDs_green = detect_LEDs_in_color_channel(image_gray=image_colorchannel_gray, color_channel=color_channel, \
image_gray_copy=image_colorchannel_gray_copy, image_bgr_copy=image_bgr_copy)
if detected_LEDs_green is not None:
return detected_LEDs_green
else:
return None
def detect_red_LEDs(image_colorchannel_gray, image_colorchannel_gray_copy, image_bgr_copy):
color_channel = "red"
detected_LEDs_red = detect_LEDs_in_color_channel(image_gray=image_colorchannel_gray, color_channel=color_channel, \
image_gray_copy=image_colorchannel_gray_copy, image_bgr_copy=image_bgr_copy)
if detected_LEDs_red is not None:
return detected_LEDs_red
else:
return None
def detect_LEDs(image_b, image_g, image_r, image_b_copy, image_g_copy, image_r_copy, image_bgr_copy):
detected_LEDs_blue = detect_blue_LEDs(image_colorchannel_gray=image_b, image_colorchannel_gray_copy=image_b_copy, image_bgr_copy=image_bgr_copy)
detected_LEDs_green = detect_green_LEDs(image_colorchannel_gray=image_g, image_colorchannel_gray_copy=image_g_copy, image_bgr_copy=image_bgr_copy)
detected_LEDs_red = detect_red_LEDs(image_colorchannel_gray=image_r, image_colorchannel_gray_copy=image_r_copy, image_bgr_copy=image_bgr_copy)
# check the cases:
# case 1: r
if detected_LEDs_blue is None and detected_LEDs_green is None and detected_LEDs_red is not None:
detected_LEDs = detected_LEDs_red
# case 2: g
elif detected_LEDs_blue is None and detected_LEDs_green is not None and detected_LEDs_red is None:
detected_LEDs = detected_LEDs_green
# case 3: b
elif detected_LEDs_blue is not None and detected_LEDs_green is None and detected_LEDs_red is None:
detected_LEDs = detected_LEDs_blue
# case 4: y = r+g
elif detected_LEDs_blue is None and detected_LEDs_green is not None and detected_LEDs_red is not None:
detected_LEDs_all = np.vstack((detected_LEDs_red, detected_LEDs_green))
detected_LEDs = detected_LEDs_all
# case 5: m = r+b
elif detected_LEDs_blue is not None and detected_LEDs_green is None and detected_LEDs_red is not None:
detected_LEDs_all = np.vstack((detected_LEDs_red, detected_LEDs_blue))
detected_LEDs = detected_LEDs_all
# case 6: c = g+b
elif detected_LEDs_blue is not None and detected_LEDs_green is not None and detected_LEDs_red is None:
detected_LEDs_all = np.vstack((detected_LEDs_green, detected_LEDs_blue))
detected_LEDs = detected_LEDs_all
# case 7: w = r+g+b
elif detected_LEDs_blue is not None and detected_LEDs_green is not None and detected_LEDs_red is not None:
detected_LEDs_all = np.vstack((detected_LEDs_red, detected_LEDs_green, detected_LEDs_blue))
detected_LEDs = detected_LEDs_all
return detected_LEDs
def lane_detection(image_b, image_g, image_r, image_b_copy, image_g_copy, image_r_copy, image_bgr_copy):
# Detect LEDs
detected_LEDs = detect_LEDs(image_b, image_g, image_r, image_b_copy, image_g_copy, image_r_copy, image_bgr_copy)
return detected_LEDs
def main():
filenames_of_images = [f for f in os.listdir(folder_path) if f.endswith('.png')]
for i, filename_of_image in enumerate(filenames_of_images):
print(f"image:{filename_of_image}")
full_path_of_image = os.path.join(folder_path, filename_of_image)
image_bgr = cv.imread(full_path_of_image, cv.IMREAD_COLOR) # load original image
image_b,image_g,image_r = cv.split(image_bgr) # Split colour channels and get grayscale images
# create copy of images, to draw on them
image_bgr_copy = image_bgr.copy()
image_b_copy = image_b.copy()
image_g_copy = image_g.copy()
image_r_copy = image_r.copy()
detected_LEDs = lane_detection(image_b, image_g, image_r, image_b_copy, image_g_copy, image_r_copy, image_bgr_copy)
print(detected_LEDs)
print(f"_____________________________________")
# show images: only region of interest
cv.imshow("Original image", image_bgr[150:,:])
cv.imshow("All detected LEDs", image_bgr_copy[150:,:])
cv.imshow("Blue channel", image_b[150:,:])
cv.imshow("Blue channel detected", image_b_copy[150:,:])
cv.imshow("Green channel", image_g[150:,:])
cv.imshow("Green channel detected", image_g_copy[150:,:])
cv.imshow("Red channel", image_r[150:,:])
cv.imshow("Red channel detected", image_r_copy[150:,:])
cv.waitKey(0) # display and wait if a key is pressed and then continue
cv.destroyAllWindows()
if __name__ == "__main__":
main()