0xSynapse's picture
Upload 2 files
5577bf7 verified
import cv2
import numpy as np
import gradio as gr
import tempfile
# Define Utility Functions
def region_of_interest(img, vertices):
"""Select the region of interest (ROI) from a defined list of vertices."""
mask = np.zeros_like(img)
if len(img.shape) > 2:
channel_count = img.shape[2] # 3 or 4 depending on your image.
ignore_mask_color = (255,) * channel_count
else:
ignore_mask_color = 255
cv2.fillPoly(mask, [vertices], ignore_mask_color)
masked_image = cv2.bitwise_and(img, mask)
return masked_image
def draw_lines(img, lines, color=[255, 0, 0], thickness=2):
"""Utility for drawing lines."""
if lines is not None:
for line in lines:
for x1,y1,x2,y2 in line:
cv2.line(img, (x1, y1), (x2, y2), color, thickness)
def hough_lines(img, rho, theta, threshold, min_line_len, max_line_gap):
"""Utility for defining Line Segments."""
lines = cv2.HoughLinesP(
img, rho, theta, threshold, np.array([]),
minLineLength=min_line_len, maxLineGap=max_line_gap)
line_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
draw_lines(line_img, lines)
return line_img, lines
def separate_left_right_lines(lines):
"""Separate left and right lines depending on the slope."""
left_lines = []
right_lines = []
if lines is not None:
for line in lines:
for x1, y1, x2, y2 in line:
if x1 == x2:
continue # Skip vertical lines to avoid division by zero
slope = (y2 - y1) / (x2 - x1)
if slope < 0: # Negative slope = left lane
left_lines.append([x1, y1, x2, y2])
else: # Positive slope = right lane
right_lines.append([x1, y1, x2, y2])
return left_lines, right_lines
def cal_avg(values):
"""Calculate average value."""
if values is not None and len(values) > 0:
return sum(values) / len(values)
else:
return 0
def extrapolate_lines(lines, upper_border, lower_border):
"""Extrapolate lines keeping in mind the lower and upper border intersections."""
slopes = []
consts = []
if lines is not None and len(lines) != 0:
for x1, y1, x2, y2 in lines:
if x1 == x2:
continue # Avoid division by zero
slope = (y1 - y2) / (x1 - x2)
slopes.append(slope)
c = y1 - slope * x1
consts.append(c)
avg_slope = cal_avg(slopes)
avg_consts = cal_avg(consts)
if avg_slope == 0:
return None
x_lane_lower_point = int((lower_border - avg_consts) / avg_slope)
x_lane_upper_point = int((upper_border - avg_consts) / avg_slope)
return [x_lane_lower_point, lower_border, x_lane_upper_point, upper_border]
else:
return None
def extrapolated_lane_image(img, lines, roi_upper_border, roi_lower_border):
"""Main function called to get the final lane lines."""
lanes_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
lines_left, lines_right = separate_left_right_lines(lines)
lane_left = extrapolate_lines(lines_left, roi_upper_border, roi_lower_border)
lane_right = extrapolate_lines(lines_right, roi_upper_border, roi_lower_border)
if lane_left is not None and lane_right is not None:
draw_con(lanes_img, [[lane_left], [lane_right]])
return lanes_img
def draw_con(img, lines):
"""Fill in lane area."""
points = []
if lines is not None:
for x1, y1, x2, y2 in lines[0]:
points.append([x1, y1])
points.append([x2, y2])
for x1, y1, x2, y2 in lines[1]:
points.append([x2, y2])
points.append([x1, y1])
if points:
points = np.array([points], dtype='int32')
cv2.fillPoly(img, [points], (0, 255, 0))
def process_image(image, low_threshold, high_threshold, kernel_size, rho, theta, hough_threshold, min_line_len, max_line_gap, roi_vertices):
# Convert to grayscale.
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
# Intensity selection.
gray_select = cv2.inRange(gray, 150, 255)
# Region masking: Select vertices according to the input image.
roi_vertices_np = np.array(roi_vertices, dtype=np.int32)
gray_select_roi = region_of_interest(gray_select, roi_vertices_np)
# Canny Edge Detection.
img_canny = cv2.Canny(gray_select_roi, low_threshold, high_threshold)
# Remove noise using Gaussian blur.
if kernel_size % 2 == 0:
kernel_size += 1 # kernel_size must be odd
canny_blur = cv2.GaussianBlur(img_canny, (kernel_size, kernel_size), 0)
# Hough transform parameters set according to the input image.
hough, lines = hough_lines(canny_blur, rho, theta, hough_threshold, min_line_len, max_line_gap)
# Extrapolate lanes.
roi_upper_border = min([vertex[1] for vertex in roi_vertices]) # smallest y value
roi_lower_border = max([vertex[1] for vertex in roi_vertices]) # largest y value
lane_img = extrapolated_lane_image(image, lines, roi_upper_border, roi_lower_border)
# Combined using weighted image.
image_result = cv2.addWeighted(image, 1, lane_img, 0.4, 0.0)
return image_result
def process_video(input_video_path, low_threshold, high_threshold, kernel_size, rho, theta, hough_threshold, min_line_len, max_line_gap, roi_vertices):
# Initialize video capture
video_cap = cv2.VideoCapture(input_video_path)
if not video_cap.isOpened():
raise Exception("Error opening video stream or file")
# Retrieve video frame properties.
frame_w = int(video_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_h = int(video_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
frame_fps = video_cap.get(cv2.CAP_PROP_FPS)
# Output video file
temp_video = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False)
output_video_path = temp_video.name
# Video writer
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
vid_out = cv2.VideoWriter(output_video_path, fourcc, frame_fps, (frame_w,frame_h))
# Process each frame
while True:
ret, frame = video_cap.read()
if not ret:
break
result = process_image(frame, low_threshold, high_threshold, kernel_size, rho, theta, hough_threshold, min_line_len, max_line_gap, roi_vertices)
vid_out.write(result)
# Release resources
video_cap.release()
vid_out.release()
return output_video_path
def gradio_process_video(input_video_path, low_threshold, high_threshold, kernel_size, rho, theta_degree, hough_threshold, min_line_len, max_line_gap,
x1, y1, x2, y2, x3, y3, x4, y4):
# Define ROI vertices from user input
roi_vertices = [[x1, y1], [x2, y2], [x3, y3], [x4, y4]]
# Convert theta_degree to radians
theta = np.deg2rad(theta_degree)
# Process the video
output_video_path = process_video(input_video_path, low_threshold, high_threshold, kernel_size, rho, theta, hough_threshold, min_line_len, max_line_gap, roi_vertices)
return output_video_path
# Create the Gradio interface
iface = gr.Interface(
fn=gradio_process_video,
inputs=[
gr.Video(label="Input Video"),
gr.Slider(0, 255, step=1, value=50, label="Canny Low Threshold"),
gr.Slider(0, 255, step=1, value=150, label="Canny High Threshold"),
gr.Slider(1, 31, step=2, value=5, label="Gaussian Kernel Size"),
gr.Slider(1, 10, step=1, value=1, label="Hough Transform Rho"),
gr.Slider(0, 180, step=1, value=1, label="Hough Transform Theta (degrees)"),
gr.Slider(1, 500, step=1, value=100, label="Hough Transform Threshold"),
gr.Slider(1, 500, step=1, value=50, label="Hough Transform Min Line Length"),
gr.Slider(1, 500, step=1, value=300, label="Hough Transform Max Line Gap"),
gr.Number(value=100, label="ROI x1"),
gr.Number(value=540, label="ROI y1"),
gr.Number(value=900, label="ROI x2"),
gr.Number(value=540, label="ROI y2"),
gr.Number(value=525, label="ROI x3"),
gr.Number(value=330, label="ROI y3"),
gr.Number(value=440, label="ROI x4"),
gr.Number(value=330, label="ROI y4"),
],
outputs=gr.Video(label="Processed Video"),
title="Lane Detection Video Processing",
description="Upload a video and adjust parameters to process the video for lane detection.",
)
# Launch the interface
iface.launch()