OpenCV Python Tutorials Alexander Mordvintsev v1 SP
OpenCV Python Tutorials Alexander Mordvintsev v1 SP
Tutoriales de OpenCVPython
Documentación
Lanzamiento 1
Alexander Mordvintsev y Abid K.
11 de abril de 2017
Machine Translated by Google
Machine Translated by Google
Contenido
1 Tutoriales de OpenCVPython
1.1 Introducción a OpenCV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . .
1.2 Características de la interfaz gráfica de usuario en OpenCV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 6 .
1.3 Operaciones principales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.4 Procesamiento de imágenes en OpenCV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 .
1.5 Detección y descripción de características . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 .
1.6 Análisis de vídeo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 . 189
1.7 Calibración de cámara y reconstrucción 3D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
1.8 Aprendizaje automático . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 .
1.9 Fotografía computacional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250 .
1.10 Detección de . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259 .
objetos 1.11 Enlaces OpenCVPython . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
2 Índices y tablas 269
i
Machine Translated by Google
yo
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Contenido:
Contenido 1
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
2 Contenido
Machine Translated by Google
CAPÍTULO 1
Tutoriales de OpenCVPython
• Introducción a OpenCV
¡Aprenda a configurar OpenCVPython en su computadora!
• Características de la interfaz gráfica de usuario en OpenCV
Aquí aprenderá cómo mostrar y guardar imágenes y videos, controlar los eventos del mouse y crear
una barra de seguimiento.
• Operaciones principales
En esta sección aprenderá operaciones básicas sobre imágenes como edición de píxeles,
transformaciones geométricas, optimización de código, algunas herramientas matemáticas, etc.
• Procesamiento de imágenes en OpenCV
3
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
En esta sección aprenderá diferentes funciones de procesamiento de imágenes dentro de
OpenCV.
• Detección y descripción de características
En esta sección aprenderá acerca de los detectores y descriptores de características.
• Análisis de vídeo
En esta sección aprenderá diferentes técnicas para trabajar con videos como seguimiento de
objetos, etc.
• Calibración de cámara y reconstrucción 3D
En esta sección, aprenderemos sobre la calibración de la cámara, la imagen estéreo, etc.
• Aprendizaje automático
En esta sección aprenderá diferentes funciones de procesamiento de imágenes dentro de
OpenCV.
• Fotografía Computacional
En esta sección, aprenderá diferentes técnicas de fotografía computacional, como eliminar el
ruido de la imagen, etc.
• Detección de objetos
4 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
En esta sección, aprenderá técnicas de detección de objetos como la detección de rostros, etc.
• Enlaces OpenCVPython
En esta sección, veremos cómo se generan los enlaces OpenCVPython
5
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Introducción a OpenCV
• Tutoriales de Introducción a OpenCVPython
Primeros pasos con OpenCVPython
• Instalar OpenCVPython en Windows
Configurar OpenCVPython en Windows
• Instalar OpenCVPython en Fedora
Configurar OpenCVPython en Fedora
6 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Tutoriales de Introducción a OpenCVPython
OpenCV
OpenCV se inició en Intel en 1999 por Gary Bradsky y el primer lanzamiento salió en 2000. Vadim Pisarevsky se unió a Gary Bradsky para
administrar el equipo OpenCV de software ruso de Intel. En 2005, OpenCV se utilizó en Stanley, el vehículo que ganó el DARPA Grand
Challenge de 2005. Posteriormente, su desarrollo activo continuó bajo el apoyo de Willow Garage, con Gary Bradsky y Vadim Pisarevsky al
frente del proyecto. En este momento, OpenCV admite una gran cantidad de algoritmos relacionados con la visión artificial y el aprendizaje
automático y se está expandiendo día a día.
Actualmente, OpenCV admite una amplia variedad de lenguajes de programación como C++, Python, Java, etc. y está disponible en
diferentes plataformas, incluidas Windows, Linux, OS X, Android, iOS, etc. Además, las interfaces basadas en CUDA y OpenCL también
están en desarrollo activo para alta Velocidad de las operaciones de la GPU.
OpenCVPython es la API Python de OpenCV. Combina las mejores cualidades de la API C++ de OpenCV y el lenguaje Python.
OpenCVPython
Python es un lenguaje de programación de propósito general iniciado por Guido van Rossum, que se hizo muy popular en poco tiempo
principalmente debido a su simplicidad y legibilidad del código. Permite al programador expresar sus ideas en menos líneas de código sin
reducir la legibilidad.
Comparado con otros lenguajes como C/C++, Python es más lento. Pero otra característica importante de Python es que se puede ampliar
fácilmente con C/C++. Esta característica nos ayuda a escribir códigos computacionalmente intensivos en C/C++ y crear un envoltorio de
Python para que podamos usar estos envoltorios como módulos de Python. Esto nos da dos ventajas: primero, nuestro código es tan rápido
como el código C/C++ original (ya que es el código C++ real trabajando en segundo plano) y segundo, es muy fácil codificar en Python. Así
es como funciona OpenCVPython, es un contenedor de Python alrededor de la implementación original de C++.
Y el soporte de Numpy facilita la tarea. Numpy es una biblioteca altamente optimizada para operaciones numéricas.
Proporciona una sintaxis de estilo MATLAB. Todas las estructuras de arreglos de OpenCV se convierten a y desde arreglos Numpy.
Entonces, independientemente de las operaciones que pueda hacer en Numpy, puede combinarlas con OpenCV, lo que aumenta la
cantidad de armas en su arsenal. Además de eso, varias otras bibliotecas como SciPy, Matplotlib que admite Numpy se pueden usar con esto.
Por lo tanto, OpenCVPython es una herramienta adecuada para la creación rápida de prototipos de problemas de visión por computadora.
Tutoriales de OpenCVPython
OpenCV presenta un nuevo conjunto de tutoriales que lo guiarán a través de varias funciones disponibles en OpenCVPython.
Esta guía se centra principalmente en la versión OpenCV 3.x (aunque la mayoría de los tutoriales también funcionarán con OpenCV 2.x).
Se requiere un conocimiento previo de Python y Numpy antes de comenzar porque no se tratarán en esta guía.
Especialmente, se debe tener un buen conocimiento de Numpy para escribir códigos optimizados en OpenCVPython.
Este tutorial lo inició Abid Rahman K. como parte del programa Google Summer of Code 2013, bajo la dirección de Alexander Mordvintsev.
¡OpenCV te necesita!
Dado que OpenCV es una iniciativa de código abierto, todos son bienvenidos a hacer contribuciones a esta biblioteca. Y es lo mismo para
este tutorial también.
Por lo tanto, si encuentra algún error en este tutorial (ya sea un pequeño error de ortografía o un gran error en el código o los conceptos, lo
que sea), siéntase libre de corregirlo.
1.1. Introducción a OpenCV 7
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Y esa será una buena tarea para los principiantes que comienzan a contribuir con proyectos de código abierto. Simplemente bifurque OpenCV en
github, haga las correcciones necesarias y envíe una solicitud de extracción a OpenCV. Los desarrolladores de OpenCV verificarán su solicitud de
extracción, le brindarán comentarios importantes y, una vez que pase la aprobación del revisor, se fusionará con OpenCV.
Entonces te conviertes en un colaborador de código abierto. Similar es el caso con otros tutoriales, documentación, etc.
A medida que se agreguen nuevos módulos a OpenCVPython, este tutorial deberá ampliarse. Entonces, aquellos que conocen un algoritmo en
particular pueden escribir un tutorial que incluya una teoría básica del algoritmo y un código que muestre el uso básico del algoritmo y enviarlo a
OpenCV.
Recuerda, juntos podemos hacer de este proyecto un gran éxito!!!
Colaboradores
A continuación se muestra la lista de colaboradores que enviaron tutoriales a OpenCVPython.
1. Alexander Mordvintsev (mentor de GSoC2013)
2. Abid Rahman K. (pasante de GSoC2013)
Recursos adicionales
1. Una guía rápida de Python: un byte de Python
2. Tutoriales básicos de Numpy
3. Lista de ejemplos Numpy
4. Documentación OpenCV
5. Foro OpenCV
Instalar OpenCVPython en Windows
Objetivos
En este tutorial
• Aprenderemos a configurar OpenCVPython en su sistema Windows.
Los pasos a continuación se prueban en una máquina con Windows 7 a 64 bits con Visual Studio 2010 y Visual Studio 2012. Las capturas de pantalla
muestran VS2012.
Instalación de OpenCV desde binarios precompilados
1. Los siguientes paquetes de Python deben descargarse e instalarse en sus ubicaciones predeterminadas.
1.1. Python2.7.x.
1.2. entumecido
1.3. matplotlib (Matplotlib es opcional, pero recomendado ya que lo usamos mucho en nuestros tutoriales).
2. Instale todos los paquetes en sus ubicaciones predeterminadas. Python se instalará en C:/Python27/.
3. Después de la instalación, abra Python IDLE. Ingrese import numpy y asegúrese de que Numpy funcione bien.
4. Descargue la última versión de OpenCV del sitio de sourceforge y haga doble clic para extraerlo.
7. Vaya a la carpeta opencv/build/python/2.7 .
8 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
8. Copie cv2.pyd en C:/Python27/lib/sitepackeges.
9. Abra Python IDLE y escriba los siguientes códigos en la terminal de Python.
>>> importar cv2
>>> imprimir cv2.__versión__
Si los resultados se imprimen sin errores, ¡felicidades! Ha instalado OpenCVPython con éxito.
Construyendo OpenCV desde la fuente
1. Descargue e instale Visual Studio y CMake.
1.1. estudio visual 2012
1.2. CHacer
2. Descargue e instale los paquetes de Python necesarios en sus ubicaciones predeterminadas
2.1. Pitón 2.7.x
2.2. entumecido
2.3. matplotlib (Matplotlib es opcional, pero recomendado ya que lo usamos mucho en nuestros tutoriales).
Nota: En este caso, estamos usando binarios de 32 bits de paquetes de Python. Pero si desea utilizar OpenCV para x64, se deben instalar
paquetes binarios de Python de 64 bits. El problema es que no hay archivos binarios oficiales de 64 bits de Numpy. Tienes que construirlo por tu
cuenta. Para eso, debe usar el mismo compilador que se usó para compilar Python. Cuando inicia Python IDLE, muestra los detalles del
compilador. Puede obtener más información aquí. Por lo tanto, su sistema debe tener la misma versión de Visual Studio y compilar Numpy desde
la fuente.
Nota: Otro método para tener paquetes de Python de 64 bits es usar distribuciones de Python listas para usar de terceros como Anaconda,
pensado etc. Será más grande en tamaño, pero tendrá todo lo que necesita. Todo en un solo caparazón.
También puede descargar versiones de 32 bits también.
3. Asegúrese de que Python y Numpy funcionen bien.
4. Descargue la fuente de OpenCV. Puede ser de Sourceforge (para la versión de lanzamiento oficial) o de Github (para la última
fuente).
5. Extráigalo a una carpeta, abra CV y cree una nueva compilación de carpeta en él.
6. Abra CMakegui (Inicio > Todos los programas > CMakegui)
7. Complete los campos de la siguiente manera (vea la imagen a continuación):
7.1. Haga clic en Examinar fuente... y localice la carpeta opencv.
7.2. Haga clic en Examinar compilación... y localice la carpeta de compilación que creamos.
7.3. Haga clic en Configurar.
1.1. Introducción a OpenCV 9
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
7.4. Se abrirá una nueva ventana para seleccionar el compilador. Elija el compilador adecuado (aquí, Visual Studio 11) y haga
clic en Finalizar.
7.5. Espere hasta que finalice el análisis.
8. Verás que todos los campos están marcados en rojo. Haga clic en el campo CON para expandirlo. Decide qué características adicionales
necesitas. Así que marque los campos apropiados. Vea la imagen de abajo:
10 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
9. Ahora haga clic en el campo CONSTRUIR para expandirlo. Los primeros campos configuran el método de compilación. Vea la imagen de abajo:
1.1. Introducción a OpenCV 11
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
10. Los campos restantes especifican qué módulos se van a construir. Dado que los módulos de GPU aún no son compatibles con
OpenCV Python, puede evitarlos por completo para ahorrar tiempo (pero si trabaja con ellos, manténgalo allí). Vea la imagen a
continuación:
12 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
11. Ahora haga clic en el campo HABILITAR para expandirlo. Asegúrese de que ENABLE_SOLUTION_FOLDERS no esté marcado (así que
Las carpetas de solución no son compatibles con la edición Visual Studio Express). Vea la imagen a continuación:
1.1. Introducción a OpenCV 13
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
12. También asegúrese de que en el campo PYTHON , todo esté lleno. (Ignorar PYTHON_DEBUG_LIBRARY). Ver
imagen a continuación:
13. Finalmente haga clic en el botón Generar .
14. Ahora ve a nuestra carpeta opencv/build . Allí encontrará el archivo OpenCV.sln . Ábralo con Visual Studio.
15. Marque el modo de compilación como Lanzamiento en lugar de Depuración.
16. En el explorador de soluciones, haga clic con el botón derecho en la solución (o ALL_BUILD) y constrúyala. Tomará algún tiempo para
finalizar.
17. Nuevamente, haga clic derecho en INSTALAR y constrúyalo. Ahora se instalará OpenCVPython.
14 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
18. Abra Python IDLE e ingrese import cv2. Si no hay error, está instalado correctamente.
Nota: Lo hemos instalado sin ningún otro soporte como TBB, Eigen, Qt, Documentación, etc. Sería difícil explicarlo aquí. Pronto se agregará
un video más detallado o simplemente puede piratear.
Recursos adicionales
Ejercicios
1. Si tiene una máquina con Windows, compile OpenCV desde la fuente. Haz todo tipo de trucos. Si te encuentras con alguno
problema, visite el foro de OpenCV y explique su problema.
Instalar OpenCVPython en Fedora
Objetivos
En este tutorial
• Aprenderemos a configurar OpenCVPython en su sistema Fedora. Los pasos a continuación se prueban para Fedora 18 (64
bits) y Fedora 19 (32 bits).
Introducción
OpenCVPython se puede instalar en Fedora de dos maneras: 1) Instalar desde archivos binarios preconstruidos disponibles en los
repositorios de Fedora, 2) Compilar desde la fuente. En esta sección, veremos ambos.
Otra cosa importante son las bibliotecas adicionales requeridas. OpenCVPython requiere solo Numpy (además de otras dependencias, que
veremos más adelante). Pero en estos tutoriales, también usamos Matplotlib para algunos propósitos de trazado fáciles y agradables (que
me siento mucho mejor en comparación con OpenCV). Matplotlib es opcional, pero muy recomendable.
De igual forma también veremos IPython, una Terminal de Python Interactiva, que también es muy recomendable.
Instalación de OpenCVPython desde binarios prediseñados
Instale todos los paquetes con el siguiente comando en la terminal como root.
$ yum instalar numpy opencv*
Abra Python IDLE (o IPython) y escriba los siguientes códigos en la terminal de Python.
>>> importar cv2 >>>
imprimir cv2.__versión__
Si los resultados se imprimen sin errores, ¡felicidades! Ha instalado OpenCVPython con éxito.
Es bastante fácil. Pero hay un problema con esto. Los repositorios de Yum pueden no contener siempre la última versión de OpenCV. Por
ejemplo, al momento de escribir este tutorial, el repositorio de yum contiene 2.4.5 mientras que la última versión de OpenCV es 2.4.6. Con
respecto a la API de Python, la última versión siempre tendrá un soporte mucho mejor. Además, puede haber problemas con el soporte de la
cámara, la reproducción de video, etc. dependiendo de los controladores, ffmpeg, paquetes gstreamer presentes, etc.
Así que mi preferencia personal es el siguiente método, es decir, compilar desde la fuente. También en algún momento, si desea contribuir
con OpenCV, necesitará esto.
1.1. Introducción a OpenCV 15
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Instalar OpenCV desde la fuente
Compilar desde el código fuente puede parecer un poco complicado al principio, pero una vez que lo lograste, no hay nada complicado.
Primero instalaremos algunas dependencias. Algunos son obligatorios, otros son opcionales. Dependencias opcionales, puedes dejarlas si
no quieres.
Dependencias Obligatorias
Necesitamos CMake para configurar la instalación, GCC para la compilación, Pythondevel y Numpy para crear extensiones de Python, etc.
yum install cmake yum
install pythondevel numpy yum install gcc gcc
c++
A continuación, necesitamos compatibilidad con GTK para funciones de GUI, compatibilidad con cámara (libdc1394, libv4l), compatibilidad con medios (ffmpeg, gstreamer),
etc.
yum install gtk2devel yum install
libdc1394devel yum install libv4ldevel
yum install ffmpegdevel yum install
gstreamerpluginsbasedevel
Dependencias opcionales
Las dependencias anteriores son suficientes para instalar OpenCV en su máquina fedora. Pero dependiendo de sus requisitos, es posible
que necesite algunas dependencias adicionales. A continuación se proporciona una lista de dichas dependencias opcionales. Puedes
dejarlo o instalarlo, tu llamada :)
OpenCV viene con archivos de soporte para formatos de imagen como PNG, JPEG, JPEG2000, TIFF, WebP, etc. Pero puede ser un poco
antiguo. Si desea obtener las bibliotecas más recientes, puede instalar archivos de desarrollo para estos formatos.
yum install libpngdevel yum install
libjpegturbodevel yum install jasperdevel yum
install openexrdevel yum install libtiff
devel yum install libwebpdevel
Varias funciones de OpenCV están en paralelo con Threading Building Blocks (TBB) de Intel. Pero si desea habilitarlo, primero debe
instalar TBB. (Además, al configurar la instalación con CMake, no olvide pasar D WITH_TBB=ON. Más detalles a continuación).
yum instalar tbbdevel
OpenCV usa otra biblioteca Eigen para operaciones matemáticas optimizadas. Entonces, si tiene Eigen instalado en su sistema, puede
explotarlo. (Además, al configurar la instalación con CMake, no olvide pasar D WITH_EIGEN=ON.
Más detalles a continuación.)
yum instalar eigen3devel
dieciséis
Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Si desea crear documentación (Sí, puede crear una versión fuera de línea de la documentación oficial completa de OpenCV en su sistema en
HTML con función de búsqueda completa para que no necesite acceder a Internet siempre si tiene alguna pregunta, ¡y es bastante RÁPIDO!),
necesita instalar Sphinx (una herramienta de generación de documentación) y pdflatex (si desea crear una versión en PDF). (Además, mientras
configura la instalación con CMake, no olvide pasar D BUILD_DOCS=ON.
Más detalles a continuación.)
yum instalar pythonsphinx yum instalar
texlive
Descargando OpenCV
A continuación tenemos que descargar OpenCV. Puede descargar la última versión de OpenCV desde el sitio de sourceforge. Luego extraiga la
carpeta.
O puede descargar la fuente más reciente del repositorio github de OpenCV. (Si desea contribuir con OpenCV, elija esto. Siempre mantiene su
OpenCV actualizado). Para eso, primero debes instalar Git .
yum instala git git clon
https://github.com/Itseez/opencv.git
Creará una carpeta OpenCV en el directorio de inicio (o el directorio que especifique). La clonación puede tardar algún tiempo dependiendo de
su conexión a Internet.
Ahora abra una ventana de terminal y navegue hasta la carpeta OpenCV descargada. Cree una nueva carpeta de compilación y navegue hasta
ella.
construir mkdir
compilación de CD
Configuración e instalación
Ahora que hemos instalado todas las dependencias requeridas, instalemos OpenCV. La instalación debe configurarse con CMake. Especifica
qué módulos se instalarán, la ruta de instalación, qué bibliotecas adicionales se utilizarán, si la documentación y los ejemplos se compilarán, etc.
El siguiente comando se usa normalmente para la configuración (ejecutado desde la carpeta de compilación).
cmake D CMAKE_BUILD_TYPE=LIBERAR D CMAKE_INSTALL_PREFIX=/usr/local ..
Especifica que el tipo de compilación es "Modo de lanzamiento" y la ruta de instalación es /usr/local. Observe la D antes de cada opción y
.. al final. En resumen, este es el formato:
cmake [D <bandera>] [D <bandera>] ..
Puede especificar tantos indicadores como desee, pero cada indicador debe estar precedido por D.
Entonces, en este tutorial, estamos instalando OpenCV con soporte TBB y Eigen. También creamos la documentación, pero excluimos las pruebas
de rendimiento y las muestras de construcción. También deshabilitamos los módulos relacionados con GPU (dado que usamos OpenCVPython,
no necesitamos módulos relacionados con GPU. Nos ahorra algo de tiempo).
(Todos los comandos a continuación se pueden hacer en una sola instrucción cmake, pero se divide aquí para una mejor comprensión).
• Habilite la compatibilidad con TBB y Eigen:
cmake D CON_TBB=ENCENDIDO D CON_EIGEN=ENCENDIDO ..
1.1. Introducción a OpenCV 17
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
• Habilitar documentación y deshabilitar pruebas y muestras
cmake D BUILD_DOCS=ON D BUILD_TESTS=OFF D BUILD_PERF_TESTS=OFF D →BUILD_EXAMPLES=OFF ..
• Deshabilite todos los módulos relacionados con la GPU.
cmake D WITH_OPENCL=OFF D WITH_CUDA=OFF D BUILD_opencv_gpu=OFF D
→BUILD_opencv_gpuarithm=OFF D BUILD_opencv_gpubgsegm=OFF D BUILD_
→opencv_gpucodec=OFF D BUILD_opencv_gpufeatures2d=OFF D BUILD_opencv_
→gpufilters=DESACTIVADO D BUILD_opencv_gpuimgproc=DESACTIVADO D BUILD_opencv_
→gpulegacy=DESACTIVADO D BUILD_opencv_gpuoptflow=DESACTIVADO D BUILD_opencv_
→gpustereo=DESACTIVADO D BUILD_opencv_gpuwarping=DESACTIVADO ..
• Establecer la ruta de instalación y el tipo de compilación
cmake D CMAKE_BUILD_TYPE=LIBERAR D CMAKE_INSTALL_PREFIX=/usr/local ..
Cada vez que ingresa la declaración cmake, imprime la configuración resultante. En la configuración final que obtuvo, asegúrese de que los
siguientes campos estén completos (a continuación se muestran algunas partes importantes de la configuración que obtuve). Estos campos
también deben completarse adecuadamente en su sistema. De lo contrario, ha ocurrido algún problema. Por lo tanto, compruebe si ha realizado
correctamente los pasos anteriores.
Interfaz gráfica de usuario:
GTK+ 2.x: SÍ (versión 2.24.19)
Hilo: SÍ (versión 2.36.3)
E/S de vídeo: DC1394 2.x:
SÍ (versión 2.2.0)
FFMPEG: SÍ
códec: SÍ (versión 54.92.100)
formato: SÍ (versión 54.63.104)
útil: SÍ (versión 52.18.100)
swescala: SÍ (versión 2.2.100)
estilo gentoo: SÍ
GStreamer:
base: SÍ (versión 0.10.36)
video: SÍ (versión 0.10.36)
SÍ (versión 0.10.36)
aplicación: riff: SÍ (versión 0.10.36)
butilo: SÍ (versión 0.10.36)
V4L/V4L2: Usando libv4l (ver 1.0.0)
Otras bibliotecas de terceros:
Usar propio: SÍ (versión 3.1.4)
Usar TBB: SÍ (ver 4.0 interfaz 6004)
Pitón:
Intérprete: /usr/bin/python2 (versión 2.7.5) /lib/libpython2.7.so
Bibliotecas: (versión 2.7.5) /usr/lib/python2.7/sitepackages/numpy/
numpy:
→core/include (ver 1.7.1) ruta de
paquetes: lib/python2.7/paquetes del sitio
Documentación:
Documentación de compilación: SÍ
Esfinge: /usr/bin/sphinxbuild (versión 1.1.3)
18 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Compilador PdfLaTeX: /usr/bin/pdflatex
Pruebas y muestras:
Pruebas: NO
Pruebas de rendimiento: NO
Ejemplos de C/C++: NO
Muchas otras banderas y configuraciones están ahí. Se deja para usted para una mayor exploración.
Ahora construyes los archivos usando el comando make e instálalos usando el comando make install. make install debe ejecutarse como root.
hacer
son
hacer instalar
La instalación ha terminado. Todos los archivos se instalan en la carpeta /usr/local/. Pero para usarlo, su Python debería poder encontrar el módulo
OpenCV. Tienes dos opciones para eso.
1. Mueva el módulo a cualquier carpeta en Python Path : la ruta de Python se puede encontrar ingresando import sys;print sys.path en la terminal
de Python. Imprimirá muchas ubicaciones. Mueva /usr/local/lib/python2.7/sitepackages/cv2.so a cualquiera de esta carpeta. Por ejemplo,
su mv /usr/local/lib/python2.7/sitepackages/cv2.so /usr/lib/python2.7/ →sitepackages
Pero tendrás que hacer esto cada vez que instales OpenCV.
2. Agregue ''/usr/local/lib/python2.7/sitepackages'' a PYTHON_PATH: se debe hacer solo una vez. Simplemente abra ~/.bashrc y agregue la
siguiente línea, luego cierre la sesión y regrese.
exportar PYTHONPATH=$PYTHONPATH:/usr/local/lib/python2.7/sitepackages
Así finaliza la instalación de OpenCV. Abra una terminal e intente importar cv2.
Para construir la documentación, simplemente ingrese los siguientes comandos:
hacer documentos
hacer html_docs
Luego abra opencv/build/doc/_html/index.html y márquelo como favorito en el navegador.
Recursos adicionales
Ejercicios
1. Compile OpenCV desde la fuente en su máquina Fedora.
Características de la interfaz gráfica de usuario en OpenCV
• Introducción a las imágenes
1.2. Características de la interfaz gráfica de usuario en OpenCV 19
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Aprende a cargar una imagen, mostrarla y guardarla de nuevo
• Introducción a los vídeos
Aprenda a reproducir videos, capturar videos desde la cámara y escribirlos como un video
• Funciones de dibujo en OpenCV
Aprende a dibujar líneas, rectángulos, elipses, círculos, etc. con OpenCV
• Ratón como pincel
Dibuja cosas con tu mouse
• Trackbar como paleta de colores
Crear trackbar para controlar ciertos parámetros
20 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Introducción a las imágenes
Objetivos
• Aquí, aprenderá cómo leer una imagen, cómo mostrarla y cómo volver a guardarla
• Aprenderá estas funciones: cv2.imread(), cv2.imshow() , cv2.imwrite()
• Opcionalmente, aprenderá a mostrar imágenes con Matplotlib
Usando OpenCV
leer una imagen
Utilice la función cv2.imread() para leer una imagen. La imagen debe estar en el directorio de trabajo o se debe proporcionar una ruta completa de la imagen.
El segundo argumento es una bandera que especifica la forma en que se debe leer la imagen.
• cv2.IMREAD_COLOR : Carga una imagen a color. Se descuidará cualquier transparencia de la imagen. es el predeterminado
bandera.
• cv2.IMREAD_GRAYSCALE : Carga la imagen en modo de escala de grises
• cv2.IMREAD_UNCHANGED: Carga la imagen como tal, incluido el canal alfa
Nota: En lugar de estas tres banderas, simplemente puede pasar los números enteros 1, 0 o 1 respectivamente.
Vea el código a continuación:
importar numpy como np
importar cv2
# Cargar una imagen a color en escala de grises img
= cv2.imread('messi5.jpg',0)
Advertencia: incluso si la ruta de la imagen es incorrecta, no arrojará ningún error, pero imprimir img le dará Ninguno
mostrar una imagen
Utilice la función cv2.imshow() para mostrar una imagen en una ventana. La ventana se ajusta automáticamente al tamaño de la imagen.
El primer argumento es un nombre de ventana que es una cadena. El segundo argumento es nuestra imagen. Puede crear tantas ventanas como desee, pero
con diferentes nombres de ventana.
cv2.imshow('imagen',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Una captura de pantalla de la ventana se verá así (en la máquina FedoraGnome):
1.2. Características de la interfaz gráfica de usuario en OpenCV 21
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
cv2.waitKey() es una función de enlace de teclado. Su argumento es el tiempo en milisegundos. La función espera los milisegundos especificados para
cualquier evento de teclado. Si presiona cualquier tecla en ese tiempo, el programa continúa. Si se pasa 0 , espera indefinidamente una pulsación de
tecla. También se puede configurar para detectar pulsaciones de teclas específicas, como si se presiona la tecla a, etc., que discutiremos a continuación.
cv2.destroyAllWindows() simplemente destruye todas las ventanas que creamos. Si desea destruir una ventana específica, use la función
cv2.destroyWindow() donde pasa el nombre exacto de la ventana como argumento.
Nota: Existe un caso especial en el que ya puede crear una ventana y cargarle una imagen más tarde. En ese caso, puede especificar si la ventana es
redimensionable o no. Se hace con la función cv2.namedWindow(). De forma predeterminada, el indicador es cv2.WINDOW_AUTOSIZE. Pero si
especifica que el indicador sea cv2.WINDOW_NORMAL, puede cambiar el tamaño de la ventana. Será útil cuando la imagen tenga una dimensión
demasiado grande y agregue una barra de seguimiento a las ventanas.
Vea el código a continuación:
cv2.namedWindow('imagen', cv2.VENTANA_NORMAL)
cv2.imshow('imagen',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
escribir una imagen
Utilice la función cv2.imwrite() para guardar una imagen.
El primer argumento es el nombre del archivo, el segundo argumento es la imagen que desea guardar.
cv2.imwrite('messigray.png',img)
Esto guardará la imagen en formato PNG en el directorio de trabajo.
22 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Añádelo
El siguiente programa carga una imagen en escala de grises, la muestra, guarda la imagen si presiona 's' y sale, o simplemente sale sin
guardar si presiona la tecla ESC.
importar numpy como np
importar cv2
img = cv2.imread('messi5.jpg',0)
cv2.imshow('imagen',img) k =
cv2.waitKey(0) if k == 27:
# espere a que la tecla ESC salga de
cv2.destroyAllWindows() elif k ==
ord('s'): # espere a que la tecla 's' guarde y salga de cv2.imwrite('messigray.png',img)
cv2.destroyAllWindows( )
Advertencia: si está utilizando una máquina de 64 bits, deberá modificar la línea k = cv2.waitKey(0) de la siguiente manera: k =
cv2.waitKey(0) & 0xFF
Usando Matplotlib
Matplotlib es una biblioteca de trazado para Python que le brinda una amplia variedad de métodos de trazado. Los verás en próximos
artículos. Aquí, aprenderá cómo mostrar imágenes con Matplotlib. Puede hacer zoom en las imágenes, guardarlas, etc. usando Matplotlib.
importar numpy como np
importar cv2
desde matplotlib importar pyplot como plt
img = cv2.imread('messi5.jpg',0) plt.imshow(img,
cmap = 'gray', interpolation = 'bicubic') plt.xticks([]), plt.yticks([]) # para ocultar marque los
valores en los ejes X e Y plt.show()
Una captura de pantalla de la ventana se verá así:
1.2. Características de la interfaz gráfica de usuario en OpenCV 23
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Ver también:
Hay muchas opciones de trazado disponibles en Matplotlib. Consulte los documentos de Matplotlib para obtener más detalles. Algunos, los
veremos en el camino.
Advertencia: la imagen en color cargada por OpenCV está en modo BGR. Pero Matplotlib se muestra en modo RGB. Por lo tanto, las
imágenes en color no se mostrarán correctamente en Matplotlib si la imagen se lee con OpenCV. Por favor vea los ejercicios para más
detalles.
Recursos adicionales
1. Estilos y características de trazado de Matplotlib
Ejercicios
1. Hay algún problema cuando intenta cargar una imagen en color en OpenCV y mostrarla en Matplotlib. lee esto
discusión y entenderlo.
Introducción a los vídeos
24 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Meta
• Aprenda a leer videos, mostrar videos y guardar videos.
• Aprenda a capturar desde la cámara y mostrarlo.
• Aprenderá estas funciones: cv2.VideoCapture(), cv2.VideoWriter()
Capturar video desde la cámara
A menudo, tenemos que capturar la transmisión en vivo con la cámara. OpenCV proporciona una interfaz muy simple para esto. Capturemos un
video de la cámara (estoy usando la cámara web incorporada de mi computadora portátil), conviértalo en un video en escala de grises y visualícelo.
Solo una tarea simple para comenzar.
Para capturar un video, debe crear un objeto VideoCapture . Su argumento puede ser el índice del dispositivo o el nombre de un archivo de video.
El índice del dispositivo es solo el número para especificar qué cámara. Normalmente se conectará una cámara (como en mi caso). Así que
simplemente paso 0 (o 1). Puede seleccionar la segunda cámara pasando 1 y así sucesivamente. Después de eso, puede capturar cuadro por
cuadro. Pero al final, no olvides soltar la captura.
importar numpy como np
importar cv2
tapa = cv2.VideoCapture(0)
mientras (Verdadero):
# Captura cuadro por cuadro ret,
cuadro = cap.read()
# Nuestras operaciones en el cuadro vienen aquí gris =
cv2.cvtColor(cuadro, cv2.COLOR_BGR2GRAY)
# Muestra el marco resultante
cv2.imshow('frame',gray) if
cv2.waitKey(1) & 0xFF == ord('q'): break
# Cuando todo esté listo, suelte la captura cap.release()
cv2.destroyAllWindows()
cap.read() devuelve un bool (Verdadero/Falso). Si el marco se lee correctamente, será Verdadero. Entonces puede verificar el final del video al
verificar este valor de retorno.
A veces, es posible que cap no haya inicializado la captura. En ese caso, este código muestra un error. Puede comprobar si está inicializado o no
mediante el método cap.isOpened(). Si es cierto, está bien. De lo contrario, ábralo usando cap.open().
También puede acceder a algunas de las funciones de este video usando el método cap.get( propId) donde propId es un número del 0 al 18. Cada
número denota una propiedad del video (si es aplicable a ese video) y los detalles completos pueden ser visto aquí: Identificador de Propiedad.
Algunos de estos valores se pueden modificar usando cap.set(propId, value). El valor es el nuevo valor que desea.
Por ejemplo, puedo verificar el ancho y la altura del marco con cap.get(3) y cap.get(4). Me da 640x480 por defecto. Pero quiero modificarlo a
320x240. Simplemente use ret = cap.set(3,320) y ret = cap.set(4,240).
Nota: si recibe un error, asegúrese de que la cámara funcione bien con cualquier otra aplicación de cámara (como Cheese en Linux).
1.2. Características de la interfaz gráfica de usuario en OpenCV 25
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Reproducción de video desde archivo
Es lo mismo que capturar desde la cámara, simplemente cambie el índice de la cámara con el nombre del archivo de video. Además, mientras
muestra el marco, use el tiempo apropiado para cv2.waitKey(). Si es demasiado bajo, el video será muy rápido y si es demasiado alto, el video será
lento (Bueno, así es como puedes mostrar videos en cámara lenta). 25 milisegundos estarán bien en casos normales.
importar numpy como np
importar cv2
tapa = cv2.VideoCapture('vtest.avi')
while(cap.isOpened()):
derecha, cuadro = cap.read()
gris = cv2.cvtColor(marco, cv2.COLOR_BGR2GRAY)
cv2.imshow('frame',gray) if
cv2.waitKey(1) & 0xFF == ord('q'): break
cap.release()
cv2.destroyAllWindows()
Nota: Asegúrese de que estén instaladas las versiones adecuadas de ffmpeg o gstreamer. A veces, es un dolor de cabeza trabajar con Video
Capture, principalmente debido a una instalación incorrecta de ffmpeg/gstreamer.
Guardar un video
Así que capturamos un video, lo procesamos cuadro por cuadro y queremos guardar ese video. Para imágenes, es muy simple, solo use cv2.imwrite().
Aquí se requiere un poco más de trabajo.
Esta vez creamos un objeto VideoWriter . Deberíamos especificar el nombre del archivo de salida (por ejemplo: salida.avi). Luego debemos
especificar el código FourCC (detalles en el siguiente párrafo). Luego se debe pasar el número de cuadros por segundo (fps) y el tamaño del cuadro.
Y el último es la bandera isColor . Si es True, el codificador espera un marco de color; de lo contrario, funciona con un marco en escala de grises.
cuatrocc es un código de 4 bytes que se utiliza para especificar el códec de vídeo. La lista de códigos disponibles se puede encontrar en fourcc.org.
Depende de la plataforma. Los siguientes códecs funcionan bien para mí.
• En Fedora: DIVX, XVID, MJPG, X264, WMV1, WMV2. (XVID es más preferible. MJPG da como resultado un tamaño alto
video. X264 da video de tamaño muy pequeño)
• En Windows: DIVX (más por probar y agregar)
• En OSX: (No tengo acceso a OSX. ¿Alguien puede llenar esto?)
El código FourCC se pasa como cv2.VideoWriter_fourcc('M','J','P','G') o cv2.
VideoWriter_fourcc(*'MJPG) para MJPG.
Debajo de la captura de código de una cámara, voltee cada cuadro en dirección vertical y guárdelo.
importar numpy como np
importar cv2
tapa = cv2.VideoCapture(0)
# Definir el códec y crear el objeto VideoWriter
26 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
fourcc = cv2.VideoWriter_fourcc(*'XVID') out =
cv2.VideoWriter('output.avi',fourcc, 20.0, (640,480))
while(cap.isOpened()):
ret, marco = cap.read() si
ret==Verdadero:
cuadro = cv2.flip(cuadro,0)
# escribe el marco invertido.write(frame)
cv2.imshow('marco',marco) si
cv2.waitKey(1) & 0xFF == ord('q'): descanso
demás:
romper
# Liberar todo si el trabajo ha terminado cap.release()
out.release()
cv2.destroyAllWindows()
Recursos adicionales
Ejercicios
Funciones de dibujo en OpenCV
Meta
• Aprende a dibujar diferentes formas geométricas con OpenCV
• Aprenderá estas funciones: cv2.line(), cv2.circle() , cv2.rectangle(), cv2.ellipse(), cv2.putText() etc.
Código
En todas las funciones anteriores, verá algunos argumentos comunes como se indica a continuación:
• img : La imagen donde quieres dibujar las formas
• color : Color de la forma. para BGR, páselo como una tupla, por ejemplo: (255,0,0) para azul. Para la escala de grises, simplemente pase el
valor escalar.
• grosor: Grosor de la línea o círculo, etc. Si se pasa 1 para figuras cerradas como círculos, se rellenará la forma.
grosor predeterminado = 1
• lineType : tipo de línea, ya sea conectada en 8, línea suavizada, etc. Por defecto, es conectada en 8. cv2.LINE_AA proporciona una línea
suavizada que se ve muy bien para las curvas.
línea de dibujo
Para dibujar una línea, debe pasar las coordenadas de inicio y finalización de la línea. Crearemos una imagen negra y dibujaremos una línea azul sobre ella
desde la esquina superior izquierda hasta la esquina inferior derecha.
1.2. Características de la interfaz gráfica de usuario en OpenCV 27
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
importar numpy como np
importar cv2
# Crea una imagen negra img =
np.zeros((512,512,3), np.uint8)
# Dibuja una línea diagonal azul con un grosor de 5 px img = cv2.line(img,(0,0),
(511,511),(255,0,0),5)
Rectángulo de dibujo
Para dibujar un rectángulo, necesita la esquina superior izquierda y la esquina inferior derecha del rectángulo. Esta vez dibujaremos un rectángulo
verde en la esquina superior derecha de la imagen.
img = cv2.rectangle(img,(384,0),(510,128),(0,255,0),3)
círculo de dibujo
Para dibujar un círculo, necesitas las coordenadas de su centro y su radio. Dibujaremos un círculo dentro del rectángulo dibujado arriba.
img = cv2.circle(img,(447,63), 63, (0,0,255), 1)
Dibujar elipse
Para dibujar la elipse, necesitamos pasar varios argumentos. Un argumento es la ubicación central (x,y). El siguiente argumento son las
longitudes de los ejes (longitud del eje mayor, longitud del eje menor). ángulo es el ángulo de rotación de la elipse en sentido antihorario.
startAngle y endAngle indican el inicio y el final del arco de elipse medido en el sentido de las agujas del reloj desde el eje principal. es
decir, dar valores 0 y 360 da la elipse completa. Para más detalles, consulte la documentación de cv2.ellipse(). El siguiente ejemplo dibuja
una media elipse en el centro de la imagen.
imagen = cv2.elipse(imagen,(256,256),(100,50),0,0,180,255,1)
Dibujar polígono
Para dibujar un polígono, primero necesitas las coordenadas de los vértices. Convierta esos puntos en una matriz de forma ROWSx1x2
donde ROWS es el número de vértices y debe ser de tipo int32. Aquí dibujamos un pequeño polígono de cuatro vértices en color amarillo.
puntos = np.matriz([[10,5],[20,30],[70,20],[50,10]], np.int32) puntos = puntos.reformar((1,1,2) )
img = cv2.polylines(img,[pts],Verdadero,
(0,255,255))
Nota: si el tercer argumento es Falso, obtendrá polilíneas que unen todos los puntos, no una forma cerrada.
Nota: cv2.polylines() se puede usar para dibujar varias líneas. Simplemente cree una lista de todas las líneas que desea dibujar y pásela
a la función. Todas las líneas se dibujarán individualmente. Es una forma más mejor y más rápida de dibujar un grupo de
28 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
líneas que llamar a cv2.line() para cada línea.
Agregar texto a las imágenes:
Para poner textos en imágenes, necesita especificar las siguientes cosas.
• Datos de texto que desea escribir
• Coordenadas de posición donde desea colocarlo (es decir, esquina inferior izquierda donde comienzan los datos).
• Tipo de fuente (consulte los documentos de cv2.putText() para conocer las fuentes admitidas)
• Escala de fuente (especifica el tamaño de la fuente)
• cosas regulares como color, grosor, tipo de línea, etc. Para una mejor apariencia, lineType = cv2.LINE_AA es
recomendado.
Escribiremos OpenCV sobre nuestra imagen en color blanco.
fuente = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,'OpenCV',(10,500), fuente, 4,(255,255,255),2,cv2.LINE_AA)
Resultado
Así que es hora de ver el resultado final de nuestro dibujo. Como estudiaste en artículos anteriores, despliega la imagen para verla.
1.2. Características de la interfaz gráfica de usuario en OpenCV 29
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
1. Los ángulos usados en la función de elipse no son nuestros ángulos circulares. Para más detalles, visite esta discusión.
Ejercicios
1. Intente crear el logotipo de OpenCV usando las funciones de dibujo disponibles en OpenCV
Ratón como pincel
Meta
• Aprenda a manejar eventos de mouse en OpenCV
• Aprenderá estas funciones: cv2.setMouseCallback()
30 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Demostración sencilla
Aquí, creamos una aplicación simple que dibuja un círculo en una imagen cada vez que hacemos doble clic en ella.
Primero creamos una función de devolución de llamada del mouse que se ejecuta cuando ocurre un evento del mouse. El evento del mouse puede ser cualquier cosa
relacionada con el mouse, como presionar el botón izquierdo, presionar el botón izquierdo, hacer doble clic con el botón izquierdo, etc. Nos da las coordenadas (x, y) para
cada evento del mouse. Con este evento y ubicación, podemos hacer lo que queramos. Para enumerar todos los eventos disponibles disponibles, ejecute el siguiente
código en la terminal de Python:
>>> importar cv2 >>>
eventos = [i for i in dir(cv2) if 'EVENT' in i] >>> imprimir eventos
La creación de la función de devolución de llamada del mouse tiene un formato específico que es el mismo en todas partes. Sólo difiere en lo que hace la función.
Entonces, nuestra función de devolución de llamada del mouse hace una cosa, dibuja un círculo donde hacemos doble clic. Así que mira el código a continuación.
El código se explica por sí mismo a partir de los comentarios:
importar cv2
importar numpy como np
# función de devolución de llamada del
mouse def draw_circle (event, x, y, flags, param):
si evento == cv2.EVENT_LBUTTONDBLCLK:
cv2.círculo(img,(x,y),100,(255,0,0),1)
# Cree una imagen negra, una ventana y vincule la función a la ventana img = np.zeros((512,512,3), np.uint8)
cv2.namedWindow('image')
cv2.setMouseCallback('image',draw_circle)
while(1):
cv2.imshow('imagen',img) if
cv2.waitKey(20) & 0xFF == 27: romper
cv2.destroyAllWindows()
Demostración más avanzada
Ahora vamos por mucho más mejor aplicación. En esto, dibujamos rectángulos o círculos (según el modo que seleccionemos) arrastrando el mouse como lo hacemos en
la aplicación Paint. Entonces, nuestra función de devolución de llamada del mouse tiene dos partes, una para dibujar un rectángulo y otra para dibujar los círculos. Este
ejemplo específico será realmente útil para crear y comprender algunas aplicaciones interactivas como el seguimiento de objetos, la segmentación de imágenes, etc.
importar cv2
importar numpy como np
dibujo = Falso # verdadero si el mouse está presionado modo =
Verdadero # si es Verdadero, dibuja un rectángulo. Presione 'm' para cambiar a la curva ix,iy = 1,1
# función de devolución de llamada del mouse
def dibujar_circulo(evento,x,y,banderas,parámetro): global
ix,iy,dibujo,modo
si evento == cv2.EVENT_LBUTTONDOWN:
dibujo = Verdadero
1.2. Características de la interfaz gráfica de usuario en OpenCV 31
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
ix,iy = x,y
elif evento == cv2.EVENT_MOUSEMOVE: si dibujo ==
Verdadero:
si modo == Verdadero:
cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),1)
demás:
cv2.círculo(img,(x,y),5,(0,0,255),1)
evento elif == cv2.EVENT_LBUTTONUP:
dibujo = Falso si modo
== Verdadero:
cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),1) más:
cv2.círculo(img,(x,y),5,(0,0,255),1)
A continuación, tenemos que vincular esta función de devolución de llamada del mouse a la ventana de OpenCV. En el ciclo principal, debemos establecer
un enlace de teclado para la tecla 'm' para alternar entre rectángulo y círculo.
img = np.zeros((512,512,3), np.uint8) cv2.namedWindow('imagen')
cv2.setMouseCallback('imagen',draw_circle)
mientras(1):
cv2.imshow('imagen',img) k =
cv2.waitKey(1) & 0xFF si k == ord('m'):
modo = no modo
elif k == 27:
romper
cv2.destroyAllWindows()
Recursos adicionales
Ejercicios
1. En nuestro último ejemplo, dibujamos un rectángulo relleno. Modificas el código para dibujar un rectángulo vacío.
Trackbar como paleta de colores
Meta
• Aprenda a vincular la barra de seguimiento a las ventanas de OpenCV
• Aprenderá estas funciones: cv2.getTrackbarPos(), cv2.createTrackbar() etc.
Demostración de código
Aquí crearemos una aplicación simple que muestra el color que especifique. Tiene una ventana que muestra el color y tres barras de
seguimiento para especificar cada uno de los colores B, G, R. Deslizas la barra de seguimiento y, en consecuencia, cambia el color de la
ventana. De forma predeterminada, el color inicial se establecerá en negro.
32 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Para la función cv2.getTrackbarPos(), el primer argumento es el nombre de la barra de seguimiento, el segundo es el nombre de la ventana a la que está
adjunta, el tercer argumento es el valor predeterminado, el cuarto es el valor máximo y el quinto es la función de devolución de llamada que se ejecuta
cada vez que cambia el valor de la barra de seguimiento. La función de devolución de llamada siempre tiene un argumento predeterminado que es la
posición de la barra de seguimiento. En nuestro caso, la función no hace nada, así que simplemente pasamos.
Otra aplicación importante de la barra de seguimiento es usarla como botón o interruptor. OpenCV, por defecto, no tiene funcionalidad de botón. Entonces
puede usar la barra de seguimiento para obtener dicha funcionalidad. En nuestra aplicación, hemos creado un interruptor en el que la aplicación funciona
solo si el interruptor está ENCENDIDO; de lo contrario, la pantalla siempre está en negro.
importar cv2
importar numpy como np
definitivamente nada(x):
aprobar
# Crear una imagen negra, una ventana img =
np.zeros((300,512,3), np.uint8) cv2.namedWindow('imagen')
# crear barras de seguimiento para el cambio de color
cv2.createTrackbar('R','imagen',0,255,nada)
cv2.createTrackbar('G','imagen',0,255,nada)
cv2.createTrackbar('B','imagen', 0,255, nada)
# crear el interruptor para la funcionalidad ON/OFF switch = '0 :
OFF \n1 : ON' cv2.createTrackbar(switch,
'image',0,1,nothing)
while(1):
cv2.imshow('imagen',img) k =
cv2.waitKey(1) & 0xFF si k == 27:
romper
# obtener las posiciones actuales de cuatro trackbars r =
cv2.getTrackbarPos('R','image') g =
cv2.getTrackbarPos('G','image') b =
cv2.getTrackbarPos('B','image') s =
cv2.getTrackbarPos(cambiar,'imagen')
si s == 0:
img[:] = 0 más:
img[:] = [b, g, r]
cv2.destroyAllWindows()
La captura de pantalla de la aplicación se ve a continuación:
1.2. Características de la interfaz gráfica de usuario en OpenCV 33
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Ejercicios
1. Cree una aplicación Paint con colores ajustables y radio de pincel usando las barras de seguimiento. Para el dibujo, consulte el anterior
Tutorial sobre el manejo del ratón.
Operaciones principales
• Operaciones básicas en imágenes
Aprenda a leer y editar valores de píxeles, trabajar con ROI de imágenes y otras operaciones
básicas.
• Operaciones aritméticas en imágenes
34 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Realizar operaciones aritméticas en imágenes.
• Técnicas de Medición y Mejora del Desempeño
Conseguir una solución es importante. Pero conseguirlo de la manera más rápida es más
importante. Aprende a verificar la velocidad de tu código, optimizar el código, etc.
• Herramientas Matemáticas en OpenCV
Aprenda algunas de las herramientas matemáticas proporcionadas por OpenCV como PCA, SVD
etc.
1.3. Operaciones principales 35
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Operaciones básicas en imágenes
Meta
Aprender a:
• Acceder a valores de píxeles y modificarlos
• Acceder a las propiedades de la imagen
• Configuración de la región de la imagen (ROI)
• División y fusión de imágenes
Casi todas las operaciones en esta sección están relacionadas principalmente con Numpy en lugar de OpenCV. Se requiere un buen conocimiento
de Numpy para escribir código mejor optimizado con OpenCV.
(Los ejemplos se mostrarán en la terminal de Python, ya que la mayoría de ellos son solo códigos de una sola línea)
Acceso y modificación de valores de píxeles
Primero carguemos una imagen en color:
>>> importar cv2 >>>
importar numpy como np
>>> img = cv2.imread('messi5.jpg')
Puede acceder a un valor de píxel por sus coordenadas de fila y columna. Para la imagen BGR, devuelve una matriz de valores azul, verde y rojo. Para la imagen
en escala de grises, solo se devuelve la intensidad correspondiente.
>>> píxeles = imagen [100,100]
>>> imprimir
píxeles [157 166 200]
# accediendo solo al píxel azul >>> blue =
img[100,100,0] >>> print blue 157
Puede modificar los valores de píxel de la misma manera.
>>> imagen[100,100] = [255,255,255] >>> imprimir
imagen[100,100] [255 255 255]
Advertencia: Numpy es una biblioteca optimizada para cálculos de matrices rápidos. Por lo tanto, simplemente acceder a todos y cada uno de los valores de
píxel y modificarlos será muy lento y no se recomienda.
Nota: el método mencionado anteriormente se usa normalmente para seleccionar una región de matriz, digamos las primeras 5 filas y las últimas 3 columnas así.
Para el acceso de píxeles individuales, los métodos de matriz Numpy, array.item() y array.itemset() se consideran mejores. Pero siempre devuelve un escalar.
Entonces, si desea acceder a todos los valores B, G, R, debe llamar a array.item() por separado para todos.
Mejor método de acceso y edición de píxeles:
36 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
# accediendo al valor ROJO >>>
img.item(10,10,2) 59
# modificando el valor ROJO
>>> img.itemset((10,10,2),100) >>>
img.item(10,10,2)
100
Acceso a las propiedades de la imagen
Las propiedades de la imagen incluyen el número de filas, columnas y canales, el tipo de datos de la imagen, el número de píxeles, etc.
Se accede a la forma de la imagen mediante img.shape. Devuelve una tupla de número de filas, columnas y canales (si la imagen es color):
>>> imprimir img.forma (342,
548, 3)
Nota: si la imagen está en escala de grises, la tupla devuelta contiene solo un número de filas y columnas. Por lo tanto, es un buen método para verificar si la
imagen cargada es una imagen en escala de grises o en color.
Se accede al número total de píxeles mediante img.size:
>>> imprimir img.tamaño
562248
El tipo de datos de la imagen se obtiene mediante img.dtype:
>>> imprimir img.dtype uint8
Nota: img.dtype es muy importante durante la depuración porque una gran cantidad de errores en el código OpenCVPython son causados por tipos de datos
no válidos.
Retorno de la inversión de la imagen
A veces, tendrás que jugar con cierta región de imágenes. Para la detección de ojos en imágenes, primero realice la detección de rostros sobre la imagen hasta
que se encuentre el rostro, luego busque ojos dentro de la región del rostro. Este enfoque mejora la precisión (porque los ojos siempre están en las caras :D) y
el rendimiento (porque buscamos un área pequeña).
El ROI se obtiene nuevamente utilizando la indexación de Numpy. Aquí estoy seleccionando la pelota y copiándola en otra región de la imagen:
>>> pelota = img[280:340, 330:390] >>>
img[273:333, 100:160] = pelota
Compruebe los resultados a continuación:
1.3. Operaciones principales 37
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Dividir y fusionar canales de imagen
Los canales B, G, R de una imagen se pueden dividir en sus planos individuales cuando sea necesario. Luego, los canales individuales
se pueden volver a fusionar para formar una imagen BGR nuevamente. Esto puede ser realizado por:
>>> b,g,r = cv2.dividir(img) >>> img =
cv2.merge((b,g,r))
>>> b = imagen[:,:,0]
Supongamos que quiere hacer que todos los píxeles rojos sean cero, no necesita dividirlos así y ponerlos a cero. Simplemente puede
usar la indexación de Numpy, que es más rápida.
>>> imagen[:,:,2] = 0
Advertencia: cv2.split() es una operación costosa (en términos de tiempo), así que utilícela solo si es necesario. La indexación numpy
es mucho más eficiente y debe usarse si es posible.
Creación de bordes para imágenes (relleno)
Si desea crear un borde alrededor de la imagen, algo así como un marco de fotos, puede usar la función cv2.copyMakeBorder() . Pero
tiene más aplicaciones para operaciones de convolución, relleno de ceros, etc. Esta función toma los siguientes argumentos:
• src imagen de entrada
• arriba, abajo, izquierda, derecha : ancho del borde en número de píxeles en las direcciones correspondientes
38 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
• borderType: indicador que define qué tipo de borde se agregará. Puede ser de los siguientes tipos:
– cv2.BORDER_CONSTANT : agrega un borde de color constante. El valor debe ser dado como sigue
argumento.
– cv2.BORDER_REFLECT El borde será un reflejo especular de los elementos del borde, así: fed cba|abcdefgh|hgfedcb
– cv2.BORDER_REFLECT_101 o cv2.BORDER_DEFAULT Igual que el anterior, pero con un ligero
cambiar, así: gfedcb|abcdefgh|gfedcba
– cv2.BORDER_REPLICATE El último elemento se replica completamente, así:
aaaaaaa|abcdefgh|hhhhhh
– cv2.BORDER_WRAP No puedo explicarlo, se verá así: cdefgh|abcdefgh|abcdefg
• valor : color del borde si el tipo de borde es cv2.BORDER_CONSTANT
A continuación se muestra un código de muestra que demuestra todos estos tipos de borde para una mejor comprensión:
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
AZUL = [255,0,0]
img1 = cv2.imread('opencv_logo.png')
replicate = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REPLICATE) reflect =
cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT) reflect101 = cv2.copyMakeBorder(img1,10,
10,10,10,cv2.BORDER_REFLECT_101) wrap = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_WRAP)
constante= cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_CONSTANT, valor=AZUL)
plt.subplot(231),plt.imshow(img1,'gris'),plt.title('ORIGINAL') plt.subplot
(232),plt.imshow(replicar,'gris'),plt.title('REPLICAR ') plt.subplot(233),plt.imshow(reflejar,'gris'),plt.title('REFLECT')
plt.subplot (234),plt.imshow(reflect101,'gris'),plt.title( 'REFLECT_101')
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP') plt.subplot (236),plt.imshow(constante,'gray'),plt.
titulo('CONSTANTE')
plt.mostrar()
Vea el resultado a continuación. (La imagen se muestra con matplotlib. Entonces, los planos ROJO y AZUL se intercambiarán):
1.3. Operaciones principales 39
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
Ejercicios
Operaciones aritméticas en imágenes
Meta
• Aprenda varias operaciones aritméticas en imágenes como sumas, restas, operaciones bit a bit, etc.
• Aprenderá estas funciones: cv2.add(), cv2.addWeighted() etc.
Adición de imagen
Puede agregar dos imágenes mediante la función OpenCV, cv2.add() o simplemente mediante una operación numérica, res = img1 + img2.
Ambas imágenes deben tener la misma profundidad y tipo, o la segunda imagen puede ser simplemente un valor escalar.
Nota: Hay una diferencia entre la adición de OpenCV y la adición de Numpy. La adición de OpenCV es una operación saturada, mientras
que la adición de Numpy es una operación de módulo.
40 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Por ejemplo, considere la siguiente muestra:
>>> x = np.uint8([250]) >>> y =
np.uint8([10])
>>> imprimir cv2.add(x,y) # 250+10 = 260 => 255 [[255]]
>>> imprime x+y [4] # 250+10 = 260 % 256 = 4
Será más visible cuando agregue dos imágenes. La función OpenCV proporcionará un mejor resultado. Así que siempre es mejor ceñirse a
las funciones de OpenCV.
Fusión de imágenes
Esto también es una adición de imágenes, pero se les da diferentes pesos a las imágenes para que den una sensación de fusión o
transparencia. Las imágenes se agregan según la siguiente ecuación:
( ) = (1 − ) 0( ) + 1( )
Al variar de 0 → 1, puede realizar una transición genial entre una imagen y otra.
Aquí tomé dos imágenes para mezclarlas. La primera imagen tiene un peso de 0,7 y la segunda imagen tiene un peso de 0,3.
cv2.addWeighted() aplica la siguiente ecuación en la imagen.
Aquí se toma como cero.
img1 = cv2.imread('ml.png') img2 =
cv2.imread('opencv_logo.jpg')
dst = cv2.addWeighted(img1,0.7,img2,0.3,0)
cv2.imshow('dst',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
Compruebe el resultado a continuación:
1.3. Operaciones principales 41
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Operaciones bit a bit
Esto incluye operaciones bit a bit AND, OR, NOT y XOR. Serán muy útiles al extraer cualquier parte de la imagen (como
veremos en los próximos capítulos), definir y trabajar con ROI no rectangular, etc. A continuación, veremos un ejemplo
sobre cómo cambiar una región particular de una imagen.
Quiero poner el logotipo de OpenCV encima de una imagen. Si agrego dos imágenes, cambiará de color. Si lo difumino, obtengo un efecto transparente.
Pero quiero que sea opaco. Si fuera una región rectangular, podría usar el ROI como lo hicimos en el último capítulo. Pero el logotipo de OpenCV no
tiene forma rectangular. Entonces puede hacerlo con operaciones bit a bit como se muestra a continuación:
# Cargar dos imágenes
img1 = cv2.imread('messi5.jpg') img2 =
cv2.imread('opencv_logo.png')
# Quiero poner el logotipo en la esquina superior izquierda, así que creo un ROI
filas,columnas,canales = img2.shape roi =
img1[0:filas, 0:columnas]
# Ahora crea una máscara de logo y crea también su máscara inversa img2gray =
cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY) ret, mask =
cv2.threshold(img2gray, 10, 255, cv2.THRESH_BINARY) mask_inv = cv2.bitwise_not(mask )
# Ahora oscurezca el área del logotipo en ROI img1_bg =
cv2.bitwise_and(roi,roi,mask = mask_inv)
# Tomar solo la región del logotipo de la imagen del logotipo.
img2_fg = cv2.bitwise_and(img2,img2,máscara = máscara)
# Poner logo en ROI y modificar la imagen principal dst =
cv2.add(img1_bg,img2_fg) img1[0:rows,
0:cols ] = dst
cv2.imshow('res',img1)
cv2.waitKey(0)
cv2.destroyAllWindows()
Vea el resultado a continuación. La imagen de la izquierda muestra la máscara que creamos. La imagen de la derecha muestra el resultado final. Para
una mayor comprensión, muestre todas las imágenes intermedias en el código anterior, especialmente img1_bg e img2_fg.
42 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
Ejercicios
1. Cree una presentación de diapositivas de imágenes en una carpeta con una transición suave entre imágenes usando cv2.addWeighted
función
Técnicas de Medición y Mejora del Desempeño
Meta
En el procesamiento de imágenes, dado que se trata de una gran cantidad de operaciones por segundo, es obligatorio que su código no solo
proporcione la solución correcta, sino también de la manera más rápida. Entonces, en este capítulo, aprenderá
• Para medir el rendimiento de su código.
• Algunos consejos para mejorar el rendimiento de su código.
• Verá estas funciones: cv2.getTickCount, cv2.getTickFrequency , etc.
Además de OpenCV, Python también proporciona un tiempo de módulo que es útil para medir el tiempo de ejecución. Otro perfil de módulo
ayuda a obtener un informe detallado sobre el código, como cuánto tiempo tomó cada función en el código, cuántas veces se llamó a la
función, etc. Pero, si está utilizando IPython, todas estas funciones están integradas en un fácil de usar manera. Veremos algunos importantes
y, para obtener más detalles, consulte los enlaces en la sección Recursos adicionales .
Medición del rendimiento con OpenCV
La función cv2.getTickCount devuelve el número de ciclos de reloj después de un evento de referencia (como el momento en que se encendió
la máquina) hasta el momento en que se llama a esta función. Entonces, si lo llama antes y después de la ejecución de la función, obtiene la
cantidad de ciclos de reloj utilizados para ejecutar una función.
La función cv2.getTickFrequency devuelve la frecuencia de los ciclos de reloj, o el número de ciclos de reloj por segundo. Entonces, para
encontrar el tiempo de ejecución en segundos, puede hacer lo siguiente:
e1 = cv2.getTickCount() # la
ejecución de su código e2 =
cv2.getTickCount() time = (e2 e1)/
cv2.getTickFrequency()
Lo demostraremos con el siguiente ejemplo. El siguiente ejemplo aplica el filtrado mediano con un núcleo de tamaño impar que va de 5 a 49.
(No se preocupe por cómo se verá el resultado, ese no es nuestro objetivo):
img1 = cv2.imread('messi5.jpg')
e1 = cv2.getTickCount() for i in
xrange(5,49,2): img1 =
cv2.medianBlur(img1,i) e2 = cv2.getTickCount()
t = (e2 e1)/cv2.getTickFrequency()
print t
# El resultado que obtuve es 0.521107655 segundos
1.3. Operaciones principales 43
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Nota: Puedes hacer lo mismo con el módulo de tiempo. En lugar de cv2.getTickCount, use la función time.time().
Luego toma la diferencia de dos veces.
Optimización por defecto en OpenCV
Muchas de las funciones de OpenCV están optimizadas usando SSE2, AVX, etc. También contiene código no optimizado. Entonces, si
nuestro sistema admite estas características, deberíamos explotarlas (casi todos los procesadores modernos las admiten). Está habilitado
por defecto durante la compilación. Entonces, OpenCV ejecuta el código optimizado si está habilitado; de lo contrario, ejecuta el código no optimizado.
Puede usar cv2.useOptimized() para verificar si está habilitado/deshabilitado y cv2.setUseOptimized() para habilitarlo/deshabilitarlo.
Veamos un ejemplo sencillo.
# verificar si la optimización está habilitada En [5]:
cv2.useOptimized()
Salida[5]: Verdadero
En [6]: %timeit res = cv2.medianBlur(img,49) 10 bucles, lo mejor de 3:
34,9 ms por bucle
# Deshabilitarlo
En [7]: cv2.setUseOptimized(Falso)
En [8]: cv2.useOptimized()
Salida[8]: Falso
En [9]: %timeit res = cv2.medianBlur(img,49) 10 bucles, lo mejor de 3:
64,1 ms por bucle
Verá, el filtrado medio optimizado es ~2 veces más rápido que la versión no optimizada. Si verifica su fuente, puede ver que el filtrado medio está
optimizado para SIMD. Entonces puede usar esto para habilitar la optimización en la parte superior de su código (recuerde que está habilitado de
manera predeterminada).
Medición del rendimiento en IPython
A veces, es posible que necesite comparar el rendimiento de dos operaciones similares. IPython le da un comando mágico %timeit para
realizar esto. Ejecuta el código varias veces para obtener resultados más precisos. Una vez más, son adecuados para medir códigos de una
sola línea.
Por ejemplo, ¿sabes cuál de las siguientes operaciones de suma es mejor, x = 5; y = x**2, x = 5; y = x*x, x = np.uint8([5]); y = x*x o y =
np.square(x) ? Lo encontraremos con %timeit en el shell de IPython.
En [10]: x = 5
En [11]: %timeit y=x**2 10000000
bucles, lo mejor de 3: 73 ns por bucle
En [12]: %timeit y=x*x 10000000
bucles, lo mejor de 3: 58,3 ns por bucle
En [15]: z = np.uint8([5])
En [17]: %timeit y=z*z 1000000
bucles, lo mejor de 3: 1,25 us por bucle
44 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
En [19]: %timeit y=np.square(z) 1000000 bucles,
lo mejor de 3: 1,16 us por bucle
Puedes ver que, x = 5; y = x*x es más rápido y es alrededor de 20 veces más rápido en comparación con Numpy. Si también considera la creación de
matrices, puede alcanzar hasta 100 veces más rápido. ¿Guay, verdad? (Los desarrolladores de Numpy están trabajando en este problema)
Nota: Las operaciones escalares de Python son más rápidas que las operaciones escalares de Numpy. Entonces, para operaciones que incluyen uno o
dos elementos, Python escalar es mejor que las matrices Numpy. Numpy se aprovecha cuando el tamaño de la matriz es un poco más grande.
Probaremos un ejemplo más. Esta vez, compararemos el rendimiento de cv2.countNonZero() y np.count_nonzero() para la misma imagen.
En [35]: %timeit z = cv2.countNonZero(img) 100000 bucles, lo
mejor de 3: 15,8 us por bucle
En [36]: %timeit z = np.count_nonzero(img) 1000 bucles, lo mejor
de 3: 370 us por bucle
Mira, la función OpenCV es casi 25 veces más rápida que la función Numpy.
Nota: Normalmente, las funciones de OpenCV son más rápidas que las funciones de Numpy. Entonces, para la misma operación, se prefieren las funciones
de OpenCV. Pero puede haber excepciones, especialmente cuando Numpy trabaja con vistas en lugar de copias.
Más comandos mágicos de IPython
Hay varios otros comandos mágicos para medir el rendimiento, perfilado, perfilado de línea, medición de memoria, etc. Todos ellos están bien
documentados. Por lo tanto, aquí solo se proporcionan enlaces a esos documentos. Se recomienda a los lectores interesados que los prueben.
Técnicas de optimización del rendimiento
Existen varias técnicas y métodos de codificación para aprovechar al máximo el rendimiento de Python y Numpy. Aquí solo se mencionan los relevantes y
se brindan enlaces a fuentes importantes. Lo principal a tener en cuenta aquí es que, primero intente implementar el algoritmo de una manera simple. Una
vez que esté funcionando, perfilarlo, encontrar los cuellos de botella y optimizarlos.
1. Evite usar bucles en Python en la medida de lo posible, especialmente bucles dobles/triples, etc. Son inherentemente lentos.
2. Vectorice el algoritmo/código en la mayor medida posible porque Numpy y OpenCV están optimizados para
operaciones vectoriales.
3. Explotar la coherencia de caché.
4. Nunca haga copias de la matriz a menos que sea necesario. Trate de usar vistas en su lugar. La copia de matriz es una operación costosa.
Incluso después de realizar todas estas operaciones, si su código aún es lento o si el uso de bucles grandes es inevitable, use bibliotecas adicionales como
Cython para hacerlo más rápido.
Recursos adicionales
1. Técnicas de optimización de Python
2. Notas de clase de Scipy Numpy avanzado
1.3. Operaciones principales 45
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
3. Temporización y creación de perfiles en IPython
Ejercicios
Herramientas Matemáticas en OpenCV
Procesamiento de imágenes en OpenCV
• Cambio de espacios de color
Aprende a cambiar imágenes entre diferentes espacios de color.
Además, aprenda a rastrear un objeto de color en un video.
• Transformaciones Geométricas de Imágenes
Aprenda a aplicar diferentes transformaciones geométricas a imágenes como rotación, traslación,
etc.
• Umbral de imagen
Aprenda a convertir imágenes en imágenes binarias mediante umbralización global, umbralización
adaptativa, binarización de Otsu, etc.
• Suavizado de imágenes
Aprenda a desenfocar las imágenes, filtrar las imágenes con núcleos personalizados, etc.
• Transformaciones morfológicas
46 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Aprenda sobre transformaciones morfológicas como erosión, dilatación, apertura, cierre, etc.
• Gradientes de imagen
Aprenda a encontrar gradientes de imagen, bordes, etc.
• Detección de borde astuto
Aprenda a encontrar bordes con Canny Edge Detection
• Pirámides de imágenes
Aprenda sobre las pirámides de imágenes y cómo usarlas para la combinación de imágenes
• Contornos en OpenCV
Todo sobre Contornos en OpenCV
• Histogramas en OpenCV
1.4. Procesamiento de imágenes en OpenCV 47
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Todo sobre histogramas en OpenCV
• Transformaciones de imagen en OpenCV
Conozca diferentes transformaciones de imagen en OpenCV como la transformada de Fourier, la
transformada de coseno, etc.
• Coincidencia de plantillas
Aprenda a buscar un objeto en una imagen mediante la coincidencia de plantillas
• Transformación de la línea de Hough
Aprende a detectar líneas en una imagen
• Transformación del círculo de Hough
Aprende a detectar círculos en una imagen
• Segmentación de imágenes con algoritmo Watershed
48 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Aprende a segmentar imágenes con segmentación de cuencas hidrográficas
• Extracción interactiva en primer plano mediante el algoritmo GrabCut
Aprenda a extraer el primer plano con el algoritmo GrabCut
1.4. Procesamiento de imágenes en OpenCV 49
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Cambio de espacios de color
Meta
• En este tutorial, aprenderá a convertir imágenes de un espacio de color a otro, como BGR ↔ Gray, BGR
↔ VHS, etc.
• Además de eso, crearemos una aplicación que extraiga un objeto coloreado en un video
• Aprenderá las siguientes funciones: cv2.cvtColor(), cv2.inRange() etc.
Cambiar el espacio de color
Hay más de 150 métodos de conversión de espacio de color disponibles en OpenCV. Pero solo veremos dos que son los más utilizados, BGR ↔
Gray y BGR ↔ HSV.
Para la conversión de color, usamos la función cv2.cvtColor(input_image, flag) donde flag determina el tipo de conversión.
Para la conversión de BGR → Gray usamos las banderas cv2.COLOR_BGR2GRAY. De manera similar, para BGR → HSV, usamos la bandera
cv2.COLOR_BGR2HSV. Para obtener otras banderas, simplemente ejecute los siguientes comandos en su terminal de Python:
>>> import cv2 >>>
flags = [i for i in dir(cv2) if i.startswith('COLOR_')] >>> print flags
Nota: Para HSV, el rango de tono es [0,179], el rango de saturación es [0,255] y el rango de valor es [0,255]. Diferentes softwares usan diferentes
escalas. Entonces, si está comparando los valores de OpenCV con ellos, debe normalizar estos rangos.
Seguimiento de objetos
Ahora que sabemos cómo convertir una imagen BGR a HSV, podemos usar esto para extraer un objeto coloreado. En HSV, es más fácil
representar un color que el espacio de color RGB. En nuestra aplicación, intentaremos extraer un objeto de color azul. Así que aquí está el método:
• Toma cada cuadro del video
• Convertir de espacio de color BGR a HSV
• Umbramos la imagen HSV para un rango de color azul
• Ahora extraiga el objeto azul solo, podemos hacer lo que queramos en esa imagen.
A continuación se muestra el código que se comenta en detalle:
importar cv2
importar numpy como np
tapa = cv2.VideoCapture(0)
mientras(1):
# Toma cada cuadro
_, marco = cap.read()
# Convertir BGR a HSV
50 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
hsv = cv2.cvtColor(marco, cv2.COLOR_BGR2HSV)
# definir rango de color azul en HSV lower_blue =
np.array([110,50,50]) upper_blue = np.array([130,255,255])
# Umbral de la imagen HSV para obtener solo máscara de colores azules =
cv2.inRange(hsv, lower_blue, upper_blue)
# BitwiseAND mask e imagen original res =
cv2.bitwise_and(frame,frame, mask= mask)
cv2.imshow('marco',marco)
cv2.imshow('máscara',máscara)
cv2.imshow('res',res) k =
cv2.waitKey(5) & 0xFF si k == 27:
romper
cv2.destroyAllWindows()
La imagen de abajo muestra el seguimiento del objeto azul:
Nota: Hay algunos ruidos en la imagen. Veremos cómo eliminarlos en capítulos posteriores.
Nota: Este es el método más simple en el seguimiento de objetos. Una vez que aprenda las funciones de los contornos, puede hacer muchas
cosas como encontrar el centroide de este objeto y usarlo para rastrear el objeto, dibujar diagramas simplemente moviendo la mano frente a la
cámara y muchas otras cosas divertidas.
¿Cómo encontrar valores HSV para rastrear?
Esta es una pregunta común que se encuentra en stackoverflow.com. Es muy simple y puedes usar la misma función, cv2.cvtColor(). En lugar de
pasar una imagen, solo pasa los valores BGR que desea. Por ejemplo, para encontrar el
1.4. Procesamiento de imágenes en OpenCV 51
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Valor HSV de Green, intente seguir los comandos en la terminal de Python:
>>> verde = np.uint8([[[0,255,0 ]]]) >>> hsv_green =
cv2.cvtColor(green,cv2.COLOR_BGR2HSV) >>> print hsv_green [[[ 60 255 255]]]
Ahora toma [H10, 100,100] y [H+10, 255, 255] como límite inferior y límite superior respectivamente. Además de este método, puede
usar cualquier herramienta de edición de imágenes como GIMP o cualquier convertidor en línea para encontrar estos valores, pero no
olvide ajustar los rangos de HSV.
Recursos adicionales
Ejercicios
1. Intente encontrar una manera de extraer más de un objeto de color, por ejemplo, extraer objetos rojos, azules y verdes simultáneamente.
Umbral de imagen
Meta
• En este tutorial, aprenderá umbralización simple, umbralización adaptativa, umbralización de Otsu, etc.
• Aprenderá estas funciones: cv2.threshold, cv2.adaptiveThreshold , etc.
Umbral simple
Aquí, el asunto es sencillo. Si el valor del píxel es mayor que un valor de umbral, se le asigna un valor (puede ser blanco), de lo contrario,
se le asigna otro valor (puede ser negro). La función utilizada es cv2.threshold. El primer argumento es la imagen de origen, que debería
ser una imagen en escala de grises. El segundo argumento es el valor de umbral que se utiliza para clasificar los valores de píxel. El
tercer argumento es maxVal, que representa el valor que se dará si el valor del píxel es mayor (a veces menor que) el valor del umbral.
OpenCV proporciona diferentes estilos de umbralización y se decide por el cuarto parámetro de la función. Los diferentes tipos son:
• cv2.THRESH_BINARIO
• cv2.THRESH_BINARY_INV
• cv2.THRESH_TRUNC
• cv2.THRESH_TOZERO
• cv2.THRESH_TOZERO_INV
La documentación explica claramente para qué sirve cada tipo. Por favor, consulte la documentación.
Se obtienen dos salidas. El primero es un retval que se explicará más adelante. La segunda salida es nuestra imagen con umbral.
Código:
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
img = cv2.imread('gradient.png',0) ret,thresh1 =
cv2.threshold(img,127,255,cv2.THRESH_BINARY) ret,thresh2 =
cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)
52 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
ret,thresh3 = cv2.threshold(img,127,255,cv2.THRESH_TRUNC) ret,thresh4 =
cv2.threshold(img,127,255,cv2.THRESH_TOZERO) ret,thresh5 =
cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)
títulos = ['Imagen original ','BINARIO','BINARIO_INV','TRUNC','TOZERO','TOZERO_INV'] imágenes = [img, umbral1, umbral2,
umbral3, umbral4, umbral5]
para i en xrange(6):
plt.subplot(2,3,i+1),plt.imshow(imágenes[i],'gris') plt.title(títulos[i])
plt.xticks([]),plt.yticks([])
plt.mostrar()
Nota: para trazar varias imágenes, hemos utilizado la función plt.subplot(). Consulte los documentos de Matplotlib para obtener más detalles.
El resultado se da a continuación:
Umbral adaptativo
En la sección anterior, usamos un valor global como valor de umbral. Pero puede que no sea bueno en todas las condiciones en las que la
imagen tiene diferentes condiciones de iluminación en diferentes áreas. En ese caso, optamos por el umbral adaptativo. En esto, el algoritmo
calcula el umbral para una pequeña región de la imagen. Entonces obtenemos diferentes umbrales para diferentes regiones de la misma
imagen y nos da mejores resultados para imágenes con iluminación variable.
Tiene tres parámetros de entrada 'especiales' y solo un argumento de salida.
Método adaptativo: decide cómo se calcula el valor de umbral.
1.4. Procesamiento de imágenes en OpenCV 53
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
• cv2.ADAPTIVE_THRESH_MEAN_C: el valor del umbral es la media del área vecina.
• cv2.ADAPTIVE_THRESH_GAUSSIAN_C: el valor del umbral es la suma ponderada del valor del vecindario
ues donde los pesos son una ventana gaussiana.
Tamaño de bloque : decide el tamaño del área del vecindario.
C Es solo una constante que se resta de la media o media ponderada calculada.
El siguiente fragmento de código compara el umbral global y el umbral adaptativo para una imagen con iluminación variable:
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
img = cv2.imread('dave.jpg',0) img =
cv2.medianBlur(img,5)
ret,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY) th2 =
cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,\
cv2.UMBRAL_BINARIO,11,2) th3 =
cv2.umbral adaptativo(img,255,cv2.UMBRAL_ADAPTADOR_GAUSSIAN_C,\ cv2.UMBRAL_BINARIO,11,2)
titles = ['Imagen original', 'Umbral global (v = 127)',
'Umbral medio adaptativo', 'Umbral gaussiano adaptativo']
imágenes = [img, th1, th2, th3]
para i en xrange(4):
plt.subplot(2,2,i+1),plt.imshow(imágenes[i],'gris') plt.title(títulos[i])
plt.xticks([]),plt.yticks([])
plt.mostrar()
Resultado :
54 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Binarización de Otsu
En la primera sección, les dije que hay un segundo parámetro retVal. Su uso viene cuando vamos por la Binarización de Otsu.
¿Así que qué es lo?
En el umbral global, usamos un valor arbitrario para el valor del umbral, ¿verdad? Entonces, ¿cómo podemos saber que un valor que
seleccionamos es bueno o no? La respuesta es, método de prueba y error. Pero considere una imagen bimodal (en palabras simples, la
imagen bimodal es una imagen cuyo histograma tiene dos picos). Para esa imagen, podemos tomar aproximadamente un valor en el
medio de esos picos como valor de umbral, ¿verdad? Eso es lo que hace la binarización de Otsu. Entonces, en palabras simples, calcula
automáticamente un valor de umbral del histograma de la imagen para una imagen bimodal. (Para imágenes que no son bimodales, la
binarización no será precisa).
Para esto, se utiliza nuestra función cv2.threshold(), pero se pasa un indicador adicional, cv2.THRESH_OTSU. Para el valor de umbral,
simplemente pase cero. Luego, el algoritmo encuentra el valor de umbral óptimo y lo devuelve como la segunda salida, retVal. Si no se
usa el umbral de Otsu, retVal es el mismo que el valor de umbral que usó.
1.4. Procesamiento de imágenes en OpenCV 55
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Mira el siguiente ejemplo. La imagen de entrada es una imagen ruidosa. En el primer caso, apliqué un umbral global para un valor
de 127. En el segundo caso, apliqué el umbral de Otsu directamente. En el tercer caso, filtré la imagen con un núcleo gaussiano de
5x5 para eliminar el ruido y luego apliqué el umbral de Otsu. Vea cómo el filtrado de ruido mejora el resultado.
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
img = cv2.imread('ruidoso2.png',0)
# umbral global ret1,th1 =
cv2.threshold(img,127,255,cv2.THRESH_BINARY)
# Umbral de Otsu ret2,th2 =
cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
# Umbral de Otsu después del desenfoque de filtrado gaussiano =
cv2.GaussianBlur(img,(5,5),0) ret3,th3 =
cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
# graficar todas las imágenes y sus histogramas imágenes = [img,
0, th1, img, 0, th2, blur, 0, th3]
titles = ['Imagen ruidosa original ','Histograma',' Umbral global (v=127)',
'Imagen ruidosa original ', 'Histograma', " Umbral de Otsu",
'Imagen filtrada gaussiana ', 'Histograma', " Umbral de Otsu"]
para i en xrange(3):
plt.subplot(3,3,i*3+1),plt.imshow(imágenes[i*3],'gris') plt.title(títulos[i*3]), plt.xticks([]),
plt.yticks([]) plt.subplot(3,3,i*3+2),plt.hist(imágenes[i*3].ravel(),256) plt.title(títulos[i*3+1 ]),
plt.xticks([]), plt.yticks([]) plt.subplot(3,3,i*3+3),plt.imshow(imágenes[i*3+2],'gris')
plt.title(títulos[i*3+2]), plt.xticks([]), plt.yticks([])
plt.mostrar()
Resultado :
56 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
¿Cómo funciona la binarización de Otsu?
Esta sección demuestra una implementación de Python de la binarización de Otsu para mostrar cómo funciona realmente. Si no
estás interesado, puedes saltarte esto.
Dado que estamos trabajando con imágenes bimodales, el algoritmo de Otsu intenta encontrar un valor de umbral (t) que minimice
la varianza dentro de la clase ponderada dada por la relación:
2 2 2
( ) = 1( ) 1 ( ) + 2 ( ) 2 ( )
dónde
( ) ( )
1( ) = ∑ & 2( ) = ∑
=1 1( ) = +1
2( )
2 ( ) 2 ( )
1 ( ) = ∑ [ − 1( )]2 & 2 ( ) = ∑ [ − 1( )]2
=1 1( ) = +1
2( )
En realidad, encuentra un valor de t que se encuentra entre dos picos, de modo que las variaciones de ambas clases son mínimas. Se puede
implementar simplemente en Python de la siguiente manera:
img = cv2.imread('ruidoso2.png',0) desenfoque =
cv2.GaussianBlur(img,(5,5),0)
# encuentra el histograma_normalizado y su función de distribución acumulativa
1.4. Procesamiento de imágenes en OpenCV 57
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
hist = cv2.calcHist([desenfoque],[0],Ninguno,[256],[0,256]) hist_norm = hist.ravel()/
hist.max()
Q = hist_norm.cumsum()
contenedores = np.arange(256)
fn_min = np.inf umbral =
1
para i en xrange(1,256):
p1,p2 = np.hsplit(hist_norm,[i]) # probabilidades q1,q2 = Q[i],Q[255]Q[i] # suma
acumulada de clases b1,b2 = np.hsplit(bins,[ i]) # pesos
# encontrar medias y varianzas m1,m2 =
np.sum(p1*b1)/q1, np.sum(p2*b2)/q2 v1,v2 = np.sum(((b1m1)**2)*p1 )/
q1,np.suma(((b2m2)**2)*p2)/q2
# calcula la función de minimización
fn = v1*q1 + v2*q2 si fn <
fn_min: fn_min = fn
umbral = i
# encuentre el valor de umbral de otsu con la función OpenCV ret, otsu =
cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) print thresh,ret
(Algunas de las funciones pueden ser nuevas aquí, pero las cubriremos en los próximos capítulos)
Recursos adicionales
1. Procesamiento Digital de Imágenes, Rafael C. González
Ejercicios
1. Hay algunas optimizaciones disponibles para la binarización de Otsu. Puedes buscarlo e implementarlo.
Transformaciones Geométricas de Imágenes
Objetivos
• Aprenda a aplicar diferentes transformaciones geométricas a imágenes como traslación, rotación, transformación afín, etc.
• Verá estas funciones: cv2.getPerspectiveTransform
Transformaciones
OpenCV proporciona dos funciones de transformación, cv2.warpAffine y cv2.warpPerspective, con las que puedes tener todo tipo de
transformaciones. cv2.warpAffine toma una matriz de transformación de 2x3 mientras que cv2.warpPerspective toma una matriz de
transformación de 3x3 como entrada.
58 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Escalada
Escalar es simplemente cambiar el tamaño de la imagen. OpenCV viene con una función cv2.resize() para este propósito. El tamaño de la
imagen se puede especificar manualmente o puede especificar el factor de escala. Se utilizan diferentes métodos de interpolación. Los
métodos de interpolación preferibles son cv2.INTER_AREA para reducir y cv2.INTER_CUBIC (lento) y cv2.INTER_LINEAR para hacer
zoom. De forma predeterminada, el método de interpolación utilizado es cv2.INTER_LINEAR para todos los fines de cambio de tamaño.
Puede cambiar el tamaño de una imagen de entrada cualquiera de los siguientes métodos:
importar cv2
importar numpy como np
img = cv2.imread('messi5.jpg')
res = cv2.resize(img,Ninguno,fx=2, fy=2, interpolación = cv2.INTER_CUBIC)
#O
alto, ancho = img.shape[:2] res =
cv2.resize(img,(2*ancho, 2*alto), interpolación = cv2.INTER_CUBIC)
Traducción
La traducción es el cambio de ubicación del objeto. Si conoce el cambio en la dirección (x, y), sea ( la matriz de , ), puede crear
transformación M de la siguiente manera:
= [ 0
10 ]
1
Puede convertirlo en una matriz Numpy de tipo np.float32 y pasarlo a la función cv2.warpAffine() . Vea el siguiente ejemplo para un cambio
de (100,50):
importar cv2
importar numpy como np
img = cv2.imread('messi5.jpg',0) filas,columnas =
img.forma
M = np.float32([[1,0,100],[0,1,50]]) dst =
cv2.warpAffine(img,M,(columnas,filas))
cv2.imshow('img',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
Advertencia: el tercer argumento de la función cv2.warpAffine() es el tamaño de la imagen de salida, que debe tener la forma de
(ancho, alto). Recuerda ancho = número de columnas y alto = número de filas.
Vea el resultado a continuación:
1.4. Procesamiento de imágenes en OpenCV 59
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Rotación
La rotación de una imagen para un ángulo se logra mediante la matriz de transformación de la forma
= [ − ]
Pero OpenCV proporciona una rotación escalada con un centro de rotación ajustable para que pueda rotar en cualquier lugar que
prefiera. La matriz de transformación modificada viene dada por
(1 ) ∙ . . − ∙
.
− ∙ + (1 − ) ∙ . ]
[
dónde:
= ∙ porque ,
= ∙ pecado
Para encontrar esta matriz de transformación, OpenCV proporciona una función, cv2.getRotationMatrix2D. Consulte el
siguiente ejemplo que gira la imagen 90 grados con respecto al centro sin ninguna escala.
img = cv2.imread('messi5.jpg',0) filas,columnas =
img.forma
M = cv2.getRotationMatrix2D((cols/2,rows/2),90,1) dst = cv2.warpAffine(img,M,
(cols,rows))
Vea el resultado:
60 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Transformacion afin
En la transformación afín, todas las líneas paralelas en la imagen original seguirán siendo paralelas en la imagen de salida. Para encontrar la
matriz de transformación, necesitamos tres puntos de la imagen de entrada y sus ubicaciones correspondientes en la imagen de salida.
Luego, cv2.getAffineTransform creará una matriz de 2x3 que se pasará a cv2.warpAffine.
Verifique el ejemplo a continuación y también mire los puntos que seleccioné (que están marcados en color verde):
img = cv2.imread('dibujo.png') filas,columnas,ch
= img.forma
pts1 = np.float32([[50.50],[200.50],[50.200]]) pts2 = np.float32([[10,100],
[200.50],[100,250]])
M = cv2.getAffineTransform(pts1,pts2)
dst = cv2.warpAffine(img,M,(columnas,filas))
plt.subplot(121),plt.imshow(img),plt.title('Entrada') plt.subplot
(122),plt.imshow(dst),plt.title('Salida') plt.show()
Vea el resultado:
Transformación de perspectiva
Para la transformación de perspectiva, necesita una matriz de transformación de 3x3. Las líneas rectas permanecerán rectas incluso después
de la transformación. Para encontrar esta matriz de transformación, necesita 4 puntos en la imagen de entrada y los puntos correspondientes
en la imagen de salida. Entre estos 4 puntos, 3 de ellos no deben ser colineales. Entonces la matriz de transformación se puede encontrar
mediante la función cv2.getPerspectiveTransform. Luego aplique cv2.warpPerspective con esta matriz de transformación de 3x3.
Vea el código a continuación:
1.4. Procesamiento de imágenes en OpenCV 61
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
img = cv2.imread('sudokusmall.png') filas,columnas,ch
= img.forma
pts1 = np.float32([[56.65],[368.52],[28.387],[389.390]]) pts2 = np.float32([[0.0],[300.0],
[0.300] ,[300,300]])
M = cv2.getPerspectiveTransform(pts1,pts2)
dst = cv2.warpPerspective(img,M,(300,300))
plt.subplot(121),plt.imshow(img),plt.title('Entrada') plt.subplot
(122),plt.imshow(dst),plt.title('Salida') plt.show()
Resultado:
Recursos adicionales
1. “Visión por Computador: Algoritmos y Aplicaciones”, Richard Szeliski
Ejercicios
Suavizado de imágenes
Objetivos
Aprender a:
• Imágenes borrosas con varios filtros de paso bajo
• Aplicar filtros personalizados a las imágenes (convolución 2D)
62 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Convolución 2D (filtrado de imágenes)
En cuanto a las señales unidimensionales, las imágenes también se pueden filtrar con varios filtros de paso bajo (LPF), filtros de paso
alto (HPF), etc. Un LPF ayuda a eliminar el ruido o desenfocar la imagen. Un filtro HPF ayuda a encontrar bordes en una imagen.
OpenCV proporciona una función, cv2.filter2D(), para convolucionar un kernel con una imagen. Como ejemplo, probaremos un filtro
promedio en una imagen. Un kernel de filtro promedio de 5x5 se puede definir de la siguiente manera:
1 1 1 1 1
1 1 1 1 1
1
= 1 1 1 1 1
25
1 1 1 1 1
1 1 1 1 1
El filtrado con el kernel anterior da como resultado lo siguiente: para cada píxel, se centra una ventana de 5x5 en este píxel,
se suman todos los píxeles que caen dentro de esta ventana y el resultado se divide por 25. Esto equivale a calcular el
promedio de los valores de píxeles dentro de esa ventana. Esta operación se realiza para todos los píxeles de la imagen
para producir la imagen filtrada de salida. Prueba este código y comprueba el resultado:
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
img = cv2.imread('opencv_logo.png')
kernel = np.ones((5,5),np.float32)/25 dst =
cv2.filter2D(img,1,kernel)
plt.subplot(121),plt.imshow(img),plt.title('Original') plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(dst) ,plt.title('Promedio')
plt.xticks([]), plt.yticks([]) plt.show()
Resultado:
1.4. Procesamiento de imágenes en OpenCV 63
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Imagen borrosa (suavizado de imagen)
El desenfoque de la imagen se logra convolucionando la imagen con un núcleo de filtro de paso bajo. Es útil para eliminar el ruido. En
realidad, elimina el contenido de alta frecuencia (p. ej., ruido, bordes) de la imagen, lo que hace que los bordes se vean borrosos cuando
se aplica este filtro. (Bueno, existen técnicas de desenfoque que no desdibujan los bordes). OpenCV proporciona principalmente cuatro
tipos de técnicas de desenfoque.
1. Promedio
Esto se hace convolucionando la imagen con un filtro de caja normalizado. Simplemente toma el promedio de todos los
píxeles bajo el área del núcleo y reemplaza el elemento central con este promedio. Esto lo hace la función cv2.blur() o
cv2.boxFilter(). Consulte los documentos para obtener más detalles sobre el kernel. Debemos especificar el ancho y la altura del núcleo.
Un filtro de caja normalizado de 3x3 se vería así:
1 1 1
1
= 1 1 1
9
1
1 1
Nota: si no desea utilizar un filtro de cuadro normalizado, utilice cv2.boxFilter() y pase el argumento normalize=False a la
función.
Verifique la demostración de muestra a continuación con un kernel de tamaño 5x5:
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
img = cv2.imread('opencv_logo.png')
64 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
desenfoque = cv2.desenfoque(img,(5,5))
plt.subplot(121),plt.imshow(img),plt.title('Original') plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(desenfoque) ,plt.title('Blurred')
plt.xticks([]), plt.yticks([]) plt.show()
Resultado:
2. Filtrado gaussiano
En este enfoque, en lugar de un filtro de caja que consta de coeficientes de filtro iguales, se utiliza un núcleo gaussiano. Se hace con la función
cv2.GaussianBlur(). Deberíamos especificar el ancho y la altura del núcleo, que debería ser positivo e impar. También debemos especificar la
desviación estándar en las direcciones X e Y, sigmaX y sigmaY respectivamente. Si solo se especifica sigmaX, sigmaY se considera igual a
sigmaX. Si ambos se dan como ceros, se calculan a partir del tamaño del kernel. El filtrado gaussiano es muy eficaz para eliminar el ruido
gaussiano de la imagen.
Si lo desea, puede crear un kernel gaussiano con la función cv2.getGaussianKernel().
El código anterior se puede modificar para el desenfoque gaussiano:
desenfoque = cv2.GaussianBlur(img,(5,5),0)
Resultado:
1.4. Procesamiento de imágenes en OpenCV sesenta y cinco
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
3. Filtrado mediano
Aquí, la función cv2.medianBlur() calcula la mediana de todos los píxeles debajo de la ventana del kernel y el píxel central se
reemplaza con este valor de la mediana. Esto es muy eficaz para eliminar el ruido de sal y pimienta. Una cosa interesante a tener
en cuenta es que, en los filtros Gaussiano y de caja, el valor filtrado para el elemento central puede ser un valor que puede no
existir en la imagen original. Sin embargo, este no es el caso en el filtrado de mediana, ya que el elemento central siempre se
reemplaza por algún valor de píxel en la imagen. Esto reduce el ruido de manera efectiva. El tamaño del núcleo debe ser un
número entero impar positivo.
En esta demostración, agregamos un 50% de ruido a nuestra imagen original y usamos un filtro mediano. Compruebe el resultado:
mediana = cv2.medianBlur(img,5)
Resultado:
66 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
4. Filtrado bilateral
Como notamos, los filtros que presentamos anteriormente tienden a desenfocar los bordes. Este no es el caso del filtro bilateral, cv2.bilateralFilter(),
que se definió para eliminar el ruido y es muy eficaz al mismo tiempo que preserva los bordes. Pero el funcionamiento es más lento en comparación
con otros filtros. Ya vimos que un filtro gaussiano toma la vecindad alrededor del píxel y encuentra su promedio ponderado gaussiano. Este filtro
gaussiano es una función del espacio únicamente, es decir, los píxeles cercanos se consideran durante el filtrado. No considera si los píxeles tienen
casi el mismo valor de intensidad y no considera si el píxel se encuentra en un borde o no. El efecto resultante es que los filtros gaussianos tienden a
desenfocar los bordes, lo que no es deseable.
El filtro bilateral también usa un filtro gaussiano en el dominio del espacio, pero también usa un componente de filtro gaussiano más (multiplicativo)
que es una función de las diferencias de intensidad de píxeles. La función gaussiana del espacio asegura que solo los píxeles que son 'vecinos
espaciales' se consideren para el filtrado, mientras que el componente gaussiano aplicado en el dominio de intensidad (una función gaussiana de
diferencias de intensidad) asegura que solo aquellos píxeles con intensidades similares a la del centro. se incluyen píxeles ("vecinos de intensidad")
para calcular el valor de intensidad borrosa. Como resultado, este método conserva los bordes, ya que para los píxeles que se encuentran cerca de
los bordes, los píxeles vecinos ubicados en el otro lado del borde y, por lo tanto, presentan grandes variaciones de intensidad en comparación con el
píxel central, no se incluirán en el desenfoque.
El siguiente ejemplo demuestra el uso de filtrado bilateral (para obtener detalles sobre los argumentos, consulte los documentos de OpenCV).
desenfoque = cv2.bilateralFilter(img,9,75,75)
Resultado:
1.4. Procesamiento de imágenes en OpenCV 67
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Tenga en cuenta que la textura de la superficie ha desaparecido, pero los bordes aún se conservan.
Recursos adicionales
1. Los detalles sobre el filtrado bilateral se pueden encontrar en
Ejercicios
Tome una imagen, agregue ruido gaussiano y ruido de sal y pimienta, compare el efecto de desenfoque a través de filtros de caja, gaussianos,
medianos y bilaterales para ambas imágenes ruidosas, a medida que cambia el nivel de ruido.
Transformaciones morfológicas
Meta
En este capítulo,
• Aprenderemos diferentes operaciones morfológicas como Erosión, Dilatación, Apertura, Cierre, etc.
• Veremos diferentes funciones como: cv2.erode(), cv2.dilate(), cv2.morphologyEx() etc.
Teoría
Las transformaciones morfológicas son algunas operaciones simples basadas en la forma de la imagen. Normalmente se realiza en
imágenes binarias. Necesita dos entradas, una es nuestra imagen original, la segunda se llama elemento estructurante o kernel que decide
la naturaleza de la operación. Dos operadores morfológicos básicos son la Erosión y la Dilatación. Luego, sus formas variantes como
Apertura, Cierre, Gradiente, etc. también entran en juego. Los veremos uno por uno con la ayuda de la siguiente imagen:
68 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
1. Erosión
La idea básica de la erosión es como la erosión del suelo, erosiona los límites del objeto en primer plano (siempre trate de mantener el
primer plano en blanco). Entonces ¿Qué es lo que hace? El núcleo se desliza a través de la imagen (como en la convolución 2D). Un
píxel en la imagen original (ya sea 1 o 0) se considerará 1 solo si todos los píxeles debajo del núcleo son 1; de lo contrario, se erosiona
(se hace cero).
Entonces, lo que sucede es que todos los píxeles cercanos al límite se descartarán según el tamaño del kernel. Entonces, el grosor o el
tamaño del objeto en primer plano disminuye o simplemente disminuye la región blanca en la imagen. Es útil para eliminar pequeños
ruidos blancos (como hemos visto en el capítulo de espacio de color), separar dos objetos conectados, etc.
Aquí, como ejemplo, usaría un kernel 5x5 lleno de unos. Veamos cómo funciona:
importar cv2
importar numpy como np
img = cv2.imread('j.png',0) kernel =
np.ones((5,5),np.uint8) erosión =
cv2.erode(img,kernel,iteraciones = 1)
Resultado:
2. Dilatación
Es justo lo contrario de la erosión. Aquí, un elemento de píxel es '1' si al menos un píxel debajo del kernel es '1'. Por lo tanto, aumenta
la región blanca en la imagen o aumenta el tamaño del objeto en primer plano. Normalmente, en casos como eliminación de ruido, erosión
1.4. Procesamiento de imágenes en OpenCV 69
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
le sigue la dilatación. Porque la erosión elimina los ruidos blancos, pero también encoge nuestro objeto. Entonces lo dilatamos. Dado que
el ruido se ha ido, no volverán, pero el área de nuestro objeto aumenta. También es útil para unir partes rotas de un objeto.
dilatación = cv2.dilate(img,kernel,iteraciones = 1)
Resultado:
3. Apertura
La apertura es solo otro nombre de la erosión seguida de la dilatación. Es útil para eliminar el ruido, como explicamos anteriormente.
Aquí usamos la función, cv2.morphologyEx()
apertura = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
Resultado:
4. Cierre
El cierre es el reverso de la apertura, la dilatación seguida de la erosión. Es útil para cerrar pequeños agujeros dentro de los objetos de
primer plano o pequeños puntos negros en el objeto.
cierre = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
Resultado:
70 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
5. Gradiente morfológico
Es la diferencia entre dilatación y erosión de una imagen.
El resultado se verá como el contorno del objeto.
degradado = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
Resultado:
6. Sombrero de copa
Es la diferencia entre imagen de entrada y Apertura de la imagen. El siguiente ejemplo está hecho para un kernel 9x9.
sombrero de copa = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
Resultado:
1.4. Procesamiento de imágenes en OpenCV 71
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
7. Sombrero negro
Es la diferencia entre el cierre de la imagen de entrada y la imagen de entrada.
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
Resultado:
Elemento Estructurante
Creamos manualmente elementos de estructuración en los ejemplos anteriores con la ayuda de Numpy. Es de forma rectangular.
Pero en algunos casos, es posible que necesite granos de forma elíptica o circular. Entonces, para este propósito, OpenCV tiene una función,
cv2.getStructuringElement(). Simplemente pasa la forma y el tamaño del kernel, obtiene el kernel deseado.
# Kernel rectangular >>>
cv2.getStructuringElement(cv2.MORPH_RECT,(5,5)) array([[1, 1, 1, 1, 1], [1, 1, 1,
1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1], [1, 1, 1, 1,
1]], dtipo=uint8)
# Núcleo elíptico >>>
cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5)) array([[0, 0, 1, 0, 0], [1, 1, 1 , 1 ,
1], [1, 1, 1, 1, 1],
72 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
[1, 1, 1, 1, 1], [0, 0, 1, 0,
0]], dtipo=uint8)
# Kernel en forma de cruz >>>
cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5)) array([[0, 0, 1, 0, 0], [0, 0,
1, 0, 0], [ 1, 1, 1, 1, 1], [0, 0, 1, 0, 0],
[0, 0, 1, 0, 0]],
dtype=uint8)
Recursos adicionales
1. Operaciones morfológicas en HIPR2
Ejercicios
Gradientes de imagen
Meta
En este capítulo, aprenderemos a:
• Encuentra gradientes de imagen, bordes, etc.
• Veremos las siguientes funciones: cv2.Sobel(), cv2.Scharr(), cv2.Laplacian() etc.
Teoría
OpenCV proporciona tres tipos de filtros de gradiente o filtros de paso alto, Sobel, Scharr y Laplacian. Veremos cada
uno de ellos.
1. Derivados de Sobel y Scharr
Los operadores de Sobel son una operación conjunta de suavizado gaussiano más diferenciación, por lo que es más resistente al ruido.
Puede especificar la dirección de las derivadas a tomar, vertical u horizontal (mediante los argumentos, yorder y xorder respectivamente).
También puede especificar el tamaño del núcleo mediante el argumento ksize. Si ksize = 1, se utiliza un filtro Scharr de 3x3 que da
mejores resultados que un filtro Sobel de 3x3. Consulte los documentos para los núcleos utilizados.
2. Derivados laplacianos
2 2
0 1 0
= 1 −4 1
0
1 0
1.4. Procesamiento de imágenes en OpenCV 73
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Código
El siguiente código muestra todos los operadores en un solo diagrama. Todos los núcleos son de tamaño 5x5. Se pasa la profundidad de la imagen de salida 1
para obtener el resultado en el tipo np.uint8.
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
img = cv2.imread('dave.jpg',0)
laplacian = cv2.Laplacian(img,cv2.CV_64F) sobelx =
cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5) sobely =
cv2.Sobel(img,cv2.CV_64F,0,1,ksize= 5)
plt.subplot(2,2,1),plt.imshow(img,cmap = 'gris') plt.title('Original'), plt.xticks([]),
plt.yticks([]) plt.subplot (2,2,2),plt.imshow(laplaciano,cmap = 'gris') plt.title('Laplaciano'),
plt.xticks([]), plt.yticks([]) plt.subplot(2, 2,3),plt.imshow(sobelx,cmap = 'gris') plt.title('Sobel
X'), plt.xticks([]), plt.yticks([]) plt.subplot(2,2, 4),plt.imshow(sobely,cmap = 'gris')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.mostrar()
Resultado:
74 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
¡Un asunto importante!
En nuestro último ejemplo, el tipo de datos de salida es cv2.CV_8U o np.uint8. Pero hay un pequeño problema con eso. La transición de negro a
blanco se toma como una pendiente positiva (tiene un valor positivo) mientras que la transición de blanco a negro se toma como una pendiente
negativa (tiene un valor negativo). Entonces, cuando convierte datos a np.uint8, todas las pendientes negativas se vuelven cero. En palabras
simples, extrañas esa ventaja.
Si desea detectar ambos bordes, la mejor opción es mantener el tipo de datos de salida en algunas formas superiores, como cv2.CV_16S,
cv2.CV_64F, etc., tome su valor absoluto y luego vuelva a convertirlo a cv2.CV_8U. El siguiente código demuestra este procedimiento para un filtro
Sobel horizontal y la diferencia en los resultados.
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
1.4. Procesamiento de imágenes en OpenCV 75
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
img = cv2.imread('caja.png',0)
# Tipo de salida = cv2.CV_8U sobelx8u =
cv2.Sobel(img,cv2.CV_8U,1,0,ksize=5)
# Tipo de salida = cv2.CV_64F. Luego tome su absoluto y conviértalo a cv2.CV_8U sobelx64f =
cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5) abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)
plt.subplot(1,3,1),plt.imshow(img,cmap = 'gris') plt.title('Original'), plt.xticks([]),
plt.yticks([]) plt.subplot (1,3,2),plt.imshow(sobelx8u,cmap = 'gris') plt.title('Sobel
CV_8U'), plt.xticks([]), plt.yticks([]) plt.subplot(1 ,3,3),plt.imshow(sobel_8u,cmap =
'gris') plt.title('Sobel abs(CV_64F)'), plt.xticks([]), plt.yticks([])
plt.mostrar()
Compruebe el resultado a continuación:
Recursos adicionales
Ejercicios
Detección de borde astuto
Meta
En este capítulo aprenderemos sobre
• Concepto de detección de bordes Canny
• Funciones OpenCV para eso: cv2.Canny()
Teoría
Canny Edge Detection es un popular algoritmo de detección de bordes. Fue desarrollado por John F. Canny en 1986. Es un algoritmo de
varias etapas y pasaremos por cada etapa.
1. Reducción de ruido
76 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Dado que la detección de bordes es susceptible al ruido en la imagen, el primer paso es eliminar el ruido en la imagen con un filtro
gaussiano de 5x5. Ya lo hemos visto en capítulos anteriores.
2. Encontrar el gradiente de intensidad de la imagen
Luego, la imagen suavizada se filtra con un núcleo Sobel en dirección horizontal y vertical para obtener la primera derivada en dirección
horizontal ( ) y dirección vertical ( ). A partir de estas dos imágenes, podemos encontrar el degradado del borde y la dirección de cada píxel
de la siguiente manera:
_ + 2
( ) = √ 2 ( )
= tan−1 ( )
La dirección del degradado siempre es perpendicular a los bordes. Se redondea a uno de los cuatro ángulos que representan las direcciones
vertical, horizontal y dos diagonales.
3. Supresión no máxima
Después de obtener la magnitud y la dirección del gradiente, se realiza un escaneo completo de la imagen para eliminar los píxeles no
deseados que pueden no constituir el borde. Para esto, en cada píxel, se verifica si el píxel es un máximo local en su vecindad en la
dirección del gradiente. Revisa la imagen a continuación:
El punto A está en el borde (en dirección vertical). La dirección del degradado es normal al borde. Los puntos B y C están en direcciones
de gradiente. Entonces, el punto A se verifica con el punto B y C para ver si forma un máximo local. Si es así, se considera para la
siguiente etapa, de lo contrario, se suprime (se pone a cero).
En resumen, el resultado que obtiene es una imagen binaria con "bordes delgados".
4. Umbral de histéresis
Esta etapa decide cuáles son todos los bordes que son realmente bordes y cuáles no. Para esto, necesitamos dos valores de umbral,
minVal y maxVal. Cualquier borde con un gradiente de intensidad superior a maxVal seguramente será borde y aquellos por debajo de
minVal seguramente no serán bordes, por lo tanto, deséchelos. Aquellos que se encuentran entre estos dos umbrales se clasifican como
bordes o no bordes en función de su conectividad. Si están conectados a píxeles de "borde seguro", se consideran parte de los bordes.
De lo contrario, también se desechan. Vea la imagen a continuación:
1.4. Procesamiento de imágenes en OpenCV 77
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
El borde A está por encima de maxVal, por lo que se considera "borde seguro". Aunque el borde C está por debajo de maxVal, está conectado
al borde A, por lo que también se considera un borde válido y obtenemos esa curva completa. Pero el borde B, aunque está por encima de
minVal y está en la misma región que el borde C, no está conectado a ningún "borde seguro", por lo que se descarta. Por lo tanto, es muy
importante que seleccionemos minVal y maxVal en consecuencia para obtener el resultado correcto.
Esta etapa también elimina los ruidos de los píxeles pequeños asumiendo que los bordes son líneas largas.
Entonces, lo que finalmente obtenemos son bordes fuertes en la imagen.
Detección de Canny Edge en OpenCV
OpenCV pone todo lo anterior en una sola función, cv2.Canny(). Veremos cómo usarlo. El primer argumento es nuestra imagen de entrada. El
segundo y tercer argumento son nuestro minVal y maxVal respectivamente. El tercer argumento es tamaño_apertura. Es el tamaño del núcleo
Sobel utilizado para encontrar gradientes de imagen. Por defecto es 3. El último argumento es L2gradient que especifica la ecuación para
encontrar la magnitud del gradiente. Si es True, utiliza la ecuación mencionada anteriormente, que es más precisa, |. Por defecto, es Falso. de
_ ( ) = | | + | lo contrario, utiliza esta función:
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
img = cv2.imread('messi5.jpg',0) bordes =
cv2.Canny(img,100,200)
plt.subplot(121),plt.imshow(img,cmap = 'gray') plt.title(' Imagen original'),
plt.xticks([]), plt.yticks([]) plt.subplot(122) ,plt.imshow(bordes,cmap = 'gris') plt.title(' Imagen de
borde'), plt.xticks([]), plt.yticks([])
plt.mostrar()
Vea el resultado a continuación:
78 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
1. Canny detector de bordes en Wikipedia
2. Tutorial de detección de Canny Edge por Bill Green, 2002.
Ejercicios
1. Escriba una pequeña aplicación para encontrar la detección de bordes Canny cuyos valores de umbral se pueden variar usando dos
barras de seguimiento De esta manera, puede comprender el efecto de los valores de umbral.
Pirámides de imágenes
Meta
En este capítulo,
• Aprenderemos sobre Pirámides de Imágenes
• Usaremos pirámides de imágenes para crear una nueva fruta, “Oapple”
• Veremos estas funciones: cv2.pyrUp(), cv2.pyrDown()
Teoría
Normalmente, solíamos trabajar con una imagen de tamaño constante. Pero en algunas ocasiones, necesitamos trabajar con imágenes de
diferente resolución de una misma imagen. Por ejemplo, al buscar algo en una imagen, como una cara, no estamos seguros de qué tamaño
tendrá el objeto en la imagen. En ese caso, necesitaremos crear un conjunto de imágenes con diferente resolución y buscar objetos en todas
las imágenes. Este conjunto de imágenes con diferente resolución se denomina Pirámides de imágenes (porque cuando se mantienen en una
pila con la imagen más grande en la parte inferior y la imagen más pequeña en la parte superior, se ven como una pirámide).
Hay dos tipos de pirámides de imágenes. 1) Pirámide Gaussiana y 2) Pirámides Laplacianas
El nivel superior (baja resolución) en una pirámide gaussiana se forma eliminando filas y columnas consecutivas en la imagen de nivel inferior
(resolución superior). Luego, cada píxel en el nivel superior está formado por la contribución de 5 píxeles en el nivel subyacente con pesos
gaussianos. Al hacerlo, una imagen × se convierte en una imagen /2 × /2. Entonces el área se reduce a un cuarto del área original. Se llama
Octava. El mismo patrón continúa a medida que ascendemos en la pirámide (es decir, la resolución disminuye). De manera similar, mientras
se expande, el área se vuelve 4 veces en cada nivel. Podemos encontrar pirámides de Gauss usando las funciones cv2.pyrDown() y
cv2.pyrUp() .
1.4. Procesamiento de imágenes en OpenCV 79
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
img = cv2.imread('messi5.jpg') menor_reso =
cv2.pyrDown(mayor_reso)
A continuación se muestran los 4 niveles en una pirámide de imágenes.
Ahora puedes bajar por la pirámide de imágenes con la función cv2.pyrUp() .
mayor_reso2 = cv2.pyrUp(menor_reso)
Recuerda, high_reso2 no es igual a high_reso, porque una vez que disminuyes la resolución, pierdes la información. Debajo de
la imagen hay 3 niveles por debajo de la pirámide creada a partir de la imagen más pequeña en el caso anterior. Compáralo con
la imagen original:
80 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Las pirámides laplacianas se forman a partir de las pirámides gaussianas. No hay una función exclusiva para eso. Las imágenes de
la pirámide laplaciana son solo imágenes de borde. La mayoría de sus elementos son ceros. Se utilizan en la compresión de
imágenes. Un nivel en la Pirámide Laplaciana está formado por la diferencia entre ese nivel en la Pirámide Gaussiana y la versión
expandida de su nivel superior en la Pirámide Gaussiana. Los tres niveles de un nivel laplaciano se verán a continuación (el contraste
se ajusta para mejorar los contenidos):
1.4. Procesamiento de imágenes en OpenCV 81
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
82 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Fusión de imágenes usando pirámides
Una aplicación de Pyramids es Image Blending. Por ejemplo, en la unión de imágenes, deberá apilar dos imágenes juntas, pero es posible que no se vea bien
debido a las discontinuidades entre las imágenes. En ese caso, la combinación de imágenes con Pyramids le brinda una combinación perfecta sin dejar muchos
datos en las imágenes. Un ejemplo clásico de esto es la mezcla de dos frutas, Naranja y Manzana. Vea el resultado ahora mismo para entender lo que estoy
diciendo:
Consulte la primera referencia en recursos adicionales, tiene detalles esquemáticos completos sobre la combinación de imágenes, los medios de Laplacian
Pyra, etc. Simplemente se hace de la siguiente manera:
1. Cargue las dos imágenes de manzana y naranja.
2. Encuentre las pirámides de Gauss para manzana y naranja (en este ejemplo en particular, el número de niveles es 6)
3. A partir de las pirámides gaussianas, encuentra sus pirámides laplacianas
4. Ahora une la mitad izquierda de la manzana y la mitad derecha de la naranja en cada nivel de las Pirámides Laplacianas.
5. Finalmente a partir de esta imagen conjunta de las pirámides, reconstruiremos la imagen original.
1.4. Procesamiento de imágenes en OpenCV 83
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
A continuación se muestra el código completo. (En aras de la simplicidad, cada paso se realiza por separado, lo que puede requerir más
memoria. Puede optimizarlo si lo desea).
importar cv2
importar numpy como np, sys
A = cv2.imread('manzana.jpg')
B = cv2.imread('naranja.jpg')
# generar pirámide gaussiana para A G = A.copy()
gpA = [G] para i
en xrange(6):
G = cv2.pyrDown(G)
gpA.append(G)
# generar pirámide gaussiana para B G = B.copy()
gpB = [G] para i
en xrange(6):
G = cv2.pyrDown(G)
gpB.append(G)
# generar la Pirámide Laplaciana para A lpA = [gpA[5]]
para i en xrange(5,0,1):
GE = cv2.pyrUp(gpA[i])
L = cv2.restar(gpA[i1],GE) lpA.agregar(L)
# generar la Pirámide Laplaciana para B lpB = [gpB[5]]
para i en xrange(5,0,1):
GE = cv2.pyrUp(gpB[i])
L = cv2.restar(gpB[i1],GE) lpB.agregar(L)
# Ahora agregue las mitades izquierda y derecha de las imágenes en cada nivel
LS = []
para la,lb en zip(lpA,lpB):
filas,columnas,dpt = la.forma ls =
np.hstack((la[:,0:columnas/2], lb[:,columnas/2:]))
LS.append(ls)
# ahora reconstruir
ls_ = LS[0] para
i en xrange(1,6):
ls_ = cv2.pyrUp(ls_) ls_ =
cv2.add(ls_, LS[i])
# imagen con conexión directa cada medio real =
np.hstack((A[:,:cols/2],B[:,cols/2:]))
cv2.imwrite('Pyramid_blending2.jpg',ls_)
cv2.imwrite('Direct_blending.jpg',real)
84 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
1. Fusión de imágenes
Ejercicios
Contornos en OpenCV
• Contornos: Primeros pasos
Aprende a encontrar y dibujar contornos
• Funciones de contorno
Aprenda a encontrar diferentes características de contornos como área, perímetro, rectángulo
delimitador, etc.
• Propiedades de contorno
Aprenda a encontrar diferentes propiedades de contornos como Solidez, Intensidad media
etc.
• Contornos: más funciones
Aprenda a encontrar defectos de convexidad, pointPolygonTest, haga coincidir diferentes formas,
etc.
• Jerarquía de contornos
1.4. Procesamiento de imágenes en OpenCV 85
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Más información sobre la jerarquía de contornos
86 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Contornos: Primeros pasos
Meta
• Comprender qué son los contornos.
• Aprenda a encontrar contornos, dibujar contornos, etc.
• Verá estas funciones: cv2.findContours(), cv2.drawContours()
¿Qué son los contornos?
Los contornos se pueden explicar simplemente como una curva que une todos los puntos continuos (a lo largo del límite), que tienen el mismo color o
intensidad. Los contornos son una herramienta útil para el análisis de formas y la detección y reconocimiento de objetos.
• Para mayor precisión, utilice imágenes binarias. Entonces, antes de encontrar contornos, aplique umbral o detección de bordes astutos.
• La función findContours modifica la imagen de origen. Entonces, si desea una imagen de origen incluso después de encontrar contornos,
ya almacenarlo en algunas otras variables.
• En OpenCV, encontrar contornos es como encontrar un objeto blanco en un fondo negro. Así que recuerda, el objeto a encontrar debe ser blanco
y el fondo debe ser negro.
Veamos cómo encontrar los contornos de una imagen binaria:
importar numpy como np
importar cv2
im = cv2.imread('test.jpg') imgray =
cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) ret,thresh =
cv2.threshold(imgray,127,255,0) imagen, contornos, jerarquía =
cv2.findContours(thresh, cv2.RETR_TREE,cv2.CHAIN_APPROX_ →SIMPLE)
Mira, hay tres argumentos en la función cv2.findContours() , el primero es la imagen de origen, el segundo es el modo de recuperación de contorno, el
tercero es el método de aproximación de contorno. Y genera la imagen, los contornos y la jerarquía. contornos es una lista de Python de todos los
contornos de la imagen. Cada contorno individual es una matriz Numpy de coordenadas (x, y) de puntos límite del objeto.
Nota: Discutiremos los argumentos segundo y tercero y sobre la jerarquía en detalles más adelante. Hasta entonces, los valores que se les dieron en el
ejemplo de código funcionarán bien para todas las imágenes.
¿Cómo dibujar los contornos?
Para dibujar los contornos, se utiliza la función cv2.drawContours. También se puede utilizar para dibujar cualquier forma siempre que tenga sus puntos
de contorno. Su primer argumento es la imagen de origen, el segundo argumento son los contornos que deben pasarse como una lista de Python, el
tercer argumento es el índice de contornos (útil al dibujar contornos individuales. Para dibujar todos los contornos, pase 1) y los argumentos restantes
son color, grosor etc.
Para dibujar todos los contornos de una imagen:
img = cv2.drawContours(img, contornos, 1, (0,255,0), 3)
Para dibujar un contorno individual, diga el cuarto contorno:
1.4. Procesamiento de imágenes en OpenCV 87
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
img = cv2.drawContours(img, contornos, 3, (0,255,0), 3)
Pero la mayoría de las veces, el siguiente método será útil:
cnt = contornos[4] img =
cv2.drawContours(img, [cnt], 0, (0,255,0), 3)
Nota: Los últimos dos métodos son iguales, pero cuando avance, verá que el último es más útil.
Método de aproximación de contorno
Este es el tercer argumento en la función cv2.findContours. ¿Qué denota en realidad?
Arriba, dijimos que los contornos son los límites de una forma con la misma intensidad. Almacena las coordenadas (x,y) del límite de una
forma. Pero, ¿almacena todas las coordenadas? Eso se especifica mediante este método de aproximación de contorno.
Si pasa cv2.CHAIN_APPROX_NONE, se almacenan todos los puntos límite. Pero, ¿realmente necesitamos todos los puntos?
Por ejemplo, encontraste el contorno de una línea recta. ¿Necesitas todos los puntos en la línea para representar esa línea? No, solo
necesitamos dos puntos finales de esa línea. Esto es lo que hace cv2.CHAIN_APPROX_SIMPLE. Elimina todos los puntos redundantes
y comprime el contorno, ahorrando así memoria.
La imagen de abajo de un rectángulo demuestra esta técnica. Simplemente dibuje un círculo en todas las coordenadas en la matriz de
contorno (dibujado en color azul). La primera imagen muestra los puntos que obtuve con cv2.CHAIN_APPROX_NONE (734 puntos) y la
segunda imagen muestra el que tiene cv2.CHAIN_APPROX_SIMPLE (solo 4 puntos). ¡Mira, cuánta memoria ahorra!
Recursos adicionales
Ejercicios
Características de contorno
Meta
En este artículo aprenderemos
• Para encontrar las diferentes características de los contornos, como el área, el perímetro, el centroide, el cuadro delimitador, etc.
• Verá un montón de funciones relacionadas con los contornos.
88 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
1. Momentos
Los momentos de imagen lo ayudan a calcular algunas características como el centro de masa del objeto, el área del objeto, etc. Consulte
la página de wikipedia en Momentos de imagen
La función cv2.moments() proporciona un diccionario de todos los valores de momento calculados. Vea abajo:
importar cv2
importar numpy como np
img = cv2.imread('star.jpg',0) ret,umbral =
cv2.umbral(img,127,255,0) contornos,jerarquía =
cv2.findContours(umbral, 1, 2)
cnt = contornos[0]
M = cv2.momentos(cnt)
imprimir M
10
A partir de este momento, puede extraer datos útiles como área, centroide, etc. El centroide está dado por las relaciones, = y =
00
01
00
. Esto puede hacerse de la siguiente manera:
cx = int(M['m10']/M['m00']) cy = int(M['m01']/
M['m00'])
2. Área de contorno
El área de contorno viene dada por la función cv2.contourArea() oa partir de momentos, M['m00'].
area = cv2.contourArea(cnt)
3. Perímetro de contorno
También se le llama longitud de arco. Se puede averiguar usando la función cv2.arcLength() . El segundo argumento especifica si la forma
es un contorno cerrado (si se pasa True), o simplemente una curva.
perímetro = cv2.arcLength(cnt,True)
4. Aproximación al contorno
Aproxima una forma de contorno a otra forma con menos número de vértices dependiendo de la precisión que especifiquemos.
Es una implementación del algoritmo de DouglasPeucker. Consulte la página de wikipedia para ver el algoritmo y la demostración.
Para entender esto, suponga que está tratando de encontrar un cuadrado en una imagen, pero debido a algunos problemas en la imagen,
no obtuvo un cuadrado perfecto, sino una "forma incorrecta" (como se muestra en la primera imagen a continuación). Ahora puede usar esta
función para aproximar la forma. En esto, el segundo argumento se llama épsilon, que es la distancia máxima del contorno al contorno
aproximado. Es un parámetro de precisión. Se necesita una sabia selección de épsilon para obtener la salida correcta.
épsilon = 0.1*cv2.arcLength(cnt,True) aprox. =
cv2.approxPolyDP(cnt,epsilon,True)
A continuación, en la segunda imagen, la línea verde muestra la curva aproximada para épsilon = 10 % de la longitud del arco. La tercera
imagen muestra lo mismo para epsilon = 1% de la longitud del arco. El tercer argumento especifica si la curva está cerrada o no.
1.4. Procesamiento de imágenes en OpenCV 89
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
5. Casco convexo
Casco convexo se verá similar a la aproximación de contorno, pero no lo es (Ambos pueden proporcionar los mismos resultados en algunos casos).
Aquí, la función cv2.convexHull() verifica una curva en busca de defectos de convexidad y la corrige. En términos generales, las curvas convexas son las
curvas que siempre están abultadas, o al menos planas. Y si está abombado por dentro, se llama defectos de convexidad. Por ejemplo, compruebe la
siguiente imagen de la mano. La línea roja muestra el casco convexo de la mano. Las marcas de flechas de dos lados muestran los defectos de convexidad,
que son las desviaciones máximas locales del casco con respecto a los contornos.
Hay algunas cosas que discutir sobre su sintaxis:
casco = cv2.convexHull(puntos[, casco[, en el sentido de las agujas del reloj[, returnPoints]]
Detalles de los argumentos:
• los puntos son los contornos por los que pasamos.
• casco es la salida, normalmente la evitamos.
90 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
• en el sentido de las agujas del reloj : bandera de orientación. Si es True, el casco convexo de salida está orientado en el sentido de las agujas del reloj. De lo contrario, está orientado
en sentido antihorario.
• puntos de retorno : por defecto, verdadero. Luego devuelve las coordenadas de los puntos del casco. Si es False, devuelve el
índices de puntos de contorno correspondientes a los puntos del casco.
Entonces, para obtener un casco convexo como en la imagen de arriba, lo siguiente es suficiente:
casco = cv2.convexHull(cnt)
Pero si desea encontrar defectos de convexidad, debe pasar returnPoints = False. Para entenderlo, tomaremos la imagen del rectángulo de arriba. Primero
encontré su contorno como cnt. Ahora encontré su casco convexo con returnPoints = True, obtuve los siguientes valores: [[[234 202]], [[ 51 202]], [[ 51 79]],
[[234 79]]] que son las cuatro esquinas puntos del rectángulo. Ahora, si hago lo mismo con returnPoints = False, obtengo el siguiente resultado: [[129],[67],[0],
[142]]. Estos son los índices de los puntos correspondientes en los contornos. Por ejemplo, compruebe el primer valor: cnt[129] = [[234, 202]] que es el mismo
que el primer resultado (y así sucesivamente para los demás).
Lo verá nuevamente cuando hablemos sobre los defectos de convexidad.
6. Comprobación de la convexidad
Hay una función para comprobar si una curva es convexa o no, cv2.isContourConvex(). Simplemente devuelve si es Verdadero o Falso.
No es un gran trato.
k = cv2.isContourConvex(cnt)
7. Rectángulo delimitador
Hay dos tipos de rectángulos delimitadores.
7.a. Rectángulo delimitador recto
Es un rectángulo recto, no considera la rotación del objeto. Entonces, el área del rectángulo delimitador no será mínima. Se encuentra mediante la función
cv2.boundingRect().
Sea (x,y) la coordenada superior izquierda del rectángulo y (w,h) su ancho y alto.
x,y,w,h = cv2.boundingRect(cnt) img =
cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
7.b. Rectángulo girado
Aquí, el rectángulo delimitador se dibuja con un área mínima, por lo que también considera la rotación. La función utilizada es cv2.minAreaRect(). Devuelve
una estructura Box2D que contiene los siguientes detalles: (esquina superior izquierda (x, y), (ancho, alto), ángulo de rotación). Pero para dibujar este
rectángulo, necesitamos 4 esquinas del rectángulo. Se obtiene mediante la función cv2.boxPoints()
rect = cv2.minAreaRect(cnt) cuadro =
cv2.boxPoints(rect) cuadro = np.int0(cuadro)
im = cv2.drawContours(im,
[box],0,(0,0,255),2)
1.4. Procesamiento de imágenes en OpenCV 91
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Ambos rectángulos se muestran en una sola imagen. El rectángulo verde muestra el rectángulo delimitador normal. El rectángulo rojo
es el recto rotado.
8. Círculo envolvente mínimo
A continuación, encontramos el círculo circunscrito de un objeto mediante la función cv2.minEnclosingCircle(). Es un círculo que
cubre completamente el objeto con un área mínima.
(x,y),radio = cv2.minEnclosingCircle(cnt) centro = (int(x),int(y))
radio = int(radio)
img = cv2.circle(img,centro,radio,(0,255,0),2)
92 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
9. Ajuste de una elipse
El siguiente es ajustar una elipse a un objeto. Devuelve el rectángulo rotado en el que se inscribe la elipse.
elipse = cv2.fitEllipse(cnt) im =
cv2.ellipse(im,elipse,(0,255,0),2)
1.4. Procesamiento de imágenes en OpenCV 93
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
10. Ajuste de una línea
De manera similar, podemos ajustar una línea a un conjunto de puntos. La imagen de abajo contiene un conjunto de puntos blancos. Podemos aproximarnos a
una línea recta.
filas,columnas = img.forma[:2]
[vx,vy,x,y] = cv2.fitLine(cnt, cv2.DIST_L2,0,0.01,0.01) zurdo = int((x*vy/vx) + y)
derecha = int(((columnasx)*vy/vx)+y)
img = cv2.line(img,(columnas1,derecha),
(0,izquierda),(0,255,0),2)
94 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
Ejercicios
Propiedades de contorno
Aquí aprenderemos a extraer algunas propiedades de objetos que se usan con frecuencia, como Solidez, Diámetro equivalente, Imagen de
máscara, Intensidad media, etc. Se pueden encontrar más funciones en la documentación de Matlab regionprops.
(NB: centroide, área, perímetro, etc. también pertenecen a esta categoría, pero lo hemos visto en el último capítulo)
1. Relación de aspecto
Es la relación entre el ancho y la altura del rectángulo delimitador del objeto.
x,y,w,h = cv2.boundingRect(cnt) relación_aspecto
= float(w)/h
2. Extensión
La extensión es la relación entre el área del contorno y el área del rectángulo delimitador.
area = cv2.contourArea(cnt) x,y,w,h =
cv2.boundingRect(cnt) rect_area = w*h extension
= float(area)/rect_area
1.4. Procesamiento de imágenes en OpenCV 95
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
3. Solidez
La solidez es la relación entre el área del contorno y el área convexa del casco.
area = cv2.contourArea(cnt) casco =
cv2.convexHull(cnt) casco_area =
cv2.contourArea(casco) solidez = float(area)/
hull_area
4. Diámetro equivalente
El diámetro equivalente es el diámetro del círculo cuya área es igual al área del contorno.
= √ 4 ×
área = cv2.contourArea(cnt) equi_diameter
= np.sqrt(4*area/np.pi)
5. Orientación
La orientación es el ángulo al que se dirige el objeto. El siguiente método también proporciona las longitudes del eje mayor y del eje
menor.
(x,y),(MA,ma),ángulo = cv2.fitEllipse(cnt)
6. Puntos de máscara y píxel
En algunos casos, podemos necesitar todos los puntos que componen ese objeto. Se puede hacer de la siguiente manera:
máscara = np.zeros(imgray.shape,np.uint8)
cv2.drawContours(máscara,[cnt],0,255,1) puntos de
píxeles = np.transpose(np.nonzero(máscara)) #pixelpoints =
cv2.findNonZero(máscara )
Aquí, se dan dos métodos, uno que usa funciones Numpy, el siguiente que usa la función OpenCV (última línea comentada) para hacer
lo mismo. Los resultados también son los mismos, pero con una ligera diferencia. Numpy da coordenadas en formato (fila, columna) ,
mientras que OpenCV da coordenadas en formato (x, y) . Entonces, básicamente, las respuestas se intercambiarán. Tenga en cuenta
que, fila = x y columna = y.
7. Valor Máximo, Valor Mínimo y sus ubicaciones
Podemos encontrar estos parámetros usando una imagen de máscara.
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(imgray,máscara = máscara)
96 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
8. Color medio o intensidad media
Aquí, podemos encontrar el color promedio de un objeto. O puede ser la intensidad promedio del objeto en el modo de escala de grises. Volvemos
a utilizar la misma máscara para hacerlo.
mean_val = cv2.mean(im,máscara = máscara)
9. Puntos extremos
Puntos extremos significa los puntos superior, inferior, derecho e izquierdo del objeto.
extremo izquierdo = tupla(cnt[cnt[:,:,0].argmin()][0]) extremo derecho =
tupla (cnt[cnt[:,:,0].argmax()][0]) superior = tupla( cnt[cnt[:,:,1].argmin()][0])
inferior = tupla(cnt[cnt[:,:,1].argmax()][0])
Por ejemplo, si lo aplico a un mapa indio, obtengo el siguiente resultado:
1.4. Procesamiento de imágenes en OpenCV 97
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
Ejercicios
1. Todavía quedan algunas características en el documento matlab regionprops. Trate de implementarlos.
Contornos: más funciones
Meta
En este capítulo aprenderemos sobre
• Defectos de convexidad y cómo encontrarlos.
• Encontrar la distancia más corta de un punto a un polígono
98 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
• Emparejar diferentes formas
Teoría y Código
1. Defectos de convexidad
Vimos lo que es un casco convexo en el segundo capítulo sobre contornos. Cualquier desviación del objeto de este casco se puede
considerar como un defecto de convexidad.
OpenCV viene con una función preparada para encontrar esto, cv2.convexityDefects(). Una llamada de función básica se vería a
continuación:
casco = cv2.convexHull(cnt,returnPoints = False) defectos =
cv2.convexityDefects(cnt,hull)
Nota: Recuerde que tenemos que pasar returnPoints = False mientras buscamos casco convexo, para encontrar defectos de convexidad.
Devuelve una matriz donde cada fila contiene estos valores: [punto de inicio, punto final, punto más lejano, distancia aproximada al punto
más lejano ]. Podemos visualizarlo mediante una imagen. Dibujamos una línea que une el punto de inicio y el punto final, luego dibujamos
un círculo en el punto más lejano. Recuerde que los primeros tres valores devueltos son índices de cnt. Así que tenemos que traer esos
valores de cnt.
importar cv2
importar numpy como np
img = cv2.imread('star.jpg') img_gray =
cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret, umbral =
cv2.threshold(img_gray, 127, 255,0) contornos,jerarquía =
cv2.findContours(umbral, 2,1) cnt = contornos[0]
casco = cv2.convexHull(cnt,returnPoints = False) defectos =
cv2.convexityDefects(cnt,hull)
para i en el rango (defectos.forma[0]):
s,e,f,d = defectos[i,0] inicio =
tupla(cnt[s][0]) fin = tupla(cnt[e][0])
lejano = tupla(cnt[f][0])
cv2.line(img,inicio,fin,[0,255,0],2)
cv2.circle(img,lejos,5,[0,0,255],1)
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Y mira el resultado:
1.4. Procesamiento de imágenes en OpenCV 99
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
2. Prueba de polígono de puntos
Esta función encuentra la distancia más corta entre un punto de la imagen y un contorno. Devuelve la distancia que es negativa cuando el punto está
fuera del contorno, positiva cuando el punto está dentro y cero si el punto está en el contorno.
Por ejemplo, podemos verificar el punto (50,50) de la siguiente manera:
dist = cv2.pointPolygonTest(cnt,(50,50),Verdadero)
En la función, el tercer argumento es medirDist. Si es True, encuentra la distancia con signo. Si es False, encuentra si el punto está dentro o fuera o en
el contorno (devuelve +1, 1, 0 respectivamente).
Nota: si no desea encontrar la distancia, asegúrese de que el tercer argumento sea Falso, ya que es un proceso que requiere mucho tiempo. Por lo
tanto, hacerlo Falso da una aceleración de aproximadamente 23X.
3. Combina formas
OpenCV viene con una función cv2.matchShapes() que nos permite comparar dos formas o dos contornos y devuelve una métrica que muestra la
similitud. Cuanto más bajo es el resultado, mejor coincidencia es. Se calcula en base a los valores de momento hu. Los diferentes métodos de medición
se explican en los documentos.
importar cv2
importar numpy como np
img1 = cv2.imread('estrella.jpg',0) img2 =
cv2.imread('estrella2.jpg',0)
ret, umbral = cv2.umbral(img1, 127, 255,0) ret, umbral2 =
cv2.umbral(img2, 127, 255,0) contornos,jerarquía =
cv2.findContours(umbral,2,1) cnt1 = contornos[ 0] contornos,jerarquía =
cv2.findContours(thresh2,2,1)
cnt2 = contornos[0]
derecha = cv2.matchShapes(cnt1,cnt2,1,0.0) imprimir
derecha
100 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Intenté hacer coincidir formas con diferentes formas que se dan a continuación:
Obtuve los siguientes resultados:
• Hacer coincidir la imagen A consigo misma = 0.0
• Hacer coincidir la imagen A con la imagen B = 0,001946
• Hacer coincidir la imagen A con la imagen C = 0,326911
Mira, incluso la rotación de la imagen no afecta mucho en esta comparación.
Ver también:
HuMomentos son siete momentos invariantes a la traslación, rotación y escala. El séptimo es invariante al sesgo. Esos valores se pueden
encontrar usando la función cv2.HuMoments() .
Recursos adicionales
Ejercicios
1. Consulte la documentación de cv2.pointPolygonTest(), puede encontrar una buena imagen en color rojo y azul. Representa la distancia
desde todos los píxeles hasta la curva blanca en él. Todos los píxeles dentro de la curva son azules dependiendo de la distancia. Del
mismo modo, los puntos exteriores son rojos. Los bordes del contorno están marcados con blanco. Así que el problema es simple.
Escriba un código para crear tal representación de la distancia.
2. Compare imágenes de dígitos o letras usando cv2.matchShapes(). (Eso sería un paso simple hacia OCR)
Jerarquía de contornos
Meta
Esta vez, aprendemos sobre la jerarquía de contornos, es decir, la relación padrehijo en Contornos.
Teoría
En los últimos artículos sobre contornos, hemos trabajado con varias funciones relacionadas con los contornos proporcionadas por OpenCV.
Pero cuando encontramos los contornos en la imagen usando la función cv2.findContours() , pasamos un argumento, Modo de recuperación
de contorno. Por lo general, pasamos cv2.RETR_LIST o cv2.RETR_TREE y funcionó bien. Pero, ¿qué significa realmente?
1.4. Procesamiento de imágenes en OpenCV 101
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Además, en la salida, obtuvimos tres matrices, la primera es la imagen, la segunda son nuestros contornos y una salida más que
llamamos jerarquía ( consulte los códigos en artículos anteriores). Pero nunca usamos esta jerarquía en ninguna parte.
Entonces, ¿qué es esta jerarquía y para qué sirve? ¿Cuál es su relación con el argumento de función mencionado anteriormente?
Eso es lo que vamos a tratar en este artículo.
¿Qué es Jerarquía?
Normalmente usamos la función cv2.findContours() para detectar objetos en una imagen, ¿verdad? A veces los objetos están en
diferentes lugares. Pero en algunos casos, algunas formas están dentro de otras formas. Al igual que las figuras anidadas. En este
caso, llamamos al exterior como padre y al interior como hijo. De esta manera, los contornos de una imagen tienen alguna relación entre sí.
Y podemos especificar cómo un contorno está conectado entre sí, por ejemplo, si es hijo de algún otro contorno, o es un padre, etc.
La representación de esta relación se llama Jerarquía .
Considere una imagen de ejemplo a continuación:
En esta imagen, hay algunas formas que he numerado del 0 al 5. 2 y 2a denota los contornos externo e interno de la caja más externa.
Aquí, los contornos 0,1,2 son externos o exteriores. Podemos decir que están en jerarquía0 o simplemente están en el mismo nivel
de jerarquía.
Luego viene el contorno2a. Se puede considerar como un hijo del contorno2 (o de manera opuesta, el contorno2 es el padre del
contorno2a). Así que sea en la jerarquía1. De manera similar, el contorno3 es hijo del contorno2 y viene en la siguiente jerarquía.
Finalmente los contornos 4,5 son los hijos del contorno3a, y vienen en el último nivel de jerarquía. Por la forma en que numeré los
cuadros, diría que el contorno4 es el primer hijo del contorno3a (también puede ser el contorno5).
Mencioné estas cosas para comprender términos como el mismo nivel de jerarquía, contorno externo, contorno secundario, contorno
principal, primer hijo , etc. Ahora entremos en OpenCV.
102 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Representación de jerarquía en OpenCV
Entonces, cada contorno tiene su propia información sobre qué jerarquía es, quién es su hijo, quién es su padre, etc. OpenCV lo representa como una
matriz de cuatro valores: [ Siguiente, Anterior, Primer_hijo, Padre]
"Siguiente denota el siguiente contorno en el mismo nivel jerárquico".
Por ejemplo, tome el contorno0 en nuestra imagen. ¿Quién es el próximo contorno en su mismo nivel? Es contorno1. Así que simplemente ponga Next =
1. De manera similar para Contour1, next es contorno2. Entonces Siguiente = 2.
¿Qué pasa con el contorno2? No hay contorno siguiente en el mismo nivel. Simplemente, ponga Siguiente = 1. ¿Qué pasa con el contorno 4? Está en el
mismo nivel que el contorno5. Entonces su siguiente contorno es contorno5, entonces Siguiente = 5.
"Anterior denota el contorno anterior en el mismo nivel jerárquico".
Es lo mismo que arriba. El contorno anterior del contorno1 es el contorno0 en el mismo nivel. De manera similar para el contorno2, es el contorno1. Y
para el contorno0, no hay anterior, así que póngalo como 1.
"First_Child denota su primer contorno secundario".
No hay necesidad de ninguna explicación. Para contorno2, el niño es contorno2a. Entonces obtiene el valor de índice correspondiente de contorno2a.
¿Qué pasa con el contorno3a? Tiene dos hijos. Pero tomo solamente al primer niño. Y es contorno4. Entonces First_Child = 4 para contorno3a.
"Padre denota índice de su contorno padre".
Es justo lo contrario de First_Child. Tanto para el contorno4 como para el contorno5, el contorno principal es el contorno3a. Para contorno3a, es
contorno3 y así sucesivamente.
Nota: si no hay un hijo o un padre, ese campo se toma como 1
Entonces, ahora que conocemos el estilo de jerarquía utilizado en OpenCV, podemos verificar los modos de recuperación de contorno en OpenCV con la
ayuda de la misma imagen que se muestra arriba. es decir, ¿qué significan indicadores como cv2.RETR_LIST, cv2.RETR_TREE, cv2.RETR_CCOMP,
cv2.RETR_EXTERNAL, etc.?
Modo de recuperación de contorno
1. RETR_LISTA
Esta es la más simple de las cuatro banderas (desde el punto de vista de la explicación). Simplemente recupera todos los contornos, pero no crea ninguna
relación padrehijo. Padres e hijos son iguales bajo esta regla, y son solo contornos. es decir, todos pertenecen al mismo nivel de jerarquía.
Entonces, aquí, el tercer y cuarto término en la matriz de jerarquía siempre es 1. Pero obviamente, los términos Siguiente y Anterior tendrán sus valores
correspondientes. Solo compruébalo tú mismo y compruébalo.
A continuación se muestra el resultado que obtuve, y cada fila son detalles de jerarquía del contorno correspondiente. Por ejemplo, la primera fila
corresponde al contorno 0. El siguiente contorno es el contorno 1. Entonces Siguiente = 1. No hay contorno anterior, entonces Anterior = 0. Y los dos
restantes, como se dijo antes, es 1.
>>> matriz de
jerarquía ([[[ 1, 1, 1, 1], [ 2, 0, 1, 1],
[ 3, 1, 1, 1], [ 4, 2, 1,
1], [5, 3, 1, 1], [6, 4, 1,
1],
1.4. Procesamiento de imágenes en OpenCV 103
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
[7, 5, 1, 1], [1, 6, 1, 1]]])
Esta es una buena opción para usar en su código, si no está usando ninguna función de jerarquía.
2. RETR_EXTERNO
Si usa esta bandera, solo devuelve banderas externas extremas. Todos los contornos secundarios se dejan atrás. Podemos decir, bajo esta ley,
Sólo se cuida al mayor de cada familia. No le importan los demás miembros de la familia :).
Entonces, en nuestra imagen, ¿cuántos contornos exteriores extremos hay? es decir, en el nivel de jerarquía0?. Solo 3, es decir, contornos 0,1,2, ¿verdad?
Ahora intente encontrar los contornos usando esta bandera. Aquí también, los valores dados a cada elemento son los mismos que los anteriores. Compárelo con
el resultado anterior. A continuación se muestra lo que obtuve:
>>> matriz de
jerarquía ([[[ 1, 1, 1, 1], [ 2, 0, 1, 1],
[1, 1, 1, 1]]])
Puede usar esta bandera si desea extraer solo los contornos exteriores. Puede ser útil en algunos casos.
3. RETR_CCOMP
Esta bandera recupera todos los contornos y los organiza en una jerarquía de 2 niveles. es decir, los contornos externos del objeto (es decir, su
límite) se colocan en la jerarquía1. Y los contornos de los agujeros dentro del objeto (si los hay) se colocan en la jerarquía2. Si hay algún objeto
dentro de él, su contorno se coloca nuevamente en la jerarquía1 solamente. Y su agujero en la jerarquía2 y así sucesivamente.
Solo considere la imagen de un "gran cero blanco" sobre un fondo negro. El círculo exterior de cero pertenece a la primera jerarquía y el círculo
interior de cero pertenece a la segunda jerarquía.
Podemos explicarlo con una simple imagen. Aquí he etiquetado el orden de los contornos en color rojo y la jerarquía a la que pertenecen, en color
verde (ya sea 1 o 2). El orden es el mismo que el orden en que OpenCV detecta los contornos.
104 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Así que considere el primer contorno, es decir, el contorno0. Es jerarquía1. Tiene dos agujeros, contornos 1 y 2, y pertenecen a la jerarquía 2.
Entonces, para el contorno0, el siguiente contorno en el mismo nivel de jerarquía es el contorno3. Y no hay anterior. Y su primer hijo es contorno1
en jerarquía2. No tiene padre, porque está en la jerarquía1. Entonces su matriz de jerarquía es [3,1,1,1]
Ahora toma el contorno1. Está en la jerarquía2. El siguiente en la misma jerarquía (bajo la paternidad de contorno1) es contorno2.
Ninguna anterior. Ningún hijo, pero el padre es contorno0. Entonces la matriz es [2,1,1,0].
Del mismo modo contorno2: Está en la jerarquía2. No existe el siguiente contorno en la misma jerarquía bajo el contorno0. Así que no Siguiente.
El anterior es contorno1. Ningún hijo, el padre es contorno0. Entonces la matriz es [1,1,1,0].
Contorno 3: El siguiente en la jerarquía1 es el contorno5. El anterior es contorno0. El niño es contorno4 y no tiene padre. Entonces la matriz es
[5,0,4,1].
Contorno 4: Está en la jerarquía 2 bajo contorno3 y no tiene hermanos. Entonces, no hay siguiente, no hay anterior, no hay hijo, el padre es
contorno3. Entonces la matriz es [1,1,1,3].
Restante se puede llenar. Esta es la respuesta final que obtuve:
>>> matriz de
jerarquía ([[[ 3, 1, 1, 1], [ 2, 1, 1, 0], [1,
1, 1, 0], [ 5, 0, 4 , 1], [1,
1, 1, 3], [7, 3, 6, 1],
[1, 1, 1, 5],
[8, 5, 1, 1],
[1, 7, 1, 1]]])
1.4. Procesamiento de imágenes en OpenCV 105
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
4. RETR_ÁRBOL
Y este es el chico final, Mr.Perfect. Recupera todos los contornos y crea una lista de jerarquía familiar completa. Incluso dice quién es
el abuelo, el padre, el hijo, el nieto e incluso más allá... :).
Por ejemplo, tomé la imagen de arriba, reescribí el código para cv2.RETR_TREE, reordené los contornos según el resultado dado por
OpenCV y lo analicé. Nuevamente, las letras rojas dan el número de contorno y las letras verdes dan el orden jerárquico.
Tomar contorno0: Está en jerarquía0. El siguiente contorno en la misma jerarquía es el contorno7. Sin contornos previos. El niño es
contorno1. Y sin padre. Entonces la matriz es [7,1,1,1].
Tomar contorno2: Está en jerarquía1. Sin contorno en el mismo nivel. Ninguna anterior. El niño es contorno2. El padre es contorno0.
Entonces la matriz es [1,1,2,0].
Y restante, pruébalo tú mismo. A continuación se muestra la respuesta completa:
>>> matriz de
jerarquía ([[[ 7, 1, 1, 1], [1, 1, 2, 0], [1,
1, 3, 1], [1, 1 , 4, 2],
[1, 1, 5, 3],
[6, 1, 1, 4],
[1, 5, 1, 4], [8, 0, 1, 1],
[1, 7, 1, 1]]])
Recursos adicionales
106 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Ejercicios
Histogramas en OpenCV
• Histogramas 1: ¡Buscar, Graficar, Analizar!
Aprende a encontrar y dibujar contornos
• Histogramas 2: Ecualización de histogramas
Aprenda a ecualizar histogramas para obtener un mejor contraste de las imágenes
• Histogramas 3: Histogramas 2D
Aprenda a buscar y trazar histogramas 2D
• Histograma 4: retroproyección de histograma
Aprenda la retroproyección de histogramas para segmentar objetos coloreados
1.4. Procesamiento de imágenes en OpenCV 107
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Histogramas 1: ¡Buscar, Trazar, Analizar!
Meta
Aprender a
• Encuentra histogramas, usando las funciones OpenCV y Numpy
• Trazar histogramas, utilizando las funciones de OpenCV y Matplotlib
• Verá estas funciones: cv2.calcHist(), np.histogram() etc.
Teoría
Entonces, ¿qué es el histograma? Puede considerar el histograma como un gráfico o diagrama, lo que le da una idea general sobre la distribución
de intensidad de una imagen. Es un gráfico con valores de píxeles (que van de 0 a 255, no siempre) en el eje X y el número correspondiente de
píxeles en la imagen en el eje Y.
Es sólo otra forma de entender la imagen. Al mirar el histograma de una imagen, obtiene intuición sobre el contraste, el brillo, la distribución de
intensidad, etc. de esa imagen. Casi todas las herramientas de procesamiento de imágenes de hoy en día ofrecen funciones de histograma. A
continuación se muestra una imagen del sitio web de Cambridge in Color, y te recomiendo que visites el sitio para más detalles.
Puedes ver la imagen y su histograma. (Recuerde, este histograma se dibuja para una imagen en escala de grises, no para una imagen en color).
La región izquierda del histograma muestra la cantidad de píxeles más oscuros en la imagen y la región derecha muestra la cantidad de píxeles
más brillantes. En el histograma, puede ver que la región oscura es más que una región más brillante, y la cantidad de tonos medios (valores de
píxeles en el rango medio, digamos alrededor de 127) es muy inferior.
108 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Buscar histograma
Ahora que tenemos una idea de qué es un histograma, podemos ver cómo encontrarlo. Tanto OpenCV como Numpy vienen con una función
incorporada para esto. Antes de usar esas funciones, necesitamos entender algunas terminologías relacionadas con los histogramas.
BINS : El histograma anterior muestra el número de píxeles para cada valor de píxel, es decir, de 0 a 255. Es decir, necesita 256 valores para
mostrar el histograma anterior. Pero considere, ¿qué sucede si no necesita encontrar la cantidad de píxeles para todos los valores de píxeles
por separado, sino la cantidad de píxeles en un intervalo de valores de píxeles? digamos, por ejemplo, que necesita encontrar el número de
píxeles entre 0 y 15, luego 16 a 31, ..., 240 a 255. Necesitará solo 16 valores para representar el histograma. Y eso es lo que se muestra en el
ejemplo dado en Tutoriales de OpenCV sobre histogramas.
Entonces, lo que hace es simplemente dividir todo el histograma en 16 subpartes y el valor de cada subparte es la suma de todos los píxeles
que contiene. Esta subparte se llama "BIN". En el primer caso, el número de contenedores era 256 (uno para cada píxel), mientras que en el
segundo caso, es solo 16. BINS está representado por el término histSize en los documentos de OpenCV.
DIMS : Es la cantidad de parámetros para los cuales recopilamos los datos. En este caso, recopilamos datos sobre una sola cosa, el valor de
intensidad. Así que aquí es 1.
RANGO : Es el rango de valores de intensidad que desea medir. Normalmente, es [0,256], es decir, todos los valores de intensidad.
1. Cálculo de histograma en OpenCV
Así que ahora usamos la función cv2.calcHist() para encontrar el histograma. Vamos a familiarizarnos con la función y sus parámetros:
cv2.calcHist(imágenes, canales, máscara, tamaño hist, rangos[, hist[, acumular]])
1. imágenes: es la imagen de origen de tipo uint8 o float32. debe darse entre corchetes, es decir, “[img]”.
2. canales: también se da entre corchetes. Es el índice del canal para el que calculamos el histograma. Por ejemplo, si la entrada es una
imagen en escala de grises, su valor es [0]. Para la imagen en color, puede pasar [0], [1] o [2] para calcular el histograma del canal azul,
verde o rojo, respectivamente.
3. máscara: imagen de máscara. Para encontrar el histograma de la imagen completa, se da como "Ninguno". Pero si desea encontrar el
histograma de una región particular de la imagen, debe crear una imagen de máscara para eso y darle como máscara. (Mostraré un
ejemplo más adelante.)
4. histSize: esto representa nuestro conteo de BIN. Debe darse entre corchetes. Para la escala completa, pasamos [256].
5. gamas: esta es nuestra GAMA. Normalmente, es [0,256].
Entonces, comencemos con una imagen de muestra. Simplemente cargue una imagen en modo de escala de grises y encuentre su histograma completo.
img = cv2.imread('inicio.jpg',0) hist =
cv2.calcHist([img],[0],Ninguno,[256],[0,256])
hist es una matriz de 256x1, cada valor corresponde al número de píxeles en esa imagen con su valor de píxel correspondiente.
2. Cálculo de histograma en Numpy
Numpy también le proporciona una función, np.histogram(). Entonces, en lugar de la función calcHist (), puede probar la siguiente línea:
hist,bins = np.histogram(img.ravel(),256,[0,256])
hist es el mismo que calculamos antes. Pero los contenedores tendrán 257 elementos, porque Numpy calcula los contenedores como 00,99,
11,99, 22,99, etc. Por lo tanto, el rango final sería 255255,99. Para representar eso, también agregan 256 al final de los contenedores. Pero
no necesitamos ese 256. Hasta 255 es suficiente.
1.4. Procesamiento de imágenes en OpenCV 109
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Ver también:
Numpy tiene otra función, np.bincount() , que es mucho más rápida que (alrededor de 10X) np.histogram(). Entonces, para histogramas
unidimensionales, es mejor que pruebes eso. No olvide establecer minlength = 256 en np.bincount. Por ejemplo, hist =
np.bincount(img.ravel(),minlength=256)
Nota: la función OpenCV es más rápida que (alrededor de 40X) que np.histogram(). Así que quédese con la función OpenCV.
Ahora deberíamos trazar histogramas, pero ¿cómo?
Trazado de histogramas
Hay dos maneras para esto,
1. Camino corto: use las funciones de trazado de Matplotlib
2. Long Way: use las funciones de dibujo de OpenCV
1. Usando Matplotlib
Matplotlib viene con una función de trazado de histogramas: matplotlib.pyplot.hist()
Encuentra directamente el histograma y lo traza. No necesita usar la función calcHist() o np.histogram() para encontrar el histograma.
Vea el código a continuación:
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
img = cv2.imread('inicio.jpg',0)
plt.hist(img.ravel(),256,[0,256]); plt.mostrar()
Obtendrá una trama de la siguiente manera:
110 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
O puede usar el gráfico normal de matplotlib, que sería bueno para el gráfico BGR. Para eso, primero debe encontrar los datos del
histograma. Pruebe el siguiente código:
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
img = cv2.imread('home.jpg') color = ('b','g','r')
for i,col in enumerate(color):
histr = cv2.calcHist([img],[i],Ninguno,[256],[0,256]) plt.plot(histr,color = col)
plt.xlim([0,256])
plt.mostrar()
Resultado:
Puede deducir del gráfico anterior que el azul tiene algunas áreas de alto valor en la imagen (obviamente debería ser debido al cielo)
2. Usando OpenCV
Bueno, aquí ajustas los valores de los histogramas junto con sus valores bin para que se vean como las coordenadas x, y para que puedas
dibujarlo usando la función cv2.line() o cv2.polyline() para generar la misma imagen que arriba. Esto ya está disponible con muestras
oficiales de OpenCVPython2. Revisa el código
1.4. Procesamiento de imágenes en OpenCV 111
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Aplicación de Mascarilla
Usamos cv2.calcHist() para encontrar el histograma de la imagen completa. ¿Qué sucede si desea encontrar histogramas de algunas regiones de una
imagen? Simplemente cree una imagen de máscara con color blanco en la región que desea encontrar en el histograma y negro en caso contrario.
Luego pasa esto como la máscara.
img = cv2.imread('inicio.jpg',0)
# crear una máscara
mask = np.zeros(img.shape[:2], np.uint8) mask[100:300,
100:400] = 255 masked_img =
cv2.bitwise_and(img,img,mask = mask)
# Calcular el histograma con máscara y sin máscara # Comprobar el tercer
argumento de la máscara hist_full =
cv2.calcHist([img],[0],Ninguno,[256],[0,256]) hist_mask = cv2.calcHist([img],[0 ],máscara,
[256],[0,256])
plt.subplot(221), plt.imshow(img, 'gris') plt.subplot(222),
plt.imshow(máscara, 'gris') plt.subplot(223), plt.imshow(masked_img,
'gris' ) plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask) plt.xlim([0,256])
plt.mostrar()
Vea el resultado. En el gráfico de histograma, la línea azul muestra el histograma de la imagen completa, mientras que la línea verde muestra el histograma de la
región enmascarada.
Recursos adicionales
1. Sitio web de Cambridge en color
112 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Ejercicios
Histogramas 2: Ecualización de histogramas
Meta
En esta sección,
• Aprenderemos los conceptos de ecualización de histogramas y los utilizaremos para mejorar el contraste de nuestras imágenes.
Teoría
Considere una imagen cuyos valores de píxel se limitan a un rango específico de valores únicamente. Por ejemplo, una imagen más
brillante tendrá todos los píxeles confinados a valores altos. Pero una buena imagen tendrá píxeles de todas las regiones de la imagen. Por
lo tanto, debe estirar este histograma hacia ambos extremos (como se muestra en la imagen a continuación, de wikipedia) y eso es lo que
hace la ecualización de histograma (en palabras simples). Esto normalmente mejora el contraste de la imagen.
Le recomendaría que lea la página de wikipedia sobre la ecualización de histogramas para más detalles al respecto. Tiene una muy buena
explicación con ejemplos elaborados, por lo que entenderías casi todo después de leer eso.
En cambio, aquí veremos su implementación de Numpy. Después de eso, veremos la función OpenCV.
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
img = cv2.imread('wiki.jpg',0)
hist,bins = np.histogram(img.flatten(),256,[0,256])
cdf = hist.cumsum()
cdf_normalizado = cdf * hist.max()/ cdf.max()
plt.plot(cdf_normalizado, color = 'b') plt.hist(img.flatten(),256,
[0,256], color = 'r') plt.xlim([0,256]) plt.legend(('cdf' ,'histograma'), loc = 'arriba
a la izquierda') plt.show()
1.4. Procesamiento de imágenes en OpenCV 113
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Puede ver que el histograma se encuentra en la región más brillante. Necesitamos el espectro completo. Para eso, necesitamos una función de transformación
que mapee los píxeles de entrada en la región más brillante a los píxeles de salida en la región completa. Eso es lo que hace la ecualización de histogramas.
Ahora encontramos el valor mínimo del histograma (excluyendo 0) y aplicamos la ecuación de ecualización del histograma como se indica en la
página wiki. Pero aquí he usado el concepto de matriz enmascarada de Numpy. Para una matriz enmascarada, todas las operaciones se
realizan en elementos no enmascarados. Puede leer más sobre esto en los documentos de Numpy sobre matrices enmascaradas.
cdf_m = np.ma.masked_equal(cdf,0) cdf_m =
(cdf_m cdf_m.min())*255/(cdf_m.max()cdf_m.min()) cdf = np.ma.filled(cdf_m,0 ).astype('uint8')
Ahora tenemos la tabla de búsqueda que nos brinda información sobre cuál es el valor de píxel de salida para cada valor de píxel de entrada.
Así que solo aplicamos la transformación.
img2 = cdf[img]
Ahora calculamos su histograma y cdf como antes (usted lo hace) y el resultado se ve a continuación:
114 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Otra característica importante es que, incluso si la imagen era una imagen más oscura (en lugar de una más brillante que usamos), después de la
ecualización obtendremos casi la misma imagen que obtuvimos. Como resultado, esto se usa como una "herramienta de referencia" para hacer todas
las imágenes con las mismas condiciones de iluminación. Esto es útil en muchos casos. Por ejemplo, en el reconocimiento facial, antes de entrenar
los datos faciales, las imágenes de los rostros se ecualizan en histograma para que todos tengan las mismas condiciones de iluminación.
Ecualización de histogramas en OpenCV
OpenCV tiene una función para hacer esto, cv2.equalizeHist(). Su entrada es solo una imagen en escala de grises y la salida es nuestra imagen
ecualizada de histograma.
A continuación se muestra un fragmento de código simple que muestra su uso para la misma imagen que usamos:
img = cv2.imread('wiki.jpg',0) equ =
cv2.equalizeHist(img) res =
np.hstack((img,equ)) #apilar imágenes una al lado de la otra cv2.imwrite('res.png ', res)
1.4. Procesamiento de imágenes en OpenCV 115
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Así que ahora puedes tomar diferentes imágenes con diferentes condiciones de luz, ecualizarlas y comprobar los resultados.
La ecualización del histograma es buena cuando el histograma de la imagen se limita a una región particular. No funcionará bien en lugares donde
hay grandes variaciones de intensidad donde el histograma cubre una gran región, es decir, están presentes píxeles brillantes y oscuros. Consulte
los enlaces SOF en Recursos adicionales.
CLAHE (ecualización de histograma adaptativo limitado por contraste)
La primera ecualización de histograma que acabamos de ver considera el contraste global de la imagen. En muchos casos, no es una buena idea.
Por ejemplo, la siguiente imagen muestra una imagen de entrada y su resultado después de la ecualización global del histograma.
116 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
1.4. Procesamiento de imágenes en OpenCV 117
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Es cierto que el contraste de fondo ha mejorado tras la ecualización del histograma. Pero compare la cara de la estatua en ambas imágenes. Perdimos la
mayor parte de la información allí debido al exceso de brillo. Es porque su histograma no se limita a una región en particular como vimos en casos anteriores
(intente trazar el histograma de la imagen de entrada, obtendrá más intuición).
Entonces, para resolver este problema, se usa la ecualización de histograma adaptativo . En este, la imagen se divide en pequeños bloques llamados
"mosaicos" (tileSize es 8x8 por defecto en OpenCV). Luego, cada uno de estos bloques se iguala en histograma como de costumbre. Entonces, en un área
pequeña, el histograma se limitaría a una región pequeña (a menos que haya ruido). Si hay ruido, se amplificará.
Para evitar esto, se aplica la limitación de contraste . Si algún bin de histograma está por encima del límite de contraste especificado (por defecto 40 en
OpenCV), esos píxeles se recortan y distribuyen uniformemente a otros bins antes de aplicar la ecualización de histograma.
Después de la ecualización, para eliminar los artefactos en los bordes de los mosaicos, se aplica la interpolación bilineal.
El siguiente fragmento de código muestra cómo aplicar CLAHE en OpenCV:
importar numpy como np
importar cv2
img = cv2.imread('tsukuba_l.png',0)
# crear un objeto CLAHE (los argumentos son opcionales). clahe =
cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8)) cl1 = clahe.apply(img)
cv2.imwrite('clahe_2.jpg',cl1)
Vea el resultado a continuación y compárelo con los resultados anteriores, especialmente la región de la estatua:
Recursos adicionales
1. Página de Wikipedia sobre ecualización de histogramas
2. Matrices enmascaradas en Numpy
118 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Consulte también estas preguntas de SOF sobre el ajuste de contraste:
3. ¿Cómo puedo ajustar el contraste en OpenCV en C?
4. ¿Cómo igualo el contraste y el brillo de las imágenes usando opencv?
Ejercicios
Histogramas 3: Histogramas 2D
Meta
En este capítulo, aprenderemos a encontrar y trazar histogramas 2D. Será útil en los próximos capítulos.
Introducción
En el primer artículo, calculamos y representamos un histograma unidimensional. Se llama unidimensional porque solo estamos
considerando una característica, es decir, el valor de intensidad de escala de grises del píxel. Pero en los histogramas bidimensionales,
considera dos características. Normalmente se usa para encontrar histogramas de color donde dos características son valores de tono y
saturación de cada píxel.
Hay una muestra de python en las muestras oficiales. ya para encontrar histogramas de color. Intentaremos entender cómo crear un
histograma de color de este tipo, y será útil para comprender otros temas como la retroproyección del histograma.
Histograma 2D en OpenCV
Es bastante simple y se calcula usando la misma función, cv2.calcHist(). Para histogramas de color, necesitamos convertir la imagen de
BGR a HSV. (Recuerde, para el histograma 1D, convertimos de BGR a escala de grises). Para histogramas 2D, sus parámetros se
modificarán de la siguiente manera:
• canales = [0,1] porque necesitamos procesar tanto el plano H como el S.
• bins = [180,256] 180 para el plano H y 256 para el plano S.
• rango = [0,180,0,256] El valor de tono se encuentra entre 0 y 180 y la saturación se encuentra entre 0 y 256.
Ahora revisa el siguiente código:
importar cv2
importar numpy como np
img = cv2.imread('inicio.jpg') hsv =
cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
hist = cv2.calcHist([hsv], [0, 1], Ninguno, [180, 256], [0, 180, 0, 256])
Eso es todo.
Histograma 2D en Numpy
Numpy también proporciona una función específica para esto: np.histogram2d(). (Recuerde, para el histograma 1D usamos np.histogram() ).
1.4. Procesamiento de imágenes en OpenCV 119
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
img = cv2.imread('inicio.jpg') hsv =
cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
hist, xbins, ybins = np.histogram2d(h.ravel(),s.ravel(),[180,256],[[0,180],[0,256]])
El primer argumento es el plano H, el segundo es el plano S, el tercero es el número de contenedores para cada uno y el cuarto es su rango.
Ahora podemos comprobar cómo trazar este histograma de color.
Trazado de histogramas 2D
Método 1: Usando cv2.imshow()
El resultado que obtenemos es una matriz bidimensional de tamaño 180x256. Entonces podemos mostrarlos como lo hacemos normalmente, usando
la función cv2.imshow(). Será una imagen en escala de grises y no dará mucha idea de qué colores hay, a menos que conozca los valores de Tono
de diferentes colores.
Método 2: Usando Matplotlib
Podemos usar la función matplotlib.pyplot.imshow() para trazar un histograma 2D con diferentes mapas de color. Nos da una idea mucho más clara
sobre las diferentes densidades de píxeles. Pero esto tampoco nos da una idea de qué color hay en un primer vistazo, a menos que conozca los
valores de Tono de diferentes colores. Aún así prefiero este método. Es simple y mejor.
Nota: Al usar esta función, recuerde que el indicador de interpolación debe ser el más cercano para obtener mejores resultados.
Considere el código:
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
img = cv2.imread('inicio.jpg') hsv =
cv2.cvtColor(img,cv2.COLOR_BGR2HSV) hist =
cv2.calcHist( [hsv], [0, 1], Ninguno, [180, 256], [0 , 180, 0, 256] )
plt.imshow(hist,interpolación = 'más cercano') plt.show()
A continuación se muestra la imagen de entrada y su diagrama de histograma de color. El eje X muestra los valores S y el eje Y muestra el tono.
120 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
En el histograma, puedes ver algunos valores altos cerca de H = 100 y S = 200. Corresponde al azul del cielo. De igual manera se puede ver
otro pico cerca de H = 25 y S = 100. Corresponde al amarillo del palacio. Puede verificarlo con cualquier herramienta de edición de imágenes
como GIMP.
Método 3: ¡estilo de muestra OpenCV!
Hay un código de muestra para el histograma de color en las muestras de OpenCVPython2. Si ejecuta el código, puede ver que su tograma
también muestra el color correspondiente. O simplemente genera un histograma codificado por colores. Su resultado es muy bueno (aunque
necesita agregar un montón de líneas extra).
En ese código, el autor creó un mapa de colores en HSV. Luego lo convirtió en BGR. La imagen del histograma resultante se multiplica con
este mapa de colores. También utiliza algunos pasos de preprocesamiento para eliminar pequeños píxeles aislados, lo que da como resultado
un buen histograma.
Dejo que los lectores ejecuten el código, lo analicen y tengan sus propios trucos. A continuación se muestra la salida de ese código para la
misma imagen que la anterior:
Puede ver claramente en el histograma qué colores están presentes, el azul está allí, el amarillo está allí y algo de blanco debido al tablero de
ajedrez está allí. Lindo !!!
Recursos adicionales
Ejercicios
Histograma 4: retroproyección de histograma
1.4. Procesamiento de imágenes en OpenCV 121
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Meta
En este capítulo, aprenderemos sobre la retroproyección de histogramas.
Teoría
Fue propuesto por Michael J. Swain , Dana H. Ballard en su artículo Indexación mediante histogramas de color.
¿Qué es realmente en palabras simples? Se utiliza para la segmentación de imágenes o para encontrar objetos de interés en una
imagen. En palabras simples, crea una imagen del mismo tamaño (pero de un solo canal) que la de nuestra imagen de entrada, donde
cada píxel corresponde a la probabilidad de que ese píxel pertenezca a nuestro objeto. En mundos más simples, la imagen de salida
tendrá nuestro objeto de interés más blanco en comparación con la parte restante. Bueno, esa es una explicación intuitiva. (No puedo
hacerlo más simple). La retroproyección de histograma se utiliza con el algoritmo camshift, etc.
Cómo lo hacemos ? Creamos un histograma de una imagen que contenga nuestro objeto de interés (en nuestro caso, el suelo,
dejando jugador y otras cosas). El objeto debe llenar la imagen tanto como sea posible para obtener mejores resultados. Y se prefiere
un histograma de color sobre un histograma en escala de grises, porque el color del objeto es una mejor manera de definir el objeto
que su intensidad en escala de grises. Luego, "reproyectamos" este histograma sobre nuestra imagen de prueba donde necesitamos
encontrar el objeto, es decir, en otras palabras, calculamos la probabilidad de que cada píxel pertenezca al suelo y lo mostramos. La
salida resultante en el umbral adecuado nos da el terreno solo.
Algoritmo en Numpy
1. Primero necesitamos calcular el histograma de color tanto del objeto que necesitamos encontrar (que sea 'M') como de la imagen
donde vamos a buscar (que sea 'I').
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
#roi es el objeto o región del objeto que necesitamos encontrar roi = cv2.imread('rose_red.png')
hsv = cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)
#target es la imagen que buscamos en target =
cv2.imread('rose.png') hsvt =
cv2.cvtColor(target,cv2.COLOR_BGR2HSV)
# Encuentra los histogramas usando calcHist. Se puede hacer con np.histogram2d también M = cv2.calcHist([hsv],[0, 1], None,
[180, 256], [0, 180, 0, 256] )
I = cv2.calcHist([hsvt],[0, 1], Ninguno, [180, 256], [0, 180, 0, 256] )
2. Encuentre la relación = Luego
. retroproyecte R, es decir, use R como paleta y cree una nueva imagen con cada píxel como su
probabilidad correspondiente de ser objetivo. es decir, B(x,y) = R[h(x,y),s(x,y)] donde h es el matiz ys es la saturación del píxel en
(x,y). Después de eso, aplique la condición ( , ) = [ ( , ), 1].
h,s,v = cv2.split(hsvt)
B = R[h.ravel(),s.ravel()]
B = np.mínimo(B,1)
B = B.reformar(hsvt.forma[:2])
3. Ahora aplique una convolución con un disco circular, = * , donde D es el núcleo del disco.
disco = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5)) cv2.filter2D(B,1,disco,B)
122 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
B = np.uint8(B)
cv2.normalizar(B,B,0,255,cv2.NORM_MINMAX)
4. Ahora la ubicación de máxima intensidad nos da la ubicación del objeto. Si esperamos una región en la imagen, el umbral para un
valor adecuado da un buen resultado.
ret,umbral = cv2.umbral(B,50,255,0)
Eso es todo !!
Retroproyección en OpenCV
OpenCV proporciona una función incorporada cv2.calcBackProject(). Sus parámetros son casi los mismos que los de la función
cv2.calcHist() . Uno de sus parámetros es el histograma, que es el histograma del objeto y tenemos que encontrarlo. Además, el
histograma del objeto debe normalizarse antes de pasar a la función backproject. Devuelve la imagen de probabilidad. Luego
convolucionamos la imagen con un núcleo de disco y aplicamos el umbral. A continuación se muestra mi código y salida:
importar cv2
importar numpy como np
roi = cv2.imread('rosa_roja.png') hsv =
cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)
objetivo = cv2.imread('rosa.png') hsvt =
cv2.cvtColor(objetivo,cv2.COLOR_BGR2HSV)
# calculando el histograma del objeto roihist =
cv2.calcHist([hsv],[0, 1], None, [180, 256], [0, 180, 0, 256] )
# normaliza el histograma y aplica retroproyección
cv2.normalize(roihist,roihist,0,255,cv2.NORM_MINMAX) dst = cv2.calcBackProject([hsvt],
[0,1],roihist,[0,180,0,256],1)
# Ahora convoluta con disco circular
disco = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5)) cv2.filter2D(dst,1,disco,dst)
# umbral y AND binario ret,thresh =
cv2.threshold(dst,50,255,0) thresh = cv2.merge((thresh,thresh,thresh))
res = cv2.bitwise_and(target,thresh)
res = np.vstack((objetivo,umbral,res)) cv2.imwrite('res.jpg',res)
A continuación se muestra un ejemplo con el que trabajé. Usé la región dentro del rectángulo azul como objeto de muestra y quería
extraer todo el terreno.
1.4. Procesamiento de imágenes en OpenCV 123
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
124 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
1. "Indización a través de histogramas de color", Swain, Michael J. , Tercera conferencia internacional sobre visión artificial, 1990.
Ejercicios
Transformaciones de imagen en OpenCV
• Transformada de Fourier
Aprende a encontrar la Transformada de Fourier de imágenes
1.4. Procesamiento de imágenes en OpenCV 125
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Transformada de Fourier
Meta
En esta sección aprenderemos
• Para encontrar la Transformada de Fourier de imágenes usando OpenCV
• Para utilizar las funciones FFT disponibles en Numpy
• Algunas aplicaciones de la Transformada de Fourier
• Veremos las siguientes funciones: cv2.dft(), cv2.idft() etc.
Teoría
La transformada de Fourier se utiliza para analizar las características de frecuencia de varios filtros. Para las imágenes, se utiliza la transformada
discreta de Fourier (DFT) 2D para encontrar el dominio de la frecuencia. Se utiliza un algoritmo rápido llamado Transformada Rápida de Fourier (FFT)
para el cálculo de DFT. Los detalles sobre estos se pueden encontrar en cualquier libro de texto de procesamiento de imágenes o procesamiento de señales.
Consulte la sección Recursos adicionales .
Para una señal sinusoidal, ( ) = sin(2 ), podemos decir que es la frecuencia de la señal, y si se toma su dominio de frecuencia, podemos ver un pico en
. discreta, obtenemos lo mismo dominio de frecuencia, pero es periódico en el rango [−, ] o [0, 2] (o [0, ]
Si la señal se muestrea para formar una señal
para DFT de N puntos). Puede considerar una imagen como una señal que se muestrea en dos direcciones. Entonces, tomar la transformada de Fourier
en las direcciones X e Y le da la representación de frecuencia de la imagen.
Más intuitivamente, para la señal sinusoidal, si la amplitud varía tan rápido en poco tiempo, se puede decir que es una señal de alta frecuencia. Si varía
lentamente, es una señal de baja frecuencia. Puedes extender la misma idea a las imágenes. ¿Dónde varía drásticamente la amplitud en las imágenes?
En los puntos de borde, o ruidos. Entonces podemos decir que los bordes y los ruidos son contenidos de alta frecuencia en una imagen. Si no hay
muchos cambios en la amplitud, es un componente de baja frecuencia. (Algunos enlaces se agregan a Recursos adicionales que explican la
transformación de frecuencia de manera intuitiva con ejemplos).
Ahora veremos cómo encontrar la transformada de Fourier.
Transformada de Fourier en Numpy
Primero veremos cómo encontrar la Transformada de Fourier usando Numpy. Numpy tiene un paquete FFT para hacer esto. np.fft.fft2() nos proporciona
la transformación de frecuencia que será una matriz compleja. Su primer argumento es la imagen de entrada, que está en escala de grises. El segundo
argumento es opcional y decide el tamaño de la matriz de salida. Si es mayor que el tamaño de la imagen de entrada, la imagen de entrada se rellena
con ceros antes del cálculo de FFT. Si es menor que la imagen de entrada, la imagen de entrada se recortará.
Si no se pasan argumentos, el tamaño de la matriz de salida será el mismo que el de la entrada.
Ahora, una vez que obtenga el resultado, el componente de frecuencia cero (componente de CC) estará en la esquina superior izquierda. Si desea
traerlo al centro, debe cambiar el resultado en ambas direcciones. Esto simplemente lo hace la función np.fft.fftshift(). (Es más fácil de analizar). Una
2
vez que haya encontrado la transformación de frecuencia, puede encontrar el espectro de magnitud.
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
img = cv2.imread('messi5.jpg',0) f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
magnitud_espectro = 20*np.log(np.abs(fshift))
126 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
plt.subplot(121),plt.imshow(img, cmap = 'gris') plt.title(' Imagen de
entrada'), plt.xticks([]), plt.yticks([]) plt.subplot(122) ,plt.imshow(magnitud_espectro,
cmap = 'gris') plt.title('Magnitud Espectro'), plt.xticks([]), plt.yticks([]) plt.show()
El resultado se ve a continuación:
Mira, puedes ver una región más blanca en el centro que muestra que el contenido de baja frecuencia es más.
Así que encontraste la transformada de frecuencia. Ahora puedes hacer algunas operaciones en el dominio de la frecuencia, como el filtrado de
paso alto y reconstruir la imagen, es decir, encontrar la DFT inversa. Para eso, simplemente elimina las frecuencias bajas enmascarando con
una ventana rectangular de tamaño 60x60. Luego aplique el cambio inverso usando np.fft.ifftshift() para que el componente DC vuelva a
aparecer en la esquina superior izquierda. Luego encuentre la FFT inversa usando la función np.ifft2() . El resultado, de nuevo, será un número
complejo. Puede tomar su valor absoluto.
filas, columnas = img.forma
cuervo,ccol = filas/2 columnas/2 ,
fshift[cuervo30:cuervo+30, ccol30:ccol+30] = 0 f_ishift =
np.fft.ifftshift(fshift) img_back = np .fft.ifft2(f_ishift)
img_back = np.abs(img_back)
plt.subplot(131),plt.imshow(img, cmap = 'gris') plt.title(' Imagen de
entrada'), plt.xticks([]), plt.yticks([]) plt.subplot(132) ,plt.imshow(img_back, cmap = 'gray')
plt.title('Imagen después de HPF'), plt.xticks([]), plt.yticks([])
plt.subplot(133),plt.imshow( img_back) plt.title('Resultado en JET'), plt.xticks([]), plt.yticks([])
plt.mostrar()
El resultado se ve a continuación:
1.4. Procesamiento de imágenes en OpenCV 127
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
El resultado muestra que el filtrado de paso alto es una operación de detección de bordes. Esto es lo que hemos visto en el capítulo Gradientes
de imagen. Esto también muestra que la mayoría de los datos de imagen están presentes en la región de baja frecuencia del espectro. De
todos modos, hemos visto cómo encontrar DFT, IDFT, etc. en Numpy. Ahora veamos cómo hacerlo en OpenCV.
Si observa de cerca el resultado, especialmente la última imagen en color JET, puede ver algunos artefactos (una instancia que he marcado
con una flecha roja). Muestra algunas estructuras similares a ondas allí, y se llama efectos de llamada. Es causado por la ventana rectangular
que usamos para enmascarar. Esta máscara se convierte en forma sincronizada, lo que provoca este problema. Entonces, las ventanas
rectangulares no se usan para filtrar. La mejor opción es Gaussian Windows.
Transformada de Fourier en OpenCV
OpenCV proporciona las funciones cv2.dft() y cv2.idft() para esto. Devuelve el mismo resultado que el anterior, pero con dos canales. El primer
canal tendrá la parte real del resultado y el segundo canal tendrá la parte imaginaria del resultado. La imagen de entrada debe convertirse
primero a np.float32. Veremos cómo hacerlo.
importar numpy como np
importar cv2
desde matplotlib importar pyplot como plt
img = cv2.imread('messi5.jpg',0)
dft = cv2.dft(np.float32(img),flags = cv2.DFT_COMPLEX_OUTPUT) dft_shift = np.fft.fftshift(dft)
magnitud_espectro = 20*np.log(cv2.magnitud(dft_shift[:,:,0],dft_shift[:,:,1]))
plt.subplot(121),plt.imshow(img, cmap = 'gris') plt.title(' Imagen de
entrada'), plt.xticks([]), plt.yticks([]) plt.subplot(122) ,plt.imshow(magnitud_espectro,
cmap = 'gris') plt.title('Magnitud Espectro'), plt.xticks([]), plt.yticks([]) plt.show()
Nota: también puede usar cv2.cartToPolar() que devuelve tanto la magnitud como la fase en un solo disparo
Entonces, ahora tenemos que hacer DFT inversa. En la sesión anterior, creamos un HPF, esta vez veremos cómo eliminar los contenidos de
alta frecuencia en la imagen, es decir, aplicamos LPF a la imagen. De hecho, desenfoca la imagen. Para esto, primero creamos una máscara
con valor alto (1) en bajas frecuencias, es decir, pasamos el contenido de LF y 0 en la región de HF.
filas, cols = img.shape cuervo,ccol
= filas/2 , columnas/2
128 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
# crear una máscara primero, el cuadrado central es 1, el resto todos los ceros mask =
np.zeros((rows,cols,2),np.uint8) mask[crow30:crow+30,
ccol30:ccol+30] = 1
# aplicar máscara y DFT inversa fshift =
dft_shift*mask
f_ishift = np.fft.ifftshift(fshift) img_back = cv2.idft(f_ishift)
img_back =
cv2.magnitude(img_back[:,:,0],img_back[:,:,1])
plt.subplot(121),plt.imshow(img, cmap = 'gris') plt.title(' Imagen de
entrada'), plt.xticks([]), plt.yticks([]) plt.subplot(122) ,plt.imshow(img_back, cmap = 'gris')
plt.title('Espectro de magnitud'), plt.xticks([]), plt.yticks([]) plt.show()
Vea el resultado:
Nota: Como de costumbre, las funciones de OpenCV cv2.dft() y cv2.idft() son más rápidas que las contrapartes de Numpy. Pero las funciones de Numpy son
más fáciles de usar. Para obtener más detalles sobre los problemas de rendimiento, consulte la sección a continuación.
Optimización del rendimiento de DFT
El rendimiento del cálculo de DFT es mejor para algunos tamaños de matriz. Es más rápido cuando el tamaño de la matriz es potencia de
dos. Las matrices cuyo tamaño es un producto de 2, 3 y 5 también se procesan de manera bastante eficiente. Entonces, si le preocupa el
rendimiento de su código, puede modificar el tamaño de la matriz a cualquier tamaño óptimo (rellenando con ceros) antes de encontrar DFT.
Para OpenCV, debe rellenar manualmente los ceros. Pero para Numpy, especifica el nuevo tamaño del cálculo de FFT, y automáticamente
rellenará los ceros por usted.
Entonces, ¿cómo encontramos este tamaño óptimo? OpenCV proporciona una función, cv2.getOptimalDFTSize() para esto. Es aplicable
tanto a cv2.dft() como a np.fft.fft2(). Verifiquemos su rendimiento usando el comando mágico de IPython %timeit.
En [16]: img = cv2.imread('messi5.jpg',0)
En [17]: filas,columnas = img.forma En [18]:
imprimir filas,columnas
342 548
En [19]: nrows = cv2.getOptimalDFTSize(filas)
En [20]: ncols = cv2.getOptimalDFTSize(cols)
En [21]: imprimir nrows, ncols 360 576
1.4. Procesamiento de imágenes en OpenCV 129
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Mira, el tamaño (342,548) se modifica a (360, 576). Ahora rellenémoslo con ceros (para OpenCV) y encontremos su rendimiento de
cálculo DFT. Puede hacerlo creando una nueva matriz de ceros grandes y copiando los datos en ella, o usando cv2.copyMakeBorder().
nimg = np.zeros((nrows,ncols)) nimg[:rows,:cols]
= img
O:
derecha = ncols cols bottom
= nrows filas
bordertype = cv2.BORDER_CONSTANT #solo para evitar la separación de líneas en el archivo PDF nimg =
cv2.copyMakeBorder(img,0,bottom,0,right,bordertype, value = 0)
Ahora calculamos la comparación de rendimiento DFT de la función Numpy:
En [22]: %timeit fft1 = np.fft.fft2(img) 10 bucles, lo mejor de 3:
40,9 ms por bucle En [23]: %timeit fft2 = np.fft.fft2(img,
[nrows,ncols] ) 100 bucles, lo mejor de 3: 10,4 ms por bucle
Muestra una aceleración de 4x. Ahora intentaremos lo mismo con las funciones de OpenCV.
En [24]: %timeit dft1= cv2.dft(np.float32(img),flags=cv2.DFT_COMPLEX_OUTPUT) 100 bucles, lo mejor de 3: 13,5 ms
por bucle En [27]: %timeit dft2=
cv2.dft( np.float32(nimg),flags=cv2.DFT_COMPLEX_OUTPUT) 100 bucles, lo mejor de 3: 3,11 ms por bucle
También muestra una aceleración de 4x. También puede ver que las funciones de OpenCV son alrededor de 3 veces más rápidas que las funciones de
Numpy. Esto también se puede probar para FFT inversa, y eso se deja como ejercicio para usted.
¿Por qué Laplacian es un filtro de paso alto?
Se hizo una pregunta similar en un foro. La pregunta es, ¿por qué Laplacian es un filtro de paso alto? ¿Por qué Sobel es una HPF? etc.
Y la primera respuesta que se le dio fue en términos de la Transformada de Fourier. Simplemente tome la transformada de Fourier de
Laplacian para un tamaño mayor de FFT. Analízalo:
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
# filtro promedio simple sin parámetro de escala mean_filter = np.ones((3,3))
# creando un filtro guassiano x =
cv2.getGaussianKernel(5,10) gaussian = x*xT
# diferentes filtros de detección de bordes # scharr en
dirección x scharr = np.array([[3, 0,
3], [10,0,10], [3, 0, 3]]) # sobel en x dirección
sobel_x= np.matriz([[1, 0, 1], [2, 0, 2], [1, 0,
1]])
130 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
# sobel en dirección y sobel_y=
np.array([[1,2,1], [0, 0, 0], [1, 2, 1]])
# laplacian
laplacian=np.array([[0, 1, 0], [1,4, 1], [0, 1, 0]])
filtros = [filtro_medio, gaussiano, laplaciano, sobel_x, sobel_y, scharr] nombre_filtro = ['filtro_medio',
'gaussiano','laplaciano', 'sobel_x', \ 'sobel_y', 'scharr_x']
fft_filters = [np.fft.fft2(x) para x en filtros] fft_shift = [np.fft.fftshift(y) para y
en fft_filters] mag_spectrum = [np.log(np.abs(z)+1) para z en fft_shift]
para i en xrange(6):
plt.subplot(2,3,i+1),plt.imshow(mag_spectrum[i],cmap = 'gray') plt.title(filter_name[i]), plt.xticks( []),
plt.yticks([])
plt.mostrar()
Vea el resultado:
En la imagen, puede ver qué región de frecuencia bloquea cada kernel y qué región pasa. A partir de esa información, podemos decir por
qué cada kernel es un HPF o un LPF
1.4. Procesamiento de imágenes en OpenCV 131
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
1. Una explicación intuitiva de la teoría de Fourier por Steven Lehar
2. Transformada de Fourier en HIPR
3. ¿Qué denota el dominio de la frecuencia en el caso de las imágenes?
Ejercicios
Comparación de plantillas
Objetivos
En este capítulo, aprenderá
• Para buscar objetos en una imagen mediante Coincidencia de plantillas
• Verá estas funciones: cv2.matchTemplate(), cv2.minMaxLoc()
Teoría
Coincidencia de plantillas es un método para buscar y encontrar la ubicación de una imagen de plantilla en una imagen más grande. OpenCV
viene con una función cv2.matchTemplate() para este propósito. Simplemente desliza la imagen de la plantilla sobre la imagen de entrada (como
en la convolución 2D) y compara la plantilla y el parche de la imagen de entrada debajo de la imagen de la plantilla. Varios métodos de
comparación están implementados en OpenCV. (Puede consultar los documentos para obtener más detalles). Devuelve una imagen en escala
de grises, donde cada píxel indica cuánto coincide la vecindad de ese píxel con la plantilla.
Si la imagen de entrada tiene un tamaño (ancho x alto) y la imagen de la plantilla tiene un tamaño (ancho x alto), la imagen de salida tendrá un tamaño de (anchoancho+1, altoalto+1).
Una vez que obtuvo el resultado, puede usar la función cv2.minMaxLoc() para encontrar dónde está el valor máximo/mínimo. Tómelo como la
esquina superior izquierda del rectángulo y tome (w,h) como ancho y alto del rectángulo. Ese rectángulo es su región de plantilla.
Nota: si utiliza cv2.TM_SQDIFF como método de comparación, el valor mínimo proporciona la mejor coincidencia.
Coincidencia de plantillas en OpenCV
Aquí, como ejemplo, buscaremos la cara de Messi en su foto. Así que creé una plantilla de la siguiente manera:
Probaremos todos los métodos de comparación para que podamos ver cómo se ven sus resultados:
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
img = cv2.imread('messi5.jpg',0) img2 = img.copia()
plantilla =
cv2.imread('plantilla.jpg',0)
132 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
w, h = plantilla.forma[::1]
# Los 6 métodos para comparar en una lista de métodos =
['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']
para metanfetamina en métodos:
img = img2.copy() método
= eval(meth)
# Aplicar plantilla Coincidencia res =
cv2.matchTemplate(img,plantilla,método) min_val, max_val, min_loc,
max_loc = cv2.minMaxLoc(res)
# Si el método es TM_SQDIFF o TM_SQDIFF_NORMED, tome el método mínimo si en
[cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]: top_left = min_loc else:
top_left = max_loc
abajo_derecha = (arriba_izquierda[0] + w, arriba_izquierda[1] + h)
cv2.rectangle(img,arriba_izquierda, abajo_derecha, 255, 2)
plt.subplot(121),plt.imshow(res,cmap = 'gray') plt.title(' Resultado coincidente
'), plt.xticks([]), plt.yticks([]) plt.subplot(122) ,plt.imshow(img,cmap = 'gray') plt.title(' Punto detectado
'), plt.xticks([]), plt.yticks([]) plt.suptitle(meth)
plt.mostrar()
Vea los resultados a continuación:
• cv2.TM_CCOEFF
• cv2.TM_CCOEFF_NORMED
1.4. Procesamiento de imágenes en OpenCV 133
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
• cv2.TM_CCORR
• cv2.TM_CCORR_NORMED
• cv2.TM_SQDIFF
134 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
• cv2.TM_SQDIFF_NORMED
Puede ver que el resultado usando cv2.TM_CCORR no es tan bueno como esperábamos.
Coincidencia de plantillas con varios objetos
En la sección anterior, buscamos la imagen de la cara de Messi, que aparece solo una vez en la imagen. Suponga que está buscando
un objeto que tiene múltiples ocurrencias, cv2.minMaxLoc() no le dará todas las ubicaciones. En ese caso, utilizaremos el umbral.
Entonces, en este ejemplo, usaremos una captura de pantalla del famoso juego Mario y encontraremos las monedas en él.
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
img_rgb = cv2.imread('mario.png') img_gray =
cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY) template =
cv2.imread('mario_coin.png',0) w, h = template.shape[::1 ]
res = cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED) umbral = 0,8
1.4. Procesamiento de imágenes en OpenCV 135
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
loc = np.where(res >= umbral) para pt en
zip(*loc[::1]):
cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2)
cv2.imwrite('res.png',img_rgb)
Resultado:
Recursos adicionales
Ejercicios
Transformación de línea de Hough
Meta
En este capítulo,
• Comprenderemos el concepto de Transformada de Hough.
• Veremos cómo usarlo para detectar líneas en una imagen.
• Veremos las siguientes funciones: cv2.HoughLines(), cv2.HoughLinesP()
Teoría
Hough Transform es una técnica popular para detectar cualquier forma, si puede representar esa forma en forma matemática. Puede
detectar la forma incluso si está rota o distorsionada un poco. Veremos cómo funciona para una línea.
Una recta se puede representar como = + o en forma paramétrica, como = cos + sin donde es la distancia perpendicular desde el origen
hasta la recta, y es el ángulo formado por esta recta perpendicular y el eje horizontal medido en sentido contrario a las agujas del reloj
(esa dirección varía sobre cómo representas el sistema de coordenadas. Esta representación se usa en OpenCV). Compruebe la
imagen de abajo:
136 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Entonces, si la línea pasa por debajo del origen, tendrá un rho positivo y un ángulo menor de 180. Si pasa por encima del origen, en lugar de
tomar un ángulo mayor de 180, se toma un ángulo menor de 180 y rho se toma negativo. Cualquier línea vertical tendrá 0 grados y las líneas
horizontales tendrán 90 grados.
Ahora veamos cómo funciona Hough Transform para las líneas. Cualquier línea se puede representar en estos dos términos, ( , ). Entonces,
primero crea una matriz o acumulador 2D (para contener valores de dos parámetros) y se establece en 0 inicialmente. Deje que las filas
indiquen y las columnas . El tamaño de la matriz depende de la precisión que necesite. Supongamos que desea la precisión de los
indiquen que sea 1 grado, necesita 180 columnas. , ángulos, la distancia máxima posible es la longitud diagonal de la imagen. Entonces
Para obtener una precisión de un píxel, el número de filas puede ser la longitud diagonal de la imagen.
Considere una imagen de 100x100 con una línea horizontal en el medio. Toma el primer punto de la línea. Conoces sus valores (x, y). Ahora
en la ecuación de la línea, pon los valores = 0, 1, 2, ...., 180 y comprueba lo que obtienes. Por cada par ( , ), incrementas el valor en uno en
nuestro acumulador en sus celdas correspondientes ( , ). Así que ahora en el acumulador, la celda (50,90) = 1 junto con algunas otras celdas.
Ahora toma el segundo punto de la línea. Haz lo mismo que arriba. Incrementa los valores en las celdas correspondientes a ( , ) que obtuviste.
Esta vez, la celda (50,90) = 2. Lo que realmente haces es votar los valores ( , ). Continúa este proceso para cada punto de la línea. En cada
punto, la celda (50,90) se incrementará o votará a favor, mientras que otras celdas pueden o no votarse a favor. Así, al final, la celda (50,90)
tendrá el máximo de votos. Entonces, si busca en el acumulador el máximo de votos, obtiene el valor (50,90) que dice que hay una línea en
esta imagen a una distancia de 50 desde el origen y en un ángulo de 90 grados. Se muestra bien en la siguiente animación (Imagen cortesía:
Amos Storkey) )
Así es como funciona Hough transform for lines. Es simple, y puede ser que puedas implementarlo usando Numpy por tu cuenta. A
continuación se muestra una imagen que muestra el acumulador. Los puntos brillantes en algunos lugares indican que son los parámetros
de posibles líneas en la imagen. (Imagen cortesía: Wikipedia )
Transformada de Hough en OpenCV
Todo lo explicado anteriormente está encapsulado en la función OpenCV, cv2.HoughLines(). Simplemente devuelve una matriz de valores
( , ). se mide en píxeles y se mide en radianes. El primer parámetro, la imagen de entrada debe ser una imagen binaria, por lo tanto, aplique
el umbral o use la detección de bordes astutos antes de encontrar la aplicación de la transformación Hough. Los parámetros segundo y
tercero son y precisiones respectivamente. El cuarto argumento es el umbral, lo que significa el voto mínimo que debe obtener para que se
considere como una línea. Recuerde, el número de votos depende del número de puntos en la línea. Por lo tanto, representa la longitud
mínima de línea que debe detectarse.
importar cv2
importar numpy como np
1.4. Procesamiento de imágenes en OpenCV 137
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
img = cv2.imread('dave.jpg') gris =
cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) bordes =
cv2.Canny(gray,50,150,apertureSize = 3)
líneas = cv2.HoughLines(bordes,1,np.pi/180,200) para rho,theta en
líneas[0]: a = np.cos(theta) b =
np.sin(theta) x0 = a*rho
y0 = b*rho x1 =
int(x0 + 1000*(b)) y1 = int(y0 +
1000*(a)) x2 = int(x0 1000*(b)) y2
= int(y0 1000 *(a))
cv2.línea(img,(x1,y1),(x2,y2),(0,0,255),2)
cv2.imwrite('houghlines3.jpg',img)
Compruebe los resultados a continuación:
138 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Transformada probabilística de Hough
En la transformada de Hough, puede ver que incluso para una línea con dos argumentos, se necesitan muchos cálculos.
Probabilistic Hough Transform es una optimización de Hough Transform que vimos. No tiene en cuenta todos los puntos, sino que
toma solo un subconjunto aleatorio de puntos y eso es suficiente para la detección de líneas. Solo tenemos que disminuir el umbral.
Vea la imagen a continuación que compara la Transformada de Hough y la Transformada de Hough probabilística en el espacio de Hough.
(Imagen cortesía: página de inicio de Franck Bettinger
1.4. Procesamiento de imágenes en OpenCV 139
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
La implementación de OpenCV se basa en la detección robusta de líneas utilizando la transformada de Hough probabilística progresiva de Matas
• minLineLength Longitud mínima de la línea. Los segmentos de línea más cortos que esto son rechazados.
• maxLineGap : espacio máximo permitido entre segmentos de línea para tratarlos como una sola línea.
Lo mejor es que devuelve directamente los dos extremos de las líneas. En el caso anterior, solo obtuvo los parámetros de las líneas y tuvo
que encontrar todos los puntos. Aquí, todo es directo y simple.
140 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
importar cv2
importar numpy como np
img = cv2.imread('dave.jpg') gris =
cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) bordes =
cv2.Canny(gray,50,150,apertureSize = 3) minLineLength = 100
maxLineGap = 10 líneas =
cv2.HoughLinesP( bordes,1,np.pi/180,100,minLineLength,maxLineGap) para x1,y1,x2,y2 en líneas[0]:
cv2.line(img,(x1,y1),(x2,y2),(0,255,0 ),2)
cv2.imwrite('houghlines5.jpg',img)
Vea los resultados a continuación:
1.4. Procesamiento de imágenes en OpenCV 141
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
1. Transformada de Hough en Wikipedia
Ejercicios
Transformación del círculo de Hough
Meta
En este capítulo,
• Aprenderemos a usar la Transformada de Hough para encontrar círculos en una imagen.
• Veremos estas funciones: cv2.HoughCircles()
Teoría
2 2 = 2
Un círculo se representa matemáticamente como ( − + ( − donde
) ( ) es el centro del
)círculo ,
y es el radio del círculo.
A partir de la
ecuación, podemos ver que tenemos 3 parámetros, por lo que necesitamos un acumulador 3D para la transformación Hough, lo que
sería muy ineficaz Por lo tanto, OpenCV utiliza un método más complicado, el Método de gradiente de Hough , que utiliza la información
de gradiente de los bordes.
La función que usamos aquí es cv2.HoughCircles(). Tiene muchos argumentos que están bien explicados en la documentación. Así
que vamos directamente al código.
importar cv2
importar numpy como np
img = cv2.imread('opencv_logo.png',0) img = cv2.medianBlur(img,5)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
círculos = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,20,
param1=50,param2=30,minRadius=0,maxRadius=0)
círculos = np.uint16(np.alrededor(círculos)) para i en círculos[0,:]:
# dibujar el círculo exterior
cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2) # dibujar el centro del círculo
cv2.círculo(cimg,(i[0],i[1]),2,(0,0,255),3)
cv2.imshow(' círculos detectados',cimg) cv2.waitKey(0)
cv2.destroyAllWindows()
El resultado se muestra a continuación:
142 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
Ejercicios
Segmentación de imágenes con algoritmo Watershed
Meta
En este capítulo,
• Aprenderemos a usar la segmentación de imágenes basada en marcadores usando el algoritmo de cuencas hidrográficas
• Veremos: cv2.watershed()
Teoría
Cualquier imagen en escala de grises se puede ver como una superficie topográfica donde la alta intensidad denota picos y colinas, mientras
que la baja intensidad denota valles. Empiezas llenando cada valle aislado (mínimo local) con agua de diferentes colores (etiquetas).
A medida que el agua sube, dependiendo de los picos (gradientes) cercanos, el agua de diferentes valles, obviamente con diferentes colores,
comenzará a fusionarse. Para evitar eso, construye barreras en los lugares donde el agua se fusiona. Continúas el trabajo de llenar de agua
y construir barreras hasta que todos los picos estén bajo el agua. Luego, las barreras que creaste te dan el resultado de la segmentación.
Esta es la “filosofía” detrás de la línea divisoria de aguas. Puede visitar la página web de CMM sobre la cuenca para entenderlo con la ayuda
de algunas animaciones.
Pero este enfoque le brinda un resultado sobresegmentado debido al ruido o cualquier otra irregularidad en la imagen. Entonces, OpenCV
implementó un algoritmo de cuenca hidrográfica basado en marcadores en el que especifica cuáles son todos los puntos del valle que se
fusionarán y cuáles no. Es una segmentación de imágenes interactiva. Lo que hacemos es dar diferentes etiquetas a nuestro objeto que conocemos.
Etiquetar la región que estamos seguros de ser el primer plano u objeto con un color (o intensidad), etiquetar la región que estamos seguros
de ser el fondo o no objeto con otro color y finalmente la región de la que no estamos seguros de nada, etiquételo con 0. Ese es nuestro
marcador. Luego aplique el algoritmo de cuenca hidrográfica. Luego, nuestro marcador se actualizará con las etiquetas que le dimos, y los
límites de los objetos tendrán un valor de 1.
1.4. Procesamiento de imágenes en OpenCV 143
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Código
A continuación, veremos un ejemplo de cómo usar la Transformación de distancia junto con la línea divisoria de aguas para segmentar objetos
que se tocan entre sí.
Considere la imagen de las monedas a continuación, las monedas se tocan entre sí. Incluso si lo limita, se tocarán entre sí.
Empezamos por encontrar una estimación aproximada de las monedas. Para eso, podemos usar la binarización de Otsu.
importar numpy como np
importar cv2
desde matplotlib importar pyplot como plt
img = cv2.imread('monedas.png') gris =
cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret, thresh =
cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
Resultado:
144 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Ahora necesitamos eliminar cualquier pequeño ruido blanco en la imagen. Para eso podemos usar la apertura morfológica. Para eliminar
pequeños agujeros en el objeto, podemos usar el cierre morfológico. Entonces, ahora sabemos con certeza que la región cercana al centro
de los objetos está en primer plano y la región muy alejada del objeto está en segundo plano. La única región de la que no estamos seguros
es la región límite de las monedas.
Entonces necesitamos extraer el área que estamos seguros de que son monedas. La erosión elimina los píxeles del límite. Entonces, lo que
quede, podemos estar seguros de que es moneda. Eso funcionaría si los objetos no se tocaran entre sí. Pero dado que se tocan entre sí,
otra buena opción sería encontrar la transformación de distancia y aplicar un umbral adecuado. A continuación, debemos encontrar el área
en la que estamos seguros de que no son monedas. Para eso, dilatamos el resultado. La dilatación aumenta el límite del objeto al fondo. De
esta manera, podemos asegurarnos de que cualquier región en el fondo que resulte sea realmente un fondo, ya que se elimina la región
límite. Vea la imagen a continuación.
1.4. Procesamiento de imágenes en OpenCV 145
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Las regiones restantes son aquellas de las que no tenemos idea, ya sean monedas o fondo. El algoritmo Watershed debería encontrarlo.
Estas áreas normalmente se encuentran alrededor de los límites de las monedas donde se encuentran el primer plano y el fondo (o
incluso se encuentran dos monedas diferentes). Lo llamamos frontera. Se puede obtener restando el área sure_fg del área sure_bg.
# eliminación de ruido
kernel = np.ones((3,3),np.uint8) apertura =
cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iteraciones = 2)
# sure background area sure_bg
= cv2.dilate(opening,kernel,iterations=3)
# Encontrar un área de primer plano segura
dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5) ret, sure_fg =
cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)
# Encontrar región desconocida
sure_fg = np.uint8(sure_fg) unknown =
cv2.subtract(sure_bg,sure_fg)
Vea el resultado. En la imagen umbralizada, tenemos algunas regiones de monedas de las que estamos seguros y ahora están separadas.
(En algunos casos, es posible que solo le interese la segmentación del primer plano, no la separación de los objetos que se tocan entre
sí. En ese caso, no necesita usar la transformación de distancia, solo la erosión es suficiente. La erosión es solo otro método para extraer
un área de primer plano segura, eso es todo.)
146 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Ahora sabemos con certeza cuáles son la región de las monedas, cuáles son el fondo y todo. Entonces creamos un marcador (es una matriz del
mismo tamaño que la imagen original, pero con tipo de datos int32) y etiquetamos las regiones dentro de él. Las regiones que sabemos con
seguridad (ya sea en primer plano o en segundo plano) están etiquetadas con números enteros positivos, pero con números enteros diferentes, y el
área que no sabemos con certeza se deja como cero. Para esto usamos cv2.connectedComponents(). Etiqueta el fondo de la imagen con 0, luego
otros objetos se etiquetan con números enteros a partir de 1.
Pero sabemos que si el fondo está marcado con 0, la cuenca lo considerará como un área desconocida. Entonces queremos marcarlo con un entero
diferente. En su lugar, marcaremos la región desconocida, definida por desconocido, con 0.
# Marcador etiquetado ret,
marcadores = cv2.connectedComponents(sure_fg)
# Agregue uno a todas las etiquetas para que el fondo seguro no sea 0, sino 1 marcadores =
marcadores + 1
# Ahora, marca la región de desconocido con cero
marcadores[desconocido==255] = 0
Vea el resultado que se muestra en el mapa de colores JET. La región azul oscuro muestra una región desconocida. Las monedas seguras están
coloreadas con diferentes valores. El área restante que es un fondo seguro se muestra en azul más claro en comparación con la región desconocida.
1.4. Procesamiento de imágenes en OpenCV 147
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Ahora nuestro marcador está listo. Es hora del paso final, aplicar la cuenca hidrográfica. Luego se modificará la imagen del marcador. La región
límite se marcará con 1.
marcadores = cv2.watershed(img,marcadores)
img[marcadores == 1] = [255,0,0]
Vea el resultado a continuación. Para algunas monedas, la región donde se tocan está segmentada correctamente y para algunas no lo están.
148 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
1. Página del CMM sobre Transformación de Cuencas Hidrográficas
Ejercicios
1. Las muestras de OpenCV tienen una muestra interactiva sobre la segmentación de cuencas hidrográficas, watershed.py. Ejecútalo, disfrútalo, luego
aprenderlo.
Extracción interactiva de primer plano utilizando el algoritmo GrabCut
Meta
En este capítulo
• Veremos el algoritmo GrabCut para extraer el primer plano en las imágenes
• Crearemos una aplicación interactiva para esto.
Teoría
El algoritmo GrabCut fue diseñado por Carsten Rother, Vladimir Kolmogorov y Andrew Blake de Microsoft Research Cambridge, Reino Unido. en su
.
artículo, "GrabCut": extracción interactiva de primer plano utilizando cortes de gráfico iterados Se necesitaba un algoritmo para la extracción
de primer
plano con una interacción mínima del usuario, y el resultado fue GrabCut.
¿Cómo funciona desde el punto de vista del usuario? Inicialmente, el usuario dibuja un rectángulo alrededor de la región de primer plano (la región de
primer plano debe estar completamente dentro del rectángulo). Luego, el algoritmo lo segmenta iterativamente para obtener el mejor resultado. Hecho.
Pero en algunos casos, la segmentación no estará bien, por ejemplo, puede haber marcado alguna región de primer plano como fondo.
1.4. Procesamiento de imágenes en OpenCV 149
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
y viceversa. En ese caso, el usuario necesita hacer retoques finos. Simplemente dé algunos trazos en las imágenes donde haya algunos resultados
defectuosos. Strokes básicamente dice "Oye, esta región debería estar en primer plano, la marcaste como fondo, corrígela en la próxima iteración" o
su opuesto para el fondo. Luego, en la siguiente iteración, obtendrá mejores resultados.
Vea la imagen a continuación. El primer jugador y el balón de fútbol están encerrados en un rectángulo azul. Luego, se realizan algunos retoques
finales con trazos blancos (que denotan el primer plano) y trazos negros (que denotan el fondo). Y obtenemos un buen resultado.
Entonces, ¿qué sucede en segundo plano?
• El usuario ingresa el rectángulo. Todo lo que esté fuera de este rectángulo se tomará como fondo seguro (esa es la razón por la que se mencionó
antes que su rectángulo debe incluir todos los objetos). Todo lo que está dentro del rectángulo es desconocido. Del mismo modo, cualquier
entrada del usuario que especifique el primer plano y el fondo se considera de etiquetado estricto, lo que significa que no cambiará en el
proceso.
• La computadora hace un etiquetado inicial dependiendo de los datos que le dimos. Etiqueta los píxeles de primer plano y de fondo.
(o etiquetas duras)
• Ahora se utiliza un modelo de mezcla gaussiana (GMM) para modelar el primer plano y el fondo.
• Dependiendo de los datos que proporcionamos, GMM aprende y crea una nueva distribución de píxeles. Es decir, los píxeles desconocidos se
etiquetan como primer plano probable o fondo probable según su relación con los otros píxeles etiquetados de forma rígida en términos de
estadísticas de color (es como agrupar).
• Se construye un gráfico a partir de esta distribución de píxeles. Los nodos en los gráficos son píxeles. Se agregan dos nodos adicionales, el
nodo de origen y el nodo de receptor. Cada píxel de primer plano está conectado al nodo Fuente y cada píxel de fondo está conectado al nodo
Sumidero.
• Los pesos de los bordes que conectan los píxeles al nodo de origen/nodo final se definen por la probabilidad de que un píxel esté en primer
plano/fondo. Los pesos entre los píxeles se definen por la información del borde o la similitud de píxeles.
Si hay una gran diferencia en el color de los píxeles, el borde entre ellos tendrá un peso bajo.
• Luego se usa un algoritmo mincut para segmentar el gráfico. Corta el gráfico en dos nodos fuente y nodo sumidero separados con una función
de costo mínimo. La función de costo es la suma de todos los pesos de los bordes que se cortan. Después del corte, todos los píxeles
conectados al nodo Fuente pasan a primer plano y los conectados al nodo Sumidero pasan a segundo plano.
• El proceso continúa hasta que la clasificación converge.
Se ilustra en la imagen de abajo (Imagen cortesía: http://www.cs.ru.ac.za/research/g02m1682/)
150 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Manifestación
Ahora vamos por el algoritmo Grabcut con OpenCV. OpenCV tiene la función cv2.grabCut() para esto. Primero veremos sus argumentos:
• img Imagen de entrada
• máscara: es una imagen de máscara donde especificamos qué áreas son fondo, primer plano o probable fondo/primer plano, etc. Se realiza
mediante las siguientes banderas, cv2.GC_BGD, cv2.GC_FGD, cv2.GC_PR_BGD, cv2.GC_PR_FGD, o simplemente pase 0,1,2,3 a la imagen.
• rect Son las coordenadas de un rectángulo que incluye el objeto de primer plano en el formato (x,y,w,h)
• bdgModel, fgdModel: estas son matrices utilizadas por el algoritmo internamente. Simplemente crea dos matrices de tipo cero np.float64 de tamaño
(1,65).
• iterCount: número de iteraciones que debe ejecutar el algoritmo.
• modo Debe ser cv2.GC_INIT_WITH_RECT o cv2.GC_INIT_WITH_MASK o combinados que de
decide si estamos dibujando un rectángulo o trazos finales de retoque.
Primero veamos con el modo rectangular. Cargamos la imagen, creamos una imagen de máscara similar. Creamos fgdModel y bgdModel. Damos los
parámetros del rectángulo. Todo es sencillo. Deje que el algoritmo se ejecute durante 5 iteraciones. El modo debe ser cv2.GC_INIT_WITH_RECT ya que
estamos usando un rectángulo. Luego ejecute el agarre. Modifica la imagen de la máscara.
En la nueva imagen de máscara, los píxeles se marcarán con cuatro banderas que denotan fondo/primer plano como se especificó anteriormente. Entonces
1.4. Procesamiento de imágenes en OpenCV 151
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
modificamos la máscara de modo que todos los píxeles 0 y 2 píxeles se ponen a 0 (es decir, fondo) y todos los píxeles 1 y 3 píxeles se ponen a 1 (es
decir, píxeles de primer plano). Ahora nuestra máscara final está lista. Simplemente multiplíquelo con la imagen de entrada para obtener la imagen
segmentada.
importar numpy como np
importar cv2
desde matplotlib importar pyplot como plt
img = cv2.imread('messi5.jpg') máscara =
np.zeros(img.forma[:2],np.uint8)
bgdModel = np.zeros((1,65),np.float64) fgdModel =
np.zeros((1,65),np.float64)
rect = (50,50,450,290)
cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)
máscara2 = np.where((máscara==2)|(máscara==0),0,1).astype('uint8') img =
img*máscara2[:,:,np.nuevoeje]
plt.imshow(img),plt.colorbar(),plt.show()
Vea los resultados a continuación:
Vaya, el pelo de Messi se ha ido. ¿A quién le gusta Messi sin pelo? Tenemos que traerlo de vuelta. Así que le daremos un buen retoque con 1 píxel
(primer plano seguro). Al mismo tiempo, una parte del terreno ha llegado a la imagen que no queremos, y también un logotipo. Necesitamos eliminarlos.
Allí le damos algún retoque de 0 píxeles (fondo seguro). Entonces modificamos nuestra máscara resultante en el caso anterior como dijimos ahora.
Lo que realmente hice es que abrí la imagen de entrada en la aplicación de pintura y agregué otra capa a la imagen. Usando la herramienta de pincel
en la pintura, marqué el primer plano perdido (pelo, zapatos, pelota, etc.) con blanco y el fondo no deseado (como logotipo, suelo, etc.) con negro en
esta nueva capa. Luego llenó el fondo restante con gris. Luego cargó esa imagen de máscara en OpenCV, la imagen de máscara original editada que
obtuvimos con los valores correspondientes en la imagen de máscara recién agregada. Verifique el código a continuación:
152 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
# newmask es la imagen de máscara que etiqueté manualmente newmask =
cv2.imread('newmask.png',0)
# donde sea que esté marcado en blanco (seguro en primer plano), cambie la máscara = 1 # donde sea
que esté marcado en negro (seguro en el fondo), cambie la máscara = 0 máscara [nueva máscara == 0] =
0
máscara[nueva máscara == 255] = 1
máscara, bgdModel, fgdModel = cv2.grabCut(img,máscara,Ninguno,bgdModel,fgdModel,5,cv2.GC_INIT_
→CON_MASCARILLA)
máscara = np.where((máscara==2)|(máscara==0),0,1).astype('uint8') img =
img*máscara[:,:,np.nuevoeje]
plt.imshow(img ),plt.colorbar(),plt.show()
Vea el resultado a continuación:
Eso es todo. Aquí, en lugar de inicializar en modo rect, puede pasar directamente al modo de máscara. Simplemente marque el área del
rectángulo en la imagen de la máscara con 2 o 3 píxeles (fondo/primer plano probable). Luego marque nuestro primer plano seguro con 1 píxel
como lo hicimos en el segundo ejemplo. Luego aplique directamente la función grabCut con el modo máscara.
Recursos adicionales
Ejercicios
1. Las muestras de OpenCV contienen una muestra grabcut.py, que es una herramienta interactiva que utiliza grabcut. Revisalo. También
mira este video de youtube sobre cómo usarlo.
2. Aquí, puede convertir esto en una muestra interactiva dibujando un rectángulo y trazos con el mouse, crear una barra de seguimiento
para ajustar el ancho del trazo, etc.
Detección y descripción de características
• Comprensión de las funciones
1.5. Detección y descripción de características 153
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
¿Cuáles son las características principales de una imagen? ¿Cómo nos puede ser útil encontrar esas
características?
• Detección de esquina Harris
Bien, ¿las esquinas son buenas características? Pero, ¿cómo los encontramos?
• Detector de esquinas ShiTomasi y buenas características para rastrear
Analizaremos la detección de esquinas de ShiTomasi
• Introducción a SIFT (Transformación de características de escala invariable)
El detector de esquina Harris no es lo suficientemente bueno cuando cambia la escala de la imagen.
Lowe desarrolló un método innovador para encontrar características invariantes de escala y se llama
SIFT
• Introducción a SURF (funciones robustas aceleradas)
SIFT es realmente bueno, pero no lo suficientemente rápido, por lo que a la gente se le ocurrió una
versión acelerada llamada SURF.
• Algoritmo FAST para detección de esquinas
154 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Todos los métodos de detección de características anteriores son buenos de alguna manera. Pero no
son lo suficientemente rápidos para trabajar en aplicaciones en tiempo real como SLAM. Ahí viene el
algoritmo FAST, que es realmente “FAST”.
• BRIEF (Características elementales independientes sólidas binarias)
SIFT utiliza un descriptor de características con 128 números de punto flotante. Considere miles de
tales características. Se necesita mucha memoria y más tiempo para hacer coincidir. Podemos
comprimirlo para hacerlo más rápido. Pero todavía tenemos que calcularlo primero. Viene BRIEF,
que brinda el atajo para encontrar descriptores binarios con menos memoria, una coincidencia más
rápida y una tasa de reconocimiento aún más alta.
• ORB (RÁPIDO Orientado y BREVE Girado)
SIFT y SURF son buenos en lo que hacen, pero ¿qué sucede si tiene que pagar unos cuantos dólares
cada año para usarlos en sus aplicaciones? ¡¡¡Sí, están patentados!!! Para resolver ese problema, los
desarrolladores de OpenCV idearon una nueva alternativa "GRATUITA" a SIFT & SURF, y es ORB.
• Coincidencia de características
Sabemos mucho sobre detectores y descriptores de características. Es hora de aprender a hacer
coincidir diferentes descriptores. OpenCV proporciona dos técnicas, el comparador de fuerza bruta y
el comparador basado en FLANN.
• Coincidencia de características + Homografía para encontrar objetos
Ahora sabemos acerca de la coincidencia de características. Combinémoslo con el módulo calib3d
para encontrar objetos en una imagen compleja.
1.5. Detección y descripción de características 155
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Características de comprensión
Meta
En este capítulo, solo intentaremos comprender qué son las características, por qué son importantes, por qué las esquinas son importantes, etc.
Explicación
La mayoría de ustedes habrán jugado a los juegos de rompecabezas. Obtiene muchas piezas pequeñas de imágenes, donde necesita
ensamblarlas correctamente para formar una gran imagen real. La pregunta es, ¿cómo lo haces? ¿Qué pasa con la proyección de la misma
teoría a un programa de computadora para que la computadora pueda jugar rompecabezas? Si la computadora puede jugar rompecabezas,
¿por qué no podemos darle muchas imágenes de la vida real de un buen paisaje natural a la computadora y decirle que una todas esas
imágenes en una sola imagen grande? Si la computadora puede unir varias imágenes naturales a una, ¿qué hay de dar muchas imágenes de
un edificio o cualquier estructura y decirle a la computadora que cree un modelo 3D a partir de eso?
Bueno, las preguntas y las imaginaciones continúan. ¿Pero todo depende de la pregunta más básica? ¿Cómo juegas a los rompecabezas?
¿Cómo se organizan muchas piezas de imágenes revueltas en una sola imagen grande? ¿Cómo se pueden unir muchas imágenes naturales a
una sola imagen?
La respuesta es que estamos buscando patrones específicos o características específicas que sean únicas, que se puedan rastrear fácilmente,
que se puedan comparar fácilmente. Si buscamos una definición de tal característica, puede que nos resulte difícil expresarla con palabras,
pero sabemos cuáles son. Si alguien le pide que señale una buena característica que se pueda comparar en varias imágenes, puede señalar
una. Por eso, incluso los niños pequeños pueden simplemente jugar estos juegos. Buscamos estas características en una imagen, las
encontramos, encontramos las mismas características en otras imágenes, las alineamos. Eso es todo. (En rompecabezas, nos fijamos más en
la continuidad de diferentes imágenes). Todas estas habilidades están presentes en nosotros inherentemente.
Entonces, nuestra única pregunta básica se expande a más en número, pero se vuelve más específica. ¿Cuáles son estas características?. (La
respuesta también debe ser comprensible para una computadora).
Bueno, es difícil decir cómo los humanos encuentran estas características. Ya está programado en nuestro cerebro. Pero si miramos
profundamente en algunas imágenes y buscamos diferentes patrones, encontraremos algo interesante. Por ejemplo, tome la imagen de abajo:
156 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
La imagen es muy simple. En la parte superior de la imagen, se proporcionan seis pequeños parches de imagen. La pregunta para usted es encontrar
la ubicación exacta de estos parches en la imagen original. ¿Cuántos resultados correctos puedes encontrar?
A y B son superficies planas y están distribuidas en una gran área. Es difícil encontrar la ubicación exacta de estos parches.
C y D son mucho más simples. Son bordes del edificio. Puede encontrar una ubicación aproximada, pero la ubicación exacta sigue siendo difícil. Es
porque, a lo largo del borde, es igual en todas partes. Normal al borde, es diferente. Por lo tanto, el borde es una característica mucho mejor en
comparación con el área plana, pero no lo suficientemente bueno (es bueno en el rompecabezas para comparar la continuidad de los bordes).
Finalmente, E y F son algunas esquinas del edificio. Y se pueden descubrir fácilmente. Porque en las esquinas, donde sea que muevas este parche,
se verá diferente. Así que pueden ser considerados como una buena característica. Así que ahora pasamos a una imagen más simple (y ampliamente
utilizada) para una mejor comprensión.
1.5. Detección y descripción de características 157
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Al igual que arriba, el parche azul es un área plana y difícil de encontrar y rastrear. Dondequiera que mueva el parche azul, se ve igual. Para
el parche negro, es un borde. Si lo mueve en dirección vertical (es decir, a lo largo del gradiente), cambia. Poner a lo largo del borde (paralelo
al borde), se ve igual. Y para el parche rojo, es una esquina. Dondequiera que muevas el parche, se ve diferente, significa que es único.
Básicamente, las esquinas se consideran buenas características en una imagen. (No solo las esquinas, en algunos casos los blobs se
consideran buenas características).
Así que ahora respondimos a nuestra pregunta, "¿cuáles son estas características?". Pero surge la siguiente pregunta. ¿Cómo los encontramos? O
¿cómo encontramos las esquinas?. Eso también lo respondimos de una manera intuitiva, es decir, busque las regiones en las imágenes que tienen
la máxima variación cuando se mueven (en una pequeña cantidad) en todas las regiones a su alrededor. Esto se proyectaría en lenguaje informático
en los próximos capítulos. Entonces, encontrar estas características de imagen se llama Detección de características.
Entonces encontramos las características en la imagen (supongamos que lo hizo). Una vez que lo encontraste, deberías encontrar lo mismo
en las otras imágenes. ¿Qué hacemos? Tomamos una región alrededor de la característica, la explicamos con nuestras propias palabras,
como "la parte superior es el cielo azul, la parte inferior es la región del edificio, en ese edificio hay algunos vasos, etc." y buscas la misma
área en otras imágenes. Básicamente, estás describiendo la función. De manera similar, la computadora también debe describir la región
alrededor de la característica para que pueda encontrarla en otras imágenes. La llamada descripción se llama Descripción de funciones. Una
vez que tenga las características y su descripción, puede encontrar las mismas características en todas las imágenes y alinearlas, unirlas o
hacer lo que quiera.
Entonces, en este módulo, estamos buscando diferentes algoritmos en OpenCV para encontrar características, describirlas, unirlas, etc.
Recursos adicionales
Ejercicios
Detección de esquina Harris
Meta
En este capítulo,
• Comprenderemos los conceptos detrás de Harris Corner Detection.
• Veremos las funciones: cv2.cornerHarris(), cv2.cornerSubPix()
158 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Teoría
En el último capítulo, vimos que las esquinas son regiones de la imagen con una gran variación de intensidad en todas las direcciones.
Uno de los primeros intentos de encontrar estas esquinas fue realizado por Chris Harris y Mike Stephens en su artículo A Combined
Corner and Edge Detector en 1988, por lo que ahora se llama Harris Corner Detector. Llevó esta idea simple a una forma matemática.
Básicamente encuentra la diferencia de intensidad para un desplazamiento de ( , ) en todas las direcciones. Esto se expresa como
sigue:
2
( , ) = ∑ ( , ) [ ( + , + ) − ( , ) ]
,
función de ventana intensidad cambiada intensidad
La función de ventana es una ventana rectangular o una ventana gaussiana que otorga pesos a los píxeles que se encuentran debajo.
,detección de esquinas. Eso significa que tenemos que maximizar el segundo término.
Tenemos que maximizar esta función ( ) para la
Aplicando la Expansión de Taylor a la ecuación anterior y usando algunos pasos matemáticos (consulte cualquier libro de texto estándar que
desee para una derivación completa), obtenemos la ecuación final como:
( , ) ≈ [ ]
[ ]
dónde
= ∑ ( , )
[ ]
,
Aquí, y son derivadas de imágenes en las direcciones x e y respectivamente. (Se puede encontrar fácilmente usando cv2.Sobel()).
Luego viene la parte principal. Luego de esto, crearon una partitura, básicamente una ecuación, que determinará si una ventana
puede contener una esquina o no.
dónde
• ( ) 1 2
•
( = ) = 1 + 2
• 1
y 2 son los valores propios de M
Entonces, los valores de estos valores propios deciden si una región es esquina, borde o plana.
• Cuando < 0, lo que ocurre cuando 1 >> 2 o viceversa, la región es borde.
Se puede representar en una bonita imagen de la siguiente manera:
1.5. Detección y descripción de características 159
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Entonces, el resultado de Harris Corner Detection es una imagen en escala de grises con estas puntuaciones. Umbral para una adecuada darle las
esquinas de la imagen. Lo haremos con una simple imagen.
Detector de esquina Harris en OpenCV
OpenCV tiene la función cv2.cornerHarris() para este propósito. Sus argumentos son:
• img Imagen de entrada, debe ser en escala de grises y tipo float32.
• blockSize Es el tamaño del vecindario considerado para la detección de esquinas
• ksize Parámetro de apertura de la derivada de Sobel utilizada.
• k Parámetro libre del detector de Harris en la ecuación.
Vea el ejemplo a continuación:
importar cv2
importar numpy como np
nombre de archivo = 'tablero de ajedrez.jpg'
img = cv2.imread (nombre de archivo)
160 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
gris = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
gris = np.float32(gris) dst =
cv2.cornerHarris(gris,2,3,0.04)
#resultado se dilata para marcar las esquinas, no es importante dst = cv2.dilate(dst,None)
# Umbral para un valor óptimo, puede variar según la imagen. img[dst>0.01*dst.max()]=[0,0,255]
cv2.imshow('dst',img) si
cv2.waitKey(0) & 0xff == 27: cv2.destroyAllWindows()
A continuación se muestran los tres resultados:
1.5. Detección y descripción de características 161
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Esquina con precisión de subpíxeles
A veces, es posible que necesite encontrar las esquinas con la máxima precisión. OpenCV viene con una función cv2.cornerSubPix()
que refina aún más las esquinas detectadas con una precisión de subpíxeles. A continuación se muestra un ejemplo. Como de
costumbre, primero tenemos que encontrar las esquinas de harris. Luego pasamos los centroides de estas esquinas (puede haber un
montón de píxeles en una esquina, tomamos su centroide) para refinarlos. Las esquinas de Harris están marcadas en píxeles rojos y
las esquinas refinadas están marcadas en píxeles verdes. Para esta función, tenemos que definir los criterios de cuándo detener la
iteración. Lo detenemos después de un número específico de iteraciones o se logra cierta precisión, lo que ocurra primero. También
necesitamos definir el tamaño del vecindario que buscaría para las esquinas.
importar cv2
importar numpy como np
nombre de archivo = 'tablero de ajedrez2.jpg' img
= cv2.imread(nombre de archivo) gris =
cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# encuentra las esquinas de Harris
grises = np.float32(gray) dst =
cv2.cornerHarris(gray,2,3,0.04) dst = cv2.dilate(dst,None)
ret, dst = cv2.threshold(dst,0.01*dst.max(),255,0) dst = np.uint8(dst)
# encontrar centroides ret,
etiquetas, estadísticas, centroides = cv2.connectedComponentsWithStats(dst)
# definir los criterios para detener y refinar las esquinas criterio = (cv2.TERM_CRITERIA_EPS
+ cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001) esquinas = cv2.cornerSubPix(gray,np.float32(centroids),(5,5),(1, 1), criterios)
# Ahora dibújalos
res = np.hstack((centroides,esquinas)) res = np.int0(res)
img[res[:,1],res[:,0]]=[0,0,255]
img[res[:,3] ,res[:,2]] = [0,255,0]
cv2.imwrite('subpixel5.png',img)
A continuación se muestra el resultado, donde se muestran algunas ubicaciones importantes en una ventana ampliada para visualizar:
162 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
Ejercicios
Detector de esquinas ShiTomasi y buenas características para rastrear
Meta
En este capítulo,
• Aprenderemos sobre el otro detector de esquinas: Detector de esquinas ShiTomasi
• Veremos la función: cv2.goodFeaturesToTrack()
Teoría
En el último capítulo, vimos Harris Corner Detector. Más tarde, en 1994, J. Shi y C. Tomasi le hicieron una pequeña modificación
en su artículo Good Features to Track , que muestra mejores resultados en comparación con Harris Corner Detector. La función
de puntuación en Harris Corner Detector estuvo dada por:
= 1 2 − ( 1 + 2)
2
En lugar de esto, ShiTomasi propuso:
= ( 1, 2)
Si es mayor que un valor de umbral, se considera como una esquina. Si lo trazamos en Corner 1 − 2 espacios como hicimos en Harris
Detector, obtenemos una imagen como la siguiente:
1.5. Detección y descripción de características 163
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Código
OpenCV tiene una función, cv2.goodFeaturesToTrack(). Encuentra N esquinas más fuertes en la imagen mediante el método Shi
Tomasi (o Detección de esquinas de Harris, si lo especifica). Como de costumbre, la imagen debe ser una imagen en escala de grises.
Luego especifica el número de esquinas que desea encontrar. Luego, especifica el nivel de calidad, que es un valor entre 0 y 1, que
denota la calidad mínima de la esquina por debajo de la cual todos son rechazados. Luego proporcionamos la distancia euclidiana
mínima entre las esquinas detectadas.
Con toda esta información, la función encuentra esquinas en la imagen. Todas las esquinas por debajo del nivel de calidad son
rechazadas. Luego, ordena las esquinas restantes según la calidad en orden descendente. Luego, la función toma la primera esquina
más fuerte, desecha todas las esquinas cercanas en el rango de distancia mínima y devuelve N esquinas más fuertes.
En el siguiente ejemplo, intentaremos encontrar las 25 mejores esquinas:
importar numpy como np importar
cv2 desde matplotlib
importar pyplot como plt
img = cv2.imread('simple.jpg') gris =
cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
esquinas = cv2.goodFeaturesToTrack(gray,25,0.01,10) esquinas = np.int0(esquinas)
para i en las esquinas:
x,y = i.ravel() cv2.circle(img,
(x,y),3,255,1)
164 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
plt.imshow(img),plt.show()
Vea el resultado a continuación:
Esta función es más apropiada para el seguimiento. Eso lo veremos cuando llegue su momento.
Recursos adicionales
Ejercicios
Introducción a SIFT (Transformación de características de escala invariable)
Meta
En este capítulo,
• Aprenderemos sobre los conceptos del algoritmo SIFT
• Aprenderemos a encontrar puntos clave y descriptores SIFT.
Teoría
En los últimos dos capítulos, vimos algunos detectores de esquinas como Harris, etc. Son invariantes a la rotación, lo que significa que, incluso si
se gira la imagen, podemos encontrar las mismas esquinas. Es obvio porque las esquinas siguen siendo esquinas en la imagen girada también.
Pero, ¿qué pasa con la escala? Una esquina puede no ser una esquina si la imagen está escalada. Por ejemplo, verifique una imagen simple a
continuación. Una esquina en una imagen pequeña dentro de una ventana pequeña es plana cuando se amplía en la misma ventana. Entonces,
la esquina de Harris no es invariante de escala.
1.5. Detección y descripción de características 165
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Entonces, en 2004, D. Lowe, de la Universidad de British Columbia, ideó un nuevo algoritmo, Scale Invariant Feature Trans form (SIFT) en su
artículo, Distinctive Image Features from ScaleInvariant Keypoints, que extrae puntos clave y calcula sus descriptores. (Este documento es fácil
de entender y se considera el mejor material disponible en SIFT. Por lo tanto, esta explicación es solo un breve resumen de este documento).
Hay principalmente cuatro pasos involucrados en el algoritmo SIFT. Los veremos uno por uno.
1. Detección de extremos de espacio de escala
De la imagen de arriba, es obvio que no podemos usar la misma ventana para detectar puntos clave con diferente escala. Está bien con una
esquina pequeña. Pero para detectar esquinas más grandes necesitamos ventanas más grandes. Para esto, se utiliza el filtrado de espacio de escala.
En él se encuentra el Laplaciano de Gauss para la imagen con varios valores. LoG actúa como un detector de manchas que detecta manchas en
varios tamaños debido a cambios en En resumen, .actúa como un parámetro de escala. Por ejemplo, en la imagen de arriba, el núcleo gaussiano
con bajo da un valor alto para la esquina pequeña, mientras que el núcleo guassiano con alto se adapta bien a la esquina más grande. Entonces,
,
podemos encontrar los máximos locales a lo largo de la escala y el espacio, lo que nos da una lista de valores
( , ) lo que significa que hay un
punto clave potencial en (x,y) a escala.
Pero este LoG es un poco costoso, por lo que el algoritmo SIFT usa Diferencia de gaussianas, que es una aproximación de LoG.
La diferencia de gaussiana se obtiene como la diferencia de desenfoque gaussiano de una imagen con dos , déjalo ser
. Este proceso se realiza para diferentes octavas de la imagen en la Pirámide Gaussiana. Se representa en la siguiente imagen:
166 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Una vez que se encuentran estos DoG, se buscan imágenes extremas locales sobre escala y espacio. Por ejemplo, un píxel en una
imagen se compara con sus 8 vecinos así como con 9 píxeles en la escala siguiente y 9 píxeles en escalas anteriores. Si es un extremo
local, es un punto clave potencial. Básicamente significa que el punto clave se representa mejor en esa escala. Se muestra en la siguiente imagen:
Con respecto a los diferentes parámetros, el documento brinda algunos datos empíricos que se pueden resumir como, número de octavas
= 4, número de niveles de escala = 5, inicial = 1.6, = √ 2, etc. como valores óptimos.
1.5. Detección y descripción de características 167
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
2. Localización de puntos clave
Una vez que se encuentran las ubicaciones de puntos clave potenciales, deben refinarse para obtener resultados más precisos. Usaron la expansión
de la serie de Taylor del espacio de escala para obtener una ubicación más precisa de los extremos, y si la intensidad en este extremo es menor
que un valor de umbral (0.03 según el documento), se rechaza. Este umbral se llama contrastThreshold en OpenCV
DoG tiene una mayor respuesta para los bordes, por lo que también es necesario eliminar los bordes. Para ello se utiliza un concepto similar al
detector de esquina Harris. Usaron una matriz hessiana de 2x2 (H) para calcular la curvatura principal. Sabemos por el detector de esquinas de
Harris que, para las aristas, un valor propio es mayor que el otro. Así que aquí usaron una función simple,
Si esta relación es mayor que un umbral, llamado edgeThreshold en OpenCV, ese punto clave se descarta. Se da como 10 en papel.
Por lo tanto, elimina los puntos clave de bajo contraste y los puntos clave de borde y lo que queda son puntos de gran interés.
3. Asignación de orientación
Ahora se asigna una orientación a cada punto clave para lograr la invariancia a la rotación de la imagen. Se toma un vecindario alrededor de la
ubicación del punto clave según la escala, y se calcula la magnitud y la dirección del gradiente en esa región. Se crea un histograma de orientación
con 36 contenedores que cubren 360 grados. (Está ponderado por la magnitud del gradiente y la ventana circular con ponderación gaussiana igual
a 1,5 veces la escala del punto clave. Se toma el pico más alto en el histograma y cualquier pico por encima del 80% también se considera para
calcular la orientación. Crea puntos clave con la misma ubicación y escala, pero diferentes direcciones Contribuye a la estabilidad del emparejamiento.
4. Descriptor de punto clave
Ahora se crea el descriptor de punto clave. Se toma una vecindad de 16x16 alrededor del punto clave. Se divide en 16 subbloques de tamaño 4x4.
Para cada subbloque, se crea un histograma de orientación de 8 bins. Por lo tanto, hay un total de 128 valores de bin disponibles. Se representa
como un vector para formar un descriptor de punto clave. Además de esto, se toman varias medidas para lograr robustez frente a cambios de
iluminación, rotación, etc.
5. Coincidencia de puntos clave
Los puntos clave entre dos imágenes se emparejan mediante la identificación de sus vecinos más cercanos. Pero en algunos casos, la segunda
coincidencia más cercana puede estar muy cerca de la primera. Puede ocurrir debido al ruido u otras razones. En ese caso, se toma la relación entre
la distancia más cercana y la segunda distancia más cercana. Si es superior a 0,8, se rechazan. Elimina alrededor del 90% de las coincidencias
falsas, mientras que descarta solo el 5% de las coincidencias correctas, según el documento.
Este es un resumen del algoritmo SIFT. Para obtener más detalles y comprensión, se recomienda encarecidamente leer el artículo original. Recuerde
una cosa, este algoritmo está patentado. Entonces, este algoritmo está incluido en el módulo Nonfree en OpenCV.
TAMIZAR en OpenCV
Así que ahora veamos las funcionalidades SIFT disponibles en OpenCV. Comencemos con la detección de puntos clave y dibujémoslos. Primero
tenemos que construir un objeto SIFT. Podemos pasarle diferentes parámetros que son opcionales y están bien explicados en docs.
importar cv2
importar numpy como np
img = cv2.imread('inicio.jpg')
168 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
gris= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
sift = cv2.SIFT() kp =
sift.detect(gris,Ninguno)
img=cv2.drawKeypoints(gris,kp)
cv2.imwrite('sift_puntos clave.jpg',img)
La función sift.detect() encuentra el punto clave en las imágenes. Puede pasar una máscara si desea buscar solo una parte de la
imagen. Cada punto clave es una estructura especial que tiene muchos atributos como sus coordenadas (x,y), el tamaño del
vecindario significativo, el ángulo que especifica su orientación, la respuesta que especifica la fuerza de los puntos clave, etc.
OpenCV también proporciona la función cv2.drawKeyPoints() que dibuja pequeños círculos en las ubicaciones de los puntos clave.
Si le pasa una bandera, cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS , dibujará un círculo con el tamaño del punto
clave e incluso mostrará su orientación. Vea el siguiente ejemplo.
img=cv2.drawKeypoints(gray,kp,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imwrite('sift_keypoints.jpg',img)
Vea los dos resultados a continuación:
Ahora, para calcular el descriptor, OpenCV proporciona dos métodos.
1. Como ya encontró los puntos clave, puede llamar a sift.compute() , que calcula los descriptores a partir de los puntos clave
que hemos encontrado. Por ejemplo: kp,des = sift.compute(gray,kp)
2. Si no encontró puntos clave, busque directamente puntos clave y descriptores en un solo paso con la función,
sift.detectAndCompute().
1.5. Detección y descripción de características 169
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Veremos el segundo método:
sift = cv2.SIFT() kp, des
= sift.detectAndCompute(gris,Ninguno)
Así que obtuvimos puntos clave, descriptores, etc. Ahora queremos ver cómo hacer coincidir los puntos clave en diferentes imágenes. Eso lo
aprenderemos en los próximos capítulos.
Recursos adicionales
Ejercicios
Introducción a SURF (funciones robustas aceleradas)
Meta
En este capítulo,
• Veremos las bases del SURF
• Veremos funcionalidades SURF en OpenCV
Teoría
En el último capítulo, vimos SIFT para la detección y descripción de puntos clave. Pero fue comparativamente lento y la gente necesitaba
una versión más acelerada. En 2006, tres personas, Bay, H., Tuytelaars, T. y Van Gool, L, publicaron otro artículo, "SURF: Speeded Up
Robust Features" que introdujo un nuevo algoritmo llamado SURF. Como sugiere su nombre, es una versión acelerada de SIFT.
En SIFT, Lowe aproximó Laplacian of Gaussian con Difference of Gaussian para encontrar el espacio de escala. SURF va un poco más
allá y se aproxima a LoG con Box Filter. La imagen de abajo muestra una demostración de tal aproximación.
Una gran ventaja de esta aproximación es que la convolución con filtro de caja se puede calcular fácilmente con la ayuda de imágenes
integrales. Y se puede hacer en paralelo para diferentes escalas. Además, SURF se basa en el determinante de la matriz hessiana
tanto para la escala como para la ubicación.
170 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Para la asignación de orientación, SURF utiliza respuestas de wavelet en dirección horizontal y vertical para una vecindad de tamaño
6s. También se le aplican pesos guassianos adecuados. Luego se trazan en un espacio como se muestra en la imagen a continuación.
La orientación dominante se estima calculando la suma de todas las respuestas dentro de una ventana de orientación deslizante de
un ángulo de 60 grados. Lo interesante es que la respuesta wavelet se puede encontrar usando imágenes integrales muy fácilmente
a cualquier escala. Para muchas aplicaciones, no se requiere invariancia de rotación, por lo que no es necesario encontrar esta
orientación, lo que acelera el proceso. SURF proporciona una funcionalidad llamada UprightSURF o USURF. Mejora la velocidad y
es robusto hasta ±15 . OpenCV admite ambos, dependiendo de la bandera, en posición vertical. Si es 0, se calcula la orientación.
Si es 1, no se calcula la orientación y es más rápido.
1.5. Detección y descripción de características 171
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Para la descripción de características, SURF usa respuestas Wavelet en dirección horizontal y vertical (nuevamente, el uso de imágenes integrales
facilita las cosas). Se toma una vecindad de tamaño 20sX20s alrededor del punto clave donde s es el tamaño. Se divide en subregiones 4x4. Para
cada subregión, se toman las respuestas wavelet horizontal y vertical y se forma un vector como este, = (∑ , ∑ , ∑ | |, ∑ | |). Esto, cuando
se representa como un vector, da un descriptor de características SURF con un total de 64 dimensiones. Reduzca la dimensión, aumente la
velocidad de cálculo y coincidencia, pero proporcione una mejor distinción de las características.
Para mayor distinción, el descriptor de características SURF tiene una versión extendida de 128 dimensiones. Las sumas de y | | se calculan por
separado para < 0 y ≥ 0. De manera similar, las sumas de y | | se dividen según el signo de , duplicando así el número de entidades. No agrega
mucha complejidad de cálculo. OpenCV admite ambos estableciendo el valor de la bandera extendida con 0 y 1 para 64dim y 128dim
respectivamente (el valor predeterminado es 128dim)
Otra mejora importante es el uso del signo de Laplacian (traza de Hessian Matrix) para el punto de interés subyacente.
No agrega ningún costo de cálculo ya que ya se calcula durante la detección. El signo del Laplaciano distingue las manchas brillantes sobre
fondos oscuros de la situación inversa. En la etapa de comparación, solo comparamos características si tienen el mismo tipo de contraste (como
se muestra en la imagen a continuación). Esta información mínima permite una coincidencia más rápida, sin reducir el rendimiento del descriptor.
En definitiva, SURF añade un montón de funciones para mejorar la velocidad en cada paso. El análisis muestra que es 3 veces más rápido que
SIFT, mientras que el rendimiento es comparable al de SIFT. SURF es bueno para manejar imágenes con desenfoque y rotación, pero no es
bueno para manejar el cambio de punto de vista y el cambio de iluminación.
SURF en OpenCV
OpenCV proporciona funcionalidades SURF al igual que SIFT. Inicia un objeto SURF con algunas condiciones opcionales como descriptores de
64/128 dim, SURF vertical/normal, etc. Todos los detalles se explican bien en los documentos. Luego, como hicimos en SIFT, podemos usar
SURF.detect(), SURF.compute(), etc. para encontrar puntos clave y descriptores.
Primero veremos una demostración simple sobre cómo encontrar puntos clave y descriptores SURF y dibujarlos. Todos los ejemplos se muestran
en la terminal de Python, ya que es igual que SIFT solamente.
>>> img = cv2.imread('volar.png',0)
# Crear objeto SURF. Puede especificar parámetros aquí o más tarde.
# Aquí configuro el umbral de Hessian en 400
>>> navegar = cv2.SURF(400)
# Encuentra puntos clave y descriptores directamente >>> kp, des =
surf.detectAndCompute(img,None)
172 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
>>> largo(kp)
699
1199 puntos clave es demasiado para mostrar en una imagen. Lo reducimos a unos 50 para dibujarlo en una imagen. Mientras emparejamos, es posible
que necesitemos todas esas características, pero no ahora. Entonces aumentamos el Umbral de Hesse.
# Verifique el umbral actual de Hessian >>> print
surf.hessianThreshold 400.0
# Lo configuramos en unos 50000. Recuerde, es solo para representar en la imagen.
# En casos reales, es mejor tener un valor 300500 >>> surf.hessianThreshold = 50000
# Calcule nuevamente los puntos clave y verifique su número. >>> kp, des
= surf.detectAndCompute(img,Ninguno)
>>> imprimir len(kp) 47
Es menos de 50. Dibujémoslo en la imagen.
>>> img2 = cv2.drawKeypoints(img,kp,Ninguno,(255,0,0),4)
>>> plt.imshow(img2),plt.show()
Vea el resultado a continuación. Puede ver que SURF es más como un detector de manchas. Detecta las manchas blancas en las alas de
las mariposas. Puedes probarlo con otras imágenes.
Ahora quiero aplicar USURF, para que no encuentre la orientación.
1.5. Detección y descripción de características 173
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
# Verifique la bandera vertical, si es Falso, configúrelo como Verdadero >>>
print surf.upright Falso
>>> navegar.upright = True
# Vuelva a calcular los puntos característicos y dibújelos >>> kp =
surf.detect(img,None) >>> img2 =
cv2.drawKeypoints(img,kp,None,(255,0,0),4)
>>> plt.imshow(img2),plt.show()
Vea los resultados a continuación. Todas las orientaciones se muestran en la misma dirección. Es más rápido que el anterior. Si está
trabajando en casos en los que la orientación no es un problema (como la costura panorámica), etc., esto es mucho mejor.
Finalmente verificamos el tamaño del descriptor y lo cambiamos a 128 si es solo 64dim.
# Encuentra el tamaño del descriptor
>>> print surf.descriptorSize()
64
# Eso significa bandera, "extendida" es Falso.
>>> navegar.extendido
FALSO
# Entonces lo hacemos a True para obtener descriptores de 128 dim. >>>
navegar.extendido = Verdadero
>>> kp, des = surf.detectAndCompute(img,Ninguno) >>> print
surf.descriptorSize() 128
174 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
>>> imprimir des.forma (47,
128)
La parte restante es emparejar, lo que haremos en otro capítulo.
Recursos adicionales
Ejercicios
Algoritmo FAST para detección de esquinas
Meta
En este capítulo,
• Comprenderemos los conceptos básicos del algoritmo FAST
• Encontraremos esquinas utilizando las funcionalidades de OpenCV para el algoritmo FAST.
Teoría
Vimos varios detectores de características y muchos de ellos son realmente buenos. Pero cuando se mira desde el punto de vista de una
aplicación en tiempo real, no son lo suficientemente rápidos. Un mejor ejemplo sería el robot móvil SLAM (localización y mapeo simultáneos)
que tiene recursos computacionales limitados.
Como solución a esto, Edward Rosten y Tom Drummond propusieron el algoritmo FAST (características de la prueba de segmento acelerado)
en su artículo "Aprendizaje automático para la detección de esquinas de alta velocidad" en 2006 (luego revisado en 2010).
A continuación se presenta un resumen básico del algoritmo. Consulte el papel original para obtener más detalles (Todas las imágenes están
tomadas del papel original).
Detección de características usando FAST
1. Seleccione un píxel en la imagen que se identificará como un punto de interés o no. Sea su intensidad .
2. Seleccione el valor de umbral apropiado.
3. Considere un círculo de 16 píxeles alrededor del píxel bajo prueba. (Ver la imagen de abajo)
1.5. Detección y descripción de características 175
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
4. Ahora el píxel es una esquina si existe un conjunto de píxeles contiguos en el círculo (de 16 píxeles) que son todos o todos más
más brillante que + , oscuros que . (Se muestra como líneas discontinuas blancas en la imagen de arriba). fue elegido para ser
12.
5. Se propuso una prueba de alta velocidad para excluir una gran cantidad de no esquinas. Esta prueba examina solo los cuatro píxeles
en 1, 9, 5 y 13 (los primeros 1 y 9 se prueban si son demasiado brillantes o demasiado oscuros. Si es así, luego verifique 5 y 13).
Si es una esquina, al menos tres de ellos deben ser más brillantes que + o más oscuros que Si ninguno de estos es .el caso, entonces
no puede ser una esquina. A continuación, se puede aplicar el criterio de prueba de segmento completo a los candidatos aprobados
examinando todos los píxeles del círculo. Este detector en sí mismo exhibe un alto rendimiento, pero tiene varias debilidades:
• No rechaza tantos candidatos para n < 12.
• La elección de los píxeles no es óptima porque su eficiencia depende del orden de las preguntas y la distribución.
butión de las apariciones en las esquinas.
• Los resultados de las pruebas de alta velocidad se desechan.
• Se detectan varias características adyacentes entre sí.
Los primeros 3 puntos se abordan con un enfoque de aprendizaje automático. El último se aborda mediante supresión no máxima.
Aprendizaje automático de un detector de esquinas
1. Seleccione un conjunto de imágenes para el entrenamiento (preferiblemente del dominio de la aplicación de destino)
2. Ejecute el algoritmo FAST en cada imagen para encontrar puntos característicos.
3. Para cada punto característico, almacene los 16 píxeles a su alrededor como un vector. Hazlo para que todas las imágenes obtengan un vector de características.
4. Cada píxel (digamos) en estos 16 píxeles puede tener uno de los siguientes tres estados:
176 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
5. Dependiendo de estos estados, el vector de características se subdivide en 3 subconjuntos, , , .
6. Defina una nueva variable booleana, , lo cual es verdadero si es una esquina y falso en caso contrario.
7. Use el algoritmo ID3 (clasificador de árboles de decisión) para consultar cada subconjunto usando la variable por el conocimiento
de la verdadera clase. Selecciona el que arroja la mayor información sobre si el píxel candidato es una esquina, medida por la
entropía de .
8. Esto se aplica recursivamente a todos los subconjuntos hasta que su entropía sea cero.
9. El árbol de decisión así creado se utiliza para la detección rápida en otras imágenes.
Supresión no máxima
La detección de múltiples puntos de interés en ubicaciones adyacentes es otro problema. Se soluciona utilizando Supresión No Máxima.
1. Calcule una función de puntuación para todos los puntos característicos detectados. es la suma de la diferencia absoluta entre
y 16 valores de píxeles circundantes.
2. Considere dos puntos significativos adyacentes y calcule sus valores.
3. Descartar el de menor valor.
Resumen
Es varias veces más rápido que otros detectores de esquina existentes.
Pero no es resistente a altos niveles de ruido. Depende de un umbral.
Detector de características FAST en OpenCV
Se llama como cualquier otro detector de características en OpenCV. Si lo desea, puede especificar el umbral, si se aplicará o no la supresión
no máxima, el vecindario que se usará, etc.
Para el vecindario, se definen tres banderas, cv2.FAST_FEATURE_DETECTOR_TYPE_5_8, cv2.
FAST_FEATURE_DETECTOR_TYPE_7_12 y cv2.FAST_FEATURE_DETECTOR_TYPE_9_16. A continuación se muestra un código simple
sobre cómo detectar y dibujar los puntos de función FAST.
importar numpy como np
importar cv2
desde matplotlib importar pyplot como plt
img = cv2.imread('simple.jpg',0)
# Iniciar objeto FAST con valores predeterminados
1.5. Detección y descripción de características 177
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
rápido = cv2.FastFeatureDetector()
# encuentra y dibuja los puntos clave kp =
fast.detect(img,None) img2 =
cv2.drawKeypoints(img, kp, color=(255,0,0))
# Imprimir todos los parámetros
predeterminados print "Umbral: ", fast.getInt('threshold') print
"nonmaxSuppression: ", fast.getBool('nonmaxSuppression') print "vecindario: ", fast.getInt('type') print
"Total Puntos clave con nonmaxSuppression: ", len(kp)
cv2.imwrite('fast_true.png',img2)
# Deshabilitar nonmaxSuppression
fast.setBool('nonmaxSuppression',0) kp =
fast.detect(img,Ninguno)
imprimir "Puntos clave totales sin no maxSuppression: ", len (kp)
img3 = cv2.drawKeypoints(img, kp, color=(255,0,0))
cv2.imwrite('fast_false.png',img3)
Vea los resultados. La primera imagen muestra FAST con nonmaxSuppression y la segunda sin nonmaxSuppression:
Recursos adicionales
1. Edward Rosten y Tom Drummond, “Aprendizaje automático para la detección de esquinas a alta velocidad” en el 9.º Congreso Europeo
Conferencia sobre Visión por Computador, vol. 1, 2006, págs. 430–443.
2. Edward Rosten, Reid Porter y Tom Drummond, "Más rápido y mejor: un enfoque de aprendizaje automático para la detección de
esquinas" en IEEE Trans. Análisis de patrones e inteligencia artificial, 2010, vol 32, pp. 105119.
Ejercicios
BREVE (Características elementales independientes robustas binarias)
178 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Meta
En este capítulo
• Veremos los fundamentos del algoritmo BREVE
Teoría
Sabemos que SIFT usa un vector de 128 dim para los descriptores. Dado que utiliza números de punto flotante, se necesitan básicamente 512
bytes. De manera similar, SURF también requiere un mínimo de 256 bytes (para 64 dim). La creación de un vector de este tipo para miles de
funciones requiere una gran cantidad de memoria que no es factible para aplicaciones con restricciones de recursos, especialmente para sistemas
integrados. Cuanto mayor sea la memoria, mayor será el tiempo que se tarda en hacer coincidir.
Pero todas estas dimensiones pueden no ser necesarias para la coincidencia real. Podemos comprimirlo usando varios métodos como PCA, LDA,
etc. Incluso otros métodos como hashing usando LSH (Locality Sensitive Hashing) se usan para convertir estos descriptores SIFT en números de
coma flotante en cadenas binarias. Estas cadenas binarias se utilizan para hacer coincidir entidades mediante la distancia de Hamming. Esto
proporciona una mejor aceleración porque encontrar la distancia de hamming es solo aplicar XOR y el recuento de bits, que son muy rápidos en las
CPU modernas con instrucciones SSE. Pero aquí, primero debemos encontrar los descriptores, luego solo podemos aplicar hash, lo que no resuelve
nuestro problema inicial en la memoria.
BREVE entra en escena en este momento. Proporciona un atajo para encontrar cadenas binarias directamente sin encontrar descriptores. Toma un
parche de imagen suavizado y selecciona un conjunto de pares de ubicaciones (x, y) de una manera única (explicado en papel). Luego se realizan
algunas comparaciones de intensidad de píxeles en estos pares de ubicaciones. Por ejemplo, deje que los primeros pares de ubicación sean y Si ( )
. < ( ), entonces su resultado es 1, de lo contrario es 0. Esto se aplica a todos los pares de ubicación para obtener una cadena de bits de dimensión.
Esto puede ser 128, 256 o 512. OpenCV admite todos estos, pero por defecto, sería 256 (OpenCV lo representa en bytes. Por lo tanto, los valores
serán 16, 32 y 64). Entonces, una vez que obtenga esto, puede usar Hamming Distance para hacer coincidir estos descriptores.
Un punto importante es que BRIEF es un descriptor de funciones, no proporciona ningún método para encontrar las funciones. Por lo tanto, tendrá
que usar cualquier otro detector de características como SIFT, SURF, etc. El documento recomienda usar CenSurE, que es un detector rápido y
BRIEF funciona incluso un poco mejor para los puntos CenSurE que para los puntos SURF.
En resumen, BRIEF es un método más rápido de cálculo y coincidencia de descriptores de características. También proporciona una alta tasa de reconocimiento
a menos que haya una gran rotación en el plano.
BREVE en OpenCV
El siguiente código muestra el cálculo de los descriptores BREVE con la ayuda del detector CenSurE. (El detector CenSurE se llama detector STAR
en OpenCV)
importar numpy como np
importar cv2
desde matplotlib importar pyplot como plt
img = cv2.imread('simple.jpg',0)
# Iniciar detector STAR
estrella = cv2.FeatureDetector_create("ESTRELLA")
# Iniciar breve extractor BREVE =
cv2.DescriptorExtractor_create("BRIEF")
# encuentra los puntos clave con STAR kp =
star.detect(img,None)
1.5. Detección y descripción de características 179
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
# calcular los descriptores con BRIEF kp, des =
brief.compute(img, kp)
imprimir resumen.getInt('bytes') imprimir
des.forma
La función brief.getInt('bytes') proporciona el tamaño utilizado en bytes. Por defecto es 32. El siguiente es el emparejamiento, que se hará en otro
capítulo.
Recursos adicionales
1. Michael Calonder, Vincent Lepetit, Christoph Strecha y Pascal Fua, "BRIEF: Binary Robust Independent Elementary Features", 11.ª
Conferencia Europea sobre Visión por Computador (ECCV), Heraklion, Creta. LNCS Springer, septiembre de 2010.
2. LSH (Locality Sensitive Hasing) en wikipedia.
ORB (RÁPIDO Orientado y BREVE Girado)
Meta
En este capítulo,
• Veremos los conceptos básicos de ORB
Teoría
Como entusiasta de OpenCV, lo más importante del ORB es que proviene de "OpenCV Labs". Este algoritmo fue mencionado por Ethan Rublee,
Vincent Rabaud, Kurt Konolige y Gary R. Bradski en su artículo ORB: una alternativa eficiente a SIFT o SURF en 2011. Como dice el título, es una
buena alternativa a SIFT y SURF en computación. costo, rendimiento coincidente y principalmente las patentes. Sí, SIFT y SURF están patentados
y se supone que debes pagarles por su uso. ¡Pero ORB no lo es!
ORB es básicamente una fusión del detector de puntos clave RÁPIDO y el descriptor BREVE con muchas modificaciones para mejorar el rendimiento.
Primero usa FAST para encontrar puntos clave, luego aplica la medida de la esquina de Harris para encontrar los N puntos principales entre ellos.
También utiliza la pirámide para producir características multiescala. Pero un problema es que FAST no calcula la orientación.
Entonces, ¿qué pasa con la invariancia de rotación? A los autores se les ocurrió la siguiente modificación.
Calcula el centroide ponderado por intensidad del parche con la esquina ubicada en el centro. La dirección del vector desde este punto de esquina
hasta el centroide da la orientación. Para mejorar la invariancia de rotación, los momentos se calculan con x e y, que deben estar en una región
circular de radio , donde está el tamaño del parche.
Ahora, para los descriptores, ORB usa descriptores BREVE. Pero ya hemos visto que BRIEF funciona mal con la rotación.
Entonces, lo que hace ORB es "dirigir" BREVE de acuerdo con la orientación de los puntos clave. Para cualquier conjunto de características de
pruebas binarias en la ubicación ( , ), defina una matriz de 2 × que contenga las coordenadas de estos píxeles. Luego, usando la orientación del
parche, se encuentra su matriz de rotación y gira para obtener la versión dirigida (girada).
ORB discretiza el ángulo en incrementos de 2/30 (12 grados) y construye una tabla de búsqueda de patrones BREVE precalculados. Siempre que
la orientación del punto clave sea coherente en todas las vistas, se utilizará el conjunto correcto de puntos para calcular su descriptor.
BREVE tiene una propiedad importante de que cada característica de bit tiene una gran variación y una media cercana a 0,5. Pero una vez que se
orienta a lo largo de la dirección del punto clave, pierde esta propiedad y se distribuye más. Una varianza alta hace que una característica sea más
discriminatoria, ya que responde de manera diferente a las entradas. Otra propiedad deseable es que las pruebas no estén correlacionadas, ya que
cada prueba contribuirá al resultado. Para resolver todo esto, ORB ejecuta una búsqueda codiciosa entre todos los posibles
180 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Pruebas binarias para encontrar las que tienen una varianza alta y medias cercanas a 0,5, además de no estar correlacionadas. El
resultado se llama rBRIEF.
Para la coincidencia de descriptores, se utiliza LSH de sonda múltiple que mejora el LSH tradicional. El documento dice que ORB es
mucho más rápido que SURF y SIFT y el descriptor ORB funciona mejor que SURF. ORB es una buena opción en dispositivos de
bajo consumo para costura panorámica, etc.
ORB en OpenCV
Como de costumbre, tenemos que crear un objeto ORB con la función, cv2.ORB() o usando la interfaz común de feature2d. Tiene una
serie de parámetros opcionales. Los más útiles son nFeatures, que indica el número máximo de funciones que se conservarán (de
forma predeterminada, 500), scoreType, que indica si la puntuación de Harris o FAST para clasificar las funciones (de forma
predeterminada, puntuación de Harris), etc. Otro parámetro, WTA_K, decide la cantidad de puntos que producen cada elemento del
descriptor BREVE orientado. Por defecto es dos, es decir, selecciona dos puntos a la vez. En ese caso, para la coincidencia, se utiliza
la distancia NORM_HAMMING. Si WTA_K es 3 o 4, lo que requiere 3 o 4 puntos para producir un descriptor BREVE, entonces la
distancia de coincidencia se define mediante NORM_HAMMING2.
A continuación se muestra un código simple que muestra el uso de ORB.
importar numpy como np
importar cv2
desde matplotlib importar pyplot como plt
img = cv2.imread('simple.jpg',0)
# Iniciar detector STAR
orbe = cv2.ORB()
# encuentra los puntos clave con ORB kp =
orb.detect(img,Ninguno)
# calcular los descriptores con ORB kp, des =
orb.compute(img, kp)
# dibujar solo la ubicación de los puntos clave, no el tamaño ni la orientación img2 =
cv2.drawKeypoints(img,kp,color=(0,255,0), flags=0) plt.imshow(img2),plt.show()
Vea el resultado a continuación:
1.5. Detección y descripción de características 181
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Coincidencia de características de ORB, lo haremos en otro capítulo.
Recursos adicionales
1. Ethan Rublee, Vincent Rabaud, Kurt Konolige, Gary R. Bradski: ORB: una alternativa eficiente a SIFT o
NAVEGAR. ICCV 2011: 25642571.
Ejercicios
Coincidencia de características
Meta
En este capítulo
• Veremos cómo hacer coincidir las características de una imagen con otras.
• Usaremos BruteForce Matcher y FLANN Matcher en OpenCV
Conceptos básicos de BruteForce Matcher
El comparador de fuerza bruta es simple. Toma el descriptor de una característica en el primer conjunto y se compara con todas las demás
características en el segundo conjunto utilizando algún cálculo de distancia. Y se devuelve el más cercano.
182 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Para BF Matcher, primero tenemos que crear el objeto BFMatcher usando cv2.BFMatcher(). Se necesitan dos parámetros opcionales.
El primero es normType. Especifica la medida de distancia a utilizar. Por defecto, es cv2.NORM_L2. Es bueno para SIFT, SURF, etc. (cv2.NORM_L1
también está ahí). Para descriptores basados en cadenas binarias como ORB, BRIEF, BRISK, etc., se debe usar cv2.NORM_HAMMING, que usó la
distancia de Hamming como medida. Si ORB usa VTA_K == 3 o 4, se debe usar cv2.NORM_HAMMING2.
El segundo parámetro es una variable booleana, CrossCheck, que es falso de forma predeterminada. Si es cierto, Matcher devuelve solo aquellas
coincidencias con valor (i, j) de modo que el iésimo descriptor en el conjunto A tiene el jésimo descriptor en el conjunto B como la mejor coincidencia y viceversa.
Es decir, las dos características de ambos conjuntos deben coincidir entre sí. Proporciona resultados consistentes y es una buena alternativa a la prueba
de relación propuesta por D. Lowe en el documento SIFT.
Una vez que se crea, dos métodos importantes son BFMatcher.match() y BFMatcher.knnMatch(). El primero devuelve la mejor coincidencia. El segundo
método devuelve las mejores coincidencias k donde el usuario especifica k. Puede ser útil cuando necesitamos hacer un trabajo adicional al respecto.
Al igual que usamos cv2.drawKeypoints() para dibujar puntos clave, cv2.drawMatches() nos ayuda a dibujar las coincidencias. Apila dos imágenes
horizontalmente y dibuja líneas desde la primera imagen hasta la segunda mostrando las mejores coincidencias. También está cv2.drawMatchesKnn , que
dibuja todas las k mejores coincidencias. Si k=2, dibujará dos líneas de coincidencia para cada punto clave. Entonces tenemos que pasar una máscara si
queremos dibujarla selectivamente.
Veamos un ejemplo para cada uno de SURF y ORB (Ambos usan diferentes medidas de distancia).
Coincidencia de fuerza bruta con descriptores ORB
Aquí, veremos un ejemplo simple sobre cómo hacer coincidir las características entre dos imágenes. En este caso, tengo una imagen de consulta y una
imagen de tren. Intentaremos encontrar queryImage en trainImage usando la coincidencia de características. (Las imágenes son /samples/c/box.png y /
samples/c/box_in_scene.png)
Estamos utilizando descriptores SIFT para hacer coincidir las características. Entonces, comencemos con la carga de imágenes, la búsqueda de descriptores, etc.
importar numpy como np
importar cv2
desde matplotlib importar pyplot como plt
img1 = cv2.imread('box.png',0) img2 = # consultaImagen
cv2.imread('box_in_scene.png',0) # trenImagen
# Iniciar detector SIFT orb = cv2.ORB()
# encuentre los puntos clave y descriptores con SIFT kp1, des1 =
orb.detectAndCompute(img1,None) kp2, des2 =
orb.detectAndCompute(img2,None)
A continuación, creamos un objeto BFMatcher con medición de distancia cv2.NORM_HAMMING (ya que estamos usando ORB) y se activa CrossCheck
para obtener mejores resultados. Luego usamos el método Matcher.match() para obtener las mejores coincidencias en dos imágenes. Los clasificamos
en orden ascendente de sus distancias para que las mejores coincidencias (con distancia baja) estén al frente.
Luego, sorteamos solo los primeros 10 partidos (solo por visibilidad. Puede aumentarlo como desee)
# crear objeto BFMatcher bf =
cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
# Coincide con los descriptores.
coincidencias = bf.coincidencia(des1,des2)
# Clasificarlos en el orden de su distancia. coincidencias =
ordenado(coincidencias, clave = lambda x:x.distancia)
1.5. Detección y descripción de características 183
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
# Dibuja los primeros 10 partidos.
img3 = cv2.drawMatches(img1,kp1,img2,kp2,coincidencias[:10], banderas=2)
plt.imshow(img3),plt.show()
A continuación se muestra el resultado que obtuve:
¿Qué es este objeto Matcher?
El resultado de la línea matches = bf.match(des1,des2) es una lista de objetos DMatch. Este objeto DMatch tiene los siguientes
atributos:
• DMatch.distance Distancia entre descriptores. Cuanto más bajo, mejor.
• DMatch.trainIdx Índice del descriptor en descriptores de tren
• DMatch.queryIdx: índice del descriptor en los descriptores de consulta
• DMatch.imgIdx Índice de la imagen del tren.
Coincidencia de fuerza bruta con descriptores SIFT y prueba de relación
Esta vez, usaremos BFMatcher.knnMatch() para obtener k mejores coincidencias. En este ejemplo, tomaremos k=2 para que podamos
aplicar la prueba de razón explicada por D. Lowe en su artículo.
importar numpy como np
importar cv2
desde matplotlib importar pyplot como plt
184 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
img1 = cv2.imread('box.png',0) # consultaImagen img2 = cv2.imread('box_in_scene.png',0) #
trenImagen
# Iniciar detector SIFT
tamizar = cv2.TAMINAR()
# encuentre los puntos clave y descriptores con SIFT kp1, des1 =
sift.detectAndCompute(img1,None) kp2, des2 = sift.detectAndCompute(img2,None)
# BFMatcher con parámetros predeterminados bf =
cv2.BFMatcher()
coincidencias = bf.knnCoincidencia(des1,des2, k=2)
# Aplicar prueba de relación
buena = [] para
m,n en partidos:
si distancia m < 0,75*distancia n:
bueno.append([m])
# cv2.drawMatchesKnn espera una lista de listas como coincidencias. img3 =
cv2.drawMatchesKnn(img1,kp1,img2,kp2,bueno,banderas=2)
plt.imshow(img3),plt.show()
Vea el resultado a continuación:
Comparador basado en FLANN
FLANN significa Biblioteca rápida para vecinos más cercanos aproximados. Contiene una colección de algoritmos optimizados para la
búsqueda rápida de vecinos más cercanos en grandes conjuntos de datos y para características de alta dimensión. Funciona más rápido que
BF Matcher para grandes conjuntos de datos. Veremos el segundo ejemplo con el comparador basado en FLANN.
Para el comparador basado en FLANN, necesitamos pasar dos diccionarios que especifican el algoritmo a usar, sus parámetros relacionados,
etc. El primero es IndexParams. Para varios algoritmos, la información que se debe pasar se explica en los documentos de FLANN. Como
resumen, para algoritmos como SIFT, SURF, etc., puede pasar lo siguiente:
1.5. Detección y descripción de características 185
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
index_params = dict(algoritmo = FLANN_INDEX_KDTREE, árboles = 5)
Mientras usa ORB, puede pasar lo siguiente. Los valores comentados se recomiendan según los documentos, pero en algunos casos no
proporcionaron los resultados requeridos. Otros valores funcionaron bien.:
index_params= dict(algoritmo = FLANN_INDEX_LSH, table_number = 6, # 12
key_size = 12, # 20 multi_probe_level
= 1) #2
El segundo diccionario es SearchParams. Especifica el número de veces que se deben recorrer recursivamente los árboles del índice. Los
valores más altos brindan una mejor precisión, pero también requieren más tiempo. Si desea cambiar el valor, pase search_params =
dict(checks=100).
Con esta información, estamos listos para comenzar.
importar numpy como np
importar cv2
desde matplotlib importar pyplot como plt
img1 = cv2.imread('box.png',0) # consultaImagen img2 = cv2.imread('box_in_scene.png',0)
# trenImagen
# Iniciar detector SIFT
tamizar = cv2.TAMINAR()
# encuentre los puntos clave y descriptores con SIFT kp1, des1 =
sift.detectAndCompute(img1,None) kp2, des2 =
sift.detectAndCompute(img2,None)
# Parámetros FLANN
FLANN_INDEX_KDTREE = 0
index_params = dict(algoritmo = FLANN_INDEX_KDTREE, árboles = 5) search_params =
dict(checks=50) # o pasar diccionario vacío
flann = cv2.FlannBasedMatcher(index_params,search_params)
coincidencias = flann.knnCoincidencia(des1,des2,k=2)
# Necesita dibujar solo buenas coincidencias, así que cree una máscara
matchesMask = [[0,0] for i in xrange(len(matches))]
# prueba de proporción según el artículo de Lowe
para i,(m,n) en enumerar(coincidencias):
si distancia m < 0.7*distancia n:
máscara de coincidencias[i]=[1,0]
dibujar_parámetros = dict(matchColor = (0,255,0),
singlePointColor = (255,0,0), MatchMask =
MatchMask,
banderas = 0)
img3 = cv2.drawMatchesKnn(img1,kp1,img2,kp2,coincidencias,Ninguno,**draw_params)
plt.imshow(img3,),plt.show()
Vea el resultado a continuación:
186 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
Ejercicios
Coincidencia de características + Homografía para encontrar objetos
Meta
En este capítulo,
• Combinaremos la combinación de características y findHomography del módulo calib3d para encontrar objetos conocidos en
una imagen compleja
Lo esencial
Entonces, ¿qué hicimos en la última sesión? Usamos una imagen de consulta, encontramos algunos puntos de características en ella, tomamos otra
imagen de tren, encontramos las características en esa imagen también y encontramos las mejores coincidencias entre ellas. En resumen, encontramos
ubicaciones de algunas partes de un objeto en otra imagen desordenada. Esta información es suficiente para encontrar el objeto exactamente en la imagen del tren.
Para eso, podemos usar una función del módulo calib3d, es decir, cv2.findHomography(). Si pasamos el conjunto de puntos de ambas imágenes,
encontrará la transformación de perspectiva de ese objeto. Luego podemos usar cv2.perspectiveTransform() para encontrar el objeto. Se
necesitan al menos cuatro puntos correctos para encontrar la transformación.
Hemos visto que puede haber algunos posibles errores durante la coincidencia que pueden afectar el resultado. Para resolver este problema, el
algoritmo usa RANSAC o LAST_MEDIAN (que puede decidirse por las banderas). Por lo tanto, las buenas coincidencias que proporcionan una
estimación correcta se denominan valores internos y las restantes se denominan valores atípicos. cv2.findHomography() devuelve una máscara
que especifica los puntos internos y externos.
Hagamoslo !!!
Código
Primero, como de costumbre, busquemos características SIFT en imágenes y apliquemos la prueba de proporción para encontrar las mejores coincidencias.
1.5. Detección y descripción de características 187
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
importar numpy como np
importar cv2
desde matplotlib importar pyplot como plt
MIN_PARTIDO_CUENTA = 10
img1 = cv2.imread('box.png',0) img2 = # consultaImagen
cv2.imread('box_in_scene.png',0) # trenImagen
# Iniciar detector SIFT
tamizar = cv2.TAMINAR()
# encuentre los puntos clave y descriptores con SIFT kp1, des1 =
sift.detectAndCompute(img1,None) kp2, des2 =
sift.detectAndCompute(img2,None)
FLANN_INDEX_KDTREE = 0
index_params = dict(algoritmo = FLANN_INDEX_KDTREE, árboles = 5) search_params =
dict(comprobaciones = 50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
coincidencias = flann.knnCoincidencia(des1,des2,k=2)
# almacenar todas las buenas coincidencias según la prueba de proporción de
Lowe. bueno
= [] para m, n en coincidencias:
si distancia m < 0.7*distancia n:
bien.anexar(m)
Ahora establecemos una condición de que al menos 10 coincidencias (definidas por MIN_MATCH_COUNT) deben estar allí para encontrar el objeto.
De lo contrario, simplemente muestre un mensaje que diga que no hay suficientes coincidencias.
Si se encuentran suficientes coincidencias, extraemos las ubicaciones de los puntos clave coincidentes en ambas imágenes. Se pasan para encontrar la
transformación de perspectiva. Una vez que obtengamos esta matriz de transformación de 3x3, la usaremos para transformar las esquinas de queryImage
en los puntos correspondientes en trainImage. Luego lo dibujamos.
si len(bueno)>MIN_MATCH_COUNT:
src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(1,1,2) dst_pts = np.float32([ kp2[m.trainIdx].pt for m in
good ]).reforma(1,1,2)
M, máscara = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0) coincidenciasMask =
máscara.ravel().tolist()
h,w = img1.shape pts =
np.float32([ [0,0],[0,h1],[w1,h1],[w1,0] ]). reshape( 1,1,2) dst = cv2.perspectiveTransform(pts,M)
img2 = cv2.polylines(img2,[np.int32(dst)],Verdadero,255,3, cv2.LINE_AA)
demás:
print "No se encontraron suficientes coincidencias %d/%d" % (len(bien),MIN_MATCH_COUNT)
MatchMask = Ninguno
Finalmente, dibujamos nuestros inliers (si encontramos el objeto con éxito) o los puntos clave coincidentes (si fallamos).
draw_params = dict(matchColor = (0,255,0), # dibujar coincidencias en color verde
singlePointColor = Ninguno,
188 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
MatchMask = MatchMask, # dibujar solo banderas internas = 2)
img3 = cv2.drawMatches(img1,kp1,img2,kp2,bueno,Ninguno,**draw_params)
plt.imshow(img3, 'gris'),plt.show()
Vea el resultado a continuación. El objeto está marcado en color blanco en una imagen desordenada:
Recursos adicionales
Ejercicios
Análisis de vídeo
• Mediashift y Camshift
Ya hemos visto un ejemplo de seguimiento basado en colores. es mas simple
Esta vez, vemos algoritmos mucho mejores como "Meanshift" y su versión mejorada,
"Camshift" para encontrarlos y rastrearlos.
• Flujo óptico
1.6. Análisis de vídeo 189
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Ahora analicemos un concepto importante, "Flujo óptico", que está relacionado con
videos y tiene muchas aplicaciones.
• Sustracción de fondo
En varias aplicaciones, necesitamos extraer el primer plano para otras operaciones
como el seguimiento de objetos. La sustracción de fondo es un método bien conocido
en esos casos.
190 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Mediashift y Camshift
Meta
En este capítulo,
• Aprenderemos sobre los algoritmos Meanshift y Camshift para encontrar y rastrear objetos en videos.
cambio de media
La intuición detrás del cambio de media es simple. Considere que tiene un conjunto de puntos. (Puede ser una distribución de píxeles como una
retroproyección de histograma). Aparece una pequeña ventana (puede ser un círculo) y debe mover esa ventana al área de máxima densidad de
píxeles (o número máximo de puntos). Se ilustra en la imagen simple que se muestra a continuación:
La ventana inicial se muestra en un círculo azul con el nombre “C1”. Su centro original está marcado en un rectángulo azul, denominado “C1_o”.
Pero si encuentra el centroide de los puntos dentro de esa ventana, obtendrá el punto "C1_r" (marcado en un pequeño círculo azul) que es el
centroide real de la ventana. Seguro que no coinciden. Así que mueva su ventana de manera que el círculo de la nueva ventana coincida con el
centroide anterior. Nuevamente encuentre el nuevo centroide. Lo más probable es que no coincida. Así que muévalo nuevamente y continúe las
iteraciones de manera que el centro de la ventana y su centroide caigan en la misma ubicación (o con un pequeño error deseado). Entonces,
finalmente, lo que obtienes es una ventana con la máxima distribución de píxeles. Está marcado con un círculo verde, denominado “C2”. Como
puede ver en la imagen, tiene un número máximo de puntos. Todo el proceso se demuestra en una imagen estática a continuación:
Por lo tanto, normalmente pasamos la imagen retroproyectada del histograma y la ubicación inicial del objetivo. Cuando el objeto se mueve,
obviamente el movimiento se refleja en la imagen retroproyectada del histograma. Como resultado, el algoritmo de desplazamiento medio mueve
nuestra ventana a la nueva ubicación con la máxima densidad.
1.6. Análisis de vídeo 191
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Cambio de media en OpenCV
Para usar el desplazamiento medio en OpenCV, primero debemos configurar el objetivo, encontrar su histograma para que podamos retroproyectar el
objetivo en cada cuadro para el cálculo del desplazamiento medio. También necesitamos proporcionar la ubicación inicial de la ventana. Para el
histograma, aquí solo se considera Hue. Además, para evitar valores falsos debido a la poca luz, los valores de poca luz se descartan mediante la
función cv2.inRange() .
importar numpy como np
importar cv2
cap = cv2.VideoCapture('lento.flv')
# toma el primer fotograma del video ret,frame =
cap.read()
# configurar la ubicación inicial de la ventana r,h,c,w =
250,90,400,125 # simplemente codificar los valores track_window = (c,r,w,h)
# configurar el ROI para el seguimiento roi =
frame[r:r+h, c:c+w] hsv_roi =
cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) mask = cv2.inRange(hsv_roi,
np.array((0. , 60.,32.)), np.array((180.,255.,255.))) roi_hist = cv2.calcHist([hsv_roi],[0],máscara,[180],[0,180])
cv2 .normalizar(roi_hist,roi_hist,0,255,cv2.NORM_MINMAX)
# Configure los criterios de finalización, ya sea 10 iteraciones o avance al menos 1 punto term_crit = ( cv2.TERM_CRITERIA_EPS
| cv2.TERM_CRITERIA_COUNT, 10, 1 )
mientras(1):
derecha, cuadro = cap.read()
si ret == Verdadero:
hsv = cv2.cvtColor(marco, cv2.COLOR_BGR2HSV)
dst = cv2.calcBackProject([hsv],[0],roi_hist,[0,180],1)
# aplicar el desplazamiento medio para obtener la nueva ubicación
ret, track_window = cv2.meanShift(dst, track_window, term_crit)
# Dibujarlo en la imagen
x,y,w,h = track_window img2 =
cv2.rectangle(frame, (x,y), (x+w,y+h), 255,2) cv2.imshow('img2', img2)
k = cv2.waitKey(60) & 0xff si k == 27:
romper
demás:
cv2.imwrite(chr(k)+".jpg",img2)
demás:
romper
cv2.destroyAllWindows() cap.release()
Tres cuadros en un video que utilicé se dan a continuación:
192 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
1.6. Análisis de vídeo 193
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Cambio de levas
¿Seguiste de cerca el último resultado? Hay un problema. Nuestra ventana siempre tiene el mismo tamaño cuando el coche está más lejos y está muy cerca de
la cámara. Eso no es bueno. Necesitamos adaptar el tamaño de la ventana con el tamaño y la rotación del objetivo. Una vez más, la solución provino de "OpenCV
Labs" y se llama CAMshift (Continuously Adaptive Meanshift) publicado por Gary Bradsky en su artículo "Seguimiento facial de visión por computadora para uso
en una interfaz de usuario perceptual" en 1988.
También
Se aplica el desplazamiento medio primero. Una vez que el desplazamiento medio converge, actualiza el tamaño de la ventana como, = 2 × √ 0256 .
0
calcula la orientación de la elipse que mejor se ajusta a ella. De nuevo, aplica el desplazamiento medio con la nueva ventana de búsqueda escalada y la ubicación
de la ventana anterior. El proceso continúa hasta que se alcanza la precisión requerida.
Camshift en OpenCV
Es casi lo mismo que el cambio medio, pero devuelve un rectángulo rotado (que es nuestro resultado) y parámetros de cuadro (solía pasar como ventana de
búsqueda en la siguiente iteración). Vea el código a continuación:
importar numpy como np
importar cv2
cap = cv2.VideoCapture('lento.flv')
# toma el primer fotograma del video ret,frame =
cap.read()
# configurar la ubicación inicial de la ventana r,h,c,w =
250,90,400,125 # simplemente codificar los valores track_window = (c,r,w,h)
# configurar el ROI para el seguimiento roi =
frame[r:r+h, c:c+w] hsv_roi =
cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) mask = cv2.inRange(hsv_roi,
np.array((0. , 60.,32.)), np.array((180.,255.,255.))) roi_hist = cv2.calcHist([hsv_roi],[0],máscara,[180],[0,180])
cv2 .normalizar(roi_hist,roi_hist,0,255,cv2.NORM_MINMAX)
# Configure los criterios de finalización, ya sea 10 iteraciones o avance al menos 1 punto term_crit =
( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )
while(1):
ret ,marco = cap.read()
si ret == Verdadero:
hsv = cv2.cvtColor(marco, cv2.COLOR_BGR2HSV) dst =
cv2.calcBackProject([hsv],[0],roi_hist,[0,180],1)
# aplicar el desplazamiento medio para obtener la nueva ubicación
ret, track_window = cv2.CamShift(dst, track_window, term_crit)
# Dibujarlo en la imagen pts
= cv2.boxPoints(ret) pts = np.int0(pts)
img2 = cv2.polylines(frame,
[pts],True, 255,2) cv2.imshow('img2',img2)
194 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
k = cv2.waitKey(60) & 0xff si k == 27:
romper
demás:
cv2.imwrite(chr(k)+".jpg",img2)
demás:
romper
cv2.destroyAllWindows()
cap.release()
A continuación se muestran tres fotogramas del resultado:
1.6. Análisis de vídeo 195
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
196 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
1. Página de Wikipedia en francés sobre Camshift. (Las dos animaciones están tomadas de aquí)
2. Bradski, GR, "Seguimiento de rostros y objetos en tiempo real como componente de una interfaz de usuario perceptual", Aplicaciones
de visión por computadora, 1998. WACV '98. Actas., Cuarto Taller IEEE sobre, vol., no., pp.214,219, 1921 de octubre de 1998
Ejercicios
1. OpenCV viene con una muestra de Python en una demostración interactiva de camshift. Úsalo, hackéalo, entiéndelo.
Flujo óptico
Meta
En este capítulo,
• Comprenderemos los conceptos de flujo óptico y su estimación mediante el método de LucasKanade.
• Usaremos funciones como cv2.calcOpticalFlowPyrLK() para rastrear puntos característicos en un video.
Flujo óptico
El flujo óptico es el patrón de movimiento aparente de los objetos de la imagen entre dos fotogramas consecutivos causados por el
movimiento del objeto o la cámara. Es un campo vectorial 2D donde cada vector es un vector de desplazamiento que muestra el movimiento
de los puntos desde el primer cuadro al segundo. Considere la imagen a continuación (Imagen cortesía: artículo de Wikipedia sobre flujo óptico).
Muestra una pelota moviéndose en 5 fotogramas consecutivos. La flecha muestra su vector de desplazamiento. El flujo óptico tiene muchas
aplicaciones en áreas como:
• Estructura de movimiento
• Compresión de video
• Estabilización de vídeo ...
El flujo óptico funciona en varios supuestos:
1. Las intensidades de píxeles de un objeto no cambian entre fotogramas consecutivos.
1.6. Análisis de vídeo 197
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
2. Los píxeles vecinos tienen un movimiento similar.
Considere un píxel ( , , ) en el primer cuadro (compruebe que aquí se agrega una nueva dimensión, el tiempo. Anteriormente trabajábamos solo con
, el siguiente cuadro tomado después del tiempo. Entonces, dado que esos
imágenes, por lo que no necesitamos tiempo). Se mueve por distancia ( ) en
píxeles son los mismos y la intensidad no cambia, podemos decir:
( , , ) = ( + , + , + )
Luego tome la aproximación de la serie de Taylor del lado derecho, elimine los términos comunes y divida entre para obtener la siguiente
ecuación:
+ + = 0
dónde:
∂ ∂
= ; =
∂ ∂
= ; =
La ecuación anterior se llama ecuación de flujo óptico. En él podemos encontrar y , son degradados de imagen. De manera similar es
el gradiente a lo largo del tiempo. Pero ( , ) es desconocido. No podemos resolver esta ecuación con dos variables desconocidas. Así que
se proporcionan varios métodos para resolver este problema y uno de ellos es LucasKanade.
Método LucasKanade
Hemos visto una suposición antes, que todos los píxeles vecinos tendrán un movimiento similar. El método de LucasKanade toma un parche de
, Así que ahora
3x3 alrededor del punto. Entonces todos los 9 puntos tienen el mismo movimiento. Podemos encontrar ( , ) para estos 9 puntos.
nuestro problema se convierte en resolver 9 ecuaciones con dos variables desconocidas que están sobredeterminadas. Se obtiene una mejor
solución con el método de ajuste de mínimos cuadrados. A continuación se muestra la solución final, que es un problema desconocido de dos
ecuaciones y dos, y se resuelve para obtener la solución.
2
2 ]−1 [ − ∑
[ ] = [ ∑ ∑ ∑ ∑ − ∑ ]
(Verifique la similitud de la matriz inversa con el detector de esquinas de Harris. Denota que las esquinas son mejores puntos para rastrear).
Entonces, desde el punto de vista del usuario, la idea es simple, damos algunos puntos para rastrear, recibimos los vectores de flujo óptico de
esos puntos. Pero de nuevo hay algunos problemas. Hasta ahora, tratábamos de pequeños movimientos. Entonces falla cuando hay un gran
movimiento. Así que de nuevo vamos por las pirámides. Cuando subimos en la pirámide, los pequeños movimientos se eliminan y los grandes
movimientos se convierten en pequeños movimientos. Entonces, aplicando LucasKanade allí, obtenemos flujo óptico junto con la escala.
Flujo óptico de LucasKanade en OpenCV
OpenCV proporciona todo esto en una sola función, cv2.calcOpticalFlowPyrLK(). Aquí, creamos una aplicación simple que
rastrea algunos puntos en un video. Para decidir los puntos, usamos cv2.goodFeaturesToTrack(). Tomamos el primer cuadro,
detectamos algunos puntos de esquina de ShiTomasi en él, luego rastreamos iterativamente esos puntos usando el flujo óptico
de LucasKanade. Para la función cv2.calcOpticalFlowPyrLK() pasamos el cuadro anterior, los puntos anteriores y el cuadro siguiente.
Devuelve los siguientes puntos junto con algunos números de estado que tienen un valor de 1 si se encuentra el siguiente punto, de lo contrario, cero. Pasamos
iterativamente estos puntos siguientes como puntos anteriores en el paso siguiente. Vea el código a continuación:
importar numpy como np
importar cv2
cap = cv2.VideoCapture('lento.flv')
198 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
# parámetros para la detección de esquinas de ShiTomasi
feature_params = dict( maxCorners = 100,
nivel de calidad = 0.3, distancia
mínima = 7, tamaño de
bloque = 7 )
# Parámetros para el flujo óptico de lucas kanade lk_params = dict( winSize
= (15,15), maxLevel = 2,
criterio = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.
→03))
# Crea algunos colores aleatorios color =
np.random.randint(0,255,(100,3))
# Toma el primer cuadro y encuentra las esquinas en él
ret, old_frame = cap.read() old_gray =
cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY) p0 = cv2.goodFeaturesToTrack(old_gray,
mask = None, **feature_params)
# Crear una imagen de máscara para fines de dibujo mask =
np.zeros_like(old_frame)
while(1):
ret,frame = cap.read() frame_gray =
cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# calcula el flujo óptico p1, st, err =
cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_ →params)
# Seleccionar buenos puntos
good_new = p1[st==1] good_old =
p0[st==1]
# dibujar las pistas para i,
(nuevo,viejo) en enumerate(zip(bueno_nuevo,bueno_viejo)):
a,b = nuevo.ravel()
c,d = old.ravel() máscara =
cv2.line(máscara, (a,b),(c,d), color[i].tolist(), 2) marco = cv2.circle(marco,(a ,b),5,color[i].tolist(),1)
img = cv2.add(marco,máscara)
cv2.imshow('marco',img) k =
cv2.waitKey(30) & 0xff si k == 27:
romper
# Ahora actualice el marco anterior y los puntos anteriores old_gray = frame_gray.copy() p0
= good_new.reshape(1,1,2)
cv2.destroyAllWindows() cap.release()
(Este código no verifica qué tan correctos son los siguientes puntos clave. Por lo tanto, incluso si algún punto característico desaparece en la imagen, existe
la posibilidad de que el flujo óptico encuentre el siguiente punto que puede parecer cercano a él. Entonces, en realidad para un seguimiento sólido, esquina
1.6. Análisis de vídeo 199
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
los puntos deben ser detectados en intervalos particulares. Las muestras de OpenCV presentan una muestra de este tipo que encuentra los puntos
característicos en cada 5 fotogramas. También ejecuta una verificación hacia atrás de los puntos de flujo óptico para seleccionar solo los buenos.
Compruebe samples/python2/lk_track.py).
Vea los resultados que obtuvimos:
Flujo óptico denso en OpenCV
El método de LucasKanade calcula el flujo óptico para un conjunto de características dispersas (en nuestro ejemplo, las esquinas se detectaron
con el algoritmo de Shi Tomasi). OpenCV proporciona otro algoritmo para encontrar el flujo óptico denso. Calcula el flujo óptico para todos los
puntos en el cuadro. Se basa en el algoritmo de Gunner Farneback que se explica en "Estimación de movimiento de dos fotogramas basada en
expansión polinomial" por Gunner Farneback en 2003.
El siguiente ejemplo muestra cómo encontrar el flujo óptico denso utilizando el algoritmo anterior. Obtenemos una matriz de 2 canales con vectores
de flujo óptico, ( , ). Encontramos su magnitud y dirección. Codificamos con colores el resultado para una mejor visualización. La dirección
corresponde al valor Hue de la imagen. La magnitud corresponde al plano de valor. Vea el código a continuación:
importar cv2
importar numpy como np
cap = cv2.VideoCapture("vtest.avi")
ret, frame1 = cap.read() prvs =
cv2.cvtColor(frame1,cv2.COLOR_BGR2GRAY) hsv = np.zeros_like(frame1)
hsv[...,1] = 255
while(1): ret,
frame2 = cap.read() next =
cv2.cvtColor(frame2,cv2.COLOR_BGR2GRAY)
flujo = cv2.calcOpticalFlowFarneback(prvs,siguiente, Ninguno, 0.5, 3, 15, 3, 5, 1.2, 0)
mag, ang = cv2.cartToPolar(flujo[...,0], flujo[...,1])
200 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
hsv[...,0] = ang*180/np.pi/2 hsv[...,2] =
cv2.normalize(mag,Ninguno,0,255,cv2.NORM_MINMAX) rgb = cv2.cvtColor(hsv,cv2 .COLOR_HSV2BGR)
cv2.imshow('frame2',rgb) k =
cv2.waitKey(30) & 0xff si k == 27:
romper
elif k == palabra('s'):
cv2.imwrite('opticalfb.png',frame2)
cv2.imwrite('opticalhsv.png',rgb)
prvs = siguiente
cap.release()
cv2.destroyAllWindows()
Vea el resultado a continuación:
1.6. Análisis de vídeo 201
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
OpenCV viene con una muestra más avanzada sobre flujo óptico denso, consulte samples/python2/opt_flow.
pi.
202 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
Ejercicios
1. Verifique el código en samples/python2/lk_track.py. Trate de entender el código.
2. Verifique el código en samples/python2/opt_flow.py. Trate de entender el código.
Resta de fondo
Meta
En este capítulo,
• Nos familiarizaremos con los métodos de sustracción de fondo disponibles en OpenCV.
Lo esencial
La sustracción de fondo es uno de los principales pasos de preprocesamiento en muchas aplicaciones basadas en visión. Por ejemplo,
considere los casos como el contador de visitantes donde una cámara estática registra el número de visitantes que ingresan o salen de la
habitación, o una cámara de tráfico que extrae información sobre los vehículos, etc. En todos estos casos, primero debe extraer solo a la
persona o los vehículos. . Técnicamente, debe extraer el primer plano en movimiento del fondo estático.
Si tiene una imagen de fondo sola, como la imagen de la habitación sin visitantes, la imagen de la carretera sin vehículos, etc., es un
trabajo fácil. Simplemente reste la nueva imagen del fondo. Obtienes los objetos de primer plano solos. Pero en la mayoría de los casos,
es posible que no tenga esa imagen, por lo que debemos extraer el fondo de cualquier imagen que tengamos. Se vuelve más complicado
cuando hay sombra de los vehículos. Dado que la sombra también se está moviendo, la simple resta marcará eso también como primer
plano. Complica las cosas.
Se introdujeron varios algoritmos para este propósito. OpenCV ha implementado tres algoritmos de este tipo que son muy fáciles de usar.
Los veremos uno por uno.
FondoSubtractorMOG
Es un algoritmo de segmentación de fondo/primer plano basado en una mezcla gaussiana. Fue presentado en el documento "Un modelo
de mezcla de fondo adaptativo mejorado para seguimiento en tiempo real con detección de sombras" por P. KadewTraKuPong y R.
Bowden en 2001. Utiliza un método para modelar cada píxel de fondo mediante una mezcla de distribuciones K Gaussianas ( K = 3 a 5).
Los pesos de la mezcla representan las proporciones de tiempo que esos colores permanecen en la escena. Los colores de fondo
probables son los que permanecen más tiempo y más estáticos.
Mientras codificamos, necesitamos crear un objeto de fondo usando la función, cv2.createBackgroundSubtractorMOG().
Tiene algunos parámetros opcionales como la duración del historial, el número de mezclas gaussianas, el umbral, etc. Todo está configurado con
algunos valores predeterminados. Luego, dentro del bucle de video, use el método backgroundsubtractor.apply() para obtener la máscara de
primer plano.
Vea un ejemplo simple a continuación:
importar numpy como np
importar cv2
tapa = cv2.VideoCapture('vtest.avi')
fgbg = cv2.createBackgroundSubtractorMOG()
mientras(1):
1.6. Análisis de vídeo 203
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
derecha, cuadro = cap.read()
fgmask = fgbg.apply(marco)
cv2.imshow('marco',fgmask) k =
cv2.waitKey(30) & 0xff si k == 27:
romper
cap.release()
cv2.destroyAllWindows()
(Todos los resultados se muestran al final para comparar).
FondoSubtractorMOG2
También es un algoritmo de segmentación de fondo/primer plano basado en una mezcla gaussiana. Se basa en dos artículos
de Z.Zivkovic, "Modelo de mezcla gausiana adaptable mejorado para la sustracción de fondo" en 2004 y "Estimación de densidad
adaptativa eficiente por píxel de imagen para la tarea de sustracción de fondo" en 2006. Una característica importante de este
algoritmo es que selecciona el número apropiado de distribución gaussiana para cada píxel. (Recuerde, en el último caso,
tomamos una K distribuciones gaussianas a lo largo del algoritmo). Proporciona una mejor adaptabilidad a diferentes escenas
debido a cambios de iluminación, etc.
Como en el caso anterior, tenemos que crear un objeto sustractor de fondo. Aquí, tiene la opción de seleccionar si la sombra se
detectará o no. Si detectShadows = True (que es así por defecto), detecta y marca sombras, pero disminuye la velocidad. Las
sombras se marcarán en color gris.
importar numpy como np
importar cv2
tapa = cv2.VideoCapture('vtest.avi')
fgbg = cv2.createBackgroundSubtractorMOG2()
while(1): ret,
cuadro = cap.read()
fgmask = fgbg.apply(marco)
cv2.imshow('marco',fgmask) k =
cv2.waitKey(30) & 0xff si k == 27:
romper
cap.release()
cv2.destroyAllWindows()
(Resultados dados al final)
FondoSubtractorGMG
Este algoritmo combina la estimación estadística de la imagen de fondo y la segmentación bayesiana por píxel. Fue presentado
por Andrew B. Godbehere, Akihiro Matsukawa, Ken Goldberg en su artículo "Seguimiento visual de visitantes humanos bajo
condiciones de iluminación variable para una instalación de arte de audio sensible" en 2012. Según el artículo, el
204 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
El sistema ejecutó una exitosa instalación de arte de audio interactivo llamada "¿Ya llegamos?" del 31 de marzo al 31 de julio de 2011 en el Museo
Judío Contemporáneo de San Francisco, California.
Utiliza los primeros fotogramas (120 por defecto) para el modelado de fondo. Emplea un algoritmo de segmentación de primer plano probabilístico
que identifica posibles objetos de primer plano mediante la inferencia bayesiana. Las estimaciones son adaptativas; las observaciones más nuevas
tienen un mayor peso que las observaciones antiguas para adaptarse a la iluminación variable. Se realizan varias operaciones de filtrado
morfológico, como el cierre y la apertura, para eliminar el ruido no deseado. Obtendrá una ventana negra durante los primeros cuadros.
Sería mejor aplicar apertura morfológica al resultado para eliminar los ruidos.
importar numpy como np
importar cv2
tapa = cv2.VideoCapture('vtest.avi')
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3)) fgbg =
cv2.createBackgroundSubtractorGMG()
while(1): ret,
cuadro = cap.read()
fgmask = fgbg.apply(marco) fgmask =
cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
cv2.imshow('marco',fgmask) k =
cv2.waitKey(30) & 0xff si k == 27:
romper
cap.release()
cv2.destroyAllWindows()
Resultados
marco originales
La imagen de abajo muestra el cuadro 200 de un video
Resultado de BackgroundSubtractorMOG
1.6. Análisis de vídeo 205
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Resultado de BackgroundSubtractorMOG2
La región de color gris muestra la región de la sombra.
Resultado de BackgroundSubtractorGMG
El ruido se elimina con la apertura morfológica.
206 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
Ejercicios
Calibración de cámara y reconstrucción 3D
• Calibración de la cámara
Averigüemos qué tan buena es nuestra cámara. ¿Hay alguna distorsión en las imágenes
tomadas con él? Si es así, ¿cómo corregirlo?
• Estimación de poses
Esta es una pequeña sección que lo ayudará a crear algunos efectos 3D geniales con el
módulo de calibre.
• Geometría Epipolar
Comprendamos la geometría epipolar y la restricción epipolar.
• Mapa de profundidad de imágenes estéreo
Extraiga información de profundidad de imágenes 2D.
1.7. Calibración de cámara y reconstrucción 3D 207
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Calibración de la cámara
Meta
En esta sección,
• Aprenderemos sobre distorsiones en la cámara, parámetros intrínsecos y extrínsecos de la cámara, etc.
• Aprenderemos a encontrar estos parámetros, no distorsionar imágenes, etc.
Lo esencial
Las cámaras estenopeicas baratas de hoy introducen mucha distorsión en las imágenes. Dos distorsiones principales son la distorsión radial y la
distorsión tangencial.
Debido a la distorsión radial, las líneas rectas aparecerán curvas. Su efecto es mayor a medida que nos alejamos del centro de la imagen.
Por ejemplo, a continuación se muestra una imagen, donde dos bordes de un tablero de ajedrez están marcados con líneas rojas. Pero puedes ver
que el borde no es una línea recta y no coincide con la línea roja. Todas las líneas rectas esperadas están abultadas.
Visite Distorsión (óptica) para más detalles.
208 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Esta distorsión se resuelve de la siguiente manera:
2 + 2 4 6
= (1 + 1 = + 3 )
2 + 2 4 6
(1 + 1 + 3 )
De forma similar, otra distorsión es la distorsión tangencial que se produce porque la lente de toma de imágenes no está alineada perfectamente paralela al
plano de formación de imágenes. Por lo tanto, algunas áreas de la imagen pueden verse más cerca de lo esperado. Se resuelve de la siguiente manera:
2 + 2 2
= + [2 1 + 2(
2 + 2 2
= + [ 1( ) + 2 2 )] ]
En resumen, necesitamos encontrar cinco parámetros, conocidos como coeficientes de distorsión dados por:
= ( 1 2 1 2 3)
Además de esto, necesitamos encontrar un poco más de información, como los parámetros intrínsecos y extrínsecos de una cámara. Los
, ( ), centros ópticos ( ) etc. E,s
parámetros intrínsecos son específicos de una cámara. Incluye información como distancia focal
1.7. Calibración de cámara y reconstrucción 3D 209
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
también llamada matriz de cámara. Depende únicamente de la cámara, por lo que una vez calculada, se puede almacenar para fines futuros. Se expresa
como una matriz de 3x3:
0
= 0
0 0 1
Los parámetros extrínsecos corresponden a los vectores de rotación y traslación que traducen las coordenadas de un punto 3D a un sistema de coordenadas.
Para aplicaciones estéreo, estas distorsiones deben corregirse primero. Para encontrar todos estos parámetros, lo que tenemos que hacer es proporcionar
algunas imágenes de muestra de un patrón bien definido (por ejemplo, un tablero de ajedrez). Encontramos algunos puntos específicos en él (esquinas
cuadradas en tablero de ajedrez). Conocemos sus coordenadas en el espacio del mundo real y conocemos sus coordenadas en la imagen. Con estos datos
se resuelve en segundo plano algún problema matemático para obtener los coeficientes de distorsión. Ese es el resumen de toda la historia. Para obtener
mejores resultados, necesitamos al menos 10 patrones de prueba.
Código
Como se mencionó anteriormente, necesitamos al menos 10 patrones de prueba para la calibración de la cámara. OpenCV viene con algunas imágenes del
tablero de ajedrez (ver samples/cpp/left01.jpg left14.jpg), así que lo utilizaremos. En aras de la comprensión, considere solo una imagen de un tablero de
ajedrez. Los datos de entrada importantes necesarios para la calibración de la cámara son un conjunto de puntos del mundo real en 3D y sus correspondientes
puntos de imagen en 2D. Los puntos de imagen 2D están bien y podemos encontrarlos fácilmente en la imagen. (Estos puntos de imagen son ubicaciones
donde dos cuadrados negros se tocan en tableros de ajedrez)
¿Qué pasa con los puntos 3D del espacio del mundo real? Esas imágenes se toman de una cámara estática y los tableros de ajedrez se colocan en
diferentes ubicaciones y orientaciones. Entonces necesitamos saber los valores ( , ). Pero para ,simplificar, podemos decir que el tablero de ajedrez se
mantuvo estacionario en el plano XY (por lo que siempre Z = 0) y la cámara se movió en consecuencia. Esta consideración nos ayuda a encontrar solo
valores X,Y. Ahora, para los valores X,Y, simplemente podemos pasar los puntos como (0,0), (1,0), (2,0), ... lo que denota la ubicación de los puntos. En
este caso, los resultados que obtendremos estarán en la escala del tamaño del tablero de ajedrez. Pero si conocemos el tamaño del cuadrado, (digamos 30
mm), y podemos pasar los valores como (0,0),(30,0),(60,0),..., obtenemos los resultados en mm.
(En este caso, no sabemos el tamaño del cuadrado ya que no tomamos esas imágenes, así que pasamos en términos de tamaño del cuadrado).
Los puntos 3D se denominan puntos de objeto y los puntos de imagen 2D se denominan puntos de imagen.
Configuración
Entonces, para encontrar un patrón en el tablero de ajedrez, usamos la función cv2.findChessboardCorners(). También necesitamos pasar qué tipo de
patrón estamos buscando, como una cuadrícula de 8x8, una cuadrícula de 5x5, etc. En este ejemplo, usamos una cuadrícula de 7x6. (Normalmente, un
tablero de ajedrez tiene cuadrados de 8x8 y esquinas internas de 7x7). Devuelve los puntos de las esquinas y el retval, que será Verdadero si se obtiene el patrón.
Estas esquinas se colocarán en un orden (de izquierda a derecha, de arriba a abajo)
Ver también:
Es posible que esta función no pueda encontrar el patrón requerido en todas las imágenes. Entonces, una buena opción es escribir el código de tal manera
que inicie la cámara y verifique cada cuadro para el patrón requerido. Una vez obtenido el patrón, busque las esquinas y guárdelo en una lista. También
proporciona un intervalo antes de leer el siguiente cuadro para que podamos ajustar nuestro tablero de ajedrez en una dirección diferente. Continúe este
proceso hasta obtener el número requerido de buenos patrones. Incluso en el ejemplo proporcionado aquí, no estamos seguros de las 14 imágenes dadas,
cuántas son buenas. Así que leemos todas las imágenes y tomamos las buenas.
Ver también:
En lugar de un tablero de ajedrez, podemos usar una cuadrícula circular, pero luego usamos la función cv2.findCirclesGrid() para encontrar el patrón. Se
dice que una menor cantidad de imágenes es suficiente cuando se usa una cuadrícula circular.
Una vez que encontramos las esquinas, podemos aumentar su precisión usando cv2.cornerSubPix(). También podemos dibujar el patrón usando
cv2.drawChessboardCorners(). Todos estos pasos están incluidos en el siguiente código:
210 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
importar numpy como np
importar cv2
importar glob
# criterios de terminación
criterio = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# preparar puntos de objeto, como (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0) objp = np.zeros((6* 7,3), np.float32) objp[:,:2]
= np.mgrid[0:7,0:6].T.reshape(1,2)
# Matrices para almacenar puntos de objetos y puntos de imagen de todas las imágenes. objpoints = [] # Punto 3d
en el espacio del mundo real imgpoints = [] # Puntos 2d en el plano de la
imagen.
imágenes = globo.glob('*.jpg')
para fname en imágenes: img =
cv2.imread(fname) gray =
cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# Encuentra las esquinas del tablero de ajedrez
ret, esquinas = cv2.findChessboardCorners(gris, (7,6),Ninguno)
# Si lo encuentra, agregue puntos de objeto, puntos de imagen (después de refinarlos) if ret == True:
puntosobj.append(objp)
esquinas2 = cv2.cornerSubPix(gris,esquinas,(11,11),(1,1),criterios) imgpoints.append(esquinas2)
# Dibujar y mostrar las esquinas img =
cv2.drawChessboardCorners(img, (7,6), corners2,ret) cv2.imshow('img',img) cv2.waitKey(500)
cv2.destroyAllWindows()
A continuación se muestra una imagen con un patrón dibujado:
1.7. Calibración de cámara y reconstrucción 3D 211
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Calibración
Entonces, ahora que tenemos nuestros puntos de objeto y puntos de imagen, estamos listos para la calibración. Para eso usamos la
función, cv2.calibrateCamera(). Devuelve la matriz de la cámara, los coeficientes de distorsión, los vectores de rotación y traslación, etc.
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray. →shape[::1],Ninguno,Ninguno)
Sin distorsión
Tenemos lo que estábamos intentando. Ahora podemos tomar una imagen y no distorsionarla. OpenCV viene con dos métodos, veremos
ambos. Pero antes de eso, podemos refinar la matriz de la cámara en función de un parámetro de escalado libre usando
212 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
cv2.getOptimalNewCameraMatrix(). Si el parámetro de escala alfa = 0, devuelve una imagen sin distorsiones con un mínimo de píxeles no deseados. Por
lo tanto, incluso puede eliminar algunos píxeles en las esquinas de la imagen. Si alfa = 1, todos los píxeles se conservan con algunas imágenes negras
adicionales. También devuelve un ROI de imagen que se puede usar para recortar el resultado.
Entonces tomamos una nueva imagen (left12.jpg en este caso. Esa es la primera imagen en este capítulo)
img = cv2.imread('left12.jpg') h, w =
img.shape[:2] newcameramtx,
roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))
1. Usando cv2.undistort()
Este es el camino más corto. Simplemente llame a la función y use el ROI obtenido anteriormente para recortar el resultado.
# desdistorsionar
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
# recortar la imagen
x,y,w,h = roi dst =
dst[y:y+h, x:x+w]
cv2.imwrite('calibresult.png',dst)
2. Usando la reasignación
Este es un camino curvo. Primero encuentre una función de mapeo de imagen distorsionada a imagen no distorsionada. A continuación, utilice la función de
reasignación.
# desdistorsionar
mapx,mapy = cv2.initUndistortRectifyMap(mtx,dist,None,newcameramtx,(w,h),5) dst =
cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
# recortar la imagen
x,y,w,h = roi dst =
dst[y:y+h, x:x+w]
cv2.imwrite('calibresult.png',dst)
Ambos métodos dan el mismo resultado. Vea el resultado a continuación:
1.7. Calibración de cámara y reconstrucción 3D 213
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Puedes ver en el resultado que todos los bordes son rectos.
Ahora puede almacenar la matriz de la cámara y los coeficientes de distorsión usando funciones de escritura en Numpy (np.savez, np.savetxt,
etc.) para usos futuros.
Error de reproyección
El error de reproyección da una buena estimación de cuán exactos son los parámetros encontrados. Este debe ser lo más cercano a cero
posible. Dadas las matrices intrínseca, distorsión, rotación y traslación, primero transformamos el punto del objeto en el punto de la imagen
usando cv2.projectPoints(). Luego calculamos la norma absoluta entre lo que obtuvimos con nuestra transformación y el algoritmo de búsqueda
de esquinas. Para encontrar el error promedio, calculamos la media aritmética de los errores calculados para todas las imágenes de calibración.
error_medio = 0 para
i en xrange(len(objpoints)): imgpoints2, _ error =
= cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
cv2.norm(imgpoints[i],imgpoints2, cv2.NORM_L2)/len(imgpoints2) tot_error += error
imprimir "error total: ", mean_error/len(objpoints)
Recursos adicionales
Ejercicios
1. Pruebe la calibración de la cámara con cuadrícula circular.
214 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Estimación de poses
Meta
En esta sección,
• Aprenderemos a explotar el módulo calib3d para crear algunos efectos 3D en las imágenes.
Lo esencial
Esta va a ser una pequeña sección. Durante la última sesión de calibración de la cámara, encontró la matriz de la cámara, los coeficientes de
distorsión, etc. Dada una imagen de patrón, podemos utilizar la información anterior para calcular su pose, o cómo se sitúa el objeto en el espacio,
por ejemplo, cómo gira, cómo se desplaza, etc. Para un objeto plano, podemos asumir Z = 0, de modo que el problema ahora es cómo se coloca
la cámara en el espacio para ver nuestra imagen de patrón. Entonces, si sabemos cómo se encuentra el objeto en el espacio, podemos dibujar
algunos diagramas 2D para simular el efecto 3D. Veamos cómo hacerlo.
Nuestro problema es que queremos dibujar nuestro eje de coordenadas 3D (ejes X, Y, Z) en la primera esquina de nuestro tablero de ajedrez. Eje
X en color azul, eje Y en color verde y eje Z en color rojo. Entonces, en efecto, el eje Z debería sentirse como si fuera perpendicular a nuestro plano
de tablero de ajedrez.
Primero, carguemos la matriz de la cámara y los coeficientes de distorsión del resultado de la calibración anterior.
importar cv2
importar numpy como np
importar glob
# Cargue datos previamente guardados con
np.load('B.npz') como X: = [X[i] for i in
mtx, distancia, _, _ ('mtx','dist','rvecs','tvecs')]
Ahora vamos a crear una función, draw, que toma las esquinas del tablero de ajedrez (obtenidas usando cv2.findChessboardCorners()) y los puntos
del eje para dibujar un eje 3D.
def dibujar (img, esquinas, imgpts):
esquina = tupla(esquinas[0].ravel()) img = cv2.line(img,
esquina, tupla(imgpts[0].ravel()), (255,0,0), 5) img = cv2.line (img, esquina, tupla(imgpts[1].ravel()), (0,255,0),
5) img = cv2.line(img, esquina, tupla(imgpts[2].ravel()), (0, 0,255), 5) devolver img
Luego, como en el caso anterior, creamos criterios de terminación, puntos de objeto (puntos 3D de esquinas en tablero de ajedrez) y puntos de
eje. Los puntos del eje son puntos en el espacio 3D para dibujar el eje. Dibujamos un eje de longitud 3 (las unidades serán en términos del tamaño
del cuadrado de ajedrez ya que calibramos en función de ese tamaño). Entonces, nuestro eje X se dibuja de (0,0,0) a (3,0,0), así que para el eje Y.
Para el eje Z, se dibuja de (0,0,0) a (0,0,3). Negativo denota que se dibuja hacia la cámara.
criterio = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) objp = np.zeros((6*7,3),
np.float32) objp[:,:2] = np.mgrid[0:7,0 :6].T.reformar(1,2)
eje = np.float32([[3,0,0], [0,3,0], [0,0,3]]).remodelar(1,3)
Ahora, como de costumbre, cargamos cada imagen. Busque la cuadrícula de 7x6. Si se encuentra, lo refinamos con píxeles de subesquina. Luego,
para calcular la rotación y la traslación, usamos la función cv2.solvePnPRansac(). Una vez que tenemos esas matrices de transformación, las
usamos para proyectar nuestros puntos de eje al plano de la imagen. En palabras simples, encontramos los puntos en el plano de la imagen
correspondientes a cada uno de (3,0,0), (0,3,0), (0,0,3) en el espacio 3D. Una vez que los conseguimos, dibujamos líneas desde la primera esquina
hasta cada uno de estos puntos usando nuestra función dibujar(). Hecho !!!
1.7. Calibración de cámara y reconstrucción 3D 215
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
for fname in glob.glob('left*.jpg'): img = cv2.imread(fname)
gray =
cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret, corners =
cv2.findChessboardCorners(gray, (7,6) ,Ninguno)
si ret == Verdadero:
esquinas2 = cv2.cornerSubPix(gris,esquinas,(11,11),(1,1),criterios)
# Encuentra los vectores de rotación y traslación. rvecs, tvecs, inliers
= cv2.solvePnPRansac(objp, corners2, mtx, dist)
# proyectar puntos 3D en el plano de la imagen
imgpts, jac = cv2.projectPoints(axis, rvecs, tvecs, mtx, dist)
img = dibujar(img,esquinas2,imgpts)
cv2.imshow('img',img) k =
cv2.waitKey(0) & 0xff si k == 's':
cv2.imwrite(fname[:6]+'.png', img)
cv2.destroyAllWindows()
Vea algunos resultados a continuación. Observe que cada eje tiene 3 cuadrados de largo.:
216 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Renderizar un cubo
Si desea dibujar un cubo, modifique la función dibujar() y los puntos del eje de la siguiente manera.
Función draw() modificada:
def dibujar(img, esquinas, imgpts): imgpts =
np.int32(imgpts).reshape(1,2)
# dibujar la planta baja en verde img =
cv2.drawContours(img, [imgpts[:4]],1,(0,255,0),3)
# dibujar pilares en color azul para i,j en
zip(rango(4),rango(4,8)): img = cv2.line(img,
tuple(imgpts[i]), tuple(imgpts[j]), (255),3)
# dibujar la capa superior en color rojo img =
cv2.drawContours(img, [imgpts[4:]],1,(0,0,255),3)
1.7. Calibración de cámara y reconstrucción 3D 217
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
devolver imagen
Puntos de eje modificados. Son las 8 esquinas de un cubo en el espacio 3D:
eje = np.float32([[0,0,0], [0,3,0], [3,3,0], [3,0,0],
[0,0,3],[0,3,3],[3,3,3],[3,0,3] ] )
Y mira el resultado a continuación:
Si está interesado en gráficos, realidad aumentada, etc., puede usar OpenGL para representar figuras más complicadas.
Recursos adicionales
Ejercicios
Geometría Epipolar
Meta
En esta sección,
• Aprenderemos sobre los conceptos básicos de la geometría multivista.
• Veremos qué es el epipolo, las líneas epipolares, la restricción epipolar, etc.
Conceptos básicos
Cuando tomamos una imagen con una cámara estenopeica, perdemos una información importante, es decir, la profundidad de la imagen.
O qué tan lejos está cada punto de la imagen de la cámara porque es una conversión de 3D a 2D. Entonces, es una pregunta importante si
podemos encontrar la información de profundidad usando estas cámaras. Y la respuesta es usar más de una cámara. Nuestros ojos
funcionan de manera similar cuando usamos dos cámaras (dos ojos), lo que se denomina visión estéreo. Entonces, veamos qué ofrece
OpenCV en este campo.
(Learning OpenCV de Gary Bradsky tiene mucha información en este campo).
218 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Antes de profundizar en las imágenes, comprendamos primero algunos conceptos básicos de la geometría multivista. En esta sección nos
ocuparemos de la geometría epipolar. Vea la imagen a continuación que muestra una configuración básica con dos cámaras tomando la imagen
de la misma escena.
Si usamos solo la cámara izquierda, no podemos encontrar el punto 3D correspondiente al punto en la imagen porque cada punto
en la línea se proyecta al mismo punto en el plano de la imagen. Pero considere también la imagen correcta. Ahora diferentes puntos
′
en la línea se proyectan a diferentes puntos ( ) en el plano recto. Entonces, con estas dos imágenes, podemos triangular el punto
3D correcto. Esta es toda la idea.
La proyección de los diferentes puntos sobre ). Lo llamamos elpilina
forman una ceorrespondiente
ínea en ′ al punto.
l plano derecho (línea
Significa, para encontrar el punto en la imagen de la derecha, buscar a lo largo de esta epilínea. Debería estar en algún
lugar de esta línea (Piénselo de esta manera, para encontrar el punto de coincidencia en otra imagen, no necesita buscar en toda la
imagen, solo busque a lo largo de la epilínea. Por lo tanto, proporciona un mejor rendimiento y precisión). Esto se llama Restricción
′
Epipolar. De igual forma todos los puntos tendrán sus correspondientes epilíneas en la otra imagen. El plano se llama Plano Epipolar.
y ′ son los centros de cámara. A partir de la configuración anterior, puede ver que la proyección de la cámara derecha se v′e en la
imagen de la izquierda en el punto . Se llama epipolo. El epipolo es el punto de intersección de la línea a través de los centros de
′
la cámara y los planos de la imagen. Del mismo modo es el epipolo de la cámara izquierda. En algunos casos, no podrá ubicar el
epipolo en la imagen, es posible que estén fuera de la imagen (lo que significa que una cámara no ve a la otra).
Todas las epilinas pasan por su epipolo. Entonces, para encontrar la ubicación del epipolo, podemos encontrar muchas epilíneas y encontrar su
punto de intersección.
Entonces, en esta sesión, nos enfocamos en encontrar líneas epipolares y epipolos. Pero para encontrarlos, necesitamos dos
ingredientes más, la Matriz Fundamental (F) y la Matriz Esencial (E). Essential Matrix contiene información sobre traslación y
rotación, que describen la ubicación de la segunda cámara en relación con la primera en coordenadas globales. Vea la imagen a
continuación (Imagen cortesía: Learning OpenCV por Gary Bradsky):
1.7. Calibración de cámara y reconstrucción 3D 219
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Pero preferimos que las mediciones se realicen en coordenadas de píxeles, ¿verdad? Fundamental Matrix contiene la misma información que
Essential Matrix además de la información sobre los elementos intrínsecos de ambas cámaras para que podamos relacionar las dos cámaras en
coordenadas de píxeles. (Si estamos usando imágenes rectificadas y normalizamos el punto dividiendo por las distancias focales, = ). En palabras
simples, la Matriz fundamental F, asigna un punto en una imagen a una línea (epilina) en la otra imagen.
Esto se calcula a partir de los puntos coincidentes de ambas imágenes. Se requiere un mínimo de 8 puntos de este tipo para encontrar la matriz
fundamental (mientras se utiliza el algoritmo de 8 puntos). Se prefieren más puntos y se usa RANSAC para obtener un resultado más sólido.
Código
Entonces, primero debemos encontrar tantas coincidencias posibles entre dos imágenes para encontrar la matriz fundamental. Para esto, usamos
descriptores SIFT con emparejador basado en FLANN y prueba de proporción.
importar cv2
importar numpy como np
desde matplotlib importar pyplot como plt
img1 = cv2.imread('myleft.jpg',0) #queryimage # imagen izquierda img2 =
cv2.imread('myright.jpg',0) #trainimage # imagen derecha
tamizar = cv2.TAMINAR()
# encuentre los puntos clave y descriptores con SIFT kp1, des1 =
sift.detectAndCompute(img1,None) kp2, des2 =
sift.detectAndCompute(img2,None)
# Parámetros FLANN
FLANN_INDEX_KDTREE = 0
index_params = dict(algoritmo = FLANN_INDEX_KDTREE, árboles = 5) search_params =
dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params,search_params) coincidencias =
flann.knnMatch(des1,des2,k=2)
bueno = []
pts1 = []
220 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
ptos2 = []
# prueba de proporción según el artículo de Lowe
para i,(m,n) en enumerar(coincidencias):
si distancia m < 0.8*distancia n:
good.append(m)
pts2.append(kp2[m.trainIdx].pt)
pts1.append(kp1[m.queryIdx].pt)
Ahora tenemos la lista de las mejores coincidencias de ambas imágenes. Encontremos la Matriz Fundamental.
pts1 = np.int32(pts1) pts2 =
np.int32(pts2)
F, máscara = cv2.findFundamentalMat(pts1,pts2,cv2.FM_LMEDS)
# Seleccionamos solo puntos interiores pts1 =
pts1[mask.ravel()==1] pts2 =
pts2[mask.ravel()==1]
A continuación encontramos las epilinas. Las epilinas correspondientes a los puntos de la primera imagen se dibujan en la segunda imagen. Por lo tanto,
mencionar las imágenes correctas es importante aquí. Obtenemos una matriz de líneas. Entonces definimos una nueva función para dibujar estas líneas en
las imágenes.
def dibuja líneas (img1, img2, líneas, pts1, pts2):
''' img1 imagen en la que dibujamos las epilíneas de los puntos en img2
líneas epilíneas correspondientes ''' r,c = img1.shape
img1 =
cv2.cvtColor(img1,cv2.COLOR_GRAY2BGR) img2 =
cv2.cvtColor(img2,cv2.COLOR_GRAY2BGR) for r,pt1,pt2 in
zip(lines,pts1 ,pts2): color =
tupla(np.random.randint(0,255,3).tolist()) x0,y0 = map(int, [0, r[2]/r[1] ]) x1,y1
= map(int, [c, (r[2]+r[0]*c)/r[1] ]) img1 = cv2.line(img1,
(x0,y0), (x1,y1), color, 1) img1 = cv2.circle(img1,tupla(pt1),5,color,1)
img2 = cv2.circle(img2,tupla(pt2),5,color,1) return img1,img2
Ahora encontramos las epilinas en ambas imágenes y las dibujamos.
# Encuentra las epilíneas correspondientes a los puntos en la imagen de la derecha (segunda imagen) y # dibujando
sus líneas en la imagen de la izquierda líneas1 =
cv2.computeCorrespondEpilines(pts2.reshape(1,1,2), 2,F) líneas1 = líneas1.reforma( 1,3) img5,img6
= líneas dibujadas (img1,img2,líneas1,pts1,pts2)
# Encuentra las epilíneas correspondientes a los puntos en la imagen de la izquierda (primera imagen) y #
dibujando sus líneas en la imagen de la derecha
líneas2 = cv2.computeCorrespondEpilines(pts1.reshape(1,1,2), 1,F) líneas2 = líneas2.reforma( 1,3)
img3,img4 = líneas dibujadas
(img2,img1,líneas2,pts2,pts1)
plt.subtrama(121),plt.imshow(img5)
plt.subtrama(122),plt.imshow(img3) plt.show()
A continuación se muestra el resultado que obtenemos:
1.7. Calibración de cámara y reconstrucción 3D 221
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Puede ver en la imagen de la izquierda que todas las epilíneas convergen en un punto fuera de la imagen del lado derecho. esa reunión
222 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
punto es el epipolo.
Para obtener mejores resultados, se deben utilizar imágenes con buena resolución y muchos puntos no planos.
Recursos adicionales
Ejercicios
1. Un tema importante es el movimiento de avance de la cámara. Luego se verán epipolos en los mismos lugares en
ambos con epilinas emergiendo de un punto fijo. Ver esta discusión.
2. La estimación de la matriz fundamental es sensible a la calidad de las coincidencias, valores atípicos, etc. Empeora cuando todos
las coincidencias seleccionadas se encuentran en el mismo plano. Revisa esta discusión.
Mapa de profundidad a partir de imágenes estéreo
Meta
En esta sesión,
• Aprenderemos a crear mapas de profundidad a partir de imágenes estéreo.
Lo esencial
En la última sesión, vimos conceptos básicos como restricciones epipolares y otros términos relacionados. También vimos que si tenemos dos
imágenes de la misma escena, podemos obtener información de profundidad de una manera intuitiva. A continuación se muestra una imagen
y algunas fórmulas matemáticas simples que prueban esa intuición. (Imagen de cortesía :
1.7. Calibración de cámara y reconstrucción 3D 223
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
El diagrama anterior contiene triángulos equivalentes. Escribir sus ecuaciones equivalentes nos dará el siguiente resultado:
= −
′=
′
y son la distancia entre los puntos en el plano de la imagen correspondientes al punto de escena 3D y su centro de cámara. es la
distancia entre dos cámaras (que conocemos) y es la distancia focal de la cámara (ya conocida). En resumen, la ecuación anterior
dice que la profundidad de un punto en una escena es inversamente proporcional a la diferencia en la distancia de los puntos de
imagen correspondientes y sus centros de cámara. Entonces, con esta información, podemos derivar la profundidad de todos los
píxeles en una imagen.
Entonces encuentra coincidencias correspondientes entre dos imágenes. Ya hemos visto cómo la restricción epilina hace que esta operación
sea más rápida y precisa. Una vez que encuentra coincidencias, encuentra la disparidad. Veamos cómo podemos hacerlo con OpenCV.
Código
El siguiente fragmento de código muestra un procedimiento simple para crear un mapa de disparidad.
importar numpy como np
importar cv2
desde matplotlib importar pyplot como plt
imgL = cv2.imread('tsukuba_l.png',0) imgR =
cv2.imread('tsukuba_r.png',0)
224 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
stereo = cv2.createStereoBM(numDisparities=16, blockSize=15) disparity =
stereo.compute(imgL,imgR) plt.imshow(disparity,'gray')
plt.show()
La imagen de abajo contiene la imagen original (izquierda) y su mapa de disparidad (derecha). Como puede ver, el resultado está contaminado
con un alto grado de ruido. Al ajustar los valores de numDisparities y blockSize, puede obtener mejores resultados.
Nota: Se agregarán más detalles
Recursos adicionales
Ejercicios
1. Las muestras de OpenCV contienen un ejemplo de cómo generar un mapa de disparidad y su reconstrucción en 3D. Compruebe
stereo_match.py en las muestras de OpenCVPython.
Aprendizaje automático
• Kvecino más cercano
Aprenda a usar kNN para la clasificación Además, aprenda sobre el reconocimiento de dígitos
escritos a mano usando kNN
• Máquinas de vectores de soporte (SVM)
1.8. Aprendizaje automático 225
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Comprender los conceptos de SVM
• Agrupación de KMeans
Aprenda a usar la agrupación en clústeres de KMeans para agrupar datos en una cantidad de clústeres.
Además, aprenda a hacer la cuantificación del color usando KMeans Clustering
226 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Kvecino más cercano
• Comprender kVecino más cercano
Obtenga una comprensión básica de lo que es kNN
• OCR de datos escritos a mano usando kNN
Ahora usemos kNN en OpenCV para reconocimiento de dígitos OCR
1.8. Aprendizaje automático 227
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Entendiendo kvecino más cercano
Meta
En este capítulo, comprenderemos los conceptos del algoritmo kvecino más cercano (kNN).
Teoría
kNN es uno de los algoritmos de clasificación más simples disponibles para el aprendizaje supervisado. La idea es buscar la coincidencia más
cercana de los datos de prueba en el espacio de características. Lo examinaremos con la imagen de abajo.
En la imagen, hay dos familias, Blue Squares y Red Triangles. Llamamos a cada familia como Clase. Sus casas se muestran en su mapa de la
ciudad que llamamos espacio de características. (Puede considerar un espacio de características como un espacio donde se proyectan todos los
datos. Por ejemplo, considere un espacio de coordenadas 2D. Cada dato tiene dos características, coordenadas x e y. Puede representar estos
datos en su espacio de coordenadas 2D, ¿verdad? Ahora imagina si hay tres características, necesitas espacio 3D.
Ahora considere N características, donde necesita un espacio Ndimensional, ¿verdad? Este espacio Ndimensional es su espacio característico.
En nuestra imagen, puede considerarlo como un caso 2D con dos características).
Ahora, un nuevo miembro llega a la ciudad y crea un nuevo hogar, que se muestra como un círculo verde. Debería ser agregado a una de estas
familias Azul/Roja. Llamamos a ese proceso, Clasificación. ¿Qué hacemos? Ya que estamos tratando con kNN, apliquemos este algoritmo.
Un método es verificar quién es su vecino más cercano. De la imagen, está claro que es la familia Red Triangle. Entonces él también se agrega al
Triángulo Rojo. Este método se llama simplemente vecino más cercano, porque la clasificación depende únicamente del vecino más cercano.
Pero hay un problema con eso. El Triángulo Rojo puede ser el más cercano. Pero, ¿y si hay muchos cuadrados azules cerca de él? Entonces los
Cuadrados Azules tienen más fuerza en esa localidad que el Triángulo Rojo. Por lo tanto, solo verificar el más cercano no es suficiente. En su
lugar, comprobamos algunas k familias más cercanas. Entonces quien sea mayoritario en ellos, el nuevo pertenece a esa familia. En nuestra
imagen, tomemos k=3, es decir, 3 familias más cercanas. Tiene dos Rojos y un Azul (hay dos Azules equidistantes, pero como k=3, tomamos solo
uno de ellos), por lo que nuevamente debe agregarse a la familia Roja. Pero, ¿y si tomamos k=7? Luego tiene 5 familias Azules y 2 familias Rojas.
¡¡Excelente!! Ahora debería ser agregado a la familia Blue. Entonces todo cambia con el valor de k. Lo más gracioso es, ¿y si k = 4? Tiene 2
vecinos rojos y 2 azules. es un empate!!!
Así que mejor toma k como un número impar. Entonces, este método se llama kVecino más cercano ya que la clasificación depende de los k
vecinos más cercanos.
Nuevamente, en kNN, es cierto que estamos considerando k vecinos, pero le estamos dando la misma importancia a todos, ¿no? ¿Es justicia?
Por ejemplo, tome el caso de k=4. Dijimos que es un empate. Pero mira, las 2 familias Rojas están más cerca de él que los
228 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
otras 2 familias azules. Por lo tanto, es más elegible para ser agregado a Red. Entonces, ¿cómo explicamos matemáticamente eso? Damos
algunos pesos a cada familia dependiendo de su distancia al recién llegado. Para aquellos que están cerca de él, obtienen pesos más altos,
mientras que aquellos que están lejos obtienen pesos más bajos. Luego sumamos los pesos totales de cada familia por separado. Quien
obtenga los pesos totales más altos, el recién llegado pasa a esa familia. Esto se llama kNN modificado.
Entonces, ¿cuáles son algunas de las cosas importantes que ves aquí?
• Necesitas tener información sobre todas las casas de la ciudad, ¿verdad? Porque tenemos que verificar la distancia desde el recién
llegado a todas las casas existentes para encontrar al vecino más cercano. Si hay muchas casas y familias, se necesita mucha
memoria y también más tiempo para el cálculo.
• Casi no hay tiempo para ningún tipo de entrenamiento o preparación.
Ahora veámoslo en OpenCV.
kNN en OpenCV
Haremos un ejemplo simple aquí, con dos familias (clases), como arriba. Luego, en el próximo capítulo, haremos mucho más mejor ejemplo.
Así que aquí, etiquetamos a la familia Roja como Clase0 (indicada por 0) y a la familia Azul como Clase1 (indicada por 1). Creamos 25
familias o 25 datos de entrenamiento y los etiquetamos como Clase0 o Clase1. Hacemos todo esto con la ayuda del generador de números
aleatorios en Numpy.
Luego lo trazamos con la ayuda de Matplotlib. Las familias rojas se muestran como triángulos rojos y las familias azules se muestran como
cuadrados azules.
importar cv2
importar numpy como np
importar matplotlib.pyplot como plt
# Conjunto de funciones que contiene valores (x,y) de 25 datos conocidos/de entrenamiento trainData
= np.random.randint(0,100,(25,2)).astype(np.float32)
# Etiqueta cada uno de ellos rojo o azul con los números 0 y 1 respuestas =
np.random.randint(0,2,(25,1)).astype(np.float32)
# Tomar familias rojas y trazarlas red =
trainData[responses.ravel()==0]
plt.scatter(red[:,0],red[:,1],80,'r','^')
# Tomar familias azules y trazarlas blue =
trainData[responses.ravel()==1]
plt.scatter(blue[:,0],blue[:,1],80,'b','s')
plt.mostrar()
Obtendrá algo similar a nuestra primera imagen. Dado que está utilizando un generador de números aleatorios, obtendrá datos diferentes
cada vez que ejecute el código.
A continuación, inicie el algoritmo kNN y pase los datos del tren y las respuestas para entrenar el kNN (construye un árbol de búsqueda).
Luego traeremos a un recién llegado y lo clasificaremos en una familia con la ayuda de kNN en OpenCV. Antes de ir a kNN, necesitamos
saber algo sobre nuestros datos de prueba (datos de los recién llegados). Nuestros datos deben ser una matriz de punto flotante con tamaño.
×
Luego encontramos los vecinos más cercanos .
del recién llegado. Podemos especificar cuántos vecinos queremos. Vuelve:
1. La etiqueta dada al recién llegado según la teoría kNN que vimos anteriormente. Si quieres Vecino más cercano
algoritmo, simplemente especifique k=1 donde k es el número de vecinos.
1.8. Aprendizaje automático 229
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
2. Las etiquetas de kvecinos más cercanos.
3. Distancias correspondientes del recién llegado a cada vecino más cercano.
Entonces, veamos cómo funciona. El recién llegado está marcado en color verde.
recién llegado = np.random.randint(0,100,(1,2)).astype(np.float32) plt.scatter(recién llegado[:,0],recién
llegado[:,1],80,'g','o' )
knn = cv2.KNearest()
knn.train(trainData,responses) ret, resultados,
vecinos ,dist = knn.find_nearest(recién llegado, 3)
print "resultado: ", resultados,"\n" print "vecinos: ",
vecinos,"\n" print "distancia: ", dist
plt.mostrar()
Obtuve el resultado de la siguiente manera:
resultado: [[ 1.]] vecinos:
[[ 1. 1. 1.]] distancia: [[ 53. 58. 61.]]
Dice que nuestro recién llegado tiene 3 vecinos, todos de la familia Blue. Por lo tanto, está etiquetado como familia azul. Es obvio de la siguiente trama:
Si tiene una gran cantidad de datos, puede simplemente pasarlos como una matriz. Los resultados correspondientes también se obtienen como matrices.
230 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
#10 recién llegados
recién llegados = np.random.randint(0,100,(10,2)).astype(np.float32) ret, resultados,vecinos,dist =
knn.find_nearest(recién llegado, 3)
# Los resultados también contendrán 10 etiquetas.
Recursos adicionales
1. Notas de NPTEL sobre reconocimiento de patrones, Capítulo 11
Ejercicios
OCR de datos escritos a mano usando kNN
Meta
En este capítulo
• Usaremos nuestro conocimiento sobre kNN para construir una aplicación básica de OCR.
• Probaremos con los datos de dígitos y alfabetos disponibles que vienen con OpenCV.
OCR de dígitos escritos a mano
Nuestro objetivo es construir una aplicación que pueda leer los dígitos escritos a mano. Para esto necesitamos algunos train_data y test_data.
OpenCV viene con una imagen digits.png (en la carpeta opencv/samples/python2/data/) que tiene 5000 dígitos escritos a mano (500 por
cada dígito). Cada dígito es una imagen de 20x20. Así que nuestro primer paso es dividir esta imagen en 5000 dígitos diferentes. Para cada
dígito, lo aplanamos en una sola fila con 400 píxeles. Ese es nuestro conjunto de características, es decir, los valores de intensidad de todos
los píxeles. Es el conjunto de características más simple que podemos crear. Usamos las primeras 250 muestras de cada dígito como
train_data y las siguientes 250 muestras como test_data. Así que vamos a prepararlos primero.
importar numpy como np
importar cv2
desde matplotlib importar pyplot como plt
img = cv2.imread('dígitos.png') gris =
cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# Ahora dividimos la imagen en 5000 celdas, cada celda de tamaño 20x20 = [np.hsplit(row,100) for
row in np.vsplit(gray,50)]
# Conviértalo en una matriz Numpy. Su tamaño será (50,100,20,20) x = np.array(cells)
# Ahora preparamos train_data y test_data. tren =
x[:,:50].reshape(1,400).astype(np.float32) # Tamaño = (2500,400) prueba = x[:,50:100].reshape(1,400).astype(np .float32) #
Tamaño = (2500,400)
# Crear etiquetas para entrenar y probar datos k = np.arange(10)
train_labels = np.repeat(k,250)
[:,np.newaxis] test_labels = train_labels.copy()
# Iniciar kNN, entrenar los datos, luego probarlos con datos de prueba para k=1
1.8. Aprendizaje automático 231
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
knn = cv2.KNearest()
knn.train(train,train_labels) ret,resultado,vecinos,dist
= knn.find_nearest(test,k=5)
# Ahora comprobamos la precisión de la clasificación
# Para eso, compare el resultado con test_labels y verifique cuáles son las coincidencias incorrectas = result==test_labels
correcto = np.count_nonzero(coincidencias) precisión =
correcto*100.0/resultado.tamaño precisión de impresión
Así que nuestra aplicación OCR básica está lista. Este ejemplo en particular me dio una precisión del 91%. Una opción para mejorar la precisión es
agregar más datos para el entrenamiento, especialmente los incorrectos. Entonces, en lugar de encontrar estos datos de entrenamiento cada vez
que inicio la aplicación, es mejor que los guarde, para que la próxima vez, lea directamente estos datos de un archivo y comience la clasificación.
Puede hacerlo con la ayuda de algunas funciones de Numpy como np.savetxt, np.savez, np.load, etc. Consulte sus documentos para obtener más detalles.
# guardar los datos
np.savez('knn_data.npz',tren=tren, tren_etiquetas=tren_etiquetas)
# Ahora carga los datos
con np.load('knn_data.npz') como datos:
imprimir datos.archivos tren
= datos['tren'] tren_etiquetas =
datos['tren_etiquetas']
En mi sistema, ocupa alrededor de 4,4 MB de memoria. Dado que estamos utilizando valores de intensidad (datos uint8) como características, sería
mejor convertir los datos a np.uint8 primero y luego guardarlos. En este caso, solo ocupa 1,1 MB. Luego, mientras carga, puede volver a convertirlo
en float32.
OCR de alfabetos ingleses
A continuación, haremos lo mismo con los alfabetos ingleses, pero hay un ligero cambio en el conjunto de datos y características. Aquí, en lugar de
imágenes, OpenCV viene con un archivo de datos, letterrecognition.data en la carpeta opencv/samples/cpp/. Si lo abre, verá 20000 líneas que, a
primera vista, pueden parecer basura. En realidad, en cada fila, la primera columna es un alfabeto que es nuestra etiqueta. Los siguientes 16
números que le siguen son sus diferentes características. Estas funciones se obtienen del repositorio de aprendizaje automático de UCI. Puede
encontrar los detalles de estas características en esta página.
Hay 20000 muestras disponibles, por lo que tomamos los primeros 10000 datos como muestras de entrenamiento y los 10000 restantes como
muestras de prueba. Deberíamos cambiar los alfabetos a caracteres ascii porque no podemos trabajar con alfabetos directamente.
importar cv2
importar numpy como np
importar matplotlib.pyplot como plt
# Cargue los datos, los convertidores convierten la letra en un número data= np.loadtxt('letter
recognition.data', dtype= 'float32', delimiter = ',',
convertidores = {0: lambda ch: ord(ch)ord('A')})
# dividir los datos en dos, 10000 cada uno para tren y tren de prueba, test = np.vsplit(data,2)
# divide trainData y testData en características y respuestas , trainData = np.hsplit(train,[1]) etiquetas,
testData = np.hsplit(test,[1])
# Iniciar el kNN, clasificar, medir la precisión.
232 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
knn = cv2.KNearest()
knn.train(trainData, respuestas) ret, resultado,
vecinos, dist = knn.find_nearest(testData, k=5)
correcto = np.count_nonzero(resultado == etiquetas) precisión =
correcto*100.0/10000 precisión de impresión
Me da una precisión del 93,22%. Nuevamente, si desea aumentar la precisión, puede agregar iterativamente datos de error en cada nivel.
Recursos adicionales
Ejercicios
Máquinas de vectores de soporte (SVM)
• Comprender la SVM
Obtenga una comprensión básica de lo que es SVM
• OCR de datos escritos a mano usando SVM
Usemos las funcionalidades de SVM en OpenCV
1.8. Aprendizaje automático 233
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Comprender la SVM
Meta
En este capítulo
• Veremos una comprensión intuitiva de SVM
Teoría
Datos linealmente separables
Considere la imagen a continuación que tiene dos tipos de datos, rojo y azul. En kNN, para un dato de prueba, solíamos medir su distancia a
todas las muestras de entrenamiento y tomar la que tenía la distancia mínima. Se necesita mucho tiempo para medir todas las distancias y
mucha memoria para almacenar todas las muestras de entrenamiento. Pero teniendo en cuenta los datos proporcionados en la imagen,
¿deberíamos necesitar tanto?
Considere otra idea. Encontramos una línea, ( ) = 1 + 2 + que divide ambos datos en dos regiones. Cuando nosotros , simplemente
test_data Podemos sustituyámoslo en ( ). Si ( ) > 0, pertenece al grupo azul, de lo contrario pertenece al grupo rojo. obtener un nuevo
llamar a esta línea Límite de decisión. Es muy simple y eficiente en memoria. Estos datos que se pueden dividir en dos con una línea recta (o
hiperplanos en dimensiones superiores) se denominan separables lineales.
Entonces, en la imagen de arriba, puede ver que muchas de esas líneas son posibles. ¿Cuál tomaremos? Muy intuitivamente podemos decir
que la línea debe pasar lo más lejos posible de todos los puntos. ¿Por qué? Porque puede haber ruido en los datos entrantes. Estos datos
no deberían afectar la precisión de la clasificación. Por lo tanto, tomar la línea más lejana proporcionará más inmunidad contra el ruido.
Entonces, lo que hace SVM es encontrar una línea recta (o hiperplano) con la distancia mínima más grande a las muestras de entrenamiento.
Vea la línea en negrita en la imagen de abajo que pasa por el centro.
Entonces, para encontrar este límite de decisión, necesita datos de entrenamiento. ¿Necesitas todo? NO. Solo los que están cerca del grupo
opuesto son suficientes. En nuestra imagen, son un círculo relleno de azul y dos cuadrados rellenos de rojo. Podemos llamarlos Vectores de
Soporte y las rectas que los atraviesan se llaman Planos de Soporte. Son adecuados para encontrar nuestro límite de decisión. No tenemos
que preocuparnos por todos los datos. Ayuda en la reducción de datos.
Lo que sucedió es que se encuentran los primeros dos hiperplanos que representan mejor los datos. Por ejemplo, los datos azules están
representados por + 0 > 1 mientras que los datos rojos están representados por + 0 < −1 donde es el vector de peso ( = [ 1, 2, ..., ]) y
es el vector de características ( = [ 1, 2, ..., ]). 0 es el sesgo. El vector de peso decide la orientación del límite de decisión, mientras que el
punto de polarización decide su ubicación. Ahora, el límite de decisión se define como el punto medio entre estos hiperplanos, por lo que se
expresa como + 0 = 0. La distancia mínima desde el vector de soporte hasta el límite de decisión viene dada por,
=
1 || || . El margen es el doble de esta distancia, y necesitamos maximizar este margen. es decir, tenemos que
234 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
minimizar una nueva función ( , 0) con algunas limitaciones que se pueden expresar a continuación:
1
min ( , 0) = || ||2 sujeto a ( + 0) ≥ 1
, 0
2
donde es la etiqueta de cada clase, [−1, 1].
Datos separables no lineales
Considere algunos datos que no se pueden dividir en dos con una línea recta. Por ejemplo, considere datos unidimensionales donde 'X'
está en 3 y +3 y 'O' está en 1 y +1. Claramente no es linealmente separable. Pero existen métodos para resolver este tipo de problemas.
2
Si podemos mapear este conjunto de datos con una función, ( ) = obtenemos 'X' en 9 y 'O' en 1,, que son separables linealmente.
2
De lo contrario, podemos convertir estos datos unidimensionales en bidimensionales. Podemos usar la función ( ) = ( , ) para mapear
estos datos. Entonces 'X' se convierte en (3,9) y (3,9) mientras que 'O' se convierte en (1,1) y (1,1). Esto también es separable lineal.
En resumen, es más probable que los datos separables no lineales en el espacio de menor dimensión se conviertan en separables lineales en
el espacio de mayor dimensión.
En general, es posible mapear puntos en un espacio ddimensional a algún espacio Ddimensional ( > ) para verificar la posibilidad de
separabilidad lineal. Hay una idea que ayuda a calcular el producto escalar en el espacio de alta dimensión (núcleo) realizando cálculos
en el espacio de entrada (característica) de baja dimensión. Podemos ilustrar con el siguiente ejemplo.
Considere dos puntos en un espacio bidimensional, = ( 1, 2) y = ( 1, 2). Sea una función de mapeo que mapea un punto bidimensional a
un espacio tridimensional de la siguiente manera:
Definamos una función kernel ( , ) que hace un producto escalar entre dos puntos, como se muestra a continuación:
( , ) = ( ). ( ) = ( ) , ( ) √ 2
2
= ( 1 1 + 2 2)
2
( ). ( ) = ( . )
Significa que se puede lograr un producto escalar en un espacio tridimensional utilizando un producto escalar al cuadrado en un espacio
bidimensional. Esto se puede aplicar al espacio dimensional superior. Entonces podemos calcular características de dimensiones más altas a partir
de dimensiones más bajas. Una vez que los mapeamos, obtenemos un espacio dimensional superior.
Además de todos estos conceptos, viene el problema de la clasificación errónea. Por lo tanto, solo encontrar el límite de decisión con el
margen máximo no es suficiente. Necesitamos considerar también el problema de los errores de clasificación. A veces, puede ser posible
encontrar un límite de decisión con menos margen, pero con errores de clasificación reducidos. De todos modos, necesitamos modificar
nuestro modelo para que encuentre el límite de decisión con el máximo margen, pero con menos errores de clasificación.
El criterio de minimización se modifica como:
|| ||2 + ( )
La siguiente imagen muestra este concepto. Para cada muestra de los datos de entrenamiento se define un nuevo parámetro. Es la
distancia desde su muestra de entrenamiento correspondiente a su región de decisión correcta. Para los que no están mal clasificados,
caen en sus correspondientes planos de apoyo, por lo que su distancia es cero.
1.8. Aprendizaje automático 235
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Entonces el nuevo problema de optimización es:
¿Cómo se debe elegir el parámetro C? Es obvio que la respuesta a esta pregunta depende de cómo se distribuyan los datos de
entrenamiento. Aunque no hay una respuesta general, es útil tener en cuenta estas reglas:
• Los valores grandes de C dan soluciones con menos errores de clasificación pero con un margen más pequeño. Considere
que en este caso es costoso cometer errores de clasificación. Dado que el objetivo de la optimización es minimizar el
argumento, se permiten pocos errores de clasificación.
• Valores pequeños de C dan soluciones con mayor margen y más errores de clasificación. En este caso la minimización no
considera tanto el término de la suma por lo que se enfoca más en encontrar un hiperplano con gran margen.
Recursos adicionales
1. Notas de NPTEL sobre Reconocimiento de Patrones Estadísticos, Capítulos 2529.
Ejercicios
OCR de datos escritos a mano usando SVM
Meta
En este capítulo
• Revisaremos el OCR de datos escritos a mano, pero con SVM en lugar de kNN.
OCR de dígitos escritos a mano
En kNN, usamos directamente la intensidad de píxel como vector de características. Esta vez usaremos Histograma de Gradientes Orientados (HOG)
como vectores de características.
236 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Aquí, antes de encontrar el HOG, desviamos la imagen usando sus momentos de segundo orden. Así que primero definimos una función alinear() que
toma una imagen de dígito y la alinea. A continuación se muestra la función de desfase():
def enderezar(img): m =
cv2.momentos(img) if
abs(m['mu02']) < 1e2: return img.copy()
skew = m['mu11']/m['mu02']
M = np.float32([[1, sesgo, 0.5*SZ*sesgo], [0, 1, 0]]) img = cv2.warpAffine(img,M,
(SZ, SZ),flags=affine_flags) return imagen
La imagen de abajo muestra la función de alineamiento de arriba aplicada a una imagen de cero. La imagen de la izquierda es la imagen original y la
imagen de la derecha es la imagen sesgada.
A continuación tenemos que encontrar el Descriptor HOG de cada celda. Para eso, encontramos las derivadas de Sobel de cada celda en la dirección X e
Y. Luego encuentre su magnitud y dirección de gradiente en cada píxel. Este gradiente se cuantifica en 16 valores enteros. Divide esta imagen en cuatro
subcuadrados. Para cada subcuadrado, calcule el histograma de dirección (16 contenedores) ponderado con su magnitud. Entonces, cada subcuadrado
te da un vector que contiene 16 valores. Cuatro de estos vectores (de cuatro subcuadrados) juntos nos dan un vector de características que contiene 64
valores. Este es el vector de características que usamos para entrenar nuestros datos.
def cerdo(img): gx
= cv2.Sobel(img, cv2.CV_32F, 1, 0) gy = cv2.Sobel(img,
cv2.CV_32F, 0, 1) mag, ang = cv2.cartToPolar(gx, gy)
# cuantificación de binvalues en (0...16) bins =
np.int32(bin_n*ang/(2*np.pi))
# Divide en 4 subcuadrados bin_cells
= bins[:10,:10], bins[10:,:10], bins[:10,10:], bins[10:,10:]
mag_cells = mag[:10,:10], mag[10:,:10], mag[:10,10:], mag[10:,10:] hists = [np.bincount(b.ravel(),
m.ravel(), bin_n) para b, m en zip(bin_cells, mag_
→células)]
hist = np.hstack(hists) devuelve hist
1.8. Aprendizaje automático 237
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Finalmente, como en el caso anterior, comenzamos por dividir nuestro gran conjunto de datos en celdas individuales. Por cada dígito, se reservan 250 celdas
para datos de entrenamiento y los 250 datos restantes se reservan para pruebas. El código completo se proporciona a continuación:
importar cv2
importar numpy como np
SZ=20
bin_n = 16 # Número de contenedores
svm_params = dict( kernel_type = cv2.SVM_LINEAR,
tipo_svm = cv2.SVM_C_SVC, C=2.67,
gamma=5.383 )
affine_flags = cv2.WARP_INVERSE_MAP|cv2.INTER_LINEAR
def enderezar(img): m =
cv2.momentos(img) if
abs(m['mu02']) < 1e2: return img.copy()
skew = m['mu11']/m['mu02']
M = np.float32([[1, sesgo, 0.5*SZ*sesgo], [0, 1, 0]]) img = cv2.warpAffine(img,M,(SZ,
SZ),flags=affine_flags) return imagen
def cerdo(img): gx =
cv2.Sobel(img, cv2.CV_32F, 1, 0) gy = cv2.Sobel(img,
cv2.CV_32F, 0, 1) mag, ang = cv2.cartToPolar(gx, gy)
contenedores = np.int32(bin_n*ang/(2*np.pi)) bin_cells
= contenedores[:10,:10], contenedores[10:,:10], # cuantificación de binvalues en (0...16)
contenedores[:10,10:], contenedores[10 :,10:] mag_cells = mag[:10,:10], mag[10:,:10], mag[:10,10:], mag[10:,10:]
hists = [np.bincount(b .ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells, mag_ →cells)] hist =
np.hstack(hists) return hist
# hist es un vector de 64 bits
img = cv2.imread('dígitos.png',0)
celdas = [np.hsplit(fila,100) para fila en np.vsplit(img,50)]
# La primera mitad es trainData, el resto es testData train_cells = [ i[:50] para i
en celdas] test_cells = [ i[50:] para i en celdas]
enderezado = [mapa(enderezado,fila) para fila en train_cells] hogdata =
[mapa(hog,fila) para fila en enderezado] trainData =
np.float32(hogdata).reshape(1,64) respuestas =
np.float32( np.repeat(np.arange(10),250)[:,np.nuevoeje])
svm = cv2.SVM()
svm.train(trainData,responses, params=svm_params) svm.save('svm_data.dat')
238 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
enderezado = [mapa(enderezar, fila) para fila en test_cells] hogdata =
[mapa(hog, fila) para fila en enderezar] testData =
np.float32(hogdata).reshape(1,bin_n*4) resultado = svm. predecir_todos(datos
de prueba)
####### Comprobar precisión ####################### máscara =
resultado==respuestas correctas =
np.count_nonzero(máscara) imprimir correcto*
100.0/resultado.tamaño
Esta técnica en particular me dio casi un 94% de precisión. Puede probar diferentes valores para varios parámetros de SVM para verificar
si es posible una mayor precisión. O puede leer documentos técnicos sobre esta área e intentar implementarlos.
Recursos adicionales
1. Video de histogramas de gradientes orientados
Ejercicios
1. Las muestras de OpenCV contienen digits.py que aplica una ligera mejora del método anterior para mejorar
resultado. También contiene la referencia. Compruébalo y entiéndelo.
Agrupación de KMeans
• Comprensión de la agrupación en clústeres de KMeans
Lea para obtener una comprensión intuitiva de KMeans Clustering
• Agrupación de KMeans en OpenCV
Ahora probemos las funciones KMeans en OpenCV
1.8. Aprendizaje automático 239
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Comprensión de la agrupación en clústeres de KMeans
Meta
En este capítulo, comprenderemos los conceptos de KMeans Clustering, cómo funciona, etc.
Teoría
Trataremos esto con un ejemplo que se usa comúnmente.
Problema con la talla de la camiseta
Considere una empresa que va a lanzar un nuevo modelo de camiseta al mercado. Obviamente tendrán que fabricar modelos en diferentes
tamaños para satisfacer a personas de todos los tamaños. Entonces, la compañía crea datos de la altura y el peso de las personas, y los
representa en un gráfico, como se muestra a continuación:
La empresa no puede crear camisetas con todos los tamaños. En su lugar, dividen a las personas en pequeñas, medianas y grandes, y fabrican
solo estos 3 modelos que encajarán en todas las personas. Esta agrupación de personas en tres grupos se puede hacer mediante la agrupación
de kmeans, y el algoritmo nos proporciona los mejores 3 tamaños, que satisfarán a todas las personas. Y si no es así, la empresa puede dividir
a las personas en más grupos, pueden ser cinco, y así sucesivamente. Compruebe la imagen a continuación:
240 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Como funciona ?
Este algoritmo es un proceso iterativo. Te lo explicamos paso a paso con ayuda de imágenes.
Considere un conjunto de datos como se muestra a continuación (puede considerarlo como un problema de camiseta). Necesitamos agrupar estos datos en dos grupos.
Paso: 1 : el algoritmo elige aleatoriamente dos centroides, 1 y 2 (a veces, dos datos cualesquiera se toman como centroides).
1.8. Aprendizaje automático 241
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Paso : 2 Calcula la distancia de cada punto a ambos centroides. Si los datos de una prueba están más cerca de 1, entonces esos datos se etiquetan con '0'. Si
está más cerca de 2, entonces etiquételo como '1' (si hay más centroides, etiquételos como '2', '3', etc.).
En nuestro caso, colorearemos todos los '0' etiquetados con rojo y los '1' etiquetados con azul. Entonces obtenemos la siguiente imagen después de las
operaciones anteriores.
Paso : 3 Luego calculamos el promedio de todos los puntos azules y rojos por separado y esos serán nuestros nuevos centroides.
Es decir, 1 y 2 se desplazan a los centroides recién calculados. (Recuerde, las imágenes que se muestran no son valores reales y no están a escala real, es solo
para demostración).
Y nuevamente, realice el paso 2 con nuevos centroides y etiquete los datos en '0' y '1'.
Entonces obtenemos el resultado de la siguiente manera:
242 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Ahora el Paso 2 y el Paso 3 se repiten hasta que ambos centroides converjan en puntos fijos. (O puede detenerse
según los criterios que proporcionamos, como el número máximo de iteraciones, o se alcanza una precisión específica, etc.)
Estos puntos son tales que la suma de las distancias entre los datos de prueba y sus centroides correspondientes son mínimas.
O simplemente, la suma de distancias entre 1 ↔ _y 2 ↔ es mínima. _
El resultado final casi se ve a continuación:
1.8. Aprendizaje automático 243
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Así que esto es solo una comprensión intuitiva de KMeans Clustering. Para obtener más detalles y una explicación matemática, lea
cualquier libro de texto de aprendizaje automático estándar o consulte los enlaces en recursos adicionales. Es solo una capa superior del
agrupamiento de KMeans. Hay muchas modificaciones a este algoritmo, como elegir los centroides iniciales, acelerar el proceso de
iteración, etc.
Recursos adicionales
1. Curso de aprendizaje automático, Video conferencias del Prof. Andrew Ng (Algunas de las imágenes están tomadas de esto)
Ejercicios
Agrupación de KMeans en OpenCV
Meta
• Aprenda a usar la función cv2.kmeans() en OpenCV para la agrupación de datos
Comprender los parámetros
Parámetros de entrada
1. muestras : debe ser del tipo de datos np.float32 , y cada característica debe colocarse en una sola columna.
244 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
2. nclusters(K) : número de clústeres requeridos al final
3. criterio [Es el criterio de terminación de la iteración. Cuando se cumple este criterio, la iteración del algoritmo se detiene. De hecho,
debe ser una tupla de 3 parámetros. Ellos son (tipo, max_iter, epsilon):]
• 3.a tipo de criterio de terminación [Tiene 3 banderas como se muestra a continuación:] cv2.TERM_CRITERIA_EPS detiene la
iteración del algoritmo si se alcanza la precisión especificada, épsilon. cv2.TERM_CRITERIA_MAX_ITER :
detiene el
algoritmo después del número especificado de iteraciones, max_iter. cv2.TERM_CRITERIA_EPS +
cv2.TERM_CRITERIA_MAX_ITER : detiene la iteración cuando se cumple alguna de las condiciones anteriores.
• 3.b max_iter: un número entero que especifica el número máximo de iteraciones.
• 3.c epsilon Precisión requerida
4. intentos : Bandera para especificar el número de veces que se ejecuta el algoritmo usando diferentes etiquetas iniciales. El algoritmo devuelve
las etiquetas que producen la mejor compacidad. Esta compacidad se devuelve como salida.
5. banderas : esta bandera se utiliza para especificar cómo se toman los centros iniciales. Normalmente se utilizan dos banderas para esto:
cv2.KMEANS_PP_CENTERS y cv2.KMEANS_RANDOM_CENTERS.
Parámetros de salida
1. Compacidad : Es la suma de las distancias al cuadrado de cada punto a sus centros correspondientes.
2. etiquetas : esta es la matriz de etiquetas (igual que 'código' en el artículo anterior) donde cada elemento marcado '0', '1'...
3. centros : Esta es una matriz de centros de grupos.
Ahora veremos cómo aplicar el algoritmo KMeans con tres ejemplos.
1. Datos con una sola función
Considere que tiene un conjunto de datos con una sola característica, es decir, unidimensional. Por ejemplo, podemos tomar nuestro problema de la
camiseta donde usas solo la altura de las personas para decidir el tamaño de la camiseta.
Así que empezamos por crear datos y trazarlos en Matplotlib
importar numpy como np
importar cv2
desde matplotlib importar pyplot como plt
x = np.aleatorio.randint(25,100,25) y =
np.aleatorio.randint(175,255,25) z = np.hstack((x,y))
z = z.reformar((50,1)) z =
np.float32(z) plt.hist(z,256,
[0,256]),plt.show()
Así que tenemos 'z', que es una matriz de tamaño 50 y valores que van de 0 a 255. He reformado 'z' en un vector de columna.
Será más útil cuando haya más de una característica presente. Luego hice datos de tipo np.float32.
Obtenemos la siguiente imagen:
1.8. Aprendizaje automático 245
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Ahora aplicamos la función KMeans. Antes de eso necesitamos especificar los criterios. Mi criterio es tal que, cada vez que
se ejecutan 10 iteraciones del algoritmo, o se alcanza una precisión de épsilon = 1.0, se detiene el algoritmo y se devuelve el
respuesta.
# Definir criterio = ( tipo, max_iter = 10 criterio = , épsilon = 1.0 )
(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
# Establecer banderas (solo para evitar saltos de línea en el código)
banderas = cv2.KMEANS_RANDOM_CENTERS
# Aplicar KMeans
compacidad,etiquetas,centros = cv2.kmeans(z,2,Ninguno,criterios,10,banderas)
Esto nos da la compacidad, las etiquetas y los centros. En este caso, obtuve centros como 60 y 207. Las etiquetas tendrán el
mismo tamaño que los datos de prueba, donde cada dato se etiquetará como '0', '1', '2', etc., según sus centroides. Ahora
dividimos los datos en diferentes grupos según sus etiquetas.
A = z[etiquetas==0]
B = z[etiquetas==1]
Ahora trazamos A en color rojo y B en color azul y sus centroides en color amarillo.
# Ahora trace 'A' en rojo, 'B' en azul, 'centros' en amarillo plt.hist(A,256,[0,256],color = 'r')
plt.hist(B,256,[0,256], color = 'b') plt.hist(centros,32,
[0,256],color = 'y') plt.show()
246 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
A continuación se muestra la salida que obtuvimos:
2. Datos con múltiples características
En el ejemplo anterior, tomamos solo la altura para el problema de la camiseta. Aquí, tomaremos tanto la altura como el peso, es decir, dos
características.
Recuerde, en el caso anterior, convertimos nuestros datos en un solo vector de columna. Cada función se organiza en una columna, mientras que
cada fila corresponde a una muestra de prueba de entrada.
Por ejemplo, en este caso, configuramos unos datos de prueba de tamaño 50x2, que son alturas y pesos de 50 personas. La primera columna
corresponde a la altura de las 50 personas y la segunda columna corresponde a sus pesos. La primera fila contiene dos elementos donde el primero
es la altura de la primera persona y el segundo su peso. Del mismo modo, las filas restantes corresponden a las alturas y pesos de otras personas.
Compruebe la imagen a continuación:
1.8. Aprendizaje automático 247
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Ahora me muevo directamente al código:
importar numpy como np
importar cv2
desde matplotlib importar pyplot como plt
X = np.aleatorio.randint(25,50,(25,2))
Y = np.random.randint(60,85,(25,2))
Z = np.vstack((X,Y))
# convertir a np.float32 Z = np.float32(Z)
# definir criterios y aplicar criterios kmeans() =
(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
ret,label,center=cv2.kmeans(Z,2,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)
# Ahora separe los datos, tenga en cuenta el flatten()
A = Z[etiqueta.ravel()==0]
B = Z[etiqueta.ravel()==1]
# Graficar los datos
plt.dispersión(A[:,0],A[:,1])
plt.dispersión(B[:,0],B[:,1],c = 'r') plt.dispersión(centro[:,
0],centro[:,1],s = 80,c = 'y', marcador = 's') plt.xlabel('Altura'),plt.ylabel('Peso')
248 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
plt.mostrar()
A continuación se muestra la salida que obtenemos:
3. Cuantificación de color
La cuantificación de color es el proceso de reducir el número de colores en una imagen. Una razón para hacerlo es reducir la memoria. A
veces, algunos dispositivos pueden tener limitaciones tales que solo pueden producir una cantidad limitada de colores. En esos casos
también, se realiza la cuantificación del color. Aquí usamos el agrupamiento de kmedias para la cuantificación del color.
No hay nada nuevo que explicar aquí. Hay 3 características, digamos, R, G, B. Entonces, necesitamos remodelar la imagen a una matriz de
tamaño Mx3 (M es el número de píxeles en la imagen). Y después de la agrupación, aplicamos valores de centroide (también es R, G, B) a
todos los píxeles, de modo que la imagen resultante tenga una cantidad específica de colores. Y de nuevo tenemos que volver a darle forma
a la forma de la imagen original. A continuación se muestra el código:
importar numpy como np
importar cv2
img = cv2.imread('inicio.jpg')
Z = imagen.reformar((1,3))
# convertir a np.float32 Z =
np.float32(Z)
# definir criterios, número de clústeres (K) y aplicar criterios kmeans() =
(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
K = 8
1.8. Aprendizaje automático 249
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
ret,etiqueta,centro=cv2.kmeans(Z,K,Ninguno,criterios,10,cv2.KMEANS_RANDOM_CENTERS)
# Ahora vuelva a convertir a uint8 y haga que la imagen original sea center =
np.uint8(center) res = center[label.flatten()]
res2 = res.reforma((img.forma))
cv2.imshow('res2',res2) cv2.waitKey(0)
cv2.destroyAllWindows()
Vea el resultado a continuación para K = 8:
Recursos adicionales
Ejercicios
Fotografía Computacional
Aquí aprenderá diferentes funcionalidades de OpenCV relacionadas con la fotografía computacional, como la eliminación de ruido de imágenes, etc.
• Eliminación de ruido de imagen
250 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Vea una buena técnica para eliminar ruidos en imágenes llamada Medios no locales
eliminación de ruido
• Repintado de imágenes
¿Tienes una foto antigua degradada con muchos puntos negros y trazos?
Tómalo. Intentemos restaurarlos con una técnica llamada imagen en pintura.
1.9. Fotografía Computacional 251
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Eliminación de ruido de imagen
Meta
En este capítulo,
• Aprenderá sobre el algoritmo de eliminación de ruido de medios no locales para eliminar el ruido en la imagen.
• Verá diferentes funciones como cv2.fastNlMeansDenoising(), cv2.fastNlMeansDenoisingColored() etc.
Teoría
En capítulos anteriores, hemos visto muchas técnicas de suavizado de imágenes como Gaussian Blurring, Median Blurring, etc. y fueron buenas
hasta cierto punto para eliminar pequeñas cantidades de ruido. En esas técnicas, tomamos un pequeño vecindario alrededor de un píxel e hicimos
algunas operaciones como el promedio ponderado gaussiano, la mediana de los valores, etc. para reemplazar el elemento central. En resumen,
la eliminación de ruido en un píxel era local para su vecindario.
Hay una propiedad del ruido. Generalmente se considera que el ruido es una variable aleatoria con media cero. Considere un píxel con ruido, = 0
+ donde es el valor real del píxel y0 el ruido en ese píxel. Puede tomar una gran cantidad de píxeles iguales (digamos) de diferentes imágenes y
calcular su promedio. Idealmente, debería obtener = ya que la media de 0 ruido es cero.
Puede verificarlo usted mismo mediante una configuración simple. Sostenga una cámara estática en un lugar determinado durante un par de
segundos. Esto le dará muchos marcos o muchas imágenes de la misma escena. Luego, escriba un fragmento de código para encontrar el
promedio de todos los cuadros en el video (Esto debería ser demasiado simple para usted ahora). Compara el resultado final y el primer cuadro.
Puede ver la reducción del ruido. Desafortunadamente, este método simple no es robusto para los movimientos de la cámara y la escena.
Además, a menudo solo hay una imagen ruidosa disponible.
Entonces, la idea es simple, necesitamos un conjunto de imágenes similares para promediar el ruido. Considere una ventana pequeña (digamos
una ventana de 5x5) en la imagen. Es muy probable que el mismo parche esté en otro lugar de la imagen. A veces en un pequeño barrio a su
alrededor. ¿Qué hay de usar estos parches similares juntos y encontrar su promedio? Para esa ventana en particular, está bien. Vea una imagen
de ejemplo a continuación:
252 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Los parches azules en la imagen se ven similares. Los parches verdes se ven similares. Así que tomamos un píxel, tomamos una ventana pequeña a su
alrededor, buscamos ventanas similares en la imagen, promediamos todas las ventanas y reemplazamos el píxel con el resultado que obtuvimos. Este método
es Eliminación de ruido de medios no locales. Lleva más tiempo en comparación con las técnicas de desenfoque que vimos anteriormente, pero su resultado es
muy bueno. Se pueden encontrar más detalles y una demostración en línea en el primer enlace en recursos adicionales.
Para imágenes en color, la imagen se convierte al espacio de color CIELAB y luego elimina por separado los componentes L y AB.
Eliminación de ruido de imagen en OpenCV
OpenCV proporciona cuatro variaciones de esta técnica.
1. cv2.fastNlMeansDenoising() funciona con una sola imagen en escala de grises
2. cv2.fastNlMeansDenoisingColored() funciona con una imagen en color.
3. cv2.fastNlMeansDenoisingMulti() funciona con secuencias de imágenes capturadas en un corto período de tiempo (escala de grises
imágenes)
4. cv2.fastNlMeansDenoisingColoredMulti() igual que arriba, pero para imágenes en color.
Los argumentos comunes son:
• h : parámetro que decide la intensidad del filtro. Un valor h más alto elimina mejor el ruido, pero elimina los detalles de la imagen
también. (10 está bien)
• hForColorComponents: igual que h, pero solo para imágenes en color. (normalmente igual que h)
• templateWindowSize : debe ser impar. (recomendado 7)
• searchWindowSize: debe ser impar. (recomendado 21)
Visite el primer enlace en recursos adicionales para obtener más detalles sobre estos parámetros.
Demostraremos 2 y 3 aquí. Te queda descanso.
1. cv2.fastNlMeansDenoisingColored()
Como se mencionó anteriormente, se utiliza para eliminar el ruido de las imágenes en color. (Se espera que el ruido sea gaussiano). Vea el ejemplo a
continuación:
importar numpy como np
importar cv2
desde matplotlib importar pyplot como plt
img = cv2.imread('morir.png')
dst = cv2.fastNlMeansDenoisingColored(img,Ninguno,10,10,7,21)
plt.subplot(121),plt.imshow(img)
plt.subplot(122),plt.imshow(dst) plt.show()
A continuación se muestra una versión ampliada del resultado. Mi imagen de entrada tiene un ruido gaussiano de = 25. Vea el resultado:
1.9. Fotografía Computacional 253
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
2. cv2.fastNlMeansDenoisingMulti()
Ahora aplicaremos el mismo método a un video. El primer argumento es la lista de fotogramas ruidosos. El segundo argumento,
imgToDenoiseIndex, especifica qué cuadro necesitamos eliminar el ruido, para eso pasamos el índice del cuadro en nuestra lista de entrada.
El tercero es el tamaño de la ventana temporal, que especifica el número de fotogramas cercanos que se utilizarán para eliminar el ruido.
Debería ser extraño. En ese caso, se utiliza un total de tramas de tamaño de ventana temporal donde la trama central es la trama que se va a eliminar.
Por ejemplo, pasó una lista de 5 marcos como entrada. Sea imgToDenoiseIndex = 2 y temporalWindowSize = 3. Luego, el marco 1, el marco 2
y el marco 3 se usan para eliminar el ruido del marco 2. Veamos un ejemplo.
importar numpy como np
importar cv2
desde matplotlib importar pyplot como plt
tapa = cv2.VideoCapture('vtest.avi')
# crear una lista de los primeros 5 fotogramas
img = [cap.read()[1] para i en xrange(5)]
# convertir todo a gris en escala de grises =
[cv2.cvtColor(i, cv2.COLOR_BGR2GRAY) for i in img]
254 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
# convertir todo a float64
gris = [np.float64(i) para i en gris]
# crear un ruido de variación 25
ruido = np.random.randn(*gray[1].shape)*10
# Agregue este ruido a las imágenes
ruidoso = [i+ruido para i en gris]
# Convertir de nuevo a uint8 ruidoso
= [np.uint8(np.clip(i,0,255)) para i en ruidoso]
# Eliminación de ruido en el tercer cuadro considerando los 5 cuadros dst =
cv2.fastNlMeansDenoisingMulti(noisy, 2, 5, None, 4, 7, 35)
plt.subplot(131),plt.imshow(gris[2],'gris')
plt.subplot(132),plt.imshow(ruidoso[2],'gris') plt.subplot(133),plt.imshow
(dst,'gris') plt.mostrar()
La imagen de abajo muestra una versión ampliada del resultado que obtuvimos:
1.9. Fotografía Computacional 255
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
256 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Se necesita una cantidad considerable de tiempo para el cálculo. Como resultado, la primera imagen es el marco original, la segunda es la
ruidosa y la tercera es la imagen sin ruido.
Recursos adicionales
1. http://www.ipol.im/pub/art/2011/bcm_nlm/ (Tiene los detalles, demostración en línea, etc. Muy recomendable para visitar.
Nuestra imagen de prueba se genera a partir de este enlace)
2. Curso en línea en Coursera (Primera imagen tomada de aquí)
Ejercicios
Imagen en pintura
Meta
En este capítulo,
• Aprenderemos a eliminar pequeños ruidos, trazos, etc. en fotografías antiguas mediante un método llamado inpainting.
• Veremos funcionalidades de pintura en OpenCV.
Lo esencial
La mayoría de ustedes tendrá algunas fotos antiguas degradadas en su casa con algunos puntos negros, algunos trazos, etc. ¿Alguna vez
has pensado en restaurarlo? No podemos simplemente borrarlos en una herramienta de pintura porque simplemente reemplazará las
estructuras negras con estructuras blancas, lo cual no sirve de nada. En estos casos, se utiliza una técnica llamada imagen en pintura.
La idea básica es simple: reemplace esas malas marcas con sus píxeles vecinos para que se vea como el vecindario.
Considere la imagen que se muestra a continuación (tomada de Wikipedia):
Se diseñaron varios algoritmos para este propósito y OpenCV proporciona dos de ellos. Se puede acceder a ambos mediante la misma
función, cv2.inpaint()
El primer algoritmo se basa en el artículo "Una técnica de pintura de imágenes basada en el método de marcha rápida" de Alexandru Telea
en 2004. Se basa en el método de marcha rápida. Considere una región de la imagen para pintarla. El algoritmo comienza desde el límite de
esta región y va dentro de la región llenando gradualmente todo lo que está en el límite primero. Se necesita un pequeño vecindario alrededor
del píxel en el vecindario para pintarlo. Este píxel se reemplaza por la suma ponderada normalizada de todos los píxeles conocidos en el
vecindario. La selección de los pesos es un asunto importante.
Se otorga más ponderación a los píxeles que se encuentran cerca del punto, cerca de la normal del límite y los que se encuentran en los
contornos del límite. Una vez que se vuelve a pintar un píxel, se mueve al siguiente píxel más cercano utilizando el método de marcha rápida. FMM
1.9. Fotografía Computacional 257
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
asegura que los píxeles cercanos a los píxeles conocidos se pinten primero, de modo que funcione como una operación heurística manual.
Este algoritmo se habilita mediante el uso de la bandera, cv2.INPAINT_TELEA.
El segundo algoritmo se basa en el artículo "NavierStokes, Fluid Dynamics, and Image and Video Inpainting" de Bertalmio, Marcelo, Andrea L. Bertozzi y
Guillermo Sapiro en 2001. Este algoritmo se basa en la dinámica de fluidos y utiliza ecuaciones diferenciales parciales. El principio básico es heurístico.
Primero viaja a lo largo de los bordes desde regiones conocidas a regiones desconocidas (porque los bordes deben ser continuos). Continúa isófotas
(líneas que unen puntos con la misma intensidad, al igual que los contornos unen puntos con la misma elevación) mientras hace coincidir los vectores de
gradiente en el límite de la región de pintura. Para ello se utilizan algunos métodos de la dinámica de fluidos. Una vez que se obtienen, se rellena el color
para reducir la varianza mínima en esa área. Este algoritmo se habilita mediante el uso de la bandera, cv2.INPAINT_NS.
Código
Necesitamos crear una máscara del mismo tamaño que la de la imagen de entrada, donde los píxeles distintos de cero corresponden al área que se va a
pintar. Todo lo demás es simple. Mi imagen está degradada con algunos trazos negros (agregué manualmente). Creé los trazos correspondientes con la
herramienta Paint.
importar numpy como np
importar cv2
img = cv2.imread('messi_2.jpg') máscara =
cv2.imread('mask2.png',0)
dst = cv2.inpaint(img,máscara,3,cv2.INPAINT_TELEA)
cv2.imshow('dst',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
Vea el resultado a continuación. La primera imagen muestra una entrada degradada. La segunda imagen es la máscara. La tercera imagen es el resultado
del primer algoritmo y la última imagen es el resultado del segundo algoritmo.
258 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
1. Bertalmio, Marcelo, Andrea L. Bertozzi y Guillermo Sapiro. “Navierstokes, dinámica de fluidos y pintura de imágenes y videos”. En
Computer Vision and Pattern Recognition, 2001. CVPR 2001. Actas de la Conferencia de la IEEE Computer Society de 2001 en,
vol. 1, págs. I355. IEEE, 2001.
2. Telea, Alexandru. “Una técnica de pintura de imagen basada en el método de marcha rápida”. diario de gráficos
herramientas 9.1 (2004): 2334.
Ejercicios
1. OpenCV viene con una muestra interactiva sobre pintura, samples/python2/inpaint.py, pruébalo.
2. Hace unos meses, vi un video sobre ContentAware Fill, una técnica avanzada de pintura utilizada en Adobe Photoshop. En una
búsqueda posterior, pude encontrar que la misma técnica ya está en GIMP con un nombre diferente, "Resintetizador" (Necesita
instalar un complemento por separado). Estoy seguro de que disfrutarás de la técnica.
Detección de objetos
• Detección de rostros usando Haar Cascades
1.10. Detección de objetos 259
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Detección de rostros mediante cascadas haar
260 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Detección de rostros usando cascadas Haar
Meta
En esta sesión,
• Veremos los conceptos básicos de la detección de rostros utilizando los clasificadores en cascada basados en características de Haar.
• Extenderemos lo mismo para la detección ocular, etc.
Lo esencial
La detección de objetos mediante clasificadores en cascada basados en funciones de Haar es un método eficaz de detección de objetos
propuesto por Paul Viola y Michael Jones en su artículo, "Detección rápida de objetos mediante una cascada potenciada de funciones simples"
en 2001. Es un enfoque basado en el aprendizaje automático en el que un La función de cascada se entrena a partir de muchas imágenes
positivas y negativas. Luego se usa para detectar objetos en otras imágenes.
Aquí trabajaremos con la detección de rostros. Inicialmente, el algoritmo necesita muchas imágenes positivas (imágenes de rostros) e imágenes
negativas (imágenes sin rostros) para entrenar al clasificador. Luego necesitamos extraer características de él. Para esto, se utilizan las
características de haar que se muestran en la imagen a continuación. Son como nuestro kernel convolucional. Cada característica es un valor
único obtenido al restar la suma de píxeles debajo del rectángulo blanco de la suma de píxeles debajo del rectángulo negro.
Ahora todos los tamaños y ubicaciones posibles de cada kernel se utilizan para calcular muchas características. (Imagínense cuántos cálculos
necesita? Incluso una ventana de 24x24 da como resultado más de 160000 características). Para cada cálculo de características, necesitamos
encontrar la suma de píxeles debajo de los rectángulos blanco y negro. Para solucionar esto, introdujeron las imágenes integrales. Simplifica
el cálculo de la suma de píxeles, cuán grande puede ser el número de píxeles, a una operación que involucra solo cuatro píxeles. Bonito, ¿no?
Hace las cosas súper rápido.
Pero entre todas estas características que calculamos, la mayoría de ellas son irrelevantes. Por ejemplo, considere la imagen de abajo. La fila
superior muestra dos buenas características. La primera característica seleccionada parece centrarse en la propiedad de que la región de los
ojos suele ser más oscura que la región de la nariz y las mejillas. La segunda característica seleccionada se basa en la propiedad de que los ojos
1.10. Detección de objetos 261
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
son más oscuros que el puente de la nariz. Pero las mismas ventanas que se aplican en las mejillas o en cualquier otro lugar son irrelevantes. Entonces,
¿cómo seleccionamos las mejores funciones entre más de 160 000 funciones? Lo consigue Adaboost.
Para ello, aplicamos todas y cada una de las funciones en todas las imágenes de entrenamiento. Para cada característica, encuentra el mejor umbral
que clasificará las caras en positivas y negativas. Pero obviamente, habrá errores o clasificaciones erróneas. Seleccionamos las características con la
tasa de error mínima, lo que significa que son las características que mejor clasifican las imágenes de rostros y no rostros.
(El proceso no es tan simple como esto. A cada imagen se le da el mismo peso al principio. Después de cada clasificación, se aumentan los pesos de
las imágenes clasificadas incorrectamente. Luego, nuevamente se realiza el mismo proceso. Se calculan nuevas tasas de error. También nuevos pesos.
El el proceso continúa hasta que se logra la precisión requerida o la tasa de error o se encuentra el número requerido de características).
El clasificador final es una suma ponderada de estos clasificadores débiles. Se llama débil porque por sí solo no puede clasificar la imagen, pero junto
con otros forma un clasificador fuerte. El documento dice que incluso 200 funciones brindan detección con una precisión del 95%. Su configuración final
tenía alrededor de 6000 funciones. (Imagine una reducción de más de 160 000 funciones a 6000 funciones.
Eso es una gran ganancia).
Así que ahora tomas una imagen. Tome cada ventana de 24x24. Aplicarle 6000 características. Comprueba si es cara o no. Guau..
Wow... ¿No es un poco ineficiente y requiere mucho tiempo? Sí, lo es. Los autores tienen una buena solución para eso.
En una imagen, la mayor parte de la región de la imagen es una región sin rostro. Por lo tanto, es una mejor idea tener un método simple para verificar
si una ventana no es una región de la cara. Si no es así, deséchalo de un solo tiro. No vuelvas a procesarlo. En su lugar, concéntrese en la región donde
puede haber una cara. De esta manera, podemos encontrar más tiempo para comprobar una posible región de la cara.
Para ello introdujeron el concepto de Cascada de Clasificadores. En lugar de aplicar todas las 6000 funciones en una ventana, agrupe las funciones en
diferentes etapas de clasificadores y aplíquelas una por una. (Normalmente, las primeras etapas contendrán una cantidad muy inferior de características).
Si una ventana falla en la primera etapa, deséchela. No consideramos las funciones restantes en él.
Si pasa, aplique la segunda etapa de características y continúe el proceso. La ventana que pasa por todas las etapas es una región frontal. Que tal el
plan!!!
El detector de autores tenía más de 6000 funciones con 38 etapas con 1, 10, 25, 25 y 50 funciones en las primeras cinco etapas. (Dos funciones en la
imagen de arriba se obtienen en realidad como las dos mejores funciones de Adaboost). Según los autores, en promedio, se evalúan 10 características
de más de 6000 por subventana.
Así que esta es una explicación simple e intuitiva de cómo funciona la detección de rostros de ViolaJones. Lea el documento para obtener más detalles
o consulte las referencias en la sección Recursos adicionales.
262 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Detección de cascada de cabello en OpenCV
OpenCV viene con un entrenador y un detector. Si desea entrenar su propio clasificador para cualquier objeto como automóvil, avión, etc., puede usar
OpenCV para crear uno. Sus detalles completos se dan aquí: Entrenamiento de clasificador en cascada.
Aquí nos ocuparemos de la detección. OpenCV ya contiene muchos clasificadores preentrenados para cara, ojos, sonrisa, etc.
Esos archivos XML se almacenan en la carpeta opencv/data/haarcascades/. Vamos a crear un detector de cara y ojos con OpenCV.
Primero necesitamos cargar los clasificadores XML requeridos. Luego cargue nuestra imagen de entrada (o video) en modo de escala de grises.
importar numpy como np
importar cv2
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml') eye_cascade =
cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
img = cv2.imread('sachin.jpg') gris =
cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
Ahora encontramos las caras en la imagen. Si se encuentran rostros, devuelve las posiciones de los rostros detectados como Rect(x,y,w,h). Una vez que
obtengamos estas ubicaciones, podemos crear un ROI para la cara y aplicar la detección de ojos en este ROI (¡ya que los ojos siempre están en la cara!).
caras = face_cascade.detectMultiScale(gris, 1.3, 5) para (x,y,w,h) en caras: img
= cv2.rectangle(img,(x,y),(x+w,y+h),
( 255,0,0),2) roi_gray = gris[y:y+h, x:x+w] roi_color = img[y:y+h, x:x+w] ojos =
eye_cascade.detectMultiScale(roi_gray) para
(ej,ey,ew,eh) en los ojos:
cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
El resultado se ve a continuación:
1.10. Detección de objetos 263
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
Recursos adicionales
1. Conferencia en video sobre detección y seguimiento de rostros
2. Una interesante entrevista sobre Detección de Rostros por Adam Harvey
Ejercicios
Enlaces OpenCVPython
Aquí, aprenderá cómo se generan los enlaces OpenCVPython.
• ¿ Cómo funcionan los enlaces OpenCVPython?
Aprenda cómo se generan los enlaces OpenCVPython.
264 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
¿Cómo funcionan los enlaces OpenCVPython?
Meta
Aprender:
• ¿Cómo se generan los enlaces OpenCVPython?
• ¿Cómo extender nuevos módulos OpenCV a Python?
¿Cómo se generan los enlaces OpenCVPython?
En OpenCV, todos los algoritmos se implementan en C++. Pero estos algoritmos se pueden usar desde diferentes lenguajes como Python, Java, etc.
Esto es posible gracias a los generadores de enlaces. Estos generadores crean un puente entre C++ y Python que permite a los usuarios llamar a
funciones de C++ desde Python. Para obtener una imagen completa de lo que sucede en segundo plano, se requiere un buen conocimiento de la API
de Python/C. Puede encontrar un ejemplo simple sobre cómo extender las funciones de C++ a Python en la documentación oficial de Python[1]. Por lo
tanto, extender todas las funciones de OpenCV a Python escribiendo sus funciones de contenedor manualmente es una tarea que requiere mucho
tiempo. Entonces OpenCV lo hace de una manera más inteligente. OpenCV genera estas funciones contenedoras automáticamente desde los
encabezados de C++ utilizando algunos scripts de Python que se encuentran en módulos/python/src2. Investigaremos qué hacen.
Primero, modules/python/CMakeFiles.txt es un script de CMake que verifica los módulos que se extenderán a Python. Verificará automáticamente todos
los módulos que se extenderán y tomará sus archivos de encabezado. Estos archivos de encabezado contienen una lista de todas las clases, funciones,
constantes, etc. para esos módulos en particular.
En segundo lugar, estos archivos de encabezado se pasan a un script de Python, módulos/python/src2/gen2.py. Este es el script del generador de
enlaces de Python. Llama a otros módulos de script de Python/python/src2/hdr_parser.py. Esta es la secuencia de comandos del analizador de
encabezado. Este analizador de encabezado divide el archivo de encabezado completo en pequeñas listas de Python. Por lo tanto, estas listas contienen
todos los detalles sobre una función, clase, etc. en particular. Por ejemplo, se analizará una función para obtener una lista que contenga el nombre de la
función, el tipo de retorno, los argumentos de entrada, los tipos de argumentos, etc. La lista final contiene detalles de todas las funciones, estructuras ,
clases, etc. en ese archivo de encabezado.
Pero el analizador de encabezado no analiza todas las funciones/clases en el archivo de encabezado. El desarrollador debe especificar qué funciones
se deben exportar a Python. Para eso, se agregan ciertas macros al comienzo de estas declaraciones que permiten que el analizador de encabezado
identifique las funciones que se analizarán. Estas macros son agregadas por el desarrollador que programa la función particular. En resumen, el
desarrollador decide qué funciones se deben extender a Python y cuáles no. Los detalles de esas macros se darán en la próxima sesión.
Entonces, el analizador de encabezado devuelve una gran lista final de funciones analizadas. Nuestro script generador (gen2.py) creará funciones
contenedoras para todas las funciones/clases/enumeraciones/estructuras analizadas por el analizador de encabezado (puede encontrar estos archivos
de encabezado durante la compilación en la carpeta build/modules/python/ como archivos pyopencv_generated_*.h ). Pero puede haber algunos tipos
de datos básicos de OpenCV como Mat, Vec4i, Size. Deben extenderse manualmente. Por ejemplo, un tipo Mat debe extenderse a una matriz Numpy,
Size debe extenderse a una tupla de dos enteros, etc. De manera similar, puede haber algunas estructuras/clases/funciones complejas, etc. que deben
extenderse manualmente. Todas estas funciones de contenedor manual se colocan en módulos/python/src2/pycv2.hpp.
Así que ahora lo único que queda es la compilación de estos archivos de contenedor que nos da el módulo cv2 . Entonces, cuando llama a una función,
diga res = equalizeHist (img1, img2) en Python, pasa dos matrices numpy y espera otra matriz numpy como salida. Entonces, estas matrices numpy se
convierten en cv::Mat y luego llaman a la función equalizeHist() en C++. Resultado final, res se convertirá nuevamente en una matriz Numpy. En
resumen, casi todas las operaciones se realizan en C ++, lo que nos brinda casi la misma velocidad que la de C ++.
Así que esta es la versión básica de cómo se generan los enlaces OpenCVPython.
1.11. Enlaces OpenCVPython 265
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
¿Cómo extender nuevos módulos a Python?
El analizador de encabezado analiza los archivos de encabezado en función de algunas macros contenedoras agregadas a la declaración de la función.
Las constantes de enumeración no necesitan ninguna macro contenedora. Se envuelven automáticamente. Pero las funciones, clases, etc. restantes
necesitan macros contenedoras.
Las funciones se amplían utilizando la macro CV_EXPORTS_W. A continuación se muestra un ejemplo.
CV_EXPORTS_W void equalizeHist( InputArray src, OutputArray dst );
El analizador de encabezado puede comprender los argumentos de entrada y salida de palabras clave como InputArray, OutputArray, etc. Pero a veces,
es posible que necesitemos codificar entradas y salidas. Para ello, se utilizan macros como CV_OUT, CV_IN_OUT, etc.
CV_EXPORTS_W void minEnclosingCircle( Puntos InputArray,
CV_OUT Point2f& centro, CV_OUT float& radio);
También para clases grandes, se utiliza CV_EXPORTS_W. Para ampliar los métodos de clase, se utiliza CV_WRAP. De manera similar, CV_PROP se
usa para campos de clase.
clase CV_EXPORTS_W CLAHE : Algoritmo público { público:
Aplicación de vacío virtual CV_WRAP (InputArray src, OutputArray dst) = 0;
CV_WRAP virtual void setClipLimit(doble clipLimit) = 0;
CV_WRAP doble virtual getClipLimit() const = 0;
}
Las funciones sobrecargadas se pueden ampliar mediante CV_EXPORTS_AS. Pero necesitamos pasar un nuevo nombre para que cada función sea
llamada por ese nombre en Python. Tome el caso de la función integral a continuación. Hay tres funciones disponibles, por lo que cada una se nombra
con un sufijo en Python. De manera similar, CV_WRAP_AS se puede usar para envolver métodos sobrecargados.
//! calcula la imagen integral CV_EXPORTS_W void
integral( InputArray src, OutputArray sum, int s depth = 1 );
//! calcula la imagen integral y la integral para la imagen cuadrada CV_EXPORTS_AS(integral2) void
integral( InputArray src, OutputArray sum,
OutputArray sqsum, int s depth = 1, int
→profundidad cuadrada = 1 );
//! calcula la imagen integral, la integral para la imagen al cuadrado y la imagen integral inclinada → CV_EXPORTS_AS(integral3)
void integral( InputArray
src, OutputArray sum,
OutputArray sqsum, OutputArray inclinado, int profundidad = 1,
int profundidad cuadrada = 1 );
Las clases/estructuras pequeñas se amplían mediante CV_EXPORTS_W_SIMPLE. Estas estructuras se pasan por valor a las funciones de C++. Los
ejemplos son KeyPoint, Match, etc. Sus métodos se amplían con CV_WRAP y los campos se amplían con CV_PROP_RW.
clase CV_EXPORTS_W_SIMPLE DMatch
{ público:
CV_WRAP DMatch();
CV_WRAP DMatch(int _queryIdx, int _trainIdx, float _distance);
CV_WRAP DMatch(int _queryIdx, int _trainIdx, int _imgIdx, float _distance);
266 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
CV_PROP_RW int consultaIdx; // índice del descriptor de la consulta
CV_PROP_RW int trenIdx; // índice del descriptor del tren
CV_PROP_RW int imgIdx; // índice de la imagen del tren
CV_PROP_RW distancia flotante ;
};
Algunas otras clases/estructuras pequeñas se pueden exportar usando CV_EXPORTS_W_MAP donde se exporta a un diccionario nativo de
Python. Moments() es un ejemplo de ello.
class CV_EXPORTS_W_MAP Momentos { public: //!
momentos
espaciales CV_PROP_RW
doble m00, m10, m01, m20, m11, m02, m30, m21, m12, m03; //! momentos centrales
CV_PROP_RW doble mu20, mu11, mu02, mu30, mu21, mu12, mu03; //! momentos centrales
normalizados
CV_PROP_RW doble nu20, nu11, nu02, nu30, nu21, nu12, nu03;
};
Estas son las principales macros de extensión disponibles en OpenCV. Por lo general, un desarrollador tiene que colocar las macros
adecuadas en sus posiciones correspondientes. El resto lo hacen los scripts del generador. A veces, puede haber casos excepcionales en
los que los scripts del generador no puedan crear los contenedores. Tales funciones deben manejarse manualmente. Pero la mayoría de las
veces, un código escrito de acuerdo con las pautas de codificación de OpenCV será envuelto automáticamente por scripts generadores.
1.11. Enlaces OpenCVPython 267
Machine Translated by Google
Documentación de tutoriales de OpenCVPython, versión 1
268 Capítulo 1. Tutoriales de OpenCVPython
Machine Translated by Google
CAPÍTULO 2
índices y tablas
• volver a indexar
• índice de modificación
• buscar
269