Skip to content

Commit

Permalink
Merge pull request #232 from aviralgarg05/main
Browse files Browse the repository at this point in the history
Lane Line Detection
  • Loading branch information
UTSAVS26 authored Oct 8, 2024
2 parents c2d33bf + f1bf9ba commit b348086
Show file tree
Hide file tree
Showing 10 changed files with 332 additions and 0 deletions.
63 changes: 63 additions & 0 deletions Image processing/Lane Line Detection [OPEN CV]/README.md
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.
82 changes: 82 additions & 0 deletions Image processing/Lane Line Detection [OPEN CV]/gui.py
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
169 changes: 169 additions & 0 deletions Image processing/Lane Line Detection [OPEN CV]/main.py
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.
9 changes: 9 additions & 0 deletions Repo-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,15 @@
│ ├── README.md
│ └── Snake.py
├── Image processing
│ ├── Lane Line Detection [OPEN CV]
│ │ ├── README.md
│ │ ├── binaryimage.png
│ │ ├── finalresult.gif
│ │ ├── gui.py
│ │ ├── instancesegmentimage.png
│ │ ├── main.py
│ │ ├── testimageresult.png
│ │ └── testimg.jpg
│ └── Number plate detection
│ ├── README.md
│ ├── model
Expand Down
9 changes: 9 additions & 0 deletions repo_structure.txt
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,15 @@
│ ├── README.md
│ └── Snake.py
├── Image processing
│ ├── Lane Line Detection [OPEN CV]
│ │ ├── README.md
│ │ ├── binaryimage.png
│ │ ├── finalresult.gif
│ │ ├── gui.py
│ │ ├── instancesegmentimage.png
│ │ ├── main.py
│ │ ├── testimageresult.png
│ │ └── testimg.jpg
│ └── Number plate detection
│ ├── README.md
│ ├── model
Expand Down

0 comments on commit b348086

Please sign in to comment.