Spaces:
Sleeping
Sleeping
copilot-swe-agent[bot]
kr4phy
commited on
Commit
Β·
2b342d6
1
Parent(s):
f5b9020
Add Gradio UI with OpenCV lane detection implementation
Browse filesCo-authored-by: kr4phy <168257476+kr4phy@users.noreply.github.com>
- .gitignore +10 -0
- README.md +57 -0
- app.py +207 -0
- requirements.txt +3 -0
- 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)
|