-
Notifications
You must be signed in to change notification settings - Fork 210
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #232 from aviralgarg05/main
Lane Line Detection
- Loading branch information
Showing
10 changed files
with
332 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
## **Lane-Line-Detection** | ||
|
||
### 🎯 **Goal** | ||
|
||
The main goal of this project is to detect lane lines in images or video streams using computer vision techniques. The purpose is to provide a solution that can be used in autonomous driving systems to ensure vehicles can stay within the correct lane by identifying lane markings effectively. | ||
|
||
### 🧵 **Dataset** | ||
|
||
This project does not use a specific pre-labeled dataset. Instead, it processes video files or images provided by the user to detect lane lines in real-time. You can use any video or image containing road lane markings for testing the lane detection functionality. | ||
|
||
### 🧾 **Description** | ||
|
||
Lane-Line-Detection is a computer vision project implemented in Python using OpenCV. It detects and highlights lane lines in a video or image by applying various image processing techniques such as grayscale conversion, Gaussian blur, Canny edge detection, region of interest selection, and Hough Transform to identify lane boundaries. | ||
|
||
### 🧮 **What I had done!** | ||
|
||
1. Preprocessed the input video/image by converting it to grayscale and applying Gaussian blur to reduce noise. | ||
2. Detected edges using Canny edge detection. | ||
3. Defined a region of interest (ROI) to focus on the lane area. | ||
4. Applied Hough Transform to detect lines within the ROI. | ||
5. Drew the lane lines onto the original image/video using the detected lines. | ||
6. Displayed the processed image/video with lane lines highlighted. | ||
|
||
### 🚀 **Models Implemented** | ||
|
||
- **Canny Edge Detection**: Used for detecting edges in the image based on gradients. It helps identify the potential boundaries of lane lines. | ||
- **Hough Line Transform**: Applied to detect straight lines within the image's region of interest. This is ideal for lane line detection as lane markings are often straight. | ||
|
||
These techniques are chosen for their effectiveness in edge and line detection, crucial for identifying lane boundaries. | ||
|
||
### 📚 **Libraries Needed** | ||
|
||
- OpenCV | ||
- NumPy | ||
- Matplotlib (for visualization) | ||
|
||
### 📊 **Exploratory Data Analysis Results** | ||
|
||
**Visualizations**: | ||
|
||
- **Original Image**: | ||
![Original Image](./testimg.jpg) | ||
|
||
- **Processed Image**: | ||
![Processed Image](./testimageresult.png) | ||
|
||
- **Lane Line Detection in Action (GIF)**: | ||
![Lane Line Detection GIF](./finalresult.gif) | ||
|
||
These visualizations show how lane lines are detected and highlighted in both images and videos. | ||
|
||
### 📈 **Performance of the Models based on the Accuracy Scores** | ||
|
||
Since this is a computer vision project based on image processing techniques, accuracy is more qualitative rather than quantitative. The effectiveness is evaluated visually by observing how well the lane lines are detected in different lighting conditions, road structures, and image/video qualities. | ||
|
||
### 📢 **Conclusion** | ||
|
||
The lane detection project effectively identifies lane lines in both images and video streams. By using edge detection and line finding techniques, it demonstrates good performance in standard road scenarios. The use of Canny edge detection and Hough Line Transform provides reliable detection, making the system suitable for real-time applications in autonomous vehicles. | ||
|
||
### ✒️ **Your Signature** | ||
|
||
Aviral Garg | ||
[GitHub](https://github.com/aviralgarg05) | [LinkedIn](https://linkedin.com/in/aviralgarg05) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import tkinter as tk | ||
from tkinter import * # Importing all components from tkinter | ||
import cv2 # OpenCV for video processing | ||
from PIL import Image, ImageTk # PIL for converting OpenCV frames to images for Tkinter | ||
import os | ||
import numpy as np # Numpy for working with arrays | ||
|
||
# Global variables to store the last frames for both video captures | ||
global last_frame1 | ||
last_frame1 = np.zeros((480, 640, 3), dtype=np.uint8) # Placeholder for the first camera frame | ||
|
||
global last_frame2 | ||
last_frame2 = np.zeros((480, 640, 3), dtype=np.uint8) # Placeholder for the second camera frame | ||
|
||
# Global video capture objects for two videos or cameras | ||
global cap1 | ||
global cap2 | ||
cap1 = cv2.VideoCapture("./test2.mp4") # Capturing from the first video file or camera | ||
cap2 = cv2.VideoCapture("./test2.mp4") # Capturing from the second video file or camera | ||
|
||
# Function to display video from the first camera/video | ||
def show_vid(): | ||
if not cap1.isOpened(): # Check if the first camera is opened | ||
print("Can't open the camera1") # Error handling if the camera can't be opened | ||
flag1, frame1 = cap1.read() # Read a frame from the first camera | ||
frame1 = cv2.resize(frame1, (600, 500)) # Resize the frame to 600x500 | ||
if flag1 is None: # Error handling if no frame is received | ||
print("Major error!") | ||
elif flag1: # If a frame is received successfully | ||
global last_frame1 | ||
last_frame1 = frame1.copy() # Store the frame in the global variable | ||
pic = cv2.cvtColor(last_frame1, cv2.COLOR_BGR2RGB) # Convert BGR (OpenCV format) to RGB | ||
img = Image.fromarray(pic) # Convert the frame to a PIL image | ||
imgtk = ImageTk.PhotoImage(image=img) # Convert the PIL image to Tkinter-compatible format | ||
lmain.imgtk = imgtk # Update the image on the label widget | ||
lmain.configure(image=imgtk) | ||
lmain.after(10, show_vid) # Call the function again after 10 ms to create a loop | ||
|
||
# Function to display video from the second camera/video | ||
def show_vid2(): | ||
if not cap2.isOpened(): # Check if the second camera is opened | ||
print("Can't open the camera2") # Error handling if the camera can't be opened | ||
flag2, frame2 = cap2.read() # Read a frame from the second camera | ||
frame2 = cv2.resize(frame2, (600, 500)) # Resize the frame to 600x500 | ||
if flag2 is None: # Error handling if no frame is received | ||
print("Major error2!") | ||
elif flag2: # If a frame is received successfully | ||
global last_frame2 | ||
last_frame2 = frame2.copy() # Store the frame in the global variable | ||
pic2 = cv2.cvtColor(last_frame2, cv2.COLOR_BGR2RGB) # Convert BGR (OpenCV format) to RGB | ||
img2 = Image.fromarray(pic2) # Convert the frame to a PIL image | ||
img2tk = ImageTk.PhotoImage(image=img2) # Convert the PIL image to Tkinter-compatible format | ||
lmain2.img2tk = img2tk # Update the image on the label widget | ||
lmain2.configure(image=img2tk) | ||
lmain2.after(10, show_vid2) # Call the function again after 10 ms to create a loop | ||
|
||
if __name__ == '__main__': | ||
root = tk.Tk() # Create the root window for Tkinter | ||
heading = Label(root, image=img, text="Lane-Line Detection") # Create a label with the title | ||
heading.pack() # Add the label to the window | ||
heading2 = Label(root, text="Lane-Line Detection", pady=20, font=('arial', 45, 'bold')) # Heading label | ||
heading2.configure(foreground='#364156') # Set the text color | ||
heading2.pack() # Add the heading label to the window | ||
|
||
# Create labels to display the video streams | ||
lmain = tk.Label(master=root) # Label for the first video | ||
lmain2 = tk.Label(master=root) # Label for the second video | ||
lmain.pack(side=LEFT) # Place the first video label on the left side | ||
lmain2.pack(side=RIGHT) # Place the second video label on the right side | ||
|
||
root.title("Lane-line Detection") # Set the window title | ||
root.geometry("1250x900+100+10") # Set the window size and position | ||
|
||
# Add a quit button to exit the application | ||
exitbutton = Button(root, text='Quit', fg="red", command=root.destroy).pack(side=BOTTOM) | ||
|
||
# Start showing the video streams | ||
show_vid() # Start showing the first video | ||
show_vid2() # Start showing the second video | ||
root.mainloop() # Run the Tkinter main loop to keep the window open | ||
|
||
cap.release() # Release the video capture when done |
Binary file added
BIN
+2.98 KB
Image processing/Lane Line Detection [OPEN CV]/instancesegmentimage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
import matplotlib.pyplot as plt | ||
import numpy as np | ||
import cv2 | ||
import os | ||
import matplotlib.image as mpimg | ||
from moviepy.editor import VideoFileClip | ||
import math | ||
|
||
# Define a function to mask the region of interest in the image | ||
def interested_region(img, vertices): | ||
if len(img.shape) > 2: # If the image has multiple channels (colored image) | ||
mask_color_ignore = (255,) * img.shape[2] | ||
else: # For grayscale images | ||
mask_color_ignore = 255 | ||
|
||
# Apply the mask using the vertices to define the region of interest | ||
cv2.fillPoly(np.zeros_like(img), vertices, mask_color_ignore) | ||
return cv2.bitwise_and(img, np.zeros_like(img)) | ||
|
||
# Function to apply Hough transform to detect lines in the image | ||
def hough_lines(img, rho, theta, threshold, min_line_len, max_line_gap): | ||
# Detect lines using HoughLinesP function | ||
lines = cv2.HoughLinesP(img, rho, theta, threshold, np.array([]), | ||
minLineLength=min_line_len, maxLineGap=max_line_gap) | ||
|
||
# Create a blank image to draw lines on | ||
line_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8) | ||
|
||
# Call lines_drawn to draw lines on the image | ||
lines_drawn(line_img, lines) | ||
|
||
return line_img | ||
|
||
# Function to draw lines on the image | ||
def lines_drawn(img, lines, color=[255, 0, 0], thickness=6): | ||
global cache # Cache to store the previous frame's data | ||
global first_frame # Variable to check if this is the first frame | ||
|
||
# Arrays to store slopes and lane data for left and right lanes | ||
slope_l, slope_r = [], [] | ||
lane_l, lane_r = [], [] | ||
|
||
α = 0.2 # Smoothing factor for weighted average | ||
|
||
# Loop through all the lines detected | ||
for line in lines: | ||
for x1, y1, x2, y2 in line: | ||
slope = (y2 - y1) / (x2 - x1) # Calculate the slope of the line | ||
if slope > 0.4: # Right lane (positive slope) | ||
slope_r.append(slope) | ||
lane_r.append(line) | ||
elif slope < -0.4: # Left lane (negative slope) | ||
slope_l.append(slope) | ||
lane_l.append(line) | ||
# Store the minimum y-coordinate for the lines | ||
img.shape[0] = min(y1, y2, img.shape[0]) | ||
|
||
# Handle cases where no lane is detected to avoid errors | ||
if len(lane_l) == 0 or len(lane_r) == 0: | ||
print('no lane detected') | ||
return 1 | ||
|
||
# Calculate mean slopes and lines for left and right lanes | ||
slope_mean_l = np.mean(slope_l, axis=0) | ||
slope_mean_r = np.mean(slope_r, axis=0) | ||
mean_l = np.mean(np.array(lane_l), axis=0) | ||
mean_r = np.mean(np.array(lane_r), axis=0) | ||
|
||
# Prevent division by zero errors | ||
if slope_mean_r == 0 or slope_mean_l == 0: | ||
print('dividing by zero') | ||
return 1 | ||
|
||
# Calculate x coordinates for the lane lines based on the slopes | ||
x1_l = int((img.shape[0] - mean_l[0][1] - (slope_mean_l * mean_l[0][0])) / slope_mean_l) | ||
x2_l = int((img.shape[0] - mean_l[0][1] - (slope_mean_l * mean_l[0][0])) / slope_mean_l) | ||
x1_r = int((img.shape[0] - mean_r[0][1] - (slope_mean_r * mean_r[0][0])) / slope_mean_r) | ||
x2_r = int((img.shape[0] - mean_r[0][1] - (slope_mean_r * mean_r[0][0])) / slope_mean_r) | ||
|
||
# Adjust the coordinates if left and right lanes overlap | ||
if x1_l > x1_r: | ||
x1_l = int((x1_l + x1_r) / 2) | ||
x1_r = x1_l | ||
y1_l = int((slope_mean_l * x1_l) + mean_l[0][1] - (slope_mean_l * mean_l[0][0])) | ||
y1_r = int((slope_mean_r * x1_r) + mean_r[0][1] - (slope_mean_r * mean_r[0][0])) | ||
y2_l = int((slope_mean_l * x2_l) + mean_l[0][1] - (slope_mean_l * mean_l[0][0])) | ||
y2_r = int((slope_mean_r * x2_r) + mean_r[0][1] - (slope_mean_r * mean_r[0][0])) | ||
else: | ||
y1_l = img.shape[0] | ||
y2_l = img.shape[0] | ||
y1_r = img.shape[0] | ||
y2_r = img.shape[0] | ||
|
||
# Store the current frame's data | ||
present_frame = np.array([x1_l, y1_l, x2_l, y2_l, x1_r, y1_r, x2_r, y2_r], dtype="float32") | ||
|
||
# Smoothing the lines over frames for better stability | ||
if first_frame == 1: | ||
next_frame = present_frame | ||
first_frame = 0 | ||
else: | ||
prev_frame = cache | ||
next_frame = (1 - α) * prev_frame + α * present_frame | ||
|
||
# Draw left and right lane lines on the image | ||
cv2.line(img, (int(next_frame[0]), int(next_frame[1])), | ||
(int(next_frame[2]), int(next_frame[3])), color, thickness) | ||
cv2.line(img, (int(next_frame[4]), int(next_frame[5])), | ||
(int(next_frame[6]), int(next_frame[7])), color, thickness) | ||
|
||
cache = next_frame # Update the cache with the current frame's data | ||
|
||
# Function to combine the original image and the line image | ||
def weighted_img(img, initial_img, α=0.8, β=1., λ=0.): | ||
return cv2.addWeighted(initial_img, α, img, β, λ) | ||
|
||
# Function to process each frame in the video | ||
def process_image(image): | ||
global first_frame # Track if this is the first frame | ||
|
||
# Convert the image to grayscale and HSV | ||
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | ||
img_hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV) | ||
|
||
# Define color ranges for yellow and white in the lane markings | ||
lower_yellow = np.array([20, 100, 100], dtype="uint8") | ||
upper_yellow = np.array([30, 255, 255], dtype="uint8") | ||
mask_yellow = cv2.inRange(img_hsv, lower_yellow, upper_yellow) | ||
mask_white = cv2.inRange(gray_image, 200, 255) | ||
|
||
# Combine the yellow and white masks | ||
mask_yw = cv2.bitwise_or(mask_white, mask_yellow) | ||
mask_yw_image = cv2.bitwise_and(gray_image, mask_yw) | ||
|
||
# Apply Gaussian blur to smooth the image | ||
gauss_gray = cv2.GaussianBlur(mask_yw_image, (5, 5), 0) | ||
|
||
# Detect edges using the Canny algorithm | ||
canny_edges = cv2.Canny(gauss_gray, 50, 150) | ||
|
||
# Define the region of interest based on the image size | ||
imshape = image.shape | ||
lower_left = [imshape[1] / 9, imshape[0]] | ||
lower_right = [imshape[1] - imshape[1] / 9, imshape[0]] | ||
top_left = [imshape[1] / 2 - imshape[1] / 8, imshape[0] / 2 + imshape[0] / 10] | ||
top_right = [imshape[1] / 2 + imshape[1] / 8, imshape[0] / 2 + imshape[0] / 10] | ||
|
||
vertices = [np.array([lower_left, top_left, top_right, lower_right], dtype=np.int32)] | ||
|
||
# Apply region of interest mask to the edges image | ||
roi_image = interested_region(canny_edges, vertices) | ||
|
||
# Apply Hough transform to detect lanes | ||
theta = np.pi / 180 | ||
line_image = hough_lines(roi_image, 4, theta, 30, 100, 180) | ||
|
||
# Combine the line image with the original image | ||
result = weighted_img(line_image, image, α=0.8, β=1., λ=0.) | ||
|
||
return result | ||
|
||
if __name__ == "__main__": | ||
first_frame = 1 # Indicate this is the first frame | ||
white_output = './output.mp4' # Output video file | ||
clip1 = VideoFileClip(filename='test2.mp4') # Load input video | ||
|
||
# Process the video frames | ||
white_clip = clip1.fl_image(process_image) | ||
white_clip.write_videofile(white_output, audio=False) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters