| import cv2 |
| import numpy as np |
| import gradio as gr |
| import tempfile |
|
|
| |
| 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] |
| 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 |
| slope = (y2 - y1) / (x2 - x1) |
| if slope < 0: |
| left_lines.append([x1, y1, x2, y2]) |
| else: |
| 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 |
| 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): |
| |
| gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) |
| |
| |
| gray_select = cv2.inRange(gray, 150, 255) |
| |
| |
| roi_vertices_np = np.array(roi_vertices, dtype=np.int32) |
| gray_select_roi = region_of_interest(gray_select, roi_vertices_np) |
| |
| |
| img_canny = cv2.Canny(gray_select_roi, low_threshold, high_threshold) |
| |
| |
| if kernel_size % 2 == 0: |
| kernel_size += 1 |
| canny_blur = cv2.GaussianBlur(img_canny, (kernel_size, kernel_size), 0) |
| |
| |
| hough, lines = hough_lines(canny_blur, rho, theta, hough_threshold, min_line_len, max_line_gap) |
| |
| |
| roi_upper_border = min([vertex[1] for vertex in roi_vertices]) |
| roi_lower_border = max([vertex[1] for vertex in roi_vertices]) |
| lane_img = extrapolated_lane_image(image, lines, roi_upper_border, roi_lower_border) |
| |
| |
| 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): |
| |
| video_cap = cv2.VideoCapture(input_video_path) |
| if not video_cap.isOpened(): |
| raise Exception("Error opening video stream or file") |
| |
| |
| 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) |
| |
| |
| temp_video = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False) |
| output_video_path = temp_video.name |
| |
| |
| fourcc = cv2.VideoWriter_fourcc(*'mp4v') |
| vid_out = cv2.VideoWriter(output_video_path, fourcc, frame_fps, (frame_w,frame_h)) |
| |
| |
| 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) |
| |
| |
| 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): |
| |
| roi_vertices = [[x1, y1], [x2, y2], [x3, y3], [x4, y4]] |
| |
| |
| theta = np.deg2rad(theta_degree) |
| |
| |
| 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 |
|
|
| |
| 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.", |
| ) |
|
|
| |
| iface.launch() |