خوارزمية رؤية الكمبيوتر لحساب انحناء الطرق وإزاحة مركبة الممرات باستخدام معالجة صور OpenCV ، ومعايرة الكاميرا ، وتحويل المنظور ، وأقنعة الألوان ، والبخامات ، والتناسب متعدد الحدود.
يعد مشروع Finding Advanced Lane خطوة إلى الأمام من [اكتشاف خطوط الممرات] في تحديد هندسة الطريق إلى الأمام.
باستخدام تسجيل فيديو لقيادة الطرق السريعة ، فإن هدف هذا المشروع هو حساب نصف قطر انحناء الطريق. الطرق المنحنية هي مهمة أكثر تحديا من الطرق المستقيمة. لحساب الانحناء بشكل صحيح ، يجب تحديد خطوط الممرات ولكن علاوة على ذلك ، يجب أن تكون الصور غير مستقرة. يعد تحويل الصورة ضروريًا لمعايرة الكاميرا ولتحويل المنظور للحصول على رؤية عين الطائر للطريق.
يتم تنفيذ هذا المشروع في Python ويستخدم مكتبة معالجة صور OpenCV. يمكن العثور على الرمز المصدر في AdvancedLaneFinding.ipynb
[التشويه البصري] هي ظاهرة مادية تحدث في تسجيل الصور ، حيث يتم عرض الخطوط المستقيمة على أنها خطوط منحنية قليلاً عند إدراكها من خلال عدسات الكاميرا. يتم تسجيل فيديو القيادة على الطريق السريع باستخدام الكاميرا الأمامية المواجهة على السيارة ويتم تشويه الصور. تكون معاملات التشويه خاصة بكل كاميرا ويمكن حسابها باستخدام أشكال هندسية معروفة.
يتم توفير صور الشطرنج التي تم التقاطها مع الكاميرا المدمجة في مجلد camera_cal . ميزة هذه الصور هي أن لديهم تباين عال والهندسة المعروفة. قدمت الصور حاضرة 9 * 6 زوايا للعمل مع.
# Object points are real world points, here a 3D coordinates matrix is generated
# z coordinates are 0 and x, y are equidistant as it is known that the chessboard is made of identical squares
objp = np.zeros((6*9,3), np.float32)
objp[:,:2] = np.mgrid[0:9,0:6].T.reshape(-1,2)
يتم تعيين نقاط الكائن بناءً على الفهم المشترك بأنه في نمط لوحة الشطرنج ، جميع المربعات متساوية. هذا يعني أن نقاط الكائنات ستحصل على إحداثيات X و Y التي تم إنشاؤها من فهارس الشبكة ، و Z هي دائمًا 0. تمثل نقاط الصورة نقاط الكائن المقابلة الموجودة في الصورة باستخدام وظيفة OpenCV findChessboardCorners .
# Convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Find the chessboard corners
nx = 9
ny = 6
ret, corners = cv2.findChessboardCorners(gray, (nx, ny), None)
بعد المسح الضوئي من خلال جميع الصور ، تحتوي قائمة نقاط الصور على بيانات كافية للمقارنة مع نقاط الكائن من أجل حساب مصفوفة الكاميرا ومعاملات التشويه. هذا يؤدي إلى تحديد مصفوفة كاميرا دقيقة وتحديد معاملات التشويه باستخدام وظيفة "CiaprateCamera".
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
undist = cv2.undistort(img, mtx, dist, None, mtx)
يتم استخدام وظيفة OpenCV undistort لتحويل الصور باستخدام مصفوفة الكاميرا ومعاملات التشويه.
تكون نتيجة تقنية معايرة الكاميرا مرئية عند مقارنة هذه الصور. بينما على صورة الشطرنج ، يكون التشويه أكثر وضوحًا ، فإنه على الطريق أكثر دقة. ومع ذلك ، فإن صورة غير محفوظة تؤدي إلى حساب انحناء الطريق غير صحيح.
إلى انحناء calucluate ، المنظور المثالي هو رؤية عين الطائر. هذا يعني أن الطريق ينظر إليه من الأعلى ، بدلاً من زاوية الزجاج الأمامي للسيارة.
يتم حساب هذا التحويل المنظور باستخدام سيناريو حارة مستقيم ومعرفة مشتركة مسبقة بأن خطوط الممرات متوازية في الواقع. يتم تحديد نقاط المصدر والوجهة مباشرة من الصورة لتحويل المنظور.
#Source points taken from images with straight lane lines, these are to become parallel after the warp transform
src = np.float32([
(190, 720), # bottom-left corner
(596, 447), # top-left corner
(685, 447), # top-right corner
(1125, 720) # bottom-right corner
])
# Destination points are to be parallel, taking into account the image size
dst = np.float32([
[offset, img_size[1]], # bottom-left corner
[offset, 0], # top-left corner
[img_size[0]-offset, 0], # top-right corner
[img_size[0]-offset, img_size[1]] # bottom-right corner
])
يوفر OpenCV وظائف تحويل المنظور لحساب مصفوفة التحويل للصور بالنظر إلى نقاط المصدر والوجهة. باستخدام وظيفة warpPerspective ، يتم إجراء تحويل منظور طير الطيور.
# Calculate the transformation matrix and it's inverse transformation
M = cv2.getPerspectiveTransform(src, dst)
M_inv = cv2.getPerspectiveTransform(dst, src)
warped = cv2.warpPerspective(undist, M, img_size)
الهدف من ذلك هو معالجة الصورة بطريقة يتم الحفاظ على وحدات البكسل خط الممر وتمييزها بسهولة عن الطريق. يتم تطبيق أربع تحولات ثم مجتمعة.
يأخذ التحول الأول x sobel على الصورة المغطاة باللون الرمادي. يمثل هذا المشتق في اتجاه X ويساعد على اكتشاف الخطوط التي تميل إلى أن تكون رأسية. يتم الاحتفاظ فقط بالقيم فوق الحد الأدنى من العتبة.
# Transform image to gray scale
gray_img =cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Apply sobel (derivative) in x direction, this is usefull to detect lines that tend to be vertical
sobelx = cv2.Sobel(gray_img, cv2.CV_64F, 1, 0)
abs_sobelx = np.absolute(sobelx)
# Scale result to 0-255
scaled_sobel = np.uint8(255*abs_sobelx/np.max(abs_sobelx))
sx_binary = np.zeros_like(scaled_sobel)
# Keep only derivative values that are in the margin of interest
sx_binary[(scaled_sobel >= 30) & (scaled_sobel <= 255)] = 1
يحدد التحول الثاني البكسلات البيضاء في الصورة المقيدة الرمادية. يتم تعريف الأبيض بالقيم بين 200 و 255 تم اختيارها باستخدام التجربة والخطأ على الصور المحددة.
# Detect pixels that are white in the grayscale image
white_binary = np.zeros_like(gray_img)
white_binary[(gray_img > 200) & (gray_img <= 255)] = 1
التحول الثالث موجود على مكون التشبع باستخدام مساحة ألوان HLS. هذا مهم بشكل خاص للكشف عن الخطوط الصفراء على طريق الخرسانة الخفيفة.
# Convert image to HLS
hls = cv2.cvtColor(img, cv2.COLOR_BGR2HLS)
H = hls[:,:,0]
S = hls[:,:,2]
sat_binary = np.zeros_like(S)
# Detect pixels that have a high saturation value
sat_binary[(S > 90) & (S <= 255)] = 1
التحول الرابع على مكون Hue مع قيم من 10 إلى 25 ، والتي تم تحديدها على أنها مقابلة للأصفر.
hue_binary = np.zeros_like(H)
# Detect pixels that are yellow using the hue component
hue_binary[(H > 10) & (H <= 25)] = 1
يتم إجراء اكتشاف خط الممر على الصور الثنائية التي لم يتم بالفعل تشغيلها ومشوه. في البداية يتم حساب رسم بياني على الصورة. هذا يعني أن قيم البكسل يتم تلخيصها على كل عمود لاكتشاف الموضع X الأكثر احتمالًا لخطوط الممر الأيسر واليمين.
# Take a histogram of the bottom half of the image
histogram = np.sum(binary_warped[binary_warped.shape[0]//2:,:], axis=0)
# Find the peak of the left and right halves of the histogram
# These will be the starting point for the left and right lines
midpoint = np.int(histogram.shape[0]//2)
leftx_base = np.argmax(histogram[:midpoint])
rightx_base = np.argmax(histogram[midpoint:]) + midpoint
بدءًا من هذه المواضع الأساسية في الجزء السفلي من الصورة ، يتم تطبيق طريقة النافذة المنزلق للبحث عن البكسلات. يتم النظر في وحدات البكسلات عندما تكون إحداثيات X و Y داخل المنطقة المحددة بواسطة النافذة. عندما يتم اكتشاف ما يكفي من وحدات البكسل لتكون واثقة من أنها جزء من خط ، يتم حساب متوسط موضعها والحفاظ عليه كنقطة انطلاق للنافذة الصعودية التالية.
# Choose the number of sliding windows
nwindows = 9
# Set the width of the windows +/- margin
margin = 100
# Set minimum number of pixels found to recenter window
minpix = 50
# Identify window boundaries in x and y (and right and left)
win_y_low = binary_warped.shape[0] - (window+1)*window_height
win_y_high = binary_warped.shape[0] - window*window_height
win_xleft_low = leftx_current - margin
win_xleft_high = leftx_current + margin
win_xright_low = rightx_current - margin
win_xright_high = rightx_current + margin
# Identify the nonzero pixels in x and y within the window #
good_left_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) &
(nonzerox >= win_xleft_low) & (nonzerox < win_xleft_high)).nonzero()[0]
good_right_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) &
(nonzerox >= win_xright_low) & (nonzerox < win_xright_high)).nonzero()[0]
# Append these indices to the lists
left_lane_inds.append(good_left_inds)
right_lane_inds.append(good_right_inds)
يتم وضع كل هذه وحدات البكسل معًا في قائمة من إحداثيات X و Y. ويتم ذلك بشكل متماثل على كلا خطين الممرات. يتم إرجاع مواضع البيكسل leftx ، lefty ، rightx ، righty من الوظيفة ، وبعد ذلك ، يتم تركيب متعدد الحدود من الدرجة الثانية على كل الجانب الأيسر واليسرى للعثور على أفضل خط من البكسلات المحددة.
# Fit a second order polynomial to each with np.polyfit() ###
left_fit = np.polyfit(lefty, leftx, 2)
right_fit = np.polyfit(righty, rightx, 2)
هنا ، يتم تمييز وحدات البكسل اليمنى واليسرى المحددة باللون الأحمر والأزرق على التوالي. يتم تتبع الحدود المتعددة الدرجة الثانية على الصورة الناتجة.
لتسريع بحث خط الممر من إطار فيديو إلى آخر ، يتم استخدام المعلومات من الدورة السابقة. من المحتمل أن يكون للصورة التالية خطوط حارة على مقربة من خطوط الممرات السابقة. هذا هو المكان الذي يتم فيه استخدام ملاءمة متعدد الحدود للخط الأيسر والخط الأيمن للصورة السابقة لتحديد منطقة البحث.
لا تزال طريقة النافذة المنزلق تستخدم ، ولكن بدلاً من البدء بنقاط ذروة الرسم البياني ، يتم إجراء البحث على طول الخطوط السابقة بهامش معين لعرض النافذة.
# Set the area of search based on activated x-values within the +/- margin of our polynomial function
left_lane_inds = ((nonzerox > (prev_left_fit[0]*(nonzeroy**2) + prev_left_fit[1]*nonzeroy +
prev_left_fit[2] - margin)) & (nonzerox < (prev_left_fit[0]*(nonzeroy**2) +
prev_left_fit[1]*nonzeroy + prev_left_fit[2] + margin))).nonzero()[0]
right_lane_inds = ((nonzerox > (prev_right_fit[0]*(nonzeroy**2) + prev_right_fit[1]*nonzeroy +
prev_right_fit[2] - margin)) & (nonzerox < (prev_right_fit[0]*(nonzeroy**2) +
prev_right_fit[1]*nonzeroy + prev_right_fit[2] + margin))).nonzero()[0]
# Again, extract left and right line pixel positions
leftx = nonzerox[left_lane_inds]
lefty = nonzeroy[left_lane_inds]
rightx = nonzerox[right_lane_inds]
righty = nonzeroy[right_lane_inds]
يعيد البحث إحداثيات Pixel leftx و lefty و rightx و righty Pixel المزودة بوظيفة متعددة الحدود من الدرجة الثانية لكل الجانب الأيسر واليسرى.
لحساب نصف القطر وموضع السيارة على الطريق بالأمتار ، هناك حاجة إلى عوامل التحجيم للتحويل من وحدات البكسل. تبلغ قيم التحجيم المقابلة 30 مترًا إلى 720 بكسل في اتجاه Y و 3.7 متر إلى 700 بكسل في البعد X.
يتم استخدام ملاءمة متعدد الحدود لإجراء التحويل. باستخدام إحداثيات X للبكسلات المحاذاة من الخط المجهز لكل خط حارة يمينًا ويسارًا ، يتم تطبيق عوامل التحويل ويتم تنفيذ ملاءمة متعدد الحدود على كل منها.
# Define conversions in x and y from pixels space to meters
ym_per_pix = 30/720 # meters per pixel in y dimension
xm_per_pix = 3.7/700 # meters per pixel in x dimension
left_fit_cr = np.polyfit(ploty*ym_per_pix, left_fitx*xm_per_pix, 2)
right_fit_cr = np.polyfit(ploty*ym_per_pix, right_fitx*xm_per_pix, 2)
# Define y-value where we want radius of curvature
# We'll choose the maximum y-value, corresponding to the bottom of the image
y_eval = np.max(ploty)
# Calculation of R_curve (radius of curvature)
left_curverad = ((1 + (2*left_fit_cr[0]*y_eval*ym_per_pix + left_fit_cr[1])**2)**1.5) / np.absolute(2*left_fit_cr[0])
right_curverad = ((1 + (2*right_fit_cr[0]*y_eval*ym_per_pix + right_fit_cr[1])**2)**1.5) / np.absolute(2*right_fit_cr[0])
يتم حساب نصف قطر الانحناء باستخدام نقطة y في أسفل الصورة. لحساب موضع السيارة ، يتم استخدام الملاءمة متعدد الحدود في وحدات البكسل لتحديد موضع X في الممر الأيسر والأيمن المقابل لـ Y في أسفل الصورة.
# Define conversion in x from pixels space to meters
xm_per_pix = 3.7/700 # meters per pixel in x dimension
# Choose the y value corresponding to the bottom of the image
y_max = binary_warped.shape[0]
# Calculate left and right line positions at the bottom of the image
left_x_pos = left_fit[0]*y_max**2 + left_fit[1]*y_max + left_fit[2]
right_x_pos = right_fit[0]*y_max**2 + right_fit[1]*y_max + right_fit[2]
# Calculate the x position of the center of the lane
center_lanes_x_pos = (left_x_pos + right_x_pos)//2
# Calculate the deviation between the center of the lane and the center of the picture
# The car is assumed to be placed in the center of the picture
# If the deviation is negative, the car is on the felt hand side of the center of the lane
veh_pos = ((binary_warped.shape[1]//2) - center_lanes_x_pos) * xm_per_pix
يعطي متوسط هاتين القيمتين موضع مركز الممر في الصورة. إذا تم نقل مركز الممر إلى اليمين بواسطة كمية nbp من وحدات البكسل ، فهذا يعني أن السيارة تتحول إلى اليسار بواسطة nbp * xm_per_pix meters . يعتمد هذا على افتراض أن الكاميرا مثبتة على المحور المركزي للسيارة.