copilot-swe-agent[bot] kr4phy commited on
Commit
2b342d6
Β·
1 Parent(s): f5b9020

Add Gradio UI with OpenCV lane detection implementation

Browse files

Co-authored-by: kr4phy <168257476+kr4phy@users.noreply.github.com>

Files changed (5) hide show
  1. .gitignore +10 -0
  2. README.md +57 -0
  3. app.py +207 -0
  4. requirements.txt +3 -0
  5. test_lane_detection.py +108 -0
.gitignore CHANGED
@@ -205,3 +205,13 @@ cython_debug/
205
  marimo/_static/
206
  marimo/_lsp/
207
  __marimo__/
 
 
 
 
 
 
 
 
 
 
 
205
  marimo/_static/
206
  marimo/_lsp/
207
  __marimo__/
208
+
209
+ # Temporary video files
210
+ *.mp4
211
+ *.avi
212
+ *.mov
213
+ *.mkv
214
+
215
+ # Gradio temporary files
216
+ gradio_cached_examples/
217
+ flagged/
README.md CHANGED
@@ -1,2 +1,59 @@
1
  # OpenCVLaneDetectionDemo
2
  OpenCV Lane Detection Demo with Gradio
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # OpenCVLaneDetectionDemo
2
  OpenCV Lane Detection Demo with Gradio
3
+
4
+ ## κ°œμš” (Overview)
5
+ 이 ν”„λ‘œμ νŠΈλŠ” OpenCVλ₯Ό μ‚¬μš©ν•˜μ—¬ λΉ„λ””μ˜€μ—μ„œ 차선을 κ°μ§€ν•˜κ³ , Gradio UIλ₯Ό 톡해 κ²°κ³Όλ₯Ό μ‹œκ°ν™”ν•˜λŠ” 데λͺ¨ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μž…λ‹ˆλ‹€.
6
+
7
+ This project is a demo application that detects lane lines in videos using OpenCV and visualizes the results through a Gradio UI.
8
+
9
+ ## κΈ°λŠ₯ (Features)
10
+ - πŸŽ₯ Gradioλ₯Ό ν†΅ν•œ λΉ„λ””μ˜€ μ—…λ‘œλ“œ (Video upload via Gradio)
11
+ - πŸ›£οΈ OpenCVλ₯Ό μ΄μš©ν•œ μ‹€μ‹œκ°„ μ°¨μ„  κ²€μΆœ (Real-time lane detection using OpenCV)
12
+ - πŸ“Š 원본/처리 λΉ„λ””μ˜€ μ‚¬μ΄λ“œλ°”μ΄μ‚¬μ΄λ“œ 비ꡐ (Side-by-side comparison of original and processed videos)
13
+
14
+ ## μ„€μΉ˜ (Installation)
15
+
16
+ 1. μ €μž₯μ†Œ 클둠 (Clone the repository):
17
+ ```bash
18
+ git clone https://github.com/kr4phy/OpenCVLaneDetectionDemo.git
19
+ cd OpenCVLaneDetectionDemo
20
+ ```
21
+
22
+ 2. ν•„μš”ν•œ νŒ¨ν‚€μ§€ μ„€μΉ˜ (Install required packages):
23
+ ```bash
24
+ pip install -r requirements.txt
25
+ ```
26
+
27
+ ## μ‚¬μš©λ²• (Usage)
28
+
29
+ 1. μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹€ν–‰ (Run the application):
30
+ ```bash
31
+ python app.py
32
+ ```
33
+
34
+ 2. λΈŒλΌμš°μ €μ—μ„œ μžλ™μœΌλ‘œ μ—΄λ¦¬λŠ” Gradio UI에 μ ‘μ†ν•©λ‹ˆλ‹€.
35
+ (The Gradio UI will automatically open in your browser)
36
+
37
+ 3. λΉ„λ””μ˜€ νŒŒμΌμ„ μ—…λ‘œλ“œν•˜κ³  "Process Video" λ²„νŠΌμ„ ν΄λ¦­ν•©λ‹ˆλ‹€.
38
+ (Upload a video file and click the "Process Video" button)
39
+
40
+ 4. 처리된 κ²°κ³Όλ₯Ό ν™•μΈν•©λ‹ˆλ‹€ (μ™Όμͺ½: 원본, 였λ₯Έμͺ½: μ°¨μ„  감지 κ²°κ³Ό).
41
+ (View the processed result - left: original, right: lane detection result)
42
+
43
+ ## μ°¨μ„  감지 μ•Œκ³ λ¦¬μ¦˜ (Lane Detection Algorithm)
44
+
45
+ 1. **Grayscale λ³€ν™˜** (Convert to grayscale)
46
+ 2. **κ°€μš°μ‹œμ•ˆ λΈ”λŸ¬** 적용 (Apply Gaussian blur)
47
+ 3. **Canny 에지 κ²€μΆœ** (Canny edge detection)
48
+ 4. **관심 μ˜μ—­(ROI) λ§ˆμŠ€ν‚Ή** (Apply region of interest mask)
49
+ 5. **Hough λ³€ν™˜**으둜 직선 κ²€μΆœ (Detect lines using Hough transform)
50
+ 6. **μ°¨μ„  평균화 및 그리기** (Average and draw lane lines)
51
+
52
+ ## μš”κ΅¬μ‚¬ν•­ (Requirements)
53
+ - Python 3.7+
54
+ - gradio==4.44.0
55
+ - opencv-python==4.10.0.84
56
+ - numpy==1.26.4
57
+
58
+ ## λΌμ΄μ„ μŠ€ (License)
59
+ MIT License
app.py ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ import gradio as gr
4
+ import tempfile
5
+ import os
6
+
7
+
8
+ def region_of_interest(img, vertices):
9
+ """
10
+ Apply a region of interest mask to the image.
11
+ """
12
+ mask = np.zeros_like(img)
13
+ cv2.fillPoly(mask, vertices, 255)
14
+ masked_image = cv2.bitwise_and(img, mask)
15
+ return masked_image
16
+
17
+
18
+ def draw_lines(img, lines, color=[0, 255, 0], thickness=3):
19
+ """
20
+ Draw lines on the image.
21
+ """
22
+ if lines is None:
23
+ return img
24
+
25
+ line_img = np.zeros_like(img)
26
+
27
+ # Separate left and right lane lines
28
+ left_lines = []
29
+ right_lines = []
30
+
31
+ for line in lines:
32
+ x1, y1, x2, y2 = line[0]
33
+ if x2 == x1:
34
+ continue
35
+ slope = (y2 - y1) / (x2 - x1)
36
+
37
+ # Filter by slope to separate left and right lanes
38
+ if slope < -0.5: # Left lane (negative slope)
39
+ left_lines.append(line[0])
40
+ elif slope > 0.5: # Right lane (positive slope)
41
+ right_lines.append(line[0])
42
+
43
+ # Average lines for left and right lanes
44
+ def average_lines(lines, img_shape):
45
+ if len(lines) == 0:
46
+ return None
47
+
48
+ x_coords = []
49
+ y_coords = []
50
+
51
+ for line in lines:
52
+ x1, y1, x2, y2 = line
53
+ x_coords.extend([x1, x2])
54
+ y_coords.extend([y1, y2])
55
+
56
+ # Fit a polynomial to the points
57
+ poly = np.polyfit(y_coords, x_coords, 1)
58
+
59
+ # Calculate line endpoints
60
+ y1 = img_shape[0]
61
+ y2 = int(img_shape[0] * 0.6)
62
+ x1 = int(poly[0] * y1 + poly[1])
63
+ x2 = int(poly[0] * y2 + poly[1])
64
+
65
+ return [x1, y1, x2, y2]
66
+
67
+ # Draw averaged lines
68
+ left_line = average_lines(left_lines, img.shape)
69
+ right_line = average_lines(right_lines, img.shape)
70
+
71
+ if left_line is not None:
72
+ cv2.line(line_img, (left_line[0], left_line[1]), (left_line[2], left_line[3]), color, thickness)
73
+
74
+ if right_line is not None:
75
+ cv2.line(line_img, (right_line[0], right_line[1]), (right_line[2], right_line[3]), color, thickness)
76
+
77
+ return cv2.addWeighted(img, 1.0, line_img, 1.0, 0)
78
+
79
+
80
+ def process_frame(frame):
81
+ """
82
+ Process a single frame for lane detection.
83
+ """
84
+ height, width = frame.shape[:2]
85
+
86
+ # Convert to grayscale
87
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
88
+
89
+ # Apply Gaussian blur
90
+ blur = cv2.GaussianBlur(gray, (5, 5), 0)
91
+
92
+ # Apply Canny edge detection
93
+ edges = cv2.Canny(blur, 50, 150)
94
+
95
+ # Define region of interest (ROI)
96
+ vertices = np.array([[
97
+ (int(width * 0.1), height),
98
+ (int(width * 0.45), int(height * 0.6)),
99
+ (int(width * 0.55), int(height * 0.6)),
100
+ (int(width * 0.9), height)
101
+ ]], dtype=np.int32)
102
+
103
+ # Apply ROI mask
104
+ masked_edges = region_of_interest(edges, vertices)
105
+
106
+ # Apply Hough transform to detect lines
107
+ lines = cv2.HoughLinesP(
108
+ masked_edges,
109
+ rho=2,
110
+ theta=np.pi / 180,
111
+ threshold=50,
112
+ minLineLength=40,
113
+ maxLineGap=100
114
+ )
115
+
116
+ # Draw detected lanes on the original frame
117
+ result = draw_lines(frame.copy(), lines)
118
+
119
+ return result
120
+
121
+
122
+ def process_video(video_path):
123
+ """
124
+ Process the uploaded video and return side-by-side comparison.
125
+ """
126
+ if video_path is None:
127
+ return None
128
+
129
+ # Open the video
130
+ cap = cv2.VideoCapture(video_path)
131
+
132
+ if not cap.isOpened():
133
+ return None
134
+
135
+ # Get video properties
136
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
137
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
138
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
139
+
140
+ # Create temporary output file
141
+ temp_output = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4')
142
+ output_path = temp_output.name
143
+ temp_output.close()
144
+
145
+ # Video writer for output (side-by-side, so width is doubled)
146
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
147
+ out = cv2.VideoWriter(output_path, fourcc, fps, (width * 2, height))
148
+
149
+ # Process each frame
150
+ while True:
151
+ ret, frame = cap.read()
152
+ if not ret:
153
+ break
154
+
155
+ # Process frame for lane detection
156
+ processed_frame = process_frame(frame)
157
+
158
+ # Create side-by-side comparison
159
+ # Original on left, processed on right
160
+ combined = np.hstack((frame, processed_frame))
161
+
162
+ # Write the combined frame
163
+ out.write(combined)
164
+
165
+ # Release resources
166
+ cap.release()
167
+ out.release()
168
+
169
+ return output_path
170
+
171
+
172
+ # Create Gradio interface
173
+ with gr.Blocks(title="Lane Detection Demo") as demo:
174
+ gr.Markdown("# πŸš— OpenCV Lane Detection Demo")
175
+ gr.Markdown("Upload a video to detect lane lines. The result will show the original video on the left and the lane-detected video on the right.")
176
+
177
+ with gr.Row():
178
+ with gr.Column():
179
+ video_input = gr.Video(label="Upload Video")
180
+ process_btn = gr.Button("Process Video", variant="primary")
181
+
182
+ with gr.Column():
183
+ video_output = gr.Video(label="Result (Original | Lane Detection)")
184
+
185
+ process_btn.click(
186
+ fn=process_video,
187
+ inputs=video_input,
188
+ outputs=video_output
189
+ )
190
+
191
+ gr.Markdown("""
192
+ ### How it works:
193
+ 1. Upload a video file containing road scenes
194
+ 2. Click "Process Video" button
195
+ 3. The system will:
196
+ - Convert frames to grayscale
197
+ - Apply Gaussian blur to reduce noise
198
+ - Use Canny edge detection to find edges
199
+ - Apply region of interest (ROI) mask to focus on the road
200
+ - Use Hough transform to detect lane lines
201
+ - Draw detected lanes on the original video
202
+ 4. View the side-by-side comparison result
203
+ """)
204
+
205
+
206
+ if __name__ == "__main__":
207
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio>=4.0.0
2
+ opencv-python>=4.5.0
3
+ numpy>=1.20.0
test_lane_detection.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Basic tests for lane detection functionality
3
+ """
4
+ import numpy as np
5
+ import cv2
6
+ import sys
7
+ import os
8
+
9
+ # Add parent directory to path
10
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
11
+
12
+ from app import region_of_interest, process_frame
13
+
14
+
15
+ def test_region_of_interest():
16
+ """Test region of interest masking"""
17
+ print("Testing region_of_interest function...")
18
+
19
+ # Create a test image
20
+ img = np.ones((100, 100), dtype=np.uint8) * 255
21
+
22
+ # Define vertices
23
+ vertices = np.array([[(20, 100), (40, 50), (60, 50), (80, 100)]], dtype=np.int32)
24
+
25
+ # Apply ROI
26
+ result = region_of_interest(img, vertices)
27
+
28
+ # Check that result has correct shape
29
+ assert result.shape == img.shape, f"Expected shape {img.shape}, got {result.shape}"
30
+
31
+ # Check that areas outside ROI are masked (zero)
32
+ assert result[10, 10] == 0, "Pixels outside ROI should be 0"
33
+
34
+ print("βœ“ region_of_interest test passed")
35
+
36
+
37
+ def test_process_frame():
38
+ """Test frame processing for lane detection"""
39
+ print("Testing process_frame function...")
40
+
41
+ # Create a test frame with simulated road
42
+ height, width = 480, 640
43
+ frame = np.zeros((height, width, 3), dtype=np.uint8)
44
+
45
+ # Draw white lines to simulate lanes
46
+ cv2.line(frame, (200, height), (280, int(height*0.6)), (255, 255, 255), 5)
47
+ cv2.line(frame, (440, height), (360, int(height*0.6)), (255, 255, 255), 5)
48
+
49
+ # Process the frame
50
+ result = process_frame(frame)
51
+
52
+ # Check that result has correct shape
53
+ assert result.shape == frame.shape, f"Expected shape {frame.shape}, got {result.shape}"
54
+
55
+ # Check that result is not identical to input (lanes should be drawn)
56
+ assert not np.array_equal(result, frame), "Result should have lane lines drawn"
57
+
58
+ print("βœ“ process_frame test passed")
59
+
60
+
61
+ def test_imports():
62
+ """Test that all required modules can be imported"""
63
+ print("Testing imports...")
64
+
65
+ try:
66
+ import gradio
67
+ print("βœ“ gradio imported successfully")
68
+ except ImportError as e:
69
+ print(f"βœ— Failed to import gradio: {e}")
70
+ return False
71
+
72
+ try:
73
+ import cv2
74
+ print("βœ“ opencv-python imported successfully")
75
+ except ImportError as e:
76
+ print(f"βœ— Failed to import cv2: {e}")
77
+ return False
78
+
79
+ try:
80
+ import numpy
81
+ print("βœ“ numpy imported successfully")
82
+ except ImportError as e:
83
+ print(f"βœ— Failed to import numpy: {e}")
84
+ return False
85
+
86
+ return True
87
+
88
+
89
+ if __name__ == "__main__":
90
+ print("Running lane detection tests...\n")
91
+
92
+ # Test imports
93
+ if not test_imports():
94
+ print("\nImport tests failed!")
95
+ sys.exit(1)
96
+
97
+ print()
98
+
99
+ # Test functions
100
+ try:
101
+ test_region_of_interest()
102
+ test_process_frame()
103
+ print("\nβœ… All tests passed!")
104
+ except Exception as e:
105
+ print(f"\n❌ Test failed: {e}")
106
+ import traceback
107
+ traceback.print_exc()
108
+ sys.exit(1)