Sistema de detección de carril con opencv


Kemal Ficici     nos demuestra  con su proyecto escrito en  python,  que por cierto  ha publicado con su código completo en  github,   como es posible usando la librería  OpenCv    construir un detector de carril   que  incluso  maneja carriles con  curvas.

A pesar del gran avance , sin embargo el autor  reconoce  que la salida de su sistema  todavía se ve afectada por sombras y drásticos cambios en la textura de la carretera  lo cual invalida en gran parte su resultado ,  lo cual  le hace  pensar en futuras actualizaciones de su proyecto  haciendo  uso de técnicas de aprendizaje automático para llegar a desarrollar un sistema de detección de vehículo e increíblemente robusto carril.

En el siguiente video podemos ver el resultado de su trabajo:

En cualquier escenario de conducción, las líneas de carril son un componente esencial de lo que indica el flujo de tráfico y donde se debe conducir un vehículo  así  que también es un buen punto de partida en el desarrollo de  niveles de automatismos de ayuda a la conducción ( Sistemas ADAS).
En un proyecto anterior de detección de carril  Kemal  había implementado un sistema de detección de carril  que funcionaba decentemente en perfectas condiciones, sin embargo no detectaba curvas carriles con precisión y no era robusta a obstrucciones y sombras, de modo que  esta nueva versión mejora su   primera propuesta puesto que ha implementado  detección de lineas  curvas en los carriles , de modo que   funciona mucho mejor y es más robusto para entornos exigentes.

El sistema de detección de carril ha sido  escrito en Python usando la librería OpenCV y ha seguido resumidamente las siguientes etapas en el  procesamiento de imagen:

  • Corrección de distorsión
  • Deformación de la perspectiva
  • Filtro de Sobel
  • Detección de picos del histograma
  • Búsqueda de ventana deslizante
  • Ajuste de curvas
  • Superposición de carril detectado
  • Aplicar el resultado a  la salida  al vídeo

Respecto al hardware utililizado :

  • Nvidia Jetson TX2×1
  • Raspberry Pi 3 Model B×1

 

 

Corrección de distorsión

Las  lentes de las cámaras distorsionan la luz entrante al  enfocarla en el sensor de la cámara o CCD . Aunque esto es muy útil porque nos permite capturar imágenes de nuestro entorno, a menudo terminan distorsionando la luz ligeramente de forma imprecisa lo cual  puede ofrecernos medidas inexactas en aplicaciones de visión por ordenador . No obstante  fácilmente podemos corregir esta distorsión calibrando la imagen de un objeto conocido ( por ejemplo  tablero de ajedrez asimétrico,)y generando un modelo de distorsión que represente las distorsiones de la lente.

La cámara utilizada en la prueba video  fue utilizada para tomar 20 imágenes de un tablero de ajedrez, que fueron utilizados para generar el modelo de distorsión.El autor comenzó por convertir la imagen a escala de grises y  entonces aplico la  función cv2.findChessboardCorners .Como sabemos que este tablero de ajedrez es un objeto tridimensional  con líneas rectas exclusivamente podemos aplicar algunas transformaciones a las esquinas detectadas para alinearlos correctamente utilizando cv2.CalibrateCamera()  obteniendo así  los coeficientes de distorsión y la matriz de cámara  de modo que  así  ya estaba calibrada la cámara

Realizado el proceso anterior se puede utilizar  cv2.undistort()  para corregir el resto de sus datos de entrada.

Como demostración en la imagen se puede ver la diferencia entre la imagen original del tablero de ajedrez y la imagen corregida a continuación:

ajedrez

Aquí está el código exacto que usó el autor para esto:

def undistort_img():
    # Prepare object points 0,0,0 ... 8,5,0
    obj_pts = np.zeros((6*9,3), np.float32)
    obj_pts[:,:2] = np.mgrid[0:9, 0:6].T.reshape(-1,2)
    # Stores all object points & img points from all images
    objpoints = []
    imgpoints = []
    # Get directory for all calibration images
    images = glob.glob('camera_cal/*.jpg')
    for indx, fname in enumerate(images):
        img = cv2.imread(fname)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        ret, corners = cv2.findChessboardCorners(gray, (9,6), None)
        if ret == True:
            objpoints.append(obj_pts)
            imgpoints.append(corners)
    # Test undistortion on img
    img_size = (img.shape[1], img.shape[0])
    # Calibrate camera
    ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, img_size, None,None)
    dst = cv2.undistort(img, mtx, dist, None, mtx)
    # Save camera calibration for later use
    dist_pickle = {}
    dist_pickle['mtx'] = mtx
    dist_pickle['dist'] = dist
    pickle.dump( dist_pickle, open('camera_cal/cal_pickle.p', 'wb') )
def undistort(img, cal_dir='camera_cal/cal_pickle.p'):
    #cv2.imwrite('camera_cal/test_cal.jpg', dst)
    with open(cal_dir, mode='rb') as f:
        file = pickle.load(f)    mtx = file['mtx']
    dist = file['dist']
    dst = cv2.undistort(img, mtx, dist, None, mtx)
    return dst
undistort_img()
img = cv2.imread('camera_cal/calibration1.jpg')
dst = undistort(img) # Undistorted image

Y ahora podemos ver  la corrección de distorsión aplicada a una imagen de la carretera.

imagencorregida.png

Solo se puede notar  diferencias leves, pero esto como veremos puede tener un impacto enorme en el tratamiento de la imagen.

Deformación de la perspectiva

La detección de carriles con trazados curvas  en espacios de la cámara espacio no es uan tarea  fácil asi que la idea es  conseguir una vista de pájaro de las pistas , lo cual se e puede hacer aplicando una transformación de perspectiva en la imagen. Aquí es lo que parece:

carril

Como vemos nos es nada espectacular debido a que el carril esta sobre una superficie plana en 2D, asi  que podemos encajar un polinomio que puede representar fielmente el carril en el espacio del carril

Puede aplicar estas transformaciones a cualquier imagen usando la función cv2.getPerspectiveTransform()  para obtener la matriz de transformación, y  aplicar la función cv2.warpPerspective() a una imagen.

Aquí está el código que uso el autor para ello:

def perspective_warp(img,
                     dst_size=(1280,720),
                     src=np.float32([(0.43,0.65),(0.58,0.65),(0.1,1),(1,1)]),
                     dst=np.float32([(0,0), (1, 0), (0,1), (1,1)])):
    img_size = np.float32([(img.shape[1],img.shape[0])])
    src = src* img_size
    # For destination points, I'm arbitrarily choosing some points to be
    # a nice fit for displaying our warped result
    # again, not exact, but close enough for our purposes
    dst = dst * np.float32(dst_size)
    # Given src and dst points, calculate the perspective transform matrix
    M = cv2.getPerspectiveTransform(src, dst)
    # Warp the image using OpenCV warpPerspective()
    warped = cv2.warpPerspective(img, M, dst_size)
    return warped

Filtro de Sobel

En otras versiones una opción era filtrar las líneas de carril con el color peor sin embargo, esto no siempre es la mejor opción. Si el camino utiliza luz de color concreta en lugar de asfalto, el camino pasa fácilmente a través del filtro de color, y esta la percibirá como una línea de carril blanco, pero  eso no es correcto.

En su lugar, podemos utilizar un método similar al detector de borde, esta vez para filtrar hacia fuera de la carretera. Las líneas de carril suelen tienen un alto contraste en  la carretera, por lo que podemos utilizar esta peculiaridad para nuestro beneficio. La funcion detector de borde  Canny utilizado anteriormente  hace uso de Operador de Sobel , para obtener el gradiente de una función de la imagen. La documentación de OpenCV tiene una fantástica explicación sobre cómo funciona asi que utilizaremos esto para detectar zonas de alto contraste para las marcas de carril filtro e ignorar el resto del camino .

Todavía utilizaremos el espacio de color HLS nuevamente, esta vez para detectar cambios en la saturación y la ligereza. Los operadores de sobel se aplican a estos dos canales, y extraemos el gradiente con respecto al eje x y añadiremos los píxeles que pasan nuestro umbral de degradado a una matriz binaria que representa a los píxeles de nuestra imagen. Aquí está como se ve en cámara espacio y lane

blancoynegro.PNG:

Tenga en cuenta que las partes de la imagen que estaban más lejos de la cámara no conserven su calidad muy bien. Debido a las limitaciones de resolución de la cámara, datos de los objetos más lejos son muy borrosos y ruidosos  pero no necesitamos concentrarnos en la imagen, para que podamos utilizar sólo una parte de esta.

Detección de picos del histograma

Ahora aplicaremos un algoritmo especial llamado Sliding Window Algorithm ( algo asi como algoritmo  Desplazamiento de Ventana )para detectar nuestras líneas de carril. Sin embargo, antes de que lo podemos aplicar, debemos determinar un buen punto de partida para el algoritmo pues este funciona bien si comienza en un lugar donde haya píxeles de lineas  presentes, pero ¿cómo podemos detectar la ubicación de estos píxeles de carril en primer lugar?

Estará recibiendo un histograma de la imagen con respecto al eje X. Cada parte del histograma siguiente muestra píxeles blancos en cada columna de la imagen. Entonces tomamos los picos más altos de cada lado de la imagen, uno para cada línea de carril y tendríamos resulto esta parte

Aquí vemos como  el histograma parece, al lado de la imagen binaria:

histograma

Búsqueda de ventana deslizante

Ahora  necesitamos  utilizar el algoritmo de ventana deslizante para distinguir entre los límites del carril de la izquierda y derecha para que podemos caber dos curvas diferentes que representan los límites del carril.

El algoritmo sí mismo es muy simple. A partir de la posición inicial, la primera ventana mide cuántos píxeles se encuentran dentro de la ventana. Si la cantidad de píxeles alcanza un cierto umbral, desplaza la siguiente ventana a la posición lateral media de los píxeles detectados. Si no se detectan los suficientes píxeles, comienza la siguiente ventana en la misma posición lateral.

Esto continúa hasta que las ventanas alcanzan el otro borde de la imagen .Asimismo los píxeles que corresponden a las ventanas reciben un marcador.

En las imágenes de abajo, los píxeles marcados azules representan el carril derecho, y los rojos representan la izquierda:

lineas

Ajuste de curvas

El resto del proyecto es ya mas fácil. Aplicamos la regresión polinomial para los pixeles rojos y azules individualmente usando np.polyfit() , y entonces el detector se hace  sobre todo

Esto es lo que parecen las curvas:

 

Superposición de carril detectado

Ya estamos en la parte final del sistema de detección: la interfaz de usuario. Simplemente creamos una superposición que llena en la parte detectada del carril, y luego  finalmente lo aplicamos al vídeo.

Este es el resultado final

 

 

!Sin duda un resultado  realmente espectacular que puede servir de partida para proyectos  mas ambiciosos!

Mas información en www.hackster.io

 

Deteccion del parpadeo de los ojos mediante OpenCV


OpenCV es una biblioteca libre de visión artificial originalmente desarrollada por Intel. Desde que apareció su primera versión alfa en el mes de enero de 1999, se ha utilizado en infinidad de aplicaciones. Desde sistemas de seguridad con detección de movimiento, hasta aplicativos de control de procesos donde se requiere reconocimiento de objetos. Esto se debe a que su publicación se da bajo licencia BSD, que permite que sea usada libremente para propósitos comerciales y de investigación con las condiciones en ella expresadas.

Open CV es multiplataforma, existiendo versiones para GNU/Linux, Mac OS X y Windows  conteniendo  más de 500 funciones que abarcan una gran gama de áreas en el proceso de visión, como reconocimiento de objetos (reconocimiento facial), calibración de cámaras, visión estérea y visión robótica.

El proyecto pretende proporcionar un entorno de desarrollo fácil de utilizar y altamente eficiente. Esto se ha logrado, realizando su programación en código C y C++ optimizados, aprovechando además las capacidades que proveen los procesadores multi núcleo. OpenCV puede además utilizar el sistema de primitivas de rendimiento integradas de Intel, un conjunto de rutinas de bajo nivel específicas para procesadores Intel.

Veamos algunos ejemplos que usan esta biblioteca:

http://www.youtube.com/watch?v=v0UNbB_vyrM

Esta fantástica  aplicación creada  por  Martin Polak  es capaz de detectar parpadeo de los ojos mediante el cálculo de flujo óptico en la zona de los ojos dentro de la imagen estabilizada de cabeza. Cuando el usuario  ha cerrado los ojos durante más de 5 segundos, la aplicación comienza a sonar para despertarlo . El algoritmo hace uso de OpenCV, una biblioteca de la visión por ordenador de código abierto y el software de aprendizaje automático.

Esta aplicación  se podría  usar   utilizar dentro de los vehículos para evitar permitir dormir  al conductor detrás del volante u otras aplicaciones

bblinking

Por otro lado la aplicación  Eye Location, disponible gratuitamente en Google play aqui  intenta localizar los centros de los ojos de una persona que la cámara está capturando en tiempo real. Se basa en un algoritmo de visión por ordenador que explota la información de crominancia del ojo y la simetría radial del iris con el fin de localizar los centros de los ojos. El algoritmo hace tambien uso de OpenCV, una biblioteca de la visión por ordenador de código abierto y el software de aprendizaje automático. La detección de la cara se realiza utilizando una función de detector de rostro del OpenCV. La aplicación funciona de manera óptima en ambientes bien iluminados, donde se capturan las imágenes con buenos niveles de contraste.

Acceso en Google Play

ÁREAS DE APLICACIÓN DE ESTE TIPO DE APLICACIONES DE RECONOCIMIENTO

•Tipificación de ojos :La función de la tipificación del ojo utiliza la dirección de la mirada y del ojo parpadea para componer mensajes o realizar otras acciones. El objetivo final es ayudar a las personas con ELA, lock-in síndrome, tetraplejia o cualquier otra persona que sólo puede mover los ojos con el uso de su dispositivo de mano para comunicarse, navegar por Internet o facilitar otras tareas cotidianas.
Desplazamiento inteligente : Similar a la característica del Samsung Smart Scroll, que actualmente se basa en el reconocimiento de rostros y de inclinación (http://mashable.com/2013/03/14/smart-scroll/), desplazamiento inteligente puede optimizarse aún más al detectar el cambio de la dirección de la contemplar.
• Spoof prueba cara de desbloqueo:  La característica de la cara de desbloqueo se puede suplantar por lo general usando una foto de el propietario (http://www.geek.com/android/android-face-lock-feature-spoofed-by-photograph-1440953/). Desbloqueo facial puede hacerse más fiable mediante la introducción de un «cheque liveness ‘: una vez a la cara se verifica, el usuario está obligado a seguir un punto en movimiento al azar con sus ojos.
• Estimación de la dirección de la mirada de asistencia al conductor:Al montar el teléfono móvil en el salpicadero de un vehículo, una aplicación de estimación mirada podría supervisar y evaluar la vigilancia del conductor, determinando si el foco del conductor de atención está en el camino.
Evaluaciones de usabilidad:Los movimientos oculares y fijaciones proporcionan una indicación de la cantidad de procesamiento cognitivo requiere una pantalla y por lo tanto lo fácil que es para procesar, lo que conduce a la posible utilización de seguimiento de los ojos de contribuir a la evaluación de la usabilidad de las interfaces de usuario.

 

Para poder crear proyectos haciendo uso de esta librería con Visual Studio , se deben seguir los siguientes pasos:

  1. Bajar OpenCV desde aquí (por ejemplo  la 2.4.0
  2. Hacer doble click al ejecutable. Esto descomprimirá los archivos. Elegir una ubicación para OpenCV y descomprimir (Yo elegí C:\, pero pueden elegir cualquier otra, por lo cual de ahora en  más me referiré a esa ruta elegida como INSTALL_PATH, pero esto debe ser reemplazado SIEMPRE por la ruta que eligió.
  3. Ir a Inicio y escribir en la barra de búsqueda «Variables del sistema». Click en el resultado que aparece como «Editar las variables de entorno del sistema».
  4. Dentro de la pestaña ‘Opciones Avanzadas’ ir a ‘Variables de Entorno’.
  5. En la nueva ventana, buscar dentro del segundo listado (variables del sistema) una variable llamada Path. Seleccionarla y hacer click en editar.
  6. Ir al final del textbox ‘Valor de la variable’, agregar un punto y coma, y agregar las siguientes dos ubicaciones, también separadas por un punto y coma:
    • INSTALL_PATH\opencv\build\x86\vc10\bin
    • INSTALL_PATH\opencv\build\common\tbb\ia32\vc10

    donde
    *ia32 es en caso de tener Windows x32, sino ingresar a intl64.
    *vc10 es en caso de tener Visual C 2010. En caso de tener Visual C 2008, elegir vc9.

Luego, abrir un proyecto existente que utilice OpenCV (éste, por ejemplo), seleccionarlo en el ‘Explorador de soluciones’ y hacer click derecho para seleccionar ‘Propiedades’.

En propiedades dirigirse a:

  • C/C++
    Agregar los siguientes directorios en Additional Include Directories:

    • INSTALL_PATH\opencv\build\include\opencv
    • INSTALL_PATH\opencv\build\include
  • Linker->General
    Agregar el siguiente directorio en Additional Library Directories:

    • INSTALL_PATH\opencv\build\x86\vc10\lib
  • Linker->Input
    Agregar el siguiente listado, tendiendo en cuenta que

    • debe reemplazarse el número por la versión de OpenCV que se utiliza. Por ejemplo, 242 para la versión 2.4.2 o 240 para la 2.4.0.
    • debe quitarse la letra ‘d’ final, si se trata de un Release Project.

opencv_core240d.lib
opencv_imgproc240d.lib
opencv_highgui240d.lib
opencv_ml240d.lib
opencv_video240d.lib
opencv_features2d240d.lib
opencv_calib3d240d.lib
opencv_objdetect240d.lib
opencv_contrib240d.lib
opencv_legacy240d.lib
opencv_flann240d.lib

Si se tratara de una solución, y no de un proyecto simple, repetir esto en cada proyecto.

 

Más información aquí