0xSynapse commited on
Commit
5577bf7
1 Parent(s): ad8ba69

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +210 -0
  2. requirements.txt +2 -0
app.py ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ import gradio as gr
4
+ import tempfile
5
+
6
+ # Define Utility Functions
7
+ def region_of_interest(img, vertices):
8
+ """Select the region of interest (ROI) from a defined list of vertices."""
9
+ mask = np.zeros_like(img)
10
+ if len(img.shape) > 2:
11
+ channel_count = img.shape[2] # 3 or 4 depending on your image.
12
+ ignore_mask_color = (255,) * channel_count
13
+ else:
14
+ ignore_mask_color = 255
15
+ cv2.fillPoly(mask, [vertices], ignore_mask_color)
16
+ masked_image = cv2.bitwise_and(img, mask)
17
+ return masked_image
18
+
19
+ def draw_lines(img, lines, color=[255, 0, 0], thickness=2):
20
+ """Utility for drawing lines."""
21
+ if lines is not None:
22
+ for line in lines:
23
+ for x1,y1,x2,y2 in line:
24
+ cv2.line(img, (x1, y1), (x2, y2), color, thickness)
25
+
26
+ def hough_lines(img, rho, theta, threshold, min_line_len, max_line_gap):
27
+ """Utility for defining Line Segments."""
28
+ lines = cv2.HoughLinesP(
29
+ img, rho, theta, threshold, np.array([]),
30
+ minLineLength=min_line_len, maxLineGap=max_line_gap)
31
+ line_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
32
+ draw_lines(line_img, lines)
33
+ return line_img, lines
34
+
35
+ def separate_left_right_lines(lines):
36
+ """Separate left and right lines depending on the slope."""
37
+ left_lines = []
38
+ right_lines = []
39
+ if lines is not None:
40
+ for line in lines:
41
+ for x1, y1, x2, y2 in line:
42
+ if x1 == x2:
43
+ continue # Skip vertical lines to avoid division by zero
44
+ slope = (y2 - y1) / (x2 - x1)
45
+ if slope < 0: # Negative slope = left lane
46
+ left_lines.append([x1, y1, x2, y2])
47
+ else: # Positive slope = right lane
48
+ right_lines.append([x1, y1, x2, y2])
49
+ return left_lines, right_lines
50
+
51
+ def cal_avg(values):
52
+ """Calculate average value."""
53
+ if values is not None and len(values) > 0:
54
+ return sum(values) / len(values)
55
+ else:
56
+ return 0
57
+
58
+ def extrapolate_lines(lines, upper_border, lower_border):
59
+ """Extrapolate lines keeping in mind the lower and upper border intersections."""
60
+ slopes = []
61
+ consts = []
62
+ if lines is not None and len(lines) != 0:
63
+ for x1, y1, x2, y2 in lines:
64
+ if x1 == x2:
65
+ continue # Avoid division by zero
66
+ slope = (y1 - y2) / (x1 - x2)
67
+ slopes.append(slope)
68
+ c = y1 - slope * x1
69
+ consts.append(c)
70
+ avg_slope = cal_avg(slopes)
71
+ avg_consts = cal_avg(consts)
72
+ if avg_slope == 0:
73
+ return None
74
+ x_lane_lower_point = int((lower_border - avg_consts) / avg_slope)
75
+ x_lane_upper_point = int((upper_border - avg_consts) / avg_slope)
76
+ return [x_lane_lower_point, lower_border, x_lane_upper_point, upper_border]
77
+ else:
78
+ return None
79
+
80
+ def extrapolated_lane_image(img, lines, roi_upper_border, roi_lower_border):
81
+ """Main function called to get the final lane lines."""
82
+ lanes_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
83
+ lines_left, lines_right = separate_left_right_lines(lines)
84
+ lane_left = extrapolate_lines(lines_left, roi_upper_border, roi_lower_border)
85
+ lane_right = extrapolate_lines(lines_right, roi_upper_border, roi_lower_border)
86
+ if lane_left is not None and lane_right is not None:
87
+ draw_con(lanes_img, [[lane_left], [lane_right]])
88
+ return lanes_img
89
+
90
+ def draw_con(img, lines):
91
+ """Fill in lane area."""
92
+ points = []
93
+ if lines is not None:
94
+ for x1, y1, x2, y2 in lines[0]:
95
+ points.append([x1, y1])
96
+ points.append([x2, y2])
97
+ for x1, y1, x2, y2 in lines[1]:
98
+ points.append([x2, y2])
99
+ points.append([x1, y1])
100
+ if points:
101
+ points = np.array([points], dtype='int32')
102
+ cv2.fillPoly(img, [points], (0, 255, 0))
103
+
104
+ def process_image(image, low_threshold, high_threshold, kernel_size, rho, theta, hough_threshold, min_line_len, max_line_gap, roi_vertices):
105
+ # Convert to grayscale.
106
+ gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
107
+
108
+ # Intensity selection.
109
+ gray_select = cv2.inRange(gray, 150, 255)
110
+
111
+ # Region masking: Select vertices according to the input image.
112
+ roi_vertices_np = np.array(roi_vertices, dtype=np.int32)
113
+ gray_select_roi = region_of_interest(gray_select, roi_vertices_np)
114
+
115
+ # Canny Edge Detection.
116
+ img_canny = cv2.Canny(gray_select_roi, low_threshold, high_threshold)
117
+
118
+ # Remove noise using Gaussian blur.
119
+ if kernel_size % 2 == 0:
120
+ kernel_size += 1 # kernel_size must be odd
121
+ canny_blur = cv2.GaussianBlur(img_canny, (kernel_size, kernel_size), 0)
122
+
123
+ # Hough transform parameters set according to the input image.
124
+ hough, lines = hough_lines(canny_blur, rho, theta, hough_threshold, min_line_len, max_line_gap)
125
+
126
+ # Extrapolate lanes.
127
+ roi_upper_border = min([vertex[1] for vertex in roi_vertices]) # smallest y value
128
+ roi_lower_border = max([vertex[1] for vertex in roi_vertices]) # largest y value
129
+ lane_img = extrapolated_lane_image(image, lines, roi_upper_border, roi_lower_border)
130
+
131
+ # Combined using weighted image.
132
+ image_result = cv2.addWeighted(image, 1, lane_img, 0.4, 0.0)
133
+ return image_result
134
+
135
+ def process_video(input_video_path, low_threshold, high_threshold, kernel_size, rho, theta, hough_threshold, min_line_len, max_line_gap, roi_vertices):
136
+ # Initialize video capture
137
+ video_cap = cv2.VideoCapture(input_video_path)
138
+ if not video_cap.isOpened():
139
+ raise Exception("Error opening video stream or file")
140
+
141
+ # Retrieve video frame properties.
142
+ frame_w = int(video_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
143
+ frame_h = int(video_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
144
+ frame_fps = video_cap.get(cv2.CAP_PROP_FPS)
145
+
146
+ # Output video file
147
+ temp_video = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False)
148
+ output_video_path = temp_video.name
149
+
150
+ # Video writer
151
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
152
+ vid_out = cv2.VideoWriter(output_video_path, fourcc, frame_fps, (frame_w,frame_h))
153
+
154
+ # Process each frame
155
+ while True:
156
+ ret, frame = video_cap.read()
157
+ if not ret:
158
+ break
159
+
160
+ result = process_image(frame, low_threshold, high_threshold, kernel_size, rho, theta, hough_threshold, min_line_len, max_line_gap, roi_vertices)
161
+ vid_out.write(result)
162
+
163
+ # Release resources
164
+ video_cap.release()
165
+ vid_out.release()
166
+
167
+ return output_video_path
168
+
169
+ def gradio_process_video(input_video_path, low_threshold, high_threshold, kernel_size, rho, theta_degree, hough_threshold, min_line_len, max_line_gap,
170
+ x1, y1, x2, y2, x3, y3, x4, y4):
171
+ # Define ROI vertices from user input
172
+ roi_vertices = [[x1, y1], [x2, y2], [x3, y3], [x4, y4]]
173
+
174
+ # Convert theta_degree to radians
175
+ theta = np.deg2rad(theta_degree)
176
+
177
+ # Process the video
178
+ 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)
179
+
180
+ return output_video_path
181
+
182
+ # Create the Gradio interface
183
+ iface = gr.Interface(
184
+ fn=gradio_process_video,
185
+ inputs=[
186
+ gr.Video(label="Input Video"),
187
+ gr.Slider(0, 255, step=1, value=50, label="Canny Low Threshold"),
188
+ gr.Slider(0, 255, step=1, value=150, label="Canny High Threshold"),
189
+ gr.Slider(1, 31, step=2, value=5, label="Gaussian Kernel Size"),
190
+ gr.Slider(1, 10, step=1, value=1, label="Hough Transform Rho"),
191
+ gr.Slider(0, 180, step=1, value=1, label="Hough Transform Theta (degrees)"),
192
+ gr.Slider(1, 500, step=1, value=100, label="Hough Transform Threshold"),
193
+ gr.Slider(1, 500, step=1, value=50, label="Hough Transform Min Line Length"),
194
+ gr.Slider(1, 500, step=1, value=300, label="Hough Transform Max Line Gap"),
195
+ gr.Number(value=100, label="ROI x1"),
196
+ gr.Number(value=540, label="ROI y1"),
197
+ gr.Number(value=900, label="ROI x2"),
198
+ gr.Number(value=540, label="ROI y2"),
199
+ gr.Number(value=525, label="ROI x3"),
200
+ gr.Number(value=330, label="ROI y3"),
201
+ gr.Number(value=440, label="ROI x4"),
202
+ gr.Number(value=330, label="ROI y4"),
203
+ ],
204
+ outputs=gr.Video(label="Processed Video"),
205
+ title="Lane Detection Video Processing",
206
+ description="Upload a video and adjust parameters to process the video for lane detection.",
207
+ )
208
+
209
+ # Launch the interface
210
+ iface.launch()
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ opencv-python==4.10.0.84
2
+ gradio==4.44.0