0xSynapse commited on
Commit
069f8a3
1 Parent(s): ca60999

Upload 3 files

Browse files
Files changed (4) hide show
  1. .gitattributes +1 -0
  2. app.py +280 -0
  3. lane.mp4 +3 -0
  4. requirements.txt +2 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ lane.mp4 filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,280 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ import gradio as gr
4
+
5
+
6
+ # Define Utility Functions From Straight Lane Image.
7
+ def draw_lines(img, lines, color=[255, 0, 0], thickness=2):
8
+ """Utility for drawing lines."""
9
+ if lines is not None:
10
+ for line in lines:
11
+ for x1, y1, x2, y2 in line:
12
+ cv2.line(img, (x1, y1), (x2, y2), color, thickness)
13
+
14
+
15
+ def hough_lines(img, rho, theta, threshold, min_line_len, max_line_gap):
16
+ """Utility for defining Line Segments."""
17
+ lines = cv2.HoughLinesP(
18
+ img, rho, theta, threshold, np.array([]), minLineLength=min_line_len, maxLineGap=max_line_gap
19
+ )
20
+ line_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
21
+ draw_lines(line_img, lines)
22
+ return line_img, lines
23
+
24
+
25
+ def separate_left_right_lines(lines):
26
+ """Separate left and right lines depending on the slope."""
27
+ left_lines = []
28
+ right_lines = []
29
+ if lines is not None:
30
+ for line in lines:
31
+ for x1, y1, x2, y2 in line:
32
+ if x1 == x2:
33
+ continue # Avoid division by zero
34
+ slope = (y2 - y1) / (x2 - x1)
35
+ if slope < 0: # Negative slope = left lane.
36
+ left_lines.append([x1, y1, x2, y2])
37
+ elif slope > 0: # Positive slope = right lane.
38
+ right_lines.append([x1, y1, x2, y2])
39
+ return left_lines, right_lines
40
+
41
+
42
+ def cal_avg(values):
43
+ """Calculate average value."""
44
+ if values is not None:
45
+ if len(values) > 0:
46
+ n = len(values)
47
+ else:
48
+ n = 1
49
+ return sum(values) / n
50
+
51
+
52
+ def extrapolate_lines(lines, upper_border, lower_border):
53
+ """Extrapolate lines keeping in mind the lower and upper border intersections."""
54
+ slopes = []
55
+ consts = []
56
+ if lines:
57
+ for x1, y1, x2, y2 in lines:
58
+ if x1 == x2:
59
+ continue # Avoid division by zero
60
+ slope = (y2 - y1) / (x2 - x1)
61
+ slopes.append(slope)
62
+ c = y1 - slope * x1
63
+ consts.append(c)
64
+ avg_slope = cal_avg(slopes)
65
+ avg_consts = cal_avg(consts)
66
+
67
+ if avg_slope == 0:
68
+ return None
69
+
70
+ # Calculate average intersection at lower_border.
71
+ x_lane_lower_point = int((lower_border - avg_consts) / avg_slope)
72
+
73
+ # Calculate average intersection at upper_border.
74
+ x_lane_upper_point = int((upper_border - avg_consts) / avg_slope)
75
+
76
+ return [x_lane_lower_point, lower_border, x_lane_upper_point, upper_border]
77
+ else:
78
+ return None
79
+
80
+
81
+ def draw_con(img, lines):
82
+ """Fill in lane area."""
83
+ points = []
84
+ if lines is not None:
85
+ for x1, y1, x2, y2 in lines[0]:
86
+ points.append([x1, y1])
87
+ points.append([x2, y2])
88
+ for x1, y1, x2, y2 in lines[1]:
89
+ points.append([x2, y2])
90
+ points.append([x1, y1])
91
+ if points:
92
+ points = np.array([points], dtype="int32")
93
+ cv2.fillPoly(img, points, (0, 255, 0))
94
+
95
+
96
+ def extrapolated_lane_image(img, lines, roi_upper_border, roi_lower_border):
97
+ """Main function called to get the final lane lines."""
98
+ lanes_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
99
+ # Extract each lane.
100
+ lines_left, lines_right = separate_left_right_lines(lines)
101
+ lane_left = extrapolate_lines(lines_left, roi_upper_border, roi_lower_border)
102
+ lane_right = extrapolate_lines(lines_right, roi_upper_border, roi_lower_border)
103
+ if lane_left is not None and lane_right is not None:
104
+ draw_con(lanes_img, [[lane_left], [lane_right]])
105
+ return lanes_img
106
+
107
+
108
+ def process_image(image, points):
109
+ # process the image
110
+ gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
111
+ gray_select = cv2.inRange(gray, 150, 255)
112
+ # Create mask
113
+ roi_mask = np.zeros_like(gray_select)
114
+ points_array = np.array([points], dtype=np.int32)
115
+ # print('=========')
116
+ # print(points_array)
117
+
118
+ # Defining a 3 channel or 1 channel color to fill the mask.
119
+ if len(gray_select.shape) > 2:
120
+ channel_count = gray_select.shape[2] # 3 or 4 depending on your image.
121
+ ignore_mask_color = (255,) * channel_count
122
+ else:
123
+ ignore_mask_color = 255
124
+
125
+ cv2.fillPoly(roi_mask, points_array, ignore_mask_color)
126
+ # cv2.imwrite('mask.png', roi_mask)
127
+ roi_mask = cv2.bitwise_and(gray_select, roi_mask)
128
+ # cv2.imwrite('invmask.png', roi_mask)
129
+
130
+ # Canny Edge Detection.
131
+ low_threshold = 50
132
+ high_threshold = 100
133
+ img_canny = cv2.Canny(roi_mask, low_threshold, high_threshold)
134
+
135
+ # Remove noise using Gaussian blur.
136
+ kernel_size = 3
137
+ canny_blur = cv2.GaussianBlur(img_canny, (kernel_size, kernel_size), 0)
138
+
139
+ # Hough transform parameters set according to the input image.
140
+ rho = 1
141
+ theta = np.pi / 180
142
+ threshold = 100
143
+ min_line_len = 50
144
+ max_line_gap = 300
145
+ hough, lines = hough_lines(canny_blur, rho, theta, threshold, min_line_len, max_line_gap)
146
+
147
+ # Extrapolate lanes.
148
+ ys, xs = np.where(roi_mask > 0)
149
+ if len(ys) == 0:
150
+ # No ROI mask, return original image.
151
+ return image
152
+ roi_upper_border = np.min(ys)
153
+ roi_lower_border = np.max(ys)
154
+ lane_img = extrapolated_lane_image(image, lines, roi_upper_border, roi_lower_border)
155
+
156
+ # Combine using weighted image.
157
+ image_result = cv2.addWeighted(image, 1, lane_img, 0.4, 0.0)
158
+ # cv2.imshow('result', image_result)
159
+ return image_result
160
+
161
+
162
+ def extract_first_frame_interface(video_file):
163
+ # Read the video file.
164
+ cap = cv2.VideoCapture(video_file)
165
+ if not cap.isOpened():
166
+ print("Error opening video stream or file")
167
+ return None, None
168
+ # Read the first frame.
169
+ ret, frame = cap.read()
170
+ cap.release()
171
+ if not ret:
172
+ print("Cannot read the first frame")
173
+ return None, None
174
+ # Convert the frame to RGB (since OpenCV uses BGR).
175
+ frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
176
+ # Return the frame for display and as the original frame.
177
+ return frame_rgb, frame_rgb # Return frame twice, once for display, once for state
178
+
179
+
180
+ def get_point_interface(original_frame, points, evt: gr.SelectData):
181
+ x, y = evt.index
182
+ # Ensure points is a list
183
+ if points is None:
184
+ points = []
185
+ points = points.copy() # Make a copy to avoid modifying in-place
186
+ points.append((x, y))
187
+ # Draw the point and lines on the image
188
+ image = original_frame.copy()
189
+ # Draw the points
190
+ for pt in points:
191
+ cv2.circle(image, pt, 5, (255, 0, 0), -1)
192
+ # Draw the lines
193
+ if len(points) > 1:
194
+ for i in range(len(points) - 1):
195
+ cv2.line(image, points[i], points[i + 1], (255, 0, 0), 2)
196
+ # Optionally, draw line from last to first to close the polygon
197
+ # cv2.line(image, points[-1], points[0], (255, 0, 0), 2)
198
+ # Return the updated image and points
199
+ # print("selected points")
200
+ # print(points)
201
+ return image, points
202
+
203
+
204
+ def process_video_interface(video_file, points):
205
+ # print("=-------------------------------")
206
+ # print(points)
207
+ points = list(points)
208
+ # Ensure points is a list of tuples
209
+ if points is None or len(points) < 3:
210
+ print("Not enough points to define a polygon")
211
+ return None
212
+ # Create the ROI mask
213
+ # Read the first frame to get the image size
214
+ cap = cv2.VideoCapture(video_file)
215
+ if not cap.isOpened():
216
+ print("Error opening video stream or file")
217
+ return None
218
+ frame_w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
219
+ frame_h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
220
+ frame_fps = int(cap.get(cv2.CAP_PROP_FPS))
221
+ fourcc = cv2.VideoWriter_fourcc(*"mp4v") # For mp4 output.
222
+ output_filename = "processed_output.mp4"
223
+ out = cv2.VideoWriter(output_filename, fourcc, frame_fps, (frame_w, frame_h))
224
+ while True:
225
+ ret, frame = cap.read()
226
+ if not ret:
227
+ break
228
+ # Process the frame using roi_mask
229
+ result = process_image(frame, points)
230
+ out.write(result)
231
+ cap.release()
232
+ out.release()
233
+ return output_filename
234
+
235
+
236
+ # Gradio Interface.
237
+ with gr.Blocks() as demo:
238
+ with gr.Row(equal_height=True):
239
+ video_input = gr.Video(label="Input Video")
240
+ extract_frame_button = gr.Button("Extract First Frame")
241
+ with gr.Row(equal_height=True):
242
+ first_frame_image = gr.Image(label="Click to select ROI points")
243
+ original_frame_state = gr.State(None)
244
+ points_state = gr.State([])
245
+ with gr.Row(equal_height=True):
246
+ process_button = gr.Button("Process Video")
247
+ clear_points_button = gr.Button("Clear Points")
248
+ output_video = gr.Video(label="Processed Video")
249
+
250
+ # Extract the first frame and store it
251
+ extract_frame_button.click(
252
+ fn=extract_first_frame_interface, inputs=video_input, outputs=[first_frame_image, original_frame_state]
253
+ )
254
+
255
+ # Handle point selection on the image
256
+ first_frame_image.select(
257
+ fn=get_point_interface, inputs=[original_frame_state, points_state], outputs=[first_frame_image, points_state]
258
+ )
259
+
260
+ # Clear the selected points
261
+ clear_points_button.click(
262
+ fn=lambda original_frame: (original_frame, []),
263
+ inputs=original_frame_state,
264
+ outputs=[first_frame_image, points_state],
265
+ )
266
+
267
+ # Process the video using the selected ROI
268
+ process_button.click(fn=process_video_interface, inputs=[video_input, points_state], outputs=output_video)
269
+
270
+ # Adding examples
271
+ gr.Examples(
272
+ examples=[
273
+ "./lane.mp4"
274
+ ],
275
+ inputs=video_input
276
+ )
277
+
278
+
279
+
280
+ demo.launch()
lane.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:75a63e3b2ae2fe04ec57beace9b680daa417a905d9e9656f445e510b1b70c17d
3
+ size 8008635
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ opencv-python==4.10.0.84
2
+ gradio==5.5.0