33import time
44import math
55
6+ KERNEL = np .ones ((3 ,3 ), np .uint8 )
7+ LOWER_RED = np .array ([0 ,0 ,70 ])
8+ UPPER_RED = np .array ([200 ,50 ,255 ])
9+
610# @param: Video
7- # @ret: Mask of the video's road
8- def get_video_mask (vid , debug = False , percent = 0.65 , optimize = True , pixel_range = 6 , pic_offset = 5 , steps = 20 ):
11+ # @param: Percent of the road (top down)
12+ # @param: Optimize if you want or dont want vectorized
13+ #
14+ # Note the rest of params are for search based algo
15+ def get_video_mask (vid , debug = False , percent = 0.0 , optimize = True , pixel_range = 3 , pic_offset = 5 , steps = 40 ):
916 if vid is None :
1017 print ("Error opening video" )
1118 return None
@@ -45,7 +52,7 @@ def get_video_mask(vid, debug=False, percent=0.65, optimize=True, pixel_range=6,
4552 width = w - 1 - pic_offset
4653 y_index = int (height * percent )
4754
48- presult [y_index :height - 10 , pic_offset :width ] = result
55+ presult [y_index :height , pic_offset :width ] = result
4956
5057 right_deg = get_angle ((width , pic_offset ), (width // 2 , pic_offset ), prev_right )
5158 left_deg = get_angle ((pic_offset , pic_offset ), (width // 2 , pic_offset ), prev_left )
@@ -66,11 +73,26 @@ def get_video_mask(vid, debug=False, percent=0.65, optimize=True, pixel_range=6,
6673 video .release ()
6774 cv .destroyAllWindows ()
6875
76+ # Takes parameters for get_img_mask
77+ # Return Coords and their angles
78+ def get_img_angles (img , debug = False , percent = 0.0 , prev_right = None , prev_left = None , optimize = True , pixel_range = 3 , pic_offset = 5 , steps = 40 ):
79+ image , right , left = get_img_mask (img , debug = debug , optimize = optimize , percent = percent , prev_right = prev_right , prev_left = prev_left , pixel_range = pixel_range , pic_offset = pic_offset , steps = steps )
80+
81+ w = img .shape [1 ]
82+ width = w - 1 - pic_offset
83+
84+ right_deg = get_angle ((width , pic_offset ), (width // 2 , pic_offset ), right )
85+ left_deg = get_angle ((pic_offset , pic_offset ), (width // 2 , pic_offset ), left )
86+
87+ return (right_deg , left_deg )
88+
6989
7090# @param img: Image Matrix
71- # @param y_cutoff: Only consider y percent of image
72- # @ret: Mask of the road
73- def get_img_mask (img : np .ndarray , debug = False , percent = 0.65 , prev_right = None , prev_left = None , optimize = True , pixel_range = 6 , pic_offset = 5 , steps = 20 ):
91+ # @param: Percent of the road (top down)
92+ # @param: Optimize if you want or dont want vectorized
93+ #
94+ # @ret: Right and left angles of track
95+ def get_img_mask (img : np .ndarray , debug = False , percent = 0.0 , prev_right = None , prev_left = None , optimize = True , pixel_range = 3 , pic_offset = 5 , steps = 40 ):
7496 if img is None :
7597 print ('Error opening image!' )
7698 return None , None , None
@@ -80,16 +102,13 @@ def get_img_mask(img: np.ndarray, debug=False, percent=0.65, prev_right=None, pr
80102 height , width = h - 1 - pic_offset , w - 1 - pic_offset
81103 y_index = int (height * percent )
82104
83- roi_image = img [y_index :height - pic_offset , pic_offset :width ]
105+ roi_image = img [y_index :height , pic_offset :width ]
84106
85107 # Get hue mask
86108 image_hsv = get_hsv (roi_image )
87- lower_red = np .array ([0 , 0 , 70 ])
88- higher_red = np .array ([200 , 50 , 255 ])
89- mask_red = cv .inRange (image_hsv , lower_red , higher_red )
109+ mask_red = cv .inRange (image_hsv , LOWER_RED , UPPER_RED )
90110
91- kernel = np .ones ((3 , 3 ), np .uint8 )
92- result = cv .morphologyEx (mask_red , cv .MORPH_OPEN , kernel )
111+ result = cv .morphologyEx (mask_red , cv .MORPH_OPEN , KERNEL )
93112
94113 if prev_right is None :
95114 prev_right = (width , height )
@@ -107,6 +126,7 @@ def get_img_mask(img: np.ndarray, debug=False, percent=0.65, prev_right=None, pr
107126 right = vectorized_road_coord (result , True )
108127 left = vectorized_road_coord (result , False )
109128 else :
129+ # Old algo logic, being included for future uses just in case
110130 right = find_road_coord (img , prev_right , True , optimize = optimize , pixel_range = pixel_range , pic_offset = pic_offset , steps = steps )
111131 left = find_road_coord (img , prev_left , False , optimize = optimize , pixel_range = pixel_range , pic_offset = pic_offset , steps = steps )
112132
@@ -115,6 +135,8 @@ def get_img_mask(img: np.ndarray, debug=False, percent=0.65, prev_right=None, pr
115135
116136 return (result , right , left )
117137
138+ # Fast road lookup
139+ # @ret: Rigth and left road coords
118140def vectorized_road_coord (img , right_side , pic_offset = 5 ):
119141 h , w = img .shape [:2 ]
120142 height , width = h - 1 - pic_offset , w - 1 - pic_offset
@@ -124,7 +146,7 @@ def vectorized_road_coord(img, right_side, pic_offset=5):
124146 h_start = height
125147 h_end = pic_offset
126148
127- if img [h_start , w_start ]. any () :
149+ if img [h_start , w_start ] != 0 :
128150 column = img [h_end :h_start + 1 , w_start ]
129151 inv = np .logical_not (column [::- 1 ])
130152 idx = np .argmax (inv )
@@ -139,42 +161,7 @@ def vectorized_road_coord(img, right_side, pic_offset=5):
139161 idx = np .argmax (row )
140162 return (w_start + idx , h_start )
141163
142-
143- def find_road_coord (img , prev_pixel , right_side : bool , optimize = True , pixel_range = 6 , pic_offset = 5 , steps = 20 ):
144- h , w = img .shape [:2 ]
145- height , width = h - 1 - pic_offset , w - 1 - pic_offset
146-
147- if (optimize ):
148- new_pixel = lookup_road_coord (img , prev_pixel , right_side , pixel_range = pixel_range , pic_offset = pic_offset )
149- if new_pixel :
150- return new_pixel
151-
152- steps_h = - 1 * steps
153- steps_w = - 1 * steps if right_side else steps
154- slow_step_h = 1
155- slow_step_w = 1 if right_side else - 1
156-
157- h_start = height
158- h_end = pic_offset
159- w_start = width if right_side else pic_offset
160- w_end = width // 2
161-
162- if img [h_start , w_start ].any ():
163- for i in range (h_start , h_end , steps_h ):
164- if not img [i , w_start ].any ():
165- for j in range (i , h_start + 1 , slow_step_h ):
166- if img [j , w_start ].any ():
167- return (w_start , j )
168- else :
169- for i in range (w_start , w_end , steps_w ):
170- if img [h_start , i ].any ():
171- for j in range (i , w_start + slow_step_w , slow_step_w ):
172- if not img [h_start , j ].any ():
173- return (j , h_start )
174-
175- return prev_pixel
176-
177-
164+ # O(1) road lookup
178165def lookup_road_coord (img , prev_pixel , right_side , pixel_range = 6 , pic_offset = 5 ):
179166 h , w = img .shape [:2 ]
180167 height , width = h - 1 - pic_offset , w - 1 - pic_offset
@@ -218,12 +205,44 @@ def lookup_road_coord(img, prev_pixel, right_side, pixel_range=6, pic_offset=5):
218205
219206 return None
220207
221- def get_angle_vector (middle_coord , road_coord ):
222- dx = road_coord [0 ] - middle_coord [0 ]
223- dy = road_coord [1 ] - middle_coord [1 ]
224- angle_rad = math .atan2 (dy , dx )
225- return math .degrees (angle_rad )
226208
209+ # Old algorithm to find the road coord
210+ # Very optimized but still slower than vectorized operations
211+ def find_road_coord (img , prev_pixel , right_side : bool , optimize = True , pixel_range = 6 , pic_offset = 5 , steps = 20 ):
212+ h , w = img .shape [:2 ]
213+ height , width = h - 1 - pic_offset , w - 1 - pic_offset
214+
215+ if (optimize ):
216+ new_pixel = lookup_road_coord (img , prev_pixel , right_side , pixel_range = pixel_range , pic_offset = pic_offset )
217+ if new_pixel :
218+ return new_pixel
219+
220+ steps_h = - 1 * steps
221+ steps_w = - 1 * steps if right_side else steps
222+ slow_step_h = 1
223+ slow_step_w = 1 if right_side else - 1
224+
225+ h_start = height
226+ h_end = pic_offset
227+ w_start = width if right_side else pic_offset
228+ w_end = width // 2
229+
230+ if img [h_start , w_start ].any ():
231+ for i in range (h_start , h_end , steps_h ):
232+ if not img [i , w_start ].any ():
233+ for j in range (i , h_start + 1 , slow_step_h ):
234+ if img [j , w_start ].any ():
235+ return (w_start , j )
236+ else :
237+ for i in range (w_start , w_end , steps_w ):
238+ if img [h_start , i ].any ():
239+ for j in range (i , w_start + slow_step_w , slow_step_w ):
240+ if not img [h_start , j ].any ():
241+ return (j , h_start )
242+
243+ return prev_pixel
244+
245+ # Angle calc
227246def get_angle (zero_coord , middle_coord , road_coord ):
228247 a = get_distance (road_coord , zero_coord )
229248 b = get_distance (zero_coord , middle_coord )
@@ -235,12 +254,13 @@ def get_angle(zero_coord, middle_coord, road_coord):
235254 rads = math .acos ( numerator / denominator )
236255 return math .degrees (rads )
237256
238-
257+ # Distance calc
239258def get_distance (point1 , point2 ):
240259 dx = point1 [0 ] - point2 [0 ]
241260 dy = point1 [1 ] - point2 [1 ]
242261 return math .sqrt (dx * dx + dy * dy )
243262
263+ # If coords are within a certain threshold of each other
244264def within_difference (r_coord , l_coord , threshold ):
245265 c1 , r1 = r_coord
246266 c2 , r2 = l_coord
@@ -263,14 +283,22 @@ def draw_lines(img, r_coord, l_coord, middle=None):
263283 cv .line (img , (middle , 0 ), l_coord , (200 , 0 , 200 ), 3 )
264284 return img
265285
286+
287+ # @param: Image
288+ # @ret: Adaptive Gaussain threshold
266289def get_threshold (img ):
267290 return cv .adaptiveThreshold (img ,255 ,cv .ADAPTIVE_THRESH_GAUSSIAN_C , cv .THRESH_BINARY ,11 ,2 )
268291
292+
293+ # @param: Threshold
294+ # @ret: Traditional contours
269295def get_contours (thresh ):
270296 contours , hierachy = cv .findContours (thresh , cv .RETR_EXTERNAL , cv .CHAIN_APPROX_SIMPLE )
271297
272298 return contours
273299
300+ # @param: Image
301+ # @ret: Gaussian
274302def get_gradient (img ):
275303 h , w = img .shape [:2 ]
276304 gradient = np .linspace (0 , 1 , h ).reshape (h , 1 )
0 commit comments