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()