Skip to content

Commit d6e42bf

Browse files
authored
Merge pull request #2 from Blenders-FC/linesdetectorPR
Added Lines Detector script
2 parents 51cf6fc + 593a2be commit d6e42bf

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed

media/video1.mp4

12.9 MB
Binary file not shown.

src/lines_detector.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import cv2
2+
import numpy as np
3+
from pathlib import Path
4+
import time # Import time for FPS calculation
5+
6+
# Pre-calculate zones to exclude from drawing lines
7+
def calculate_nozone(shape, offset=50):
8+
y, x = shape[:2]
9+
y_nozone = list(range(y - offset, y + 1)) + list(range(0, offset + 1))
10+
x_nozone = list(range(x - offset, x + 1)) + list(range(0, offset + 1))
11+
return y_nozone, x_nozone
12+
13+
# Function to calculate and display FPS
14+
def calculate_fps(prev_frame_time, new_frame_time, frame, position=(7, 70), font_scale=3, color=(100, 255, 0), thickness=3):
15+
font = cv2.FONT_HERSHEY_SIMPLEX
16+
# Calculate FPS
17+
fps = 1 / (new_frame_time - prev_frame_time)
18+
prev_frame_time = new_frame_time
19+
fps_text = str(int(fps)) # Convert FPS to an integer and then to string
20+
# Put FPS text on the frame
21+
cv2.putText(frame, fps_text, position, font, font_scale, color, thickness, cv2.LINE_AA)
22+
return prev_frame_time
23+
24+
# Open the video file
25+
video_path = Path(__file__).parent.parent / "media" / "video1.mp4" # Get path relative to script location
26+
cap = cv2.VideoCapture(str(video_path))
27+
28+
# Check if the video opened successfully
29+
if not cap.isOpened():
30+
print("Error: Could not open the video.")
31+
exit()
32+
33+
# Predefined kernel for morphological operations
34+
kernel = np.ones((5, 5), np.uint8)
35+
white_kernel = np.ones((5, 5), np.uint8)
36+
kernel2 = np.ones((5, 5), np.uint8)
37+
38+
# used to record the time when we processed last frame
39+
prev_frame_time = 0
40+
41+
# Pre-calculate zones to exclude from drawing lines
42+
def calculate_nozone(shape, offset=50):
43+
y, x = shape[:2]
44+
y_nozone = list(range(y - offset, y + 1)) + list(range(0, offset + 1))
45+
x_nozone = list(range(x - offset, x + 1)) + list(range(0, offset + 1))
46+
return y_nozone, x_nozone
47+
48+
# Loop to read frames
49+
while True:
50+
ret, orig_frame = cap.read()
51+
52+
if not ret:
53+
break
54+
55+
# Start measuring time for each frame
56+
new_frame_time = time.time()
57+
58+
# Calculate zones for the current frame
59+
yimg_nozone, ximg_nozone = calculate_nozone(orig_frame.shape)
60+
61+
# Blur and convert to grayscale
62+
blurred = cv2.GaussianBlur(orig_frame, (9, 9), 0)
63+
gray = cv2.cvtColor(blurred, cv2.COLOR_BGR2GRAY)
64+
65+
# Thresholding for white mask
66+
_, white_msk = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)
67+
white_msk = cv2.dilate(white_msk, white_kernel, iterations=2)
68+
69+
# Convert to LAB color space to detect field lines
70+
lab_frame = cv2.cvtColor(orig_frame, cv2.COLOR_BGR2LAB)
71+
lower_green = np.array([0, 0, 100]) # Lower bound for green
72+
upper_green = np.array([185, 125, 180]) # Upper bound for green
73+
mask_green = cv2.inRange(lab_frame, lower_green, upper_green)
74+
75+
# Morphological transformations to clean up the mask
76+
opening = cv2.morphologyEx(mask_green, cv2.MORPH_OPEN, kernel, iterations=1)
77+
78+
# Sure background
79+
sure_bg = cv2.erode(opening, kernel, iterations=2)
80+
sure_bg = cv2.dilate(sure_bg, kernel, iterations=2)
81+
82+
# Adaptive thresholding to detect contours
83+
thresh = cv2.adaptiveThreshold(sure_bg, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
84+
cv2.THRESH_BINARY_INV, blockSize=5, C=0)
85+
86+
# Find contours of the field markings
87+
contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
88+
89+
# Draw only large contours
90+
dummy_mask = np.zeros_like(mask_green)
91+
large_contours = [c for c in contours if cv2.contourArea(c) > 100000]
92+
cv2.drawContours(dummy_mask, large_contours, -1, (255, 255, 255), 2)
93+
94+
# Morphological closing to refine mask
95+
#adap_thrs_morph = cv2.morphologyEx(dummy_mask, cv2.MORPH_CLOSE, kernel2, iterations=1)
96+
97+
# Detect lines using Hough transform
98+
lines = cv2.HoughLinesP(dummy_mask, rho=2, theta=np.pi / 180, threshold=250,
99+
minLineLength=25, maxLineGap=50)
100+
101+
# Create a copy to draw lines on
102+
image_lines_thrs = orig_frame.copy()
103+
lines_thrs = np.zeros_like(dummy_mask)
104+
105+
# Draw detected lines while avoiding no-line zones
106+
if lines is not None:
107+
for line in lines:
108+
x1, y1, x2, y2 = line[0]
109+
if (y1 not in yimg_nozone or y2 not in yimg_nozone) and (x1 not in ximg_nozone or x2 not in ximg_nozone):
110+
cv2.line(image_lines_thrs, (x1, y1), (x2, y2), (255, 0, 0), 10) # Blue lines
111+
cv2.line(lines_thrs, (x1, y1), (x2, y2), (255, 0, 0), 10)
112+
113+
# Mask lines with white areas
114+
lines_thrs = cv2.bitwise_and(lines_thrs, white_msk)
115+
116+
# Create a colored mask for detected lines
117+
colored_mask = np.zeros_like(orig_frame)
118+
colored_mask[lines_thrs == 255] = [255, 0, 0] # Blue lines
119+
120+
# Overlay the colored mask on the original frame
121+
overlay_image = cv2.addWeighted(orig_frame, 1, colored_mask, 1, 0)
122+
123+
# Calculate and display FPS
124+
prev_frame_time = calculate_fps(prev_frame_time, new_frame_time, overlay_image)
125+
126+
# Display the combined frame
127+
cv2.imshow('Combined Video', overlay_image)
128+
129+
# Break loop if 'q' is pressed
130+
if cv2.waitKey(30) & 0xFF == ord('q'):
131+
break
132+
133+
# Release video capture and close windows
134+
cap.release()
135+
cv2.destroyAllWindows()

0 commit comments

Comments
 (0)