Algoritma visi komputer untuk menghitung kelengkungan jalan dan offset kendaraan jalur menggunakan pemrosesan gambar OpenCV, kalibrasi kamera, transformasi perspektif, masker warna, sobel dan kesesuaian polinomial.
Proyek Lane Finding Advanced adalah langkah lebih jauh dari [deteksi jalur jalur] dalam mengidentifikasi geometri jalan di depan.
Menggunakan rekaman video mengemudi di jalan raya, tujuan proyek ini adalah untuk menghitung jari -jari kelengkungan jalan. Jalan melengkung adalah tugas yang lebih menantang daripada yang lurus. Untuk menghitung kelengkungan dengan benar, jalur jalur perlu diidentifikasi tetapi di atasnya, gambar harus tidak terdistorsi. Transformasi gambar diperlukan untuk kalibrasi kamera dan untuk transformasi perspektif untuk mendapatkan pandangan mata burung.
Proyek ini diimplementasikan dalam Python dan menggunakan perpustakaan pemrosesan gambar OpenCV. Kode sumber dapat ditemukan di AdvancedLaneFinding.ipynb
[Distorsi Optik] adalah fenomena fisik yang terjadi dalam perekaman gambar, di mana garis lurus diproyeksikan sebagai yang sedikit melengkung ketika dirasakan melalui lensa kamera. Video penggerak jalan raya direkam menggunakan kamera menghadap depan pada mobil dan gambar terdistorsi. Koefisien distorsi khusus untuk setiap kamera dan dapat dihitung menggunakan bentuk geometris yang diketahui.
Gambar papan catur yang ditangkap dengan kamera tertanam disediakan di folder camera_cal . Keuntungan dari gambar -gambar ini adalah mereka memiliki geometri yang kontras dan diketahui. Gambar -gambar yang disediakan menyajikan 9 * 6 sudut untuk dikerjakan.
# 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)
Titik objek ditetapkan berdasarkan pemahaman umum bahwa dalam pola papan catur, semua kotak sama. Ini menyiratkan bahwa titik objek akan memiliki koordinat x dan y yang dihasilkan dari indeks grid, dan z selalu 0. Titik gambar mewakili titik objek yang sesuai yang ditemukan dalam gambar menggunakan fungsi 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)
Setelah memindai semua gambar, daftar titik gambar memiliki data yang cukup untuk dibandingkan dengan titik objek untuk menghitung matriks kamera dan koefisien distorsi. Ini mengarah ke matriks kamera yang akurat dan identifikasi koefisien distorsi menggunakan fungsi 'Calibratecamera'.
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
undist = cv2.undistort(img, mtx, dist, None, mtx)
Fungsi OpenCV undistort digunakan untuk mengubah gambar menggunakan matriks kamera dan koefisien distorsi.
Hasil teknik kalibrasi kamera terlihat saat membandingkan gambar -gambar ini. Sementara di papan catur gambar distorsi lebih jelas, pada gambar jalan itu lebih halus. Namun demikian, gambar yang tidak terdistorsi akan menyebabkan perhitungan kelengkungan jalan yang salah.
Untuk melengkung, perspektif yang ideal adalah pandangan mata burung. Ini berarti bahwa jalan dipersepsikan dari atas, bukan pada sudut melalui kaca depan kendaraan.
Transformasi perspektif ini dihitung menggunakan skenario jalur lurus dan pengetahuan umum sebelumnya bahwa jalur jalur sebenarnya paralel. Titik sumber dan tujuan diidentifikasi langsung dari gambar untuk transformasi perspektif.
#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 menyediakan fungsi transformasi perspektif untuk menghitung matriks transformasi untuk gambar yang diberikan titik dan titik tujuan. Menggunakan fungsi warpPerspective , transformasi perspektif mata burung dilakukan.
# 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)
Tujuannya adalah untuk memproses gambar sedemikian rupa sehingga piksel jalur jalur dipertahankan dan mudah dibedakan dari jalan. Empat transformasi diterapkan dan kemudian digabungkan.
Transformasi pertama mengambil x sobel pada gambar berskala abu-abu. Ini mewakili turunan dalam arah X dan membantu mendeteksi garis yang cenderung vertikal. Hanya nilai -nilai di atas ambang batas minimum yang disimpan.
# 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
Transformasi kedua memilih piksel putih dalam gambar berskala abu -abu. Putih didefinisikan oleh nilai antara 200 dan 255 yang dipilih menggunakan coba -coba pada gambar yang diberikan.
# 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
Transformasi ketiga adalah pada komponen saturasi menggunakan ruang warna HLS. Ini sangat penting untuk mendeteksi garis kuning di jalan beton cahaya.
# 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
Transformasi keempat adalah pada komponen Hue dengan nilai dari 10 hingga 25, yang diidentifikasi sesuai dengan kuning.
hue_binary = np.zeros_like(H)
# Detect pixels that are yellow using the hue component
hue_binary[(H > 10) & (H <= 25)] = 1
Deteksi jalur jalur dilakukan pada gambar ambang biner yang telah tidak terdistorsi dan melengkung. Awalnya histogram dihitung pada gambar. Ini berarti bahwa nilai piksel dijumlahkan pada setiap kolom untuk mendeteksi posisi x yang paling mungkin dari jalur jalur kiri dan kanan.
# 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
Dimulai dengan posisi dasar ini di bagian bawah gambar, metode jendela geser diterapkan naik ke atas mencari piksel baris. Piksel jalur dipertimbangkan ketika koordinat X dan Y berada di dalam area yang ditentukan oleh jendela. Ketika piksel yang cukup terdeteksi untuk yakin bahwa mereka adalah bagian dari garis, posisi rata -rata mereka dihitung dan disimpan sebagai titik awal untuk jendela ke atas berikutnya.
# 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)
Semua piksel ini disatukan dalam daftar koordinat X dan Y mereka. Ini dilakukan secara simetris pada kedua jalur jalur. Posisi piksel leftx , lefty , rightx , righty dikembalikan dari fungsi dan setelah itu, polinomial tingkat dua dipasang pada setiap sisi kiri dan kanan untuk menemukan garis terbaik dari piksel yang dipilih.
# Fit a second order polynomial to each with np.polyfit() ###
left_fit = np.polyfit(lefty, leftx, 2)
right_fit = np.polyfit(righty, rightx, 2)
Di sini, piksel garis kiri dan kanan yang diidentifikasi masing -masing ditandai dengan warna merah dan biru. Polinomial tingkat kedua dilacak pada gambar yang dihasilkan.
Untuk mempercepat pencarian jalur jalur dari satu bingkai video ke yang berikutnya, informasi dari siklus sebelumnya digunakan. Lebih mungkin bahwa gambar berikutnya akan memiliki jalur jalur yang dekat dengan jalur jalur sebelumnya. Di sinilah kesesuaian polinomial untuk garis kiri dan garis kanan gambar sebelumnya digunakan untuk menentukan area pencarian.
Metode jendela geser masih digunakan, tetapi alih -alih memulai dengan titik puncak histogram, pencarian dilakukan di sepanjang baris sebelumnya dengan margin yang diberikan untuk lebar jendela.
# 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]
Pencarian mengembalikan koordinat piksel leftx , lefty , rightx , righty yang dilengkapi dengan fungsi polinomial tingkat kedua untuk setiap sisi kiri dan kanan.
Untuk menghitung jari -jari dan posisi kendaraan di jalan dalam meter, faktor penskalaan diperlukan untuk dikonversi dari piksel. Nilai penskalaan yang sesuai adalah 30 meter hingga 720 piksel dalam arah Y dan 3,7 meter hingga 700 piksel dalam dimensi X.
Kesesuaian polinomial digunakan untuk melakukan konversi. Menggunakan koordinat X dari piksel yang selaras dari garis yang dipasang dari setiap garis jalur kanan dan kiri, faktor konversi diterapkan dan kesesuaian polinomial dilakukan pada masing -masing.
# 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])
Jari -jari kelengkungan dihitung menggunakan titik Y di bagian bawah gambar. Untuk menghitung posisi kendaraan, kecocokan polinomial dalam piksel digunakan untuk menentukan posisi x dari jalur kiri dan kanan yang sesuai dengan Y di bagian bawah gambar.
# 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
Rata -rata dari kedua nilai ini memberikan posisi tengah jalur dalam gambar. Jika pusat jalur digeser ke kanan dengan jumlah piksel nbp yang berarti bahwa mobil digeser ke kiri oleh nbp * xm_per_pix meters . Ini didasarkan pada asumsi bahwa kamera dipasang pada sumbu pusat kendaraan.