Apuntes Graficación
Índice
- Manual de Instalación de Git
- Manual de Instalación de Python en Windows
- Entornos Virtuales en Python
- ¿Qué es un Entorno Virtual?
- Preparación: Instalación de Python y Visual Studio Code
- Crear un Entorno Virtual
- Activar el Entorno Virtual
- Configurar el Entorno Virtual en Visual Studio Code
- Instalar Paquetes en el Entorno Virtual
- Administrar Paquetes
- Desactivar el Entorno Virtual
- Eliminar el Entorno Virtual
- Consejos Adicionales para VS Code
- Introducción a la Graficación por computadora
- Graficación por Computadora
- Modelos de Color: RGB, CMY, HSV y HSL
- Introducción
- Modelo de Color RGB (Red, Green, Blue)
- Definición
- Funcionamiento de la Mezcla Aditiva
- Espacio de Color RGB
- Conversión a otros modelos de color
- Aplicaciones del Modelo RGB
- Limitaciones del Modelo RGB
- Modelo CMY (Cyan, Magenta, Yellow)
- Modelo HSV (Hue, Saturation, Value)
- Modelo HSL (Hue, Saturation, Lightness)
- Función Bitwise opencv
- Definición: Operación Bit a Bit
- Operadores Puntuales
- Proceso de Multiplicación de una Matriz de Transformación por una Coordenada de Píxel
- Transformaciones Geométricas en Imágenes
- Landmarks
- Detección de Manos con MediaPipe en Python
- Actividades
- Programación
- Programas Unidad 1
- Ejemplos de Transformaciones Geométricas en Modo Raw en Python
- Ejemplos de Transformaciones Geométricas en Python usando OpenCV
- Primitivas de Dibujo
- Parametricas
- Segmentación de color mediante el modelo de color HSV
- Capa de Harry Potter
- Efecto Gris np.where
- Flujo optico
- Ejemplo de clasificación utilizando Haarcascades
- OpenGL
- Descripción de la Proyección Isométrica
- Opengl Ejemplo
- Introducción a OpenGL
- Características de OpenGL
- Primeros pasos en OpenGL
- Sistema de Coordenadas en OpenGL
- Primitivas de Dibujo en OpenGL
- Tutorial de gluPerspective en OpenGL
- Objetos de Glut
- Transformaciones en OpenGL
- Iluminación y Materiales
- Sombras y Efectos Avanzados
- OpenGL Moderno: Shaders y el Pipeline Gráfico
- Conclusión
- Proyecto Final
Manual de Instalación de Git
Instalación en Windows
- Descarga el instalador desde [https://git-scm.com/].
- Haz clic en el botón “Download for Windows”.
- Ejecuta el archivo descargado (.exe).
- Configuración de las opciones recomendadas durante la instalación:
- Seleccionar el editor por defecto (por ejemplo, Vim o Notepad++).
- Usar Git desde la línea de comandos y aplicaciones de terceros (opción recomendada).
- Opciones de formato de fin de línea: selecciona la opción predeterminada para Windows.
- Completa la instalación.
- Verifica la instalación abriendo Git Bash o Command Prompt y ejecutando:
git --version
Instalación en macOS
Método recomendado: usando Homebrew
- Si tienes Homebrew instalado, abre la terminal y ejecuta:
brew install git
Método alternativo: paquete descargado
- Descarga el instalador desde [https://git-scm.com/].
- Ejecuta el paquete descargado (.dmg).
- Sigue las instrucciones de instalación.
Verifica la instalación abriendo la terminal y ejecutando:
git --version
Instalación en Linux (Distribuciones basadas en Debian/Ubuntu)
Abre la terminal y ejecuta:
sudo apt update sudo apt install git
Verifica la instalación ejecutando:
git --version
Configuración básica de Git
Configura tu nombre de usuario:
git config --global user.name "Tu Nombre"
Configura tu correo electrónico:
git config --global user.email "tuemail@ejemplo.com"
Verifica la configuración:
git config --list
Actualización de Git
- Windows: Ejecuta el instalador más reciente desde el sitio oficial.
macOS: Ejecuta:
brew upgrade git
Linux: Ejecuta:
sudo apt update && sudo apt upgrade git
Configurar Llave SSH con GitHub
1. Verificar si ya tienes una llave SSH
Abre la terminal y ejecuta el siguiente comando para verificar si ya tienes llaves SSH generadas:
ls -al ~/.ssh
- Si ves archivos como `idrsa` o `ided25519`, ya tienes llaves SSH. Si no, continúa con el siguiente paso.
2. Generar una nueva llave SSH
Si no tienes una llave SSH, genera una nueva con el siguiente comando (puedes cambiar `ed25519` por `rsa` si lo prefieres):
ssh-keygen -t ed25519 -C "tuemail@ejemplo.com"
- Cuando se te pregunte por la ubicación del archivo, presiona `Enter` para usar la ubicación predeterminada (`~/.ssh/ided25519`).
- Puedes agregar una contraseña para proteger tu llave, pero también puedes dejarlo en blanco.
3. Añadir la llave SSH al agente SSH
Para añadir tu nueva llave SSH al agente, asegúrate de que esté en ejecución:
eval "$(ssh-agent -s)"
Añade la llave SSH al agente:
ssh-add ~/.ssh/id_ed25519
4. Copiar la llave SSH pública
Copia el contenido de tu llave pública para añadirla a GitHub:
cat ~/.ssh/id_ed25519.pub
- Copia el texto que aparece en la terminal (comienza con `ssh-ed25519` o `ssh-rsa`).
5. Añadir la llave SSH a GitHub
- Inicia sesión en tu cuenta de GitHub.
- Ve a la sección de Settings (Configuración).
- En el menú lateral izquierdo, selecciona SSH and GPG keys.
- Haz clic en New SSH key.
- Introduce un título para identificar la llave (por ejemplo, "Mi computadora personal") y pega la llave pública copiada en el campo correspondiente.
- Haz clic en Add SSH key.
6. Probar la conexión SSH con GitHub
Para verificar que todo está configurado correctamente, ejecuta el siguiente comando:
ssh -T git@github.com
- Si es la primera vez que te conectas, verás una advertencia preguntando si deseas continuar. Escribe `yes`.
Si la conexión es exitosa, verás un mensaje similar a:
Hi username! You've successfully authenticated, but GitHub does not provide shell access.
7. Usar la conexión SSH en repositorios de GitHub
Para clonar un repositorio usando SSH, utiliza la URL SSH del repositorio:
git clone git@github.com:usuario/repo.git
Configurar Llave SSH con GitHub en Windows
- Git Bash: En Windows, utilizas Git Bash como terminal para ejecutar los comandos, en lugar de la terminal estándar de Linux o macOS.
- Ruta de las llaves: En Git Bash, las rutas siguen el formato Unix (c/Users/tuusuario.ssh/ en lugar de C:\Users\tuusuario\.ssh).
1. Abrir Git Bash
- Abre Git Bash (es la terminal que se instala junto con Git en Windows).
2. Verificar si ya tienes una llave SSH
En la terminal de Git Bash, ejecuta el siguiente comando para ver si ya tienes llaves SSH generadas:
ls -al ~/.ssh
- Si ves archivos como `idrsa` o `ided25519`, ya tienes llaves SSH. Si no, continúa con el siguiente paso.
3. Generar una nueva llave SSH
Si no tienes una llave SSH, genera una nueva con el siguiente comando:
ssh-keygen -t ed25519 -C "tuemail@ejemplo.com"
- Cuando se te pregunte por la ubicación del archivo, presiona `Enter` para usar la ubicación predeterminada (`/c/Users/tuusuario/.ssh/ided25519`).
- Puedes agregar una contraseña para proteger tu llave, pero también puedes dejar el campo vacío si no deseas protegerla con una contraseña.
4. Añadir la llave SSH al agente SSH
Asegúrate de que el agente SSH esté en ejecución. En Git Bash, ejecuta:
eval "$(ssh-agent -s)"
Añade tu nueva llave SSH al agente:
ssh-add ~/.ssh/id_ed25519
5. Copiar la llave SSH pública
Para añadir la llave SSH a GitHub, necesitas copiar tu llave pública. Ejecuta el siguiente comando en Git Bash:
cat ~/.ssh/id_ed25519.pub
- Copia el texto que aparece en la terminal, que comenzará con `ssh-ed25519` o `ssh-rsa`.
6. Añadir la llave SSH a GitHub
- Abre tu navegador web e inicia sesión en GitHub.
- Ve a la sección de Settings (Configuración).
- En el menú lateral izquierdo, selecciona SSH and GPG keys.
- Haz clic en New SSH key.
- Ponle un título descriptivo (como "Mi computadora con Windows") y pega la llave pública copiada en el campo correspondiente.
- Haz clic en Add SSH key.
7. Probar la conexión SSH con GitHub
Para asegurarte de que todo está correctamente configurado, prueba la conexión con GitHub desde Git Bash:
ssh -T git@github.com
- Si es la primera vez que te conectas, te pedirá confirmar la conexión escribiendo `yes`.
Si todo está bien, deberías ver un mensaje como:
Hi username! You've successfully authenticated, but GitHub does not provide shell access.
8. Usar la conexión SSH en repositorios de GitHub
Para clonar un repositorio usando la URL SSH, ejecuta:
git clone git@github.com:usuario/repo.git
Manual de Git en Windows
1. Instalar Git en Windows
- Visita [https://git-scm.com/](https://git-scm.com/) y descarga el instalador de Git para Windows.
- Ejecuta el archivo descargado (.exe).
- Durante la instalación, selecciona las opciones predeterminadas recomendadas.
- Abre Git Bash al finalizar la instalación.
2. Configurar Git
- Abre Git Bash y ejecuta los siguientes comandos para configurar tu identidad:
git config --global user.name "Tu Nombre" git config --global user.email "tuemail@ejemplo.com"
- Para verificar la configuración:
git config --list
3. Clonar un repositorio
- Para clonar un repositorio desde GitHub, usa el siguiente comando:
git clone https://github.com/usuario/repo.git
Esto descargará el repositorio a tu computadora.
4. Comandos básicos de Git
- Verificar el estado del repositorio:
git status
- Añadir archivos al área de preparación (staging):
git add nombre_de_archivo
- Para añadir todos los archivos modificados:
git add .
- Hacer un commit (guardar los cambios localmente):
git commit -m "Mensaje de commit"
- Enviar los cambios al repositorio remoto:
git push
5. Actualizar el repositorio local
- Para obtener los últimos cambios del repositorio remoto:
git pull
6. Crear y cambiar de ramas (branches)
- Crear una nueva rama:
git branch nombre_de_la_rama
- Cambiar a una rama existente:
git checkout nombre_de_la_rama
- Crear y cambiar a una nueva rama:
git checkout -b nombre_de_la_rama
7. Ver historial de commits
- Para ver el historial de cambios del repositorio:
git log
- Para un historial más compacto:
git log --oneline
8. Configurar una llave SSH
- Generar una llave SSH:
ssh-keygen -t ed25519 -C "tuemail@ejemplo.com"
- Añadir la llave al agente SSH:
eval "$(ssh-agent -s)" ssh-add ~/.ssh/id_ed25519
- Copiar la llave pública:
cat ~/.ssh/id_ed25519.pub
- Añádela a tu cuenta de GitHub en Settings > SSH and GPG keys.
Manual de Instalación de Python en Windows
Paso 1: Descarga de Python
- Abre el navegador web y visita el sitio: https://www.python.org.
- Ve a la pestaña "Downloads" y selecciona la versión más reciente para Windows.
- Haz clic en el botón que dice "Download Python (versión actual)" para descargar el instalador de Python.
Paso 2: Ejecutar el instalador
- Ejecuta el archivo descargado (.exe).
- Asegúrate de marcar la opción
Add Python to PATH
antes de proceder con la instalación. - Haz clic en
Install Now
para instalar con la configuración predeterminada. - Espera a que finalice el proceso de instalación y verifica el mensaje de éxito.
Paso 3: Verificación de la instalación
- Abre la línea de comandos (cmd) escribiendo "cmd" en el menú de inicio.
Verifica que Python se instaló correctamente ejecutando el siguiente comando:
python --version
Abre el intérprete de Python escribiendo
python
en la línea de comandos:python
Si todo está bien, verás el prompt interactivo de Python (tres símbolos
>>>
).- Para salir del intérprete, escribe
exit()
o presionaCtrl + Z
seguido deEnter
.
Paso 4: Instalar pip y otros paquetes
Verifica si pip está instalado escribiendo en la línea de comandos:
pip --version
Si necesitas instalar paquetes, usa pip con el siguiente comando:
pip install nombre_paquete
Paso 5: Configuración del entorno de desarrollo
- Instala un editor de código como:
- Configura el editor instalando las extensiones de Python. En Visual Studio Code, busca la extensión "Python" en el panel de extensiones.
Paso 6: Actualización de Python (opcional)
- Para actualizar Python a una nueva versión en el futuro, descarga la última versión desde python.org y sigue los pasos de instalación.
Entornos Virtuales en Python
¿Qué es un Entorno Virtual?
Un entorno virtual es una herramienta que permite crear un espacio aislado para instalar dependencias específicas de un proyecto en Python. Esto evita conflictos entre paquetes de diferentes proyectos.
Preparación: Instalación de Python y Visual Studio Code
Asegúrate de tener Python instalado en tu sistema. Puedes verificarlo con:
python --version
- Descarga e instala [Visual Studio Code](https://code.visualstudio.com/).
- Instala la extensión de Python en VS Code para habilitar características avanzadas, como la detección de entornos virtuales, depuración, y autocompletado.
Crear un Entorno Virtual
Para crear un entorno virtual en la carpeta de tu proyecto:
- Abre la terminal integrada en VS Code (menú Terminal > New Terminal).
Usa el siguiente comando para crear el entorno virtual:
python -m venv nombre_entorno
- Sustituye nombreentorno con el nombre que quieras para el entorno virtual, como `env` o `venv`.
Activar el Entorno Virtual
Para activar el entorno virtual en la terminal de VS Code:
En Linux/macOS:
source nombre_entorno/bin/activate
En Windows:
.\nombre_entorno\Scripts\activate
Cuando el entorno esté activado, verás el nombre del entorno en la terminal.
Configurar el Entorno Virtual en Visual Studio Code
Para que VS Code use el entorno virtual para ejecutar el código y detectar dependencias:
- Presiona `Ctrl+Shift+P` (o `Cmd+Shift+P` en macOS) para abrir la Command Palette.
- Busca y selecciona `Python: Select Interpreter`.
- En la lista de intérpretes, selecciona el que corresponde a tu entorno virtual (debería tener el nombre del entorno que creaste).
Instalar Paquetes en el Entorno Virtual
Con el entorno activado en la terminal de VS Code, puedes instalar paquetes con `pip`:
pip install nombre_paquete
Administrar Paquetes
Para ver los paquetes instalados en el entorno virtual:
pip list
También puedes usar el archivo requirements.txt para gestionar dependencias del proyecto. Para crearlo:
Ejecuta:
pip freeze > requirements.txt
Para instalar todas las dependencias listadas en requirements.txt en otro entorno, usa:
pip install -r requirements.txt
Desactivar el Entorno Virtual
Para desactivar el entorno virtual en la terminal, ejecuta:
deactivate
Eliminar el Entorno Virtual
Si deseas eliminar el entorno virtual, puedes simplemente borrar la carpeta:
rm -rf nombre_entorno
Consejos Adicionales para VS Code
Terminal automática: Cuando creas y activas un entorno en VS Code, puedes configurar el archivo `.vscode/settings.json` para que VS Code lo active automáticamente en cada terminal nueva:
{ "python.venvPath": "${workspaceFolder}/nombre_entorno", "python.defaultInterpreterPath": "${workspaceFolder}/nombre_entorno/bin/python" }
Introducción a la Graficación por computadora
Graficación por Computadora
La Graficación por Computadora (o simplemente Graficación) se enfoca en el estudio y aplicación de técnicas, algoritmos y herramientas para la generación y manipulación de imágenes digitales mediante el uso de computadoras. Es una rama de la informática que combina conceptos de matemáticas, física y programación para crear imágenes, animaciones y efectos visuales. A continuación, te describo los aspectos clave de esta disciplina:
Objetivos de la Graficación por Computadora
- Generación de imágenes: Crear imágenes digitales a partir de descripciones matemáticas y geométricas de objetos, en 2D y 3D.
- Modelado de objetos: Crear representaciones matemáticas de objetos y escenas usando técnicas como polígonos, mallas y curvas.
- Renderizado: Convertir representaciones matemáticas en imágenes visuales, simulando iluminación, sombras y texturas.
- Transformaciones geométricas: Aplicar traslaciones, rotaciones, escalados y proyecciones a objetos en sistemas de coordenadas 2D y 3D.
- Iluminación y sombreado: Simular cómo la luz interactúa con los objetos para crear efectos realistas.
- Animación: Generar secuencias de imágenes que cambian a lo largo del tiempo, incluyendo movimientos y simulaciones físicas.
- Texturizado: Aplicar imágenes (texturas) sobre superficies para dar detalles visuales sin aumentar la complejidad geométrica.
- Interacción gráfica: Estudiar técnicas para interactuar con imágenes o modelos gráficos, como interfaces gráficas, realidad aumentada o virtual.
Áreas de estudio en Graficación por Computadora
- Matemáticas: Uso de geometría y álgebra lineal para realizar transformaciones geométricas y modelar objetos.
- Programación: Conocimiento de lenguajes como C++, Python, GLSL y HLSL para implementar algoritmos gráficos.
- Algoritmos gráficos: Estudio de algoritmos como el trazado de rayos (ray tracing), rasterización y z-buffering.
- Herramientas gráficas: Uso de APIs como OpenGL, DirectX o Vulkan para crear gráficos y animaciones.
- Física aplicada: Simulación de fenómenos físicos como gravedad y movimiento para generar gráficos realistas.
Aplicaciones de la Graficación por Computadora
- Videojuegos: Creación de ambientes, personajes y efectos visuales.
- Cine y efectos visuales: Creación de efectos especiales y animación por computadora.
- Diseño industrial y arquitectónico: Modelado y visualización de prototipos y estructuras.
- Simulaciones: Uso en medicina, aeronáutica y otras industrias para crear simulaciones realistas.
- Realidad virtual y aumentada: Generación de entornos interactivos virtuales.
Temas principales en Graficación por Computadora
- Modelado 2D y 3D
- Algoritmos de rasterización
- Transformaciones y proyecciones
- Renderizado y sombreado
- Texturizado
- Iluminación global y local
- Técnicas de animación
- Programación de shaders
- Interacción gráfica y diseño de interfaces
Modelos de Color: RGB, CMY, HSV y HSL
Introducción
Los modelos de color son representaciones matemáticas que describen cómo los colores pueden representarse en diversas formas utilizando valores numéricos. Estos modelos son esenciales en gráficos por computadora, procesamiento de imágenes y en la percepción del color. A continuación, se detallan los modelos más comunes: RGB, CMY, HSV y HSL.
Modelo de Color RGB (Red, Green, Blue)
Definición
El modelo de color RGB (Rojo, Verde, Azul) es un modelo de mezcla aditiva, que se utiliza principalmente en dispositivos que emiten luz, como pantallas, cámaras digitales y proyectores. Los tres colores primarios (Rojo, Verde y Azul) se combinan de diferentes maneras para crear una amplia gama de colores.
Funcionamiento de la Mezcla Aditiva
En el modelo RGB, los colores se generan mediante la combinación de luz. Cuanto más intensa es la luz en cada canal (rojo, verde o azul), más claro es el color resultante. Cuando todas las intensidades están al máximo, el color resultante es blanco; cuando todas las intensidades están al mínimo, el resultado es negro.
- Fórmula para la mezcla aditiva:
\[
\text{Color} = R \cdot \mathbf{r} + G \cdot \mathbf{g} + B \cdot \mathbf{b}
\]
donde:
- \(R\), \(G\) y \(B\) son las intensidades de los canales rojo, verde y azul, respectivamente.
- \( \mathbf{r} \), \( \mathbf{g} \) y \( \mathbf{b} \) son los vectores unitarios que representan los colores primarios.
- Ejemplo:
- Si \( R = 255 \), \( G = 0 \), \( B = 0 \), obtenemos un color rojo puro.
- Si \( R = 0 \), \( G = 255 \), \( B = 0 \), obtenemos un color verde puro.
- Si \( R = 255 \), \( G = 255 \), \( B = 0 \), obtenemos el color amarillo.
Espacio de Color RGB
El espacio de color RGB puede representarse como un cubo tridimensional, donde:
- El eje X corresponde al canal rojo (\(R\)),
- El eje Y al canal verde (\(G\)),
- Y el eje Z al canal azul (\(B\)).
Cada vértice del cubo representa un color primario o la combinación de ellos, como se muestra a continuación:
- \( (0, 0, 0) \): Negro (ausencia de luz).
- \( (255, 0, 0) \): Rojo.
- \( (0, 255, 0) \): Verde.
- \( (0, 0, 255) \): Azul.
- \( (255, 255, 255) \): Blanco (máxima intensidad en todos los canales).
- \( (255, 255, 0) \): Amarillo (combinación de rojo y verde).
Este espacio de color es útil para representar los colores generados en dispositivos electrónicos, ya que estos emiten luz en diferentes combinaciones de rojo, verde y azul.
Conversión a otros modelos de color
El modelo RGB puede convertirse a otros espacios de color como CMY, HSV o HSL. A continuación se muestra la conversión básica de RGB a CMY:
- Conversión de RGB a CMY: \[ C = 1 - \left( \frac{R}{255} \right), \quad M = 1 - \left( \frac{G}{255} \right), \quad Y = 1 - \left( \frac{B}{255} \right) \] donde \(R\), \(G\), y \(B\) son las intensidades de los canales en el rango de [0, 255].
Aplicaciones del Modelo RGB
El modelo de color RGB se usa ampliamente en:
- Monitores y pantallas: Dispositivos como televisores, monitores de computadora y pantallas de teléfonos móviles utilizan píxeles que emiten luz en diferentes intensidades de rojo, verde y azul para representar imágenes.
- Cámaras digitales: Los sensores de las cámaras capturan la luz en estos tres canales para generar imágenes en color.
- Gráficos por computadora: El modelo RGB es esencial en software de edición de imágenes, renderizado 3D y creación de gráficos visuales.
Limitaciones del Modelo RGB
Aunque el modelo RGB es excelente para dispositivos que emiten luz, tiene algunas limitaciones:
- No es intuitivo para el ser humano al ajustar el brillo o la saturación de un color, ya que requiere manipular los tres canales de manera independiente.
- El modelo RGB no es el más adecuado para tareas de impresión, ya que en impresiones se usan modelos basados en la mezcla sustractiva de colores, como CMY o CMYK.
Modelo CMY (Cyan, Magenta, Yellow)
El modelo CMY es un modelo de mezcla sustractiva que utiliza los colores cian, magenta y amarillo. Es el modelo base para la impresión en color.
- Características:
- Cada color se describe en términos de cuánto absorbe (resta) de la luz blanca que incide.
- Mezcla sustractiva:
La relación entre CMY y RGB es: \[ \text{C} = 1 - \left( \frac{R}{L} \right), \quad \text{M} = 1 - \left( \frac{G}{L} \right), \quad \text{Y} = 1 - \left( \frac{B}{L} \right) \]
donde \( L \) es el valor máximo de intensidad (por ejemplo, 255).
- Conversión de RGB a CMY:
Si los valores RGB están normalizados entre 0 y 1:
\[ \text{C} = 1 - R, \quad \text{M} = 1 - G, \quad \text{Y} = 1 - B \]
- Aplicaciones:
- Utilizado en la impresión de imágenes en color, como impresoras de inyección de tinta y offset.
Modelo HSV (Hue, Saturation, Value)
El modelo HSV es una representación más intuitiva del color basada en la percepción humana, donde el matiz, la saturación y el valor describen un color.
- Características:
- Matiz (Hue, H): Representa el ángulo en el círculo cromático, en grados [0°, 360°).
- Saturación (S): Indica la pureza del color, rango de [0,1].
- Valor (V): Define el brillo del color, rango de [0,1].
- Conversión de RGB a HSV:
Primero, normalizar los valores RGB entre 0 y 1: \[ R' = \frac{R}{L}, \quad G' = \frac{G}{L}, \quad B' = \frac{B}{L} \]
donde \( L \) es el valor máximo de intensidad (por ejemplo, 255).
- Calcular el valor máximo y mínimo: \[ C_{\max} = \max(R', G', B'), \quad C_{\min} = \min(R', G', B') \]
- Diferencia: \[ \Delta = C_{\max} - C_{\min} \]
- Cálculo del Matiz (H): \[ \text{Si } \Delta = 0 \Rightarrow H = 0 \\ \text{Si } C_{\max} = R' \Rightarrow H = 60^\circ \times \left( \frac{G' - B'}{\Delta} \mod 6 \right) \\ \text{Si } C_{\max} = G' \Rightarrow H = 60^\circ \times \left( \frac{B' - R'}{\Delta} + 2 \right) \\ \text{Si } C_{\max} = B' \Rightarrow H = 60^\circ \times \left( \frac{R' - G'}{\Delta} + 4 \right) \]
- Cálculo de la Saturación (S): \[ \text{Si } C_{\max} = 0 \Rightarrow S = 0 \\ \text{Si no} \Rightarrow S = \frac{\Delta}{C_{\max}} \]
- Cálculo del Valor (V): \[ V = C_{\max} \]
- Aplicaciones:
- Edición de imágenes, interfaces de selección de color y en procesamiento de video.
- Ejemplo:
- Convertir RGB (255, 255, 0) a HSV:
- Normalizar: \[ R' = 1, \quad G' = 1, \quad B' = 0 \]
- \( C_{\max} = 1 \), \( C_{\min} = 0 \), \( \Delta = 1 \)
- Calcular H: \[ H = 60^\circ \times \left( \frac{G' - B'}{\Delta} \mod 6 \right) = 60^\circ \times (1 \mod 6) = 60^\circ \]
- Calcular S: \[ S = \frac{\Delta}{C_{\max}} = \frac{1}{1} = 1 \]
- Calcular V: \[ V = C_{\max} = 1 \]
- Resultado: H = 60°, S = 1, V = 1 (Color amarillo)
- Convertir RGB (255, 255, 0) a HSV:
HSV opencv
Tutorial: Umbrales en el Modelo de Color HSV
El espacio de color HSV (Hue, Saturation, Value) se utiliza para la segmentación de colores en OpenCV. A continuación, explicaremos los tres parámetros principales y cómo definir umbrales para detectar colores.
Definición de los Parámetros del Modelo HSV
- Hue (H): Tono o color básico (rojo, verde, azul, etc.). En OpenCV va de 0 a 179.
- Saturation (S): Intensidad del color. Va de 0 a 255.
- Value (V): Brillo del color. Va de 0 a 255.
Detectar un color en el espacio HSV
Puedes definir un rango de color utilizando los valores de Hue, Saturation y Value para segmentar colores específicos en una imagen.
- Código en Python para detectar un color (Verde)
import cv2 import numpy as np # Leer la imagen img = cv2.imread('imagen.jpg') # Convertir la imagen al espacio de color HSV hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # Definir el rango inferior y superior para detectar verde lower_green = np.array([35, 100, 100]) # Hue, Saturación, Brillo mínimos upper_green = np.array([85, 255, 255]) # Hue, Saturación, Brillo máximos # Crear una máscara que solo incluya los píxeles dentro del rango mask = cv2.inRange(hsv, lower_green, upper_green) # Aplicar la máscara a la imagen original result = cv2.bitwise_and(img, img, mask=mask) # Mostrar la imagen original y la imagen con el color detectado cv2.imshow("Imagen Original", img) cv2.imshow("Color Detectado", result) cv2.waitKey(0) cv2.destroyAllWindows()
Explicación de los Parámetros:
- lowergreen = np.array([35, 100, 100]):
- El valor mínimo del tono es 35, correspondiente a un verde.
- La saturación mínima es 100 para evitar colores desaturados.
- El valor mínimo de brillo es 100 para evitar colores muy oscuros.
- uppergreen = np.array([85, 255, 255]):
- El valor máximo del tono es 85, cubriendo tonos verdes claros y oscuros.
- La saturación máxima es 255 para incluir verdes vibrantes.
- El valor máximo de brillo es 255 para incluir colores brillantes.
Ajustes de los Umbrales
Dependiendo de las condiciones de luz y el color exacto que deseas detectar, puedes ajustar los valores de Hue, Saturación y Brillo:
- Hue (H): Ajusta el rango para detectar tonos específicos del color.
- Saturation (S): Ajusta para incluir colores más o menos saturados.
- Value (V): Ajusta para incluir colores más claros o más oscuros.
Ejemplo visual: Rango de tonos para detectar verde
Color | Hue (H) | Saturación (S) | Brillo (V) |
---|---|---|---|
Verde claro | 35 | 100 | 100 |
Verde oscuro | 85 | 255 | 255 |
Uso de cv2.inRange
La función cv2.inRange()
crea una máscara binaria donde los píxeles dentro del rango son blancos (255) y los fuera del rango son negros (0).
Modelo HSL (Hue, Saturation, Lightness)
El modelo HSL es similar al modelo HSV, pero en lugar de "valor" utiliza el término luminosidad (Lightness), que representa la cantidad de luz que refleja un color.
- Características:
- Matiz (Hue, H): Mismo que en HSV.
- Saturación (S): Diferente definición que en HSV.
- Luminosidad (L): Rango de [0,1], donde 0 es negro, 0.5 es el color puro, y 1 es blanco.
- Conversión de RGB a HSL:
- Normalizar RGB: \[ R' = \frac{R}{L}, \quad G' = \frac{G}{L}, \quad B' = \frac{B}{L} \]
- Calcular \( C_{\max} \) y \( C_{\min} \), y \( \Delta \) como en HSV.
- Cálculo de la Luminosidad (L): \[ L = \frac{C_{\max} + C_{\min}}{2} \]
- Cálculo de la Saturación (S): \[ \text{Si } \Delta = 0 \Rightarrow S = 0 \\ \text{Si } L \leq 0.5 \Rightarrow S = \frac{\Delta}{C_{\max} + C_{\min}} \\ \text{Si } L > 0.5 \Rightarrow S = \frac{\Delta}{2 - (C_{\max} + C_{\min})} \]
- Cálculo del Matiz (H):
- Igual que en HSV.
- Aplicaciones:
- Herramientas de diseño gráfico y edición de imágenes, donde se necesita un control preciso sobre la luz y el color.
- Ejemplo:
- Convertir RGB (255, 0, 0) a HSL:
- Normalizar: \[ R' = 1, \quad G' = 0, \quad B' = 0 \]
- \( C_{\max} = 1 \), \( C_{\min} = 0 \), \( \Delta = 1 \)
- Calcular L: \[ L = \frac{1 + 0}{2} = 0.5 \]
- Calcular S: \[ S = \frac{\Delta}{C_{\max} + C_{\min}} = \frac{1}{1 + 0} = 1 \]
- Calcular H: \[ H = 60^\circ \times \left( \frac{G' - B'}{\Delta} \mod 6 \right) = 0^\circ \]
- Resultado: H = 0°, S = 1, L = 0.5 (Color rojo puro)
- Convertir RGB (255, 0, 0) a HSL:
Función Bitwise opencv
Tutorial: Operaciones Bitwise en OpenCV
Las operaciones bitwise en OpenCV son útiles para realizar manipulaciones de imágenes como la creación de máscaras, la combinación de imágenes, o efectos visuales.
- 1. Operación
cv2.bitwise_and
Esta operación realiza una operación AND bit a bit sobre los píxeles correspondientes de dos imágenes. Devuelve una imagen donde un píxel será 1 si ambos píxeles en las imágenes de entrada son 1.
import cv2 import numpy as np # Crear dos imágenes en negro img1 = np.zeros((300, 300), dtype=np.uint8) img2 = np.zeros((300, 300), dtype=np.uint8) # Dibujar un rectángulo blanco en img1 cv2.rectangle(img1, (50, 50), (250, 250), 255, -1) # Dibujar un círculo blanco en img2 cv2.circle(img2, (150, 150), 100, 255, -1) # Aplicar la operación bitwise AND result = cv2.bitwise_and(img1, img2) # Mostrar las imágenes cv2.imshow("img1", img1) cv2.imshow("img2", img2) cv2.imshow("AND Result", result) cv2.waitKey(0) cv2.destroyAllWindows()
- 2. Operación
cv2.bitwise_or
Realiza una operación OR bit a bit entre los píxeles de dos imágenes. El resultado será 1 si al menos uno de los píxeles correspondientes es 1.
import cv2 import numpy as np # Crear dos imágenes en negro img1 = np.zeros((300, 300), dtype=np.uint8) img2 = np.zeros((300, 300), dtype=np.uint8) # Dibujar un rectángulo blanco en img1 cv2.rectangle(img1, (50, 50), (250, 250), 255, -1) # Dibujar un círculo blanco en img2 cv2.circle(img2, (150, 150), 100, 255, -1) # Aplicar la operación bitwise OR result = cv2.bitwise_or(img1, img2) # Mostrar las imágenes cv2.imshow("img1", img1) cv2.imshow("img2", img2) cv2.imshow("OR Result", result) cv2.waitKey(0) cv2.destroyAllWindows()
- 3. Operación
cv2.bitwise_xor
La operación XOR devuelve una imagen donde un píxel será 1 si exactamente uno de los píxeles correspondientes en las imágenes de entrada es 1.
import cv2 import numpy as np # Crear dos imágenes en negro img1 = np.zeros((300, 300), dtype=np.uint8) img2 = np.zeros((300, 300), dtype=np.uint8) # Dibujar un rectángulo blanco en img1 cv2.rectangle(img1, (50, 50), (250, 250), 255, -1) # Dibujar un círculo blanco en img2 cv2.circle(img2, (150, 150), 100, 255, -1) # Aplicar la operación bitwise XOR result = cv2.bitwise_xor(img1, img2) # Mostrar las imágenes cv2.imshow("img1", img1) cv2.imshow("img2", img2) cv2.imshow("XOR Result", result) cv2.waitKey(0) cv2.destroyAllWindows()
- 4. Operación
cv2.bitwise_not
La operación NOT invierte los colores de la imagen. Los píxeles blancos se convierten en negros y viceversa.
import cv2 import numpy as np # Crear una imagen en negro img = np.zeros((300, 300), dtype=np.uint8) # Dibujar un rectángulo blanco cv2.rectangle(img, (50, 50), (250, 250), 255, -1) # Aplicar la operación bitwise NOT result = cv2.bitwise_not(img) # Mostrar las imágenes cv2.imshow("Original", img) cv2.imshow("NOT Result", result) cv2.waitKey(0) cv2.destroyAllWindows()
- Ejemplo práctico: Enmascarar una región de interés (ROI)
Aquí aplicamos operaciones bitwise para enmascarar y combinar una imagen.
import cv2 import numpy as np # Cargar la imagen principal img1 = cv2.imread('imagen_principal.jpg') # Cargar la imagen que queremos enmascarar img2 = cv2.imread('logo.png') # Obtener las dimensiones de la segunda imagen (logo) rows, cols, channels = img2.shape # Definir la región de interés (ROI) en la imagen principal roi = img1[0:rows, 0:cols] # Convertir la imagen del logo a escala de grises img2gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) # Crear una máscara binaria a partir de la imagen en escala de grises ret, mask = cv2.threshold(img2gray, 10, 255, cv2.THRESH_BINARY) # Invertir la máscara mask_inv = cv2.bitwise_not(mask) # Hacer visible el fondo de la imagen principal en la región del logo img1_bg = cv2.bitwise_and(roi, roi, mask=mask_inv) # Extraer el logo img2_fg = cv2.bitwise_and(img2, img2, mask=mask) # Combinar el fondo y el logo dst = cv2.add(img1_bg, img2_fg) # Colocar la imagen combinada en la imagen principal img1[0:rows, 0:cols] = dst # Mostrar la imagen final cv2.imshow("Resultado", img1) cv2.waitKey(0) cv2.destroyAllWindows()
Definición: Operación Bit a Bit
Una operación bit a bit (en inglés, bitwise operation) es una operación que se realiza directamente sobre los bits de los operandos. Las operaciones se ejecutan sobre los bits correspondientes de los números en formato binario.
Principales operaciones bit a bit
- 1. AND bit a bit (
&
)
Devuelve `1` si ambos bits en la misma posición son `1`, de lo contrario devuelve `0`.
A = 1010 (10 en decimal) B = 1100 (12 en decimal) Resultado: 1000 (8 en decimal)
- 2. OR bit a bit (
|
)
Devuelve `1` si al menos uno de los bits en la misma posición es `1`, de lo contrario devuelve `0`.
A = 1010 (10 en decimal) B = 1100 (12 en decimal) Resultado: 1110 (14 en decimal)
- 3. XOR bit a bit (
^
)
Devuelve `1` si los bits en la misma posición son diferentes, y `0` si son iguales.
A = 1010 (10 en decimal) B = 1100 (12 en decimal) Resultado: 0110 (6 en decimal)
- 4. NOT bit a bit (~~)
Invierte los bits de un número: convierte los `0` en `1` y los `1` en `0`. En sistemas de complemento a dos, esto también implica cambiar el signo de un número entero.
A = 1010 (10 en decimal) Resultado: 0101 (-11 en decimal, si estamos usando complemento a dos)
- 5. Desplazamiento a la izquierda (
<<
)
Desplaza todos los bits del número hacia la izquierda por un número específico de posiciones. Los bits desplazados fuera del límite se descartan y se rellenan con ceros en el extremo derecho.
A = 0001 (1 en decimal) A << 2 = 0100 (4 en decimal)
- 6. Desplazamiento a la derecha (
>>
)
Desplaza todos los bits del número hacia la derecha por un número específico de posiciones. Los bits desplazados fuera del límite se descartan y el bit más significativo depende del signo del número.
A = 1000 (8 en decimal) A >> 2 = 0010 (2 en decimal)
- Aplicaciones de las operaciones bit a bit
- Máscaras de bits: Las operaciones bit a bit se usan para aplicar máscaras que seleccionan o modifican partes específicas de un número o secuencia binaria.
- Manipulación de imágenes: En procesamiento de imágenes, las operaciones bit a bit son útiles para combinar y modificar píxeles en OpenCV.
- Optimización de algoritmos: Las operaciones bit a bit permiten optimizar cálculos en sistemas de bajo nivel o con restricciones de recursos.
Operadores Puntuales
Definición
Los operadores puntuales son una clase de transformaciones aplicadas en el procesamiento digital de imágenes que operan sobre cada píxel de manera independiente. Esto significa que el valor de salida de un píxel depende únicamente del valor de ese mismo píxel en la imagen de entrada, sin considerar los valores de los píxeles vecinos.
Características
- Independencia espacial: Los operadores puntuales solo modifican cada píxel basado en su valor original, sin tener en cuenta su entorno.
- Simplicidad computacional: Dado que no se necesita información de los píxeles vecinos, estas operaciones suelen ser más rápidas y eficientes.
- Aplicación en tiempo real: Su bajo costo computacional los hace adecuados para aplicaciones de procesamiento de imágenes en tiempo real.
Tipos de Operadores Puntuales
- Operador de Identidad
- No altera la imagen. Cada píxel de la imagen de salida tiene el mismo valor que el píxel correspondiente en la imagen de entrada.
- Fórmula: \( g(x, y) = f(x, y) \), donde \( f(x, y) \) es el valor del píxel original y \( g(x, y) \) es el valor del píxel modificado.
- Negativo de la Imagen
- Este operador invierte los valores de los píxeles de una imagen, produciendo su negativo.
- Fórmula: \( g(x, y) = L - 1 - f(x, y) \), donde \( L \) es el valor máximo posible en la imagen (por ejemplo, 255 en imágenes de 8 bits).
- Uso: Se utiliza en técnicas como la extracción de detalles o cuando es necesario invertir una imagen para un análisis.
- Umbralización (Thresholding)
- Convierte la imagen a una versión binaria, donde los píxeles con valores por encima de un umbral se establecen en un valor (generalmente blanco), y los que están por debajo se establecen en otro (generalmente negro).
- Fórmula: \[ g(x, y) = 0, & \text{si } f(x, y) \leq T \\ L, & \text{si } f(x, y) > T \]
- Uso: Se utiliza para segmentación de imágenes y procesamiento de imágenes en blanco y negro.
- Corrección Gamma
- Ajusta los valores de intensidad de los píxeles para modificar el brillo o contraste de la imagen.
- Fórmula: \( g(x, y) = c \cdot f(x, y)^\gamma \), donde \( c \) es una constante de escala, y \( \gamma \) es el factor de corrección.
- Uso: Corrige la distorsión de brillo en pantallas o para obtener una mejor representación visual.
- Transformaciones Logarítmicas
- Aumentan los detalles en regiones oscuras de la imagen al expandir los valores de intensidad bajos.
- Fórmula: \( g(x, y) = c \cdot \log(1 + f(x, y)) \), donde \( c \) es una constante.
- Uso: Mejora la visualización de imágenes con una alta gama dinámica (HDR), como imágenes astronómicas.
- Corrección Lineal o Estiramiento de Contraste
- Expande los valores de intensidad en una imagen para cubrir un rango más amplio, aumentando el contraste.
- Fórmula: \[ g(x, y) = \frac{f(x, y) - f_{\min}}{f_{\max} - f_{\min}} \cdot (L - 1) \]
- Uso: Aumenta el contraste en imágenes con poca variación de intensidad.
- Transformaciones de Potencia (Raise to Power Transform)
- Eleva cada valor de píxel a una potencia \( n \), lo que permite ajustar el brillo y contraste de una imagen.
- Fórmula: \( g(x, y) = c \cdot f(x, y)^n \).
Aplicaciones de Operadores Puntuales
- Corrección de imágenes: Mejoran el brillo, contraste, y otros aspectos visuales de una imagen.
- Segmentación: La umbralización es comúnmente usada para separar objetos de fondo en una imagen.
- Análisis médico: Se utiliza para mejorar la visualización de imágenes de rayos X, resonancias magnéticas o ultrasonidos.
- Procesamiento en tiempo real: Los operadores puntuales son útiles en sistemas que requieren una rápida respuesta, como cámaras de vigilancia o sistemas de visión artificial.
En resumen, los operadores puntuales son una herramienta fundamental en el procesamiento de imágenes que permiten realizar transformaciones sencillas pero efectivas, mejorando la calidad visual o preparándolas para análisis posteriores.
Proceso de Multiplicación de una Matriz de Transformación por una Coordenada de Píxel
Cuando aplicamos una transformación geométrica a una imagen, multiplicamos las coordenadas de cada píxel por una matriz de transformación. Este proceso se desglosa paso a paso de la siguiente manera.
1. Representación de las coordenadas en forma homogénea
Las coordenadas \((x, y)\) de un píxel se convierten a coordenadas homogéneas para que las traslaciones puedan representarse mediante multiplicaciones de matrices. Un píxel en coordenadas homogéneas es:
\begin{pmatrix} x \\ y \\ 1 \end{pmatrix}2. Definición de la matriz de transformación
La matriz de transformación afín general tiene la forma: \[ T= \begin{pmatrix} a_{11} & a_{12} & t_x \\ a_{21} & a_{22} & t_y \\ 0 & 0 & 1 \end{pmatrix} \]
- \(a_{11}, a_{12}, a_{21}, a_{22}\) definen las transformaciones de rotación, escalado y cizallamiento.
- \(t_x, t_y\) son los parámetros de traslación en los ejes \(x\) e \(y\).
3. Multiplicación de la matriz por las coordenadas
Multiplicamos la matriz de transformación \(T\) por las coordenadas homogéneas \((x, y, 1)\) para obtener las nuevas coordenadas \((x', y', 1)\).
$$
\begin{pmatrix} x' \\ y' \\ 1 \end{pmatrix}=
\begin{pmatrix} a_{11} & a_{12} & t_x \\ a_{21} & a_{22} & t_y \\ 0 & 0 & 1 \end{pmatrix}⋅
\begin{pmatrix} x \\ y \\ 1 \end{pmatrix}$$
La multiplicación de matrices se desglosa a continuación.
- 3.1 Cálculo del nuevo valor de \(x'\)
\[ x' = a_{11} \cdot x + a_{12} \cdot y + t_x \]
- 3.2 Cálculo del nuevo valor de \(y'\)
\[ y' = a_{21} \cdot x + a_{22} \cdot y + t_y \]
- 3.3 El valor constante de la tercera coordenada
La tercera coordenada se mantiene como \(1\), ya que no cambia en las transformaciones afines.
\[1 = (0 \cdot x) + (0 \cdot y) + 1 \]
4. Resultado final
El resultado de la multiplicación es un nuevo vector con las coordenadas transformadas:
\begin{pmatrix} x' \\ y' \\ 1 \end{pmatrix}Ejemplo concreto: Rotación
Si queremos rotar un píxel \((x, y)\) en un ángulo \(\theta\), usamos la matriz de rotación:
\[ R = \begin{pmatrix} \cos(\theta) & -\sin(\theta) & 0 \\ \sin(\theta) & \cos(\theta) & 0 \\ 0 & 0 & 1 \end{pmatrix} \]
Multiplicamos esta matriz por las coordenadas homogéneas:
\[
\begin{pmatrix} x' \\ y' \\ 1 \end{pmatrix}=
\begin{pmatrix} \cos(\theta) & -\sin(\theta) & 0 \\ \sin(\theta) & \cos(\theta) & 0 \\ 0 & 0 & 1 \end{pmatrix}⋅
\begin{pmatrix} x \\ y \\ 1 \end{pmatrix}\] Esto resulta en las nuevas coordenadas después de la rotación:
\[ x' = x \cdot \cos(\theta) - y \cdot \sin(\theta) y' = x \cdot \sin(\theta) + y \cdot \cos(\theta) \]
Resumen del proceso
- Convertir las coordenadas \((x, y)\) a coordenadas homogéneas \((x, y, 1)\).
- Definir la matriz de transformación \(T\).
- Multiplicar la matriz \(T\) por las coordenadas homogéneas.
- Obtener las nuevas coordenadas \((x', y')\) que represe
Transformaciones Geométricas en Imágenes
Las transformaciones geométricas en imágenes permiten cambiar la posición, orientación y escala de las imágenes sin alterar su contenido. Estas transformaciones son esenciales en tareas como alineación de imágenes, registro de imágenes, realidad aumentada, y sistemas de visión por computadora.
1. Traslación (Translation)
La traslación desplaza la imagen en el espacio sin modificar su forma o tamaño. La fórmula matemática es:
\[ T(x, y) = (x + t_x, y + t_y) \]
Donde \(t_x\) y \(t_y\) son las cantidades de desplazamiento. La matriz de traslación es:
\[ T = \begin{pmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \end{pmatrix} \]
2. Rotación (Rotation)
La rotación gira la imagen un ángulo \(\theta\). La ecuación de rotación es:
\[ R(x, y) = (x' , y') = \left( x \cdot \cos(\theta) - y \cdot \sin(\theta), x \cdot \sin(\theta) + y \cdot \cos(\theta) \right) \]
La matriz de rotación es:
\[ R = \begin{pmatrix} \cos(\theta) & -\sin(\theta) & 0 \\ \sin(\theta) & \cos(\theta) & 0 \\ 0 & 0 & 1 \end{pmatrix} \]
3. Escalado (Scalin
g)
El escalado modifica el tamaño de la imagen mediante factores \(s_x\) y \(s_y\). La fórmula es:
\[ S(x, y) = (s_x \cdot x, s_y \cdot y) \]
La matriz correspondiente es:
\[ S = \begin{pmatrix} s_x & 0 & 0 \\ 0 & s_y & 0 \\ 0 & 0 & 1 \end{pmatrix} \]
4. Cizallamiento (Shearing)
El cizallamiento inclina la imagen en una dirección específica. Las ecuaciones para un cizallamiento horizontal y vertical son:
\[ Sh_x(x, y) = (x + h_x \cdot y, y) \] \[ Sh_y(x, y) = (x, y + h_y \cdot x) \]
Las matrices correspondientes son:
Horizontal:
\[ Sh_x = \begin{pmatrix} 1 & h_x & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{pmatrix} \]
Vertical:
\[ Sh_y = \begin{pmatrix} 1 & 0 & 0 \\ h_y & 1 & 0 \\ 0 & 0 & 1 \end{pmatrix} \]
5. Reflexión (Reflection)
La reflexión voltea la imagen sobre un eje. Las matrices para reflexiones sobre el eje \(x\) o \(y\) son:
\[ R_x = \begin{pmatrix} 1 & 0 & 0 \\ 0 & -1 & 0 \\ 0 & 0 & 1 \end{pmatrix} \]
\[ R_y = \begin{pmatrix} -1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{pmatrix} \]
6. Transformación Afín (Affine Transformation)
Una transformación afín combina varias de las anteriores. Su matriz es:
\[ A = \begin{pmatrix} a_{11} & a_{12} & t_x \\ a_{21} & a_{22} & t_y \\ 0 & 0 & 1 \end{pmatrix} \]
7. Transformación Proyectiva (Homografía)
La homografía proyecta una imagen en un nuevo plano, alterando su perspectiva. La matriz homográfica es:
\[ H = \begin{pmatrix} h_{11} & h_{12} & h_{13} \\ h_{21} & h_{22} & h_{23} \\ h_{31} & h_{32} & h_{33} \end{pmatrix} \]
Aplicaciones
Las transformaciones geométricas se utilizan en:
- Alineación de imágenes: Registrar imágenes para tener la misma perspectiva.
- Corrección de distorsión: Corregir distorsiones causadas por lentes de cámaras.
- Visión artificial: Identificar objetos en diferentes orientaciones.
- Realidad aumentada: Ajustar objetos virtuales al mundo real.
Landmarks
En visión artificial, el término landmark se refiere a puntos clave dentro de una imagen que ayudan a describir características importantes de un objeto. Estos puntos de referencia se utilizan en tareas como el reconocimiento facial, el registro de imágenes médicas, la segmentación de objetos y la detección de estructuras en imágenes biomédicas.
Fundamentos de los Landmarks
Los landmarks se caracterizan por ser:
- Puntos de interés dentro de una imagen que representan estructuras significativas.
- Invariantes a transformaciones como traslación, rotación y cambios de escala.
- Utilizados en modelos de alineación y reconocimiento de patrones.
Dependiendo del contexto, los landmarks pueden ser seleccionados manualmente o detectados automáticamente mediante algoritmos especializados.
Aplicaciones de los Landmarks
- Reconocimiento Facial
En el reconocimiento facial, se utilizan landmarks para identificar estructuras anatómicas del rostro, como los ojos, la nariz y la boca. Un ejemplo común es el modelo de 68 puntos faciales utilizado en bibliotecas como dlib, donde cada punto representa una característica clave del rostro.
- Registro y Segmentación de Imágenes Médicas
Los landmarks juegan un papel clave en el registro de imágenes médicas, facilitando la comparación de imágenes de distintos pacientes o en diferentes momentos temporales. Se utilizan en la detección de estructuras anatómicas en radiografías, tomografías y microscopía.
- Seguimiento de Movimiento y Estimación de Postura
Los sistemas de visión artificial pueden detectar landmarks en el cuerpo humano para el análisis de la postura y el seguimiento de movimientos en tiempo real, como en aplicaciones deportivas y rehabilitación médica.
- Detección de Características en Citología Cervical
En el análisis de imágenes de citología cervical, los landmarks pueden utilizarse para localizar estructuras celulares relevantes, facilitando la segmentación y clasificación de células en exámenes de Papanicolaou.
Métodos de Detección de Landmarks
Existen varios métodos para la detección de landmarks, que se pueden clasificar en dos grandes categorías:
- Métodos Clásicos
SIFT (Scale-Invariant Feature Transform): Detecta puntos clave invariables a cambios de escala y rotación.
SURF (Speeded-Up Robust Features): Similar a SIFT pero más rápido.
ORB (Oriented FAST and Rotated BRIEF): Optimizado para detección rápida en tiempo real.
- Métodos Basados en Aprendizaje Profundo
Redes neuronales convolucionales (CNNs): Utilizadas en el reconocimiento facial y análisis biomédico.
MediaPipe: Biblioteca de Google para la detección de landmarks en tiempo real.
Modelos de Regresión de Forma Activa (ASM) y Modelos de Forma Constrained Local Models (CLM): Métodos específicos para la detección de formas anatómicas.
Detección de Manos con MediaPipe en Python
Introducción
MediaPipe es una librería desarrollada por Google Research, diseñada para el procesamiento eficiente de datos en tiempo real. Fue lanzada para facilitar el desarrollo de aplicaciones de visión por computadora, realidad aumentada y aprendizaje automático en dispositivos móviles y computadoras.
MediaPipe Hands utiliza modelos preentrenados basados en redes neuronales para detectar y seguir 21 puntos clave de la mano, permitiendo análisis de gestos y seguimiento preciso.
Instalación de Dependencias
Para ejecutar el código, se necesita instalar los siguientes paquetes en Python:
pip install mediapipe opencv-python
Código para Detección de Manos en Tiempo Real
Este es un un código en Python que detecta manos usando MediaPipe y OpenCV.
import cv2 import mediapipe as mp # Inicializar MediaPipe Hands mp_hands = mp.solutions.hands mp_drawing = mp.solutions.drawing_utils hands = mp_hands.Hands(min_detection_confidence=0.5, min_tracking_confidence=0.5) # Captura de video cap = cv2.VideoCapture(0) while cap.isOpened(): ret, frame = cap.read() if not ret: break # Convertir imagen a RGB frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # Detectar manos results = hands.process(frame_rgb) # Dibujar los puntos clave y conexiones if results.multi_hand_landmarks: for hand_landmarks in results.multi_hand_landmarks: mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS) # Mostrar la imagen cv2.imshow("Salida", frame) # Salir con 'q' if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()
import cv2 import mediapipe as mp # Inicializar MediaPipe Hands mp_hands = mp.solutions.hands mp_drawing = mp.solutions.drawing_utils hands = mp_hands.Hands(min_detection_confidence=0.5, min_tracking_confidence=0.5) # Captura de video cap = cv2.VideoCapture(0) while cap.isOpened(): ret, frame = cap.read() if not ret: break # Convertir imagen a RGB frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # Detectar manos results = hands.process(frame_rgb) # Dibujar los puntos clave y conexiones if results.multi_hand_landmarks: for hand_landmarks in results.multi_hand_landmarks: for idx, landmark in enumerate(hand_landmarks.landmark): h,w,_ =frame.shape x,y=int(landmark.x*w), int(landmark.y*h) cv2.circle(frame, (x,y), 2, (12, 233,4), -1) #mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS) # Mostrar la imagen cv2.imshow("Salida", frame) # Salir con 'q' if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()
Explicación del Código
- Se inicializa MediaPipe Hands con una confianza mínima de detección y seguimiento del 50%.
- Se usa OpenCV para capturar video en tiempo real.
- Se convierte el fotograma de BGR a RGB, ya que MediaPipe trabaja con imágenes en formato RGB.
- Se procesan los fotogramas y se extraen las coordenadas de la mano si se detecta.
- Se dibujan los puntos clave de la mano y sus conexiones en la imagen original.
- Se muestra el resultado en una ventana de OpenCV.
Mejoras Posibles
- Contar cuántas manos hay con
len(results.multi_hand_landmarks)
. - Detectar gestos analizando las posiciones de los dedos.
- Guardar las coordenadas para análisis posterior.
Indice de dedos en mediapipe
Índice | Dedo / Punto | Descripción |
---|---|---|
0 | Muñeca (wrist) | Base de la mano |
1 | Base del pulgar | Unión del pulgar con la palma |
2 | Primera falange del pulgar | Parte inferior del pulgar |
3 | Segunda falange del pulgar | Parte media del pulgar |
4 | Punta del pulgar | Extremo del pulgar |
5 | Base del índice | Unión del índice con la palma |
6 | Primera falange del índice | Parte inferior del índice |
7 | Segunda falange del índice | Parte media del índice |
8 | Punta del índice | Extremo del índice |
9 | Base del medio | Unión del dedo medio con la palma |
10 | Primera falange del medio | Parte inferior del medio |
11 | Segunda falange del medio | Parte media del medio |
12 | Punta del medio | Extremo del medio |
13 | Base del anular | Unión del anular con la palma |
14 | Primera falange del anular | Parte inferior del anular |
15 | Segunda falange del anular | Parte media del anular |
16 | Punta del anular | Extremo del anular |
17 | Base del meñique | Unión del meñique con la palma |
18 | Primera falange del meñique | Parte inferior del meñique |
19 | Segunda falange del meñique | Parte media del meñique |
20 | Punta del meñique | Extremo del meñique |
Medipipe Reconocimiento de letras
import cv2 import mediapipe as mp import numpy as np # Inicializar MediaPipe Hands mp_hands = mp.solutions.hands mp_drawing = mp.solutions.drawing_utils hands = mp_hands.Hands(min_detection_confidence=0.7, min_tracking_confidence=0.7) # Función para determinar la letra según la posición de los dedos def reconocer_letra(hand_landmarks, frame): # Obtener coordenadas de los puntos clave de la mano dedos = [hand_landmarks.landmark[i] for i in range(21)] # Obtener posiciones clave (puntas y base de los dedos) pulgar = dedos[4] # Punta del pulgar indice = dedos[8] # Punta del índice medio = dedos[12] # Punta del medio anular = dedos[16] # Punta del anular meñique = dedos[20] # Punta del meñique # Distancias entre puntos (para definir gestos) distancia_pulgar_indice = np.linalg.norm([pulgar.x - indice.x, pulgar.y - indice.y]) distancia_indice_medio = np.linalg.norm([indice.x - medio.x, indice.y - medio.y]) # Lógica para reconocer algunas letras if distancia_pulgar_indice < 0.05 and distancia_indice_medio > 0.1: return "A" # Seña de la letra A (puño cerrado con pulgar al lado) elif indice.y < medio.y and medio.y < anular.y and anular.y < meñique.y: return "B" # Seña de la letra B (todos los dedos estirados, pulgar en la palma) elif distancia_pulgar_indice > 0.1 and distancia_indice_medio > 0.1: return "C" # Seña de la letra C (mano en forma de "C") return "Desconocido" # Captura de video en tiempo real cap = cv2.VideoCapture(0) while cap.isOpened(): ret, frame = cap.read() if not ret: break # Convertir a RGB frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # Procesar la imagen con MediaPipe results = hands.process(frame_rgb) # Dibujar puntos de la mano y reconocer letras if results.multi_hand_landmarks: for hand_landmarks in results.multi_hand_landmarks: mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS) # Identificar la letra letra_detectada = reconocer_letra(hand_landmarks, frame) # Mostrar la letra en pantalla cv2.putText(frame, f"Letra: {letra_detectada}", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2, cv2.LINE_AA) # Mostrar el video cv2.imshow("Reconocimiento de Letras", frame) # Salir con la tecla 'q' if cv2.waitKey(1) & 0xFF == ord('q'): break # Liberar recursos cap.release() cv2.destroyAllWindows()
import cv2 import mediapipe as mp import numpy as np # Inicializar MediaPipe Hands mp_hands = mp.solutions.hands mp_drawing = mp.solutions.drawing_utils hands = mp_hands.Hands(min_detection_confidence=0.7, min_tracking_confidence=0.7) # Función para determinar la letra según la posición de los dedos def reconocer_letra(hand_landmarks, frame): h, w, _ = frame.shape # Tamaño de la imagen # Obtener coordenadas de los puntos clave en píxeles dedos = [(int(hand_landmarks.landmark[i].x * w), int(hand_landmarks.landmark[i].y * h)) for i in range(21)] # Obtener posiciones clave (puntas de los dedos) pulgar, indice, medio, anular, meñique = dedos[4], dedos[8], dedos[12], dedos[16], dedos[20] # Mostrar los números de los landmarks en la imagen for i, (x, y) in enumerate(dedos): cv2.circle(frame, (x, y), 5, (0, 234, 0), -1) # Puntos verdes cv2.putText(frame, str(i), (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 1) # Dibujar coordenadas del pulgar cv2.putText(frame, f'({int(pulgar[0])}, {int(pulgar[1])})', (pulgar[0], pulgar[1] - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (245, 0, 0), 2, cv2.LINE_AA) cv2.line(frame, (int(pulgar[0]), int(pulgar[1])), (int(indice[0]), int(indice[1])), (244,34,12), 2) # Calcular distancias en píxeles distancia_pulgar_indice = np.linalg.norm(np.array(pulgar) - np.array(indice)) distancia_indice_medio = np.linalg.norm(np.array(indice) - np.array(medio)) cv2.putText(frame, f'({distancia_pulgar_indice})', (pulgar[0]-40, pulgar[1] - 45), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2, cv2.LINE_AA) cv2.circle(frame, (pulgar[0]+20,pulgar[1]+20), int(distancia_indice_medio), (34,234,65), -1 ) # Lógica para reconocer algunas letras if distancia_pulgar_indice < 30 and distancia_indice_medio > 50: return "A" # Seña de la letra A (puño cerrado con pulgar al lado) elif indice[1] < medio[1] and medio[1] < anular[1] and anular[1] < meñique[1]: return "B" # Seña de la letra B (todos los dedos estirados, pulgar en la palma) elif distancia_pulgar_indice > 50 and distancia_indice_medio > 50: return "C" # Seña de la letra C (mano en forma de "C") return "Desconocido" # Captura de video en tiempo real cap = cv2.VideoCapture(0) while cap.isOpened(): ret, frame = cap.read() if not ret: break # Convertir a RGB frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # Procesar la imagen con MediaPipe results = hands.process(frame_rgb) # Dibujar puntos de la mano y reconocer letras if results.multi_hand_landmarks: for hand_landmarks in results.multi_hand_landmarks: mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS) # Identificar la letra letra_detectada = reconocer_letra(hand_landmarks, frame) # Mostrar la letra en pantalla cv2.putText(frame, f"Letra: {letra_detectada}", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2, cv2.LINE_AA) # Mostrar el video cv2.imshow("Reconocimiento de Letras", frame) # Salir con la tecla 'q' if cv2.waitKey(1) & 0xFF == ord('q'): break # Liberar recursos cap.release() cv2.destroyAllWindows()
import cv2 import mediapipe as mp # Inicializar MediaPipe Face Mesh mp_face_mesh = mp.solutions.face_mesh face_mesh = mp_face_mesh.FaceMesh(static_image_mode=False, max_num_faces=2, min_detection_confidence=0.5, min_tracking_confidence=0.5) # Inicializar dibujador de MediaPipe mp_drawing = mp.solutions.drawing_utils drawing_spec = mp_drawing.DrawingSpec(thickness=1, circle_radius=1, color=(234, 255, 233)) # Puntos verdes # Captura de video cap = cv2.VideoCapture(0) while cap.isOpened(): ret, frame = cap.read() if not ret: break frqame = cv2.flip(frame, 1) # Espejo para mayor naturalidad rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results = face_mesh.process(rgb_frame) if results.multi_face_landmarks: for face_landmarks in results.multi_face_landmarks: mp_drawing.draw_landmarks(frame, face_landmarks, mp_face_mesh.FACEMESH_TESSELATION, drawing_spec, drawing_spec) cv2.imshow('PuntosFacialesMediaPipe', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()
import cv2 import mediapipe as mp import numpy as np # Inicializar MediaPipe Face Detection mp_face_detection = mp.solutions.face_detection face_detection = mp_face_detection.FaceDetection(min_detection_confidence=0.7) # Cargar la máscara con transparencia (debe ser un PNG con canal alpha) mask = cv2.imread("300.png", cv2.IMREAD_UNCHANGED) # Función para superponer la máscara en la cara def overlay_mask(frame, mask, x, y, w, h): # Redimensionar la máscara al tamaño de la cara detectada mask_resized = cv2.resize(mask, (w, h)) # Extraer los canales de la máscara (RGBA) mask_rgb = mask_resized[:, :, :3] # Canales de color mask_alpha = mask_resized[:, :, 3] / 255.0 # Canal de transparencia # Obtener la región donde se colocará la máscara roi = frame[y:y+h, x:x+w] # Mezclar la máscara con el frame for c in range(3): # Aplicar a cada canal de color (BGR) roi[:, :, c] = (1 - mask_alpha) * roi[:, :, c] + mask_alpha * mask_rgb[:, :, c] frame[y:y+h, x:x+w] = roi # Colocar la máscara en el frame # Captura de video cap = cv2.VideoCapture(0) while cap.isOpened(): ret, frame = cap.read() if not ret: break frame = cv2.flip(frame, 1) # Espejo para mejor experiencia rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results = face_detection.process(rgb_frame) if results.detections: for detection in results.detections: # Obtener la caja delimitadora del rostro bboxC = detection.location_data.relative_bounding_box ih, iw, _ = frame.shape # Alto y ancho del frame # Convertir a coordenadas de píxeles x = int(bboxC.xmin * iw) - 20 y = int(bboxC.ymin * ih) - 40 w = int(bboxC.width * iw) + 40 h = int(bboxC.height * ih) + 40 # Superponer la máscara en la imagen overlay_mask(frame, mask, x, y, w, h) cv2.imshow("Mascara Animada", frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()
Actividades
- Generar una imagen tipo pixel art utilizando una matriz de enteros en el rango de 0 a 255.
- Generar al menos cinco operadores puntuales utilizando la imagen generada o una imagen previamente cargada.
- Aplicar las transformaciones geométricas vistas en clase.
- Investigar qué son las ecuaciones paramétricas.
- Crear un dibujo mediante primitivas de dibujo utilizando OpenCV.
- Programar al menos 10 ecuaciones paramétricas.
Todas las actividades anteriores deben estar documentadas en formato Markdown en el repositorio.
Programación
Programas Unidad 1
import cv2 as cv import numpy as np img =cv.imread('tr.png',1) img2 =cv.cvtColor(img, cv.COLOR_BGR2GRAY) cv.imshow('img2a', img2) x,y=img2.shape[:2] print(x,y) for i in range(x): for j in range(y): img2[i,j]=255-img2[i,j] cv.imshow('img2b', img2) #img3 =cv.cvtColor(img, cv.COLOR_BGR2RGB) #img4 =cv.cvtColor(img, cv.COLOR_BGR2BGRA) #img5 =cv.cvtColor(img4, cv.COLOR_BGRA2BGR65) #cv.imshow('img', img) #cv.imshow('img2', img2) #cv.imshow('img3', img3)3 #cv.imshow('img4', img4) #cv.imshow('img4', img5) cv.waitKey(0) cv.destroyAllWindows()
Creación de una Imagen
import numpy as np # Importa la librería NumPy, útil para trabajar con arreglos y operaciones numéricas. import cv2 as cv # Importa la librería OpenCV, que se utiliza para procesamiento de imágenes. # Crea una imagen de 500x500 píxeles, todos con valor 240 (gris claro). # La imagen tiene solo un canal (escala de grises) y está inicializada con valores de tipo uint8 (enteros sin signo de 8 bits). img = np.ones((500, 500), dtype=np.uint8) * 240 # Modifica algunos píxeles específicos en las coordenadas (30, 30) a (30, 35) para que tengan un valor de 1 (casi negro). # Esto creará una pequeña línea vertical de 6 píxeles en la imagen de color casi negro. img[30, 30] = 1 img[30, 31] = 1 img[30, 32] = 1 img[30, 33] = 1 img[30, 34] = 1 img[30, 35] = 1 # Muestra la imagen en una ventana con el título 'img'. cv.imshow('img', img) # Espera a que el usuario presione cualquier tecla para continuar. cv.waitKey() # Cierra todas las ventanas creadas por OpenCV. cv.destroyAllWindows()
Cargar Imagen
import cv2 as cv # Importa la librería OpenCV, que se usa para el procesamiento de imágenes y videos. # Lee una imagen desde el archivo 'tr.png'. # El segundo argumento '1' indica que se leerá la imagen en color (1 para color, 0 para escala de grises, -1 para incluir el canal alfa si existe). img = cv.imread('tr.png', 1) # Muestra la imagen leída en una ventana llamada 'ejemplo'. cv.imshow('ejemplo', img) # Espera indefinidamente hasta que el usuario presione una tecla. cv.waitKey(0) # Cierra todas las ventanas abiertas por OpenCV. cv.destroyAllWindows()
Split y Merge Imagen
import cv2 as cv # Importa OpenCV, que es útil para procesamiento de imágenes. import numpy as np # Importa NumPy, que es útil para trabajar con matrices y operaciones numéricas. # Carga la imagen '1a.png' en color (1 para color, 0 para escala de grises). img = cv.imread('1a.png', 1) # Crea una imagen vacía (llena de ceros, que representa color negro) con el mismo tamaño que la imagen original. # La imagen vacía tiene un solo canal (escala de grises), por lo que se usa `img.shape[:2]` para obtener las dimensiones de la imagen (alto, ancho). img2 = np.zeros((img.shape[:2]), dtype=np.uint8) # Imprime las dimensiones de la imagen en la terminal (alto, ancho). print(img.shape[:2]) # Separa los canales rojo (r), verde (g) y azul (b) de la imagen utilizando la función `cv.split()`. r, g, b = cv.split(img) # Recombina los canales, pero los reorganiza como rojo, azul y verde (RBG en lugar de RGB). #r2 = cv.merge([img2, img2, r]) #g2 = cv.merge([img2, g, img2]) #b2 = cv.merge([b, img2, img2]) img3 = cv.merge([b, r, g]) # Crea una imagen que contiene solo el canal rojo, llenando los otros dos canales (verde y azul) con ceros (negro). #r = cv.merge([r, img2, img2]) # Crea una imagen que contiene solo el canal verde, llenando los otros dos canales (rojo y azul) con ceros. #g = cv.merge([img2, g, img2]) # Crea una imagen que contiene solo el canal azul, llenando los otros dos canales (rojo y verde) con ceros.# #b = cv.merge([img2, img2, b]) # Muestra la imagen original en una ventana llamada 'ejemplo'. cv.imshow('ejemplo', img) # Muestra la imagen que contiene solo el canal rojo. #cv.imshow('r', r) #cv.imshow('r2', r2) # Muestra la imagen que contiene solo el canal verde. #cv.imshow('g', g) #cv.imshow('g2', g2) # Muestra la imagen que contiene solo el canal azul. #cv.imshow('b', b) #cv.imshow('b2', b2) # Muestra la imagen con los canales reorganizados (RBG en lugar de RGB). cv.imshow('img3', img3) # Espera indefinidamente a que el usuario presione una tecla. cv.waitKey(0) # Cierra todas las ventanas abiertas por OpenCV. cv.destroyAllWindows()
Ejemplo de Operador Puntual en Imágenes
import cv2 as cv img = cv.imread('tr.png', 0) cv.imshow('salida', img) x,y=img.shape for i in range(x): for j in range(y): if(img[i,j]>150): img[i,j]=255 else: img[i,j]=0 cv.imshow('negativo', img) print( img.shape, x , y) cv.waitKey(0) cv.destroyAllWindows()
Cargar vídeo Opencv
import cv2 as cv import numpy as np cap = cv.VideoCapture(0) while(True): ret, img = cap.read() if ret: cv.imshow('video', img) k =cv.waitKey(1) & 0xFF if k == 27 : break else: break cap.release() cv.destroyAllWindows()
import cv2 as cv import numpy as np cap = cv.VideoCapture(0) while(True): ret, img = cap.read() if ret: cv.imshow('video', img) img2 = cv.cvtColor(img, cv.COLOR_BGR2GRAY) hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV) w,h = img2.shape img3=255-img2 cv.imshow('img2', img2) cv.imshow('hsv', hsv) cv.imshow('img3', img3) k =cv.waitKey(1) & 0xFF if k == 27 : break else: break cap.release() cv.destroyAllWindows()
Ejemplos de Transformaciones Geométricas en Modo Raw en Python
1. Traslación
En esta transformación, desplazamos la imagen en el espacio sumando un valor fijo a las coordenadas de los píxeles.
import cv2 as cv import numpy as np # Cargar la imagen en escala de grises img = cv.imread('tr.png', 0) # Obtener el tamaño de la imagen x, y = img.shape # Crear una imagen vacía para la traslación translated_img = np.zeros((x, y), dtype=np.uint8) # Definir el desplazamiento en x e y dx, dy = 100, 50 # Trasladar la imagen for i in range(x): for j in range(y): new_x = i + dy new_y = j + dx if 0 <= new_x < x and 0 <= new_y < y: translated_img[new_x, new_y] = img[i, j] # Mostrar la imagen original y la trasladada cv.imshow('Imagen Original', img) cv.imshow('Imagen Trasladada', translated_img) cv.waitKey(0) cv.destroyAllWindows()
2. Rotación (alrededor del centro de la imagen)
La rotación se realiza alrededor del centro de la imagen, utilizando las fórmulas matemáticas correspondientes.
import cv2 as cv import numpy as np import math # Cargar la imagen en escala de grises img = cv.imread('tr.png', 0) # Obtener el tamaño de la imagen x, y = img.shape # Crear una imagen vacía para almacenar el resultado rotated_img = np.zeros((x*2, y*2), dtype=np.uint8) xx, yy = rotated_img.shape # Calcular el centro de la imagen cx, cy = int(x // 2), int(y // 2) # Definir el ángulo de rotación (en grados) y convertirlo a radianes angle = 45 theta = math.radians(angle) # Rotar la imagen for i in range(x): for j in range(y): new_x = int((j - cx) * math.cos(theta) - (i - cy) * math.sin(theta) + cx) new_y = int((j - cx) * math.sin(theta) + (i - cy) * math.cos(theta) + cy) if 0 <= new_x < y and 0 <= new_y < x: rotated_img[new_y, new_x] = img[i, j] # Mostrar la imagen original y la rotada cv.imshow('Imagen Original', img) cv.imshow('Imagen Rotada (modo raw)', rotated_img) cv.waitKey(0) cv.destroyAllWindows()
3. Escalado
El escalado cambia el tamaño de la imagen multiplicando las coordenadas por un factor de escala.
import cv2 as cv import numpy as np # Cargar la imagen en escala de grises img = cv.imread('tr.png', 0) # Obtener el tamaño de la imagen x, y = img.shape # Definir el factor de escala scale_x, scale_y = 2, 2 # Crear una nueva imagen para almacenar el escalado scaled_img = np.zeros((int(x * scale_y), int(y * scale_x)), dtype=np.uint8) # Aplicar el escalado for i in range(x): for j in range(y): #orig_x = int(i * scale_y) #orig_y = int(j * scale_x) scaled_img[i*2, j*2] = img[i, j] # Mostrar la imagen original y la escalada cv.imshow('Imagen Original', img) cv.imshow('Imagen Escalada (modo raw)', scaled_img) cv.waitKey(0) cv.destroyAllWindows()
4. Cizallamiento (Shearing)
En el cizallamiento, los píxeles se desplazan en una dirección proporcional a otra, lo que inclina la imagen.
import cv2 as cv import numpy as np # Cargar la imagen en escala de grises img = cv.imread('tr.png', 0) # Obtener el tamaño de la imagen x, y = img.shape # Crear una imagen vacía para el cizallamiento sheared_img = np.zeros((x, y), dtype=np.uint8) # Definir el factor de cizallamiento shear_factor_x = 0.5 # Aplicar el cizallamiento en el eje x for i in range(x): for j in range(y): new_x = i new_y = int(j + shear_factor_x * i) if 0 <= new_x < x and 0 <= new_y < y: sheared_img[new_x, new_y] = img[i, j] # Mostrar la imagen original y la cizallada cv.imshow('Imagen Original', img) cv.imshow('Imagen Cizallada (modo raw)', sheared_img) cv.waitKey(0) cv.destroyAllWindows()
5. Reflexión (Reflection)
La reflexión voltea la imagen sobre un eje, como el eje vertical o horizontal.
- Reflexión horizontal
import cv2 as cv import numpy as np # Cargar la imagen en escala de grises img = cv.imread('tr.png', 0) # Obtener el tamaño de la imagen x, y = img.shape # Crear una imagen vacía para la reflexión reflected_img = np.zeros((x, y), dtype=np.uint8) # Aplicar la reflexión horizontal for i in range(x): for j in range(y): reflected_img[i, y - j - 1] = img[i, j] # Mostrar la imagen original y la reflejada cv.imshow('Imagen Original', img) cv.imshow('Imagen Reflejada Horizontalmente (modo raw)', reflected_img) cv.waitKey(0) cv.destroyAllWindows()
- Reflexión vertical
import cv2 as cv import numpy as np # Cargar la imagen en escala de grises img = cv.imread('tr.png', 0) # Obtener el tamaño de la imagen x, y = img.shape # Crear una imagen vacía para la reflexión reflected_img = np.zeros((x, y), dtype=np.uint8) # Aplicar la reflexión vertical for i in range(x): for j in range(y): reflected_img[x - i - 1, j] = img[i, j] # Mostrar la imagen original y la reflejada cv.imshow('Imagen Original', img) cv.imshow('Imagen Reflejada Verticalmente (modo raw)', reflected_img) cv.waitKey(0) cv.destroyAllWindows()
Ejemplos de Transformaciones Geométricas en Python usando OpenCV
1. Traslación
En OpenCV, la traslación se realiza usando matrices de transformación afín y la función `cv.warpAffine()`.
import cv2 as cv import numpy as np # Cargar la imagen en escala de grises img = cv.imread('tr.png', 0) # Obtener el tamaño de la imagen x, y = img.shape # Definir el desplazamiento en x e y dx, dy = 100, 50 # Crear la matriz de traslación M = np.float32([[1, 0, dx], [0, 1, dy]]) # Aplicar la traslación usando warpAffine translated_img = cv.warpAffine(img, M, (y, x)) # Mostrar la imagen original y la trasladada cv.imshow('Imagen Original', img) cv.imshow('Imagen Trasladada', translated_img) cv.waitKey(0) cv.destroyAllWindows()
2. Rotación (alrededor del centro de la imagen)
La rotación alrededor del centro de la imagen se puede realizar con la función `cv.getRotationMatrix2D()`.
import cv2 as cv import numpy as np # Cargar la imagen en escala de grises img = cv.imread('tr.png', 0) # Obtener el tamaño de la imagen x, y = img.shape # Calcular el centro de la imagen center = (y // 2, x // 2) # Definir el ángulo de rotación (en grados) angle = 45 # Crear la matriz de rotación M = cv.getRotationMatrix2D(center, angle, 1.0) # Aplicar la rotación usando warpAffine rotated_img = cv.warpAffine(img, M, (y, x)) # Mostrar la imagen original y la rotada cv.imshow('Imagen Original', img) cv.imshow('Imagen Rotada', rotated_img) cv.waitKey(0) cv.destroyAllWindows()
3. Escalado
El escalado se puede realizar usando la función `cv.resize()`.
import cv2 as cv import numpy as np # Cargar la imagen en escala de grises img = cv.imread('tr.png', 1) # Definir el factor de escala scale_x, scale_y = 0.5, 0.5 # Aplicar el escalado usando cv.resize() scaled_img = cv.resize(img, None, fx=scale_x, fy=scale_y) # Mostrar la imagen original y la escalada cv.imshow('Imagen Original', img) cv.imshow('Imagen Escalada', scaled_img) cv.waitKey(0) cv.destroyAllWindows()
4. Cizallamiento (Shearing)
El cizallamiento se puede realizar creando una matriz de transformación afín personalizada.
import cv2 as cv import numpy as np # Cargar la imagen en escala de grises img = cv.imread('tr.png', 0) # Obtener el tamaño de la imagen x, y = img.shape # Definir el factor de cizallamiento shear_factor = 0.5 # Crear la matriz de cizallamiento M = np.float32([[1, shear_factor, 0], [0, 1, 0]]) # Aplicar el cizallamiento usando warpAffine sheared_img = cv.warpAffine(img, M, (y, x)) # Mostrar la imagen original y la cizallada cv.imshow('Imagen Original', img) cv.imshow('Imagen Cizallada', sheared_img) cv.waitKey(0) cv.destroyAllWindows()
5. Reflexión (Reflection)
En OpenCV, la reflexión de una imagen se realiza con la función `cv.flip()`.
- Reflexión horizontal
import cv2 as cv # Cargar la imagen en escala de grises img = cv.imread('tr.png', 0) # Aplicar la reflexión horizontal usando cv.flip() reflected_img = cv.flip(img, 1) # Mostrar la imagen original y la reflejada cv.imshow('Imagen Original', img) cv.imshow('Imagen Reflejada Horizontalmente', reflected_img) cv.waitKey(0) cv.destroyAllWindows()
- Reflexión vertical
import cv2 as cv # Cargar la imagen en escala de grises img = cv.imread('tr.png', 0) # Aplicar la reflexión vertical usando cv.flip() reflected_img = cv.flip(img, 0) # Mostrar la imagen original y la reflejada cv.imshow('Imagen Original', img) cv.imshow('Imagen Reflejada Verticalmente', reflected_img) cv.waitKey(0) cv.destroyAllWindows()
Primitivas de Dibujo
import cv2 as cv import numpy as np img = np.ones((500, 500, 3), dtype=np.uint8)*255 cv.circle(img, (250, 250), 50, (0,234,21), -1) cv.circle(img, (250, 250), 30, (0,0,0), -1) cv.line(img, (1,1), (230, 240), (0,234,21), 3 ) cv.rectangle(img, (20,20), (50,60), (0,0,0), 3 ) pts = np.array([[10,5],[20,30],[70,20],[50,10]], np.int32) pts = pts.reshape((-1,1,2)) cv.polylines(img,[pts],True,(0,0,0), 3) cv.imshow('img', img) cv.waitKey() cv.destroyAllWindows()
import cv2 as cv import numpy as np img = np.ones((500, 500, 3), dtype=np.uint8)*255 for i in range(400): img = np.ones((500, 500, 3), dtype=np.uint8)*255 cv.circle(img, (0+i, 0+i), 20, (0,234,21), -1) cv.imshow('img', img) cv.waitKey(40) cv.waitKey(0) cv.destroyAllWindows()
Parametricas
import numpy as np import cv2 # Función para generar un solo punto de la elipse en función del parámetro t def generar_punto_elipse(a, b, t): x = int(a * 2* np.cos(t) + 200) # Desplazamiento para centrar y = int(b * np.sin(t) + 200) return (x, y) # Dimensiones de la imagen img_width, img_height = 800, 800 # Crear una imagen en blanco imagen = np.zeros((img_height, img_width, 3), dtype=np.uint8) # Parámetros de la elipse a = 200 # Semieje mayor b = 100 # Semieje menor num_puntos = 1000 # Crear los valores del parámetro t para la animación t_vals = np.linspace(0, 2 * np.pi, num_puntos) print(t_vals) # Bucle de animación for t in t_vals: # Crear una nueva imagen en blanco en cada iteración imagen = np.zeros((img_height, img_width, 3), dtype=np.uint8) # Generar el punto en la elipse punto = generar_punto_elipse(a, b, t) # Dibujar el punto en la elipse cv2.circle(imagen, punto, radius=30, color=(0, 255, 0), thickness=-1) # Dibujar la trayectoria completa de la elipse (opcional, si quieres ver toda la elipse) for t_tray in t_vals: pt_tray = generar_punto_elipse(a, b, t_tray) cv2.circle(imagen, pt_tray, radius=1, color=(255, 255, 255), thickness=-1) # Mostrar la imagen con el punto en movimiento cv2.imshow('img', imagen) # Controlar la velocidad de la animación (en milisegundos) cv2.waitKey(10) # Cerrar la ventana después de la animación cv2.destroyAllWindows()
import numpy as np import cv2 # Definir los parámetros de la circunferencia r = 100 # radio h = 250 # centro en x k = 250 # centro en y num_frames = 100 # número de frames en la animación t_vals = np.linspace(0, 2*np.pi, num_frames) # valores del parámetro t # Crear una ventana para mostrar la animación cv2.namedWindow('Animación Circunferencia', cv2.WINDOW_AUTOSIZE) # Animar la circunferencia for i in range(len(t_vals)): t = t_vals[i] # Calcular las coordenadas del punto en la circunferencia x = int(h + r * np.cos(t)) y = int(k + r * np.sin(t)) # Crear una imagen en blanco frame = np.ones((500, 500, 3), dtype=np.uint8) * 255 # Dibujar la circunferencia cv2.circle(frame, (h, k), r, (0, 0, 0), 2) # Dibujar el punto en movimiento cv2.circle(frame, (x, y), 10, (0, 0, 255), -1) # Mostrar el frame en la ventana cv2.imshow('Animación Circunferencia', frame) # Esperar un corto tiempo para crear la animación (ajustar velocidad) cv2.waitKey(50) # Cerrar la ventana cuando la animación termine cv2.destroyAllWindows()
import numpy as np import cv2 # Definir los parámetros iniciales width, height = 1000, 1000 # Ampliar la ventana para ver toda la figura img = np.ones((height, width, 3), dtype=np.uint8)*255 # Parámetros de la curva de Limacon a, b = 150, 100 # Reducir los valores de a y b para que la curva se ajuste mejor k = 0.7# Constante de multiplicación del ángulo theta_increment = 0.05 # Incremento del ángulo max_theta = 2 * np.pi # Un ciclo completo # Centro de la imagen center_x, center_y = width // 2, height // 2 theta = 0 # Ángulo inicial while True: # Bucle infinito # Limpiar la imagen img = np.ones((width, height, 3), dtype=np.uint8) * 255 # Dibujar la curva completa desde 0 hasta theta for t in np.arange(0, theta, theta_increment): # Calcular las coordenadas paramétricas (x, y) para la curva de Limacon r = a + b * np.cos(k * t) x = int(center_x + r * np.cos(t)) y = int(center_y + r * np.sin(t)) # Dibujar un círculo en la posición calculada #cv2.circle(img, (x, y), 3, (0, 234, 0), -1) # Color rojo cv2.circle(img, (x-2, y-2), 3, (0, 0, 0), -1) # Color rojo # Mostrar la constante k en la imagen #cv2.putText(img, f"k = {k:.2f}", (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2) # Mostrar la imagen cv2.imshow("Parametric Animation", img) img = np.ones((width, height, 3), dtype=np.uint8) * 255 # Incrementar el ángulo theta += theta_increment # Reiniciar theta si alcanza su valor máximo #if theta >= max_theta: # theta = 0 # Reinicia la animación para que se repita # Pausar para ver la animación if cv2.waitKey(30) & 0xFF == 27: # Esperar 30ms, salir con 'ESC' break # Cerrar la ventana al finalizar cv2.destroyAllWindows()
Segmentación de color mediante el modelo de color HSV
import cv2 as cv img = cv.imread('tr.png', 1) hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV) uba=(10, 255, 255) ubb=(0, 60 ,60) uba2=(180, 255, 255) ubb2=(172, 60,60) mask1 = cv.inRange(hsv, ubb, uba) mask2 = cv.inRange(hsv, ubb2, uba2) mask = mask1+mask2 res = cv.bitwise_and(img, img, mask=mask) cv.imshow('mask1', mask1) cv.imshow('mask2', mask2) cv.imshow('mask', mask) cv.imshow('res', res) cv.waitKey(0) cv.destroyAllWindows()
import cv2 as cv import numpy as np cap = cv.VideoCapture(0) while(True): ret, img = cap.read() if ret: cv.imshow('video', img) hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV) uba=(90, 255, 255) ubb=(40, 40 ,40) mask = cv.inRange(hsv, ubb, uba) res = cv.bitwise_and(img, img, mask=mask) cv.imshow('res', res) cv.imshow('mask', mask) k =cv.waitKey(1) & 0xFF if k == 27 : break else: break cap.release() cv.destroyAllWindows()
Capa de Harry Potter
import cv2 import numpy as np # Captura de video desde la cámara cap = cv2.VideoCapture(0) # Permitir que la cámara se estabilice cv2.waitKey(2000) # Capturar el fondo durante unos segundos ret, background = cap.read() if not ret: print("Error al capturar el fondo.") cap.release() exit() while cap.isOpened(): ret, frame = cap.read() if not ret: break # Convertir el cuadro a espacio de color HSV hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # Definir el rango de color de la tela (verde, en este caso) en HSV lower_green = np.array([80, 40, 40]) upper_green = np.array([145, 255, 255]) # Crear una máscara que detecta el área verde mask = cv2.inRange(hsv, lower_green, upper_green) # Refinar la máscara (puedes ajustar los parámetros para mejorar la detección) # Invertir la máscara para obtener las áreas que no son verdes mask_inv = cv2.bitwise_not(mask) # Aplicar la máscara a la imagen original para mostrar solo las partes no verdes res1 = cv2.bitwise_and(frame, frame, mask=mask_inv) # Aplicar la máscara al fondo para cubrir las partes verdes res2 = cv2.bitwise_and(background, background, mask=mask) # Combinar ambas imágenes final_output = cv2.addWeighted(res1, 1, res2, 1, 0) # Mostrar el resultado final cv2.imshow("Capa de Invisibilidad", final_output) cv2.imshow('mask', mask) # Presionar 'q' para salir if cv2.waitKey(1) & 0xFF == ord('q'): break # Liberar los recursos cap.release() cv2.destroyAllWindows()
Efecto Gris np.where
La función np.where de NumPy es muy útil cuando se quiere seleccionar elementos de un arreglo o imagen basado en una condición. Vamos a desglosar cómo funciona y cómo se aplica en el contexto de imágenes.
Sintaxis básica de np.where:
np.where(condición, valor_si_verdadero, valor_si_falso)
- condición: Una expresión booleana (True/False) que indica qué elementos cumplen la condición.
- valorsiverdadero: El valor que se asigna en las posiciones donde la condición es True.
- valorsifalso: El valor que se asigna en las posiciones donde la condición es False.
- Ejemplo sencillo:
import numpy as np # Creamos un array de ejemplo array = np.array([1, 2, 3, 4, 5]) # Aplicamos np.where para cambiar los valores mayores a 3 por 100 y el resto por 0 resultado = np.where(array > 3, 100, 0) print(resultado) # Resultado: [ 0 0 0 100 100]
En este ejemplo, la condición `array > 3` se cumple para los elementos 4 y 5, por lo que se cambian por 100, mientras que los otros elementos se cambian por 0.
Aplicación en procesamiento de imágenes:
Cuando manipulamos imágenes, cada pixel tiene tres valores (R, G, B). La idea es usar `np.where` para verificar si un pixel cumple con la condición de estar en el rango del color que queremos resaltar (por ejemplo, rojo). Si el pixel está dentro de ese rango, conservamos el valor original del color. Si no, convertimos ese pixel a escala de grises.
- Parte del código relevante:
imagen_colores_resaltados = np.where(mascara_rojo[:, :, None] == 255, imagen, imagen_gris_bgr)
- Desglose:
- `mascararojo[:, :, None] == 255`:
- La máscara es un arreglo 2D con los mismos altos y anchos que la imagen original, donde los valores son 255 para los pixeles que coinciden con el color rojo, y 0 para los que no.
- `[:, :, None]`: Añade una tercera dimensión para que la máscara coincida con la estructura 3D de la imagen original, donde tenemos tres canales (R, G, B).
- La condición `== 255` selecciona los píxeles donde el color rojo fue detectado.
- `imagen`:
- Es la imagen original en color. Se utiliza en las posiciones donde la máscara detecta el color rojo (condición `True`).
- `imagengrisbgr`:
- Es la versión de la imagen en escala de grises, convertida a formato BGR. Se usa en las posiciones donde la condición es `False`, es decir, donde no se detectó el color rojo.
- `mascararojo[:, :, None] == 255`:
- Ejemplo visual:
Imagina que tienes un pixel con los siguientes valores RGB: `(255, 0, 0)` (un rojo intenso). Si este pixel cae dentro del rango de color rojo, entonces `mascararojo` tendrá el valor 255 para ese pixel. Esto hará que `np.where` conserve el pixel original en color:
- Condición True (rojo detectado): Se conserva el pixel original.
- Condición False (no es rojo): El pixel se convierte a su equivalente en escala de grises.
****y el Ejemplo simplificado:
# Si la máscara es: mascara_rojo = [[255, 0], [0, 255]] # La imagen original en color (simplificada): imagen = [[[255, 0, 0], [0, 255, 0]], [[0, 0, 255], [255, 0, 0]]] # La imagen en gris (también simplificada): imagen_gris_bgr = [[[128, 128, 128], [128, 128, 128]], [[128, 128, 128], [128, 128, 128]]] # Aplicando np.where: resultado = np.where(mascara_rojo[:, :, None] == 255, imagen, imagen_gris_bgr) # Resultado esperado: resultado = [[[255, 0, 0], [128, 128, 128]], [[128, 128, 128], [255, 0, 0]]]
En el resultado, los pixeles que coinciden con el color rojo permanecen iguales, mientras que los otros se convierten a escala de grises.
- ¿Cómo funciona esto para toda la imagen?
`np.where` revisa cada pixel de la imagen:
- Si un pixel es rojo (según la máscara), lo toma de la imagen original.
- Si un pixel no es rojo, lo toma de la versión en escala de grises.
- np.where actúa como un filtro condicional que selecciona valores en base a una máscara booleana.
- En este caso, se utiliza para dejar intactos los pixeles que coinciden con el color deseado (rojo) y convertir el resto de la imagen a escala de grises.
import cv2 import numpy as np # Leer la imagen en formato RGB imagen = cv2.imread('man1.jpg', 1) # Convertir la imagen de RGB a HSV imagen_hsv = cv2.cvtColor(imagen, cv2.COLOR_BGR2HSV) # Definir el rango de color rojo en HSV bajo_rojo1 = np.array([0, 40, 40]) alto_rojo1 = np.array([10, 255, 255]) bajo_rojo2 = np.array([160, 40, 40]) alto_rojo2 = np.array([180, 255, 255]) # Crear una máscara para el color rojo mascara_rojo1 = cv2.inRange(imagen_hsv, bajo_rojo1, alto_rojo1) mascara_rojo2 = cv2.inRange(imagen_hsv, bajo_rojo2, alto_rojo2) #mascara_rojo = mascara_rojo1 + mascara_rojo2 mascara_rojo = cv2.add(mascara_rojo1, mascara_rojo2) # Convertir la imagen original a escala de grises imagen_gris = cv2.cvtColor(imagen, cv2.COLOR_BGR2GRAY) # Convertir la imagen gris a un formato BGR para que coincida con la original imagen_gris_bgr = cv2.cvtColor(imagen_gris, cv2.COLOR_GRAY2BGR) # Combinar la imagen en gris con las áreas en rojo resultado = np.where(mascara_rojo[:, :, None] == 255, imagen, imagen_gris_bgr) # Mostrar la imagen final cv2.imshow('Color resaltado', resultado) cv2.imshow('mascara_rojo', mascara_rojo) cv2.imshow('imagen', imagen) cv2.imshow('imagen_gris__bgr', imagen_gris_bgr) cv2.waitKey(0) cv2.destroyAllWindows()
import cv2 as cv import numpy as np cap = cv.VideoCapture(0) while(True): ret, imagen = cap.read() if ret: cv.imshow('video', imagen) imagen_hsv = cv.cvtColor(imagen, cv.COLOR_BGR2HSV) bajo_azul1 = np.array([100, 40, 40]) alto_azul1 = np.array([140, 255, 255]) # Crear una máscara para el color rojo mascara_azul1 = cv.inRange(imagen_hsv, bajo_azul1, alto_azul1) # Convertir la imagen original a escala de grises imagen_gris = cv.cvtColor(imagen, cv.COLOR_BGR2GRAY) # Convertir la imagen gris a un formato BGR para que coincida con la original imagen_gris_bgr = cv.cvtColor(imagen_gris, cv.COLOR_GRAY2BGR) # Combinar la imagen en gris con las áreas en rojo resultado = np.where(mascara_azul1[:, :, None] == 255, imagen, imagen_gris_bgr) # Mostrar la imagen final cv.imshow('Color resaltado', resultado) k =cv.waitKey(1) & 0xFF if k == 27 : break else: break cap.release() cv.destroyAllWindows()
Flujo optico
import numpy as np import cv2 as cv # Iniciar la captura de video desde la cámara cap = cv.VideoCapture(0) # Parámetros para el flujo óptico Lucas-Kanade lk_params = dict(winSize=(15, 15), maxLevel=2, criteria=(cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.03)) # Leer el primer frame de la cámara ret, first_frame = cap.read() prev_gray = cv.cvtColor(first_frame, cv.COLOR_BGR2GRAY) # Posición inicial de la pelotita (un único punto en el centro de la imagen) ball_pos = np.array([[500, 500]], dtype=np.float32) ball_pos = ball_pos[:, np.newaxis, :] while True: # Capturar el siguiente frame ret, frame = cap.read() if not ret: break x, y =frame.shape[:2] frame= cv.flip(frame,1) # Convertir el frame a escala de grises gray_frame = cv.cvtColor(frame, cv.COLOR_BGR2GRAY) # Calcular el flujo óptico para mover la pelotita new_ball_pos, st, err = cv.calcOpticalFlowPyrLK(prev_gray, gray_frame, ball_pos, None, **lk_params) # Si se detecta el nuevo movimiento, actualizar la posición de la pelotita if new_ball_pos is not None: ball_pos = new_ball_pos # Dibujar la pelotita en su nueva posición a, b = ball_pos.ravel() frame = cv.circle(frame, (int(a), int(b)), 20, (0, 255, 0), -1) #ball_pos = np.array([[500, 500]], dtype=np.float32) cv.rectangle(frame, (20,20), (y-20, x-20), (234,43 ,34) ,5) # Mostrar solo una ventana con la pelotita en movimiento cv.imshow('Pelota en movimiento', frame) # Actualizar el frame anterior para el siguiente cálculo prev_gray = gray_frame.copy() # Presionar 'Esc' para salir if cv.waitKey(30) & 0xFF == 27: break # Liberar la captura y destruir todas las ventanas cap.release() cv.destroyAllWindows()
import numpy as np import cv2 as cv # Iniciar la captura de video desde la cámara cap = cv.VideoCapture(0) # Parámetros para el flujo óptico Lucas-Kanade lk_params = dict(winSize=(15, 15), maxLevel=2, criteria=(cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.03)) # Leer el primer frame de la cámara ret, first_frame = cap.read() prev_gray = cv.cvtColor(first_frame, cv.COLOR_BGR2GRAY) # Posición inicial de la pelotita (centro de la imagen) h, w = first_frame.shape[:2] ball_pos = np.array([[w // 2, h // 2]], dtype=np.float32) ball_pos = ball_pos[:, np.newaxis, :] while True: # Capturar el siguiente frame ret, frame = cap.read() if not ret: break # Obtener dimensiones del frame h, w = frame.shape[:2] frame = cv.flip(frame, 1) # Voltear la imagen horizontalmente gray_frame = cv.cvtColor(frame, cv.COLOR_BGR2GRAY) # Calcular el flujo óptico para mover la pelotita new_ball_pos, st, err = cv.calcOpticalFlowPyrLK(prev_gray, gray_frame, ball_pos, None, **lk_params) # Si se detecta el nuevo movimiento, actualizar la posición de la pelotita if new_ball_pos is not None: a, b = new_ball_pos.ravel() # Evitar que la pelota salga de los límites a = max(20, min(a, w - 20)) b = max(20, min(b, h - 20)) ball_pos = np.array([[a, b]], dtype=np.float32)[:, np.newaxis, :] # Dibujar la pelotita en su nueva posición a, b = ball_pos.ravel() frame = cv.circle(frame, (int(a), int(b)), 20, (0, 255, 0), -1) cv.putText(frame, f'({int(a)}, {int(b)})', (int(a), int(b)), cv.FONT_HERSHEY_SIMPLEX, 0.7, (0, 25, 235), 2) # Dibujar un rectángulo alrededor de la pantalla cv.rectangle(frame, (20, 20), (w - 20, h - 20), (234, 43, 34), 5) # Mostrar la ventana con la pelotita en movimiento cv.imshow('Pelota en movimiento', frame) # Actualizar el frame anterior para el siguiente cálculo prev_gray = gray_frame.copy() # Presionar 'Esc' para salir if cv.waitKey(30) & 0xFF == 27: break # Liberar la captura y destruir todas las ventanas cap.release() cv.destroyAllWindows()
import numpy as np import cv2 as cv cap = cv.VideoCapture(0) lkparm =dict(winSize=(15,15), maxLevel=2, criteria=(cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.03)) _, vframe = cap.read() vgris = cv.cvtColor(vframe, cv.COLOR_BGR2GRAY) p0 = np.array([(100,100), (200,100), (300,100), (400,100), (500,100), (100,200), (200,200), (300,200), (400,200), (500,200), (100,300), (200,300), (300,300), (400,300), (500,300), (100,400), (200,400), (300,400), (400,400), (500,400)]) p0 = np.float32(p0[:, np.newaxis, :]) mask = np.zeros_like(vframe) cad ='' while True: _, frame = cap.read() fgris = cv.cvtColor(frame, cv.COLOR_BGR2GRAY) p1, st, err = cv.calcOpticalFlowPyrLK(vgris, fgris, p0, None, **lkparm) gridp = 5 if p1 is None: vgris = cv.cvtColor(vframe, cv.COLOR_BGR2GRAY) # p0 = np.array([(100,100), (200,100), (300,100), (400,100) ]) # p0 = np.float32(p0[:, np.newaxis, :]) # mask = np.zeros_like(vframe) # cv.imshow('ventana', frame) else: bp1 = p1[st ==1] bp0 = p0[st ==1] for i, (nv, vj) in enumerate(zip(bp1, bp0)): a, b = (int(x) for x in nv.ravel()) c, d = (int(x) for x in vj.ravel()) dist = np.linalg.norm(nv.ravel() - vj.ravel()) cv.circle(frame, (c,d), 5, (255,0,0),-1) cv.circle(frame, (a,b), 3, (0,255,0),-1) if i > 0 and i % gridp != 0: prev_a, prev_b = (int(x) for x in bp1[i - 1].ravel()) cv.line(frame, (a, b), (prev_a, prev_b), (0, 255, 0), 1) if i >= gridp: top_a, top_b = (int(x) for x in bp1[i - gridp].ravel()) cv.line(frame, (a, b), (top_a, top_b), (0, 255, 0), 1) cv.imshow('ventana', frame) vgris = fgris.copy() if(cv.waitKey(1) & 0xff) == 27: break cap.release() cv.destroyAllWindows()
Ejemplo de clasificación utilizando Haarcascades
import numpy as np import cv2 as cv rostro = cv.CascadeClassifier('haarcascade_frontalface_alt2.xml') cap = cv.VideoCapture(0) x=y=w=h= 0 count = 0 while True: ret, frame = cap.read() gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY) rostros = rostro.detectMultiScale(gray, 1.3, 5) for(x, y, w, h) in rostros: m1 = int(h/2) n1 = int(w/2) frame = cv.rectangle(frame, (x,y), (x+w, y+h), (0, 255, 0), 2) frame = cv.circle(frame, (x+n1,y+m1), int(w/2) , (255, 0 ,0), 2 ) #img = 180- frame[y:y+h,x:x+w] #count = count + 1 #name = '/home/likcos/imgs/cara'+str(count)+'.jpg' #cv.imwrite(name, frame) cv.imshow('rostros', frame) #cv.imshow('cara', img) k = cv.waitKey(1) if k == 27: break cap.release() cv.destroyAllWindows()
import cv2 as cv rostro = cv.CascadeClassifier('haarcascade_frontalface_alt.xml') cap = cv.VideoCapture(0) while True: ret, img = cap.read() gris = cv.cvtColor(img, cv.COLOR_BGR2GRAY) rostros = rostro.detectMultiScale(gris, 1.3, 5) for(x,y,w,h) in rostros: res = int((w+h)/8) img = cv.rectangle(img, (x,y), (x+w, y+h), (234, 23,23), 5) img = cv.rectangle(img, (x,int(y+h/2)), (x+w, y+h), (0,255,0),5 ) img = cv.circle(img, (x + int(w*0.3), y + int(h*0.4)) , 21, (0, 0, 0), 2 ) img = cv.circle(img, (x + int(w*0.7), y + int(h*0.4)) , 21, (0, 0, 0), 2 ) img = cv.circle(img, (x + int(w*0.3), y + int(h*0.4)) , 20, (255, 255, 255), -1 ) img = cv.circle(img, (x + int(w*0.7), y + int(h*0.4)) , 20, (255, 255, 255), -1 ) img = cv.circle(img, (x + int(w*0.3), y + int(h*0.4)) , 5, (0, 0, 255), -1 ) img = cv.circle(img, (x + int(w*0.7), y + int(h*0.4)) , 5, (0, 0, 255), -1 ) cv.imshow('img', img) if cv.waitKey(1)== ord('q'): break cap.release cv.destroyAllWindows()
import cv2 import numpy as np # Cargar la máscara que deseas agregar (asegúrate de que sea PNG con transparencia) mascara = cv2.imread('cubre3.png', cv2.IMREAD_UNCHANGED) # Cargar PNG con transparencia # Verificar si la imagen tiene un canal alfa if mascara.shape[2] != 4: print("Error: La imagen no tiene canal alfa.") exit() # Cargar el clasificador preentrenado de rostros face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_alt2.xml') # Capturar video desde la cámara video = cv2.VideoCapture(0) # Definir un desplazamiento para mover la máscara desplazamiento_x = -100 # Mover 50 píxeles hacia la derecha desplazamiento_y = 90 # Mover 30 píxeles hacia arriba while True: # Leer cada frame del video ret, frame = video.read() if not ret: break # Convertir el frame a escala de grises frame_gris = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # Detectar los rostros en el frame rostros = face_cascade.detectMultiScale(frame_gris, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)) # Procesar cada rostro detectado for (x, y, w, h) in rostros: # Redimensionar la máscara para que coincida con el tamaño del rostro detectado mascara_redimensionada = cv2.resize(mascara, (w, h)) cv2.rectangle(frame, (x,y), (x+w, y+h), (255, 0,0), 3) # Separar los canales de la máscara: color y alfa (transparencia) mascara_rgb = mascara_redimensionada[:, :, :3] mascara_alpha = mascara_redimensionada[:, :, 3] # Asegurarse de que la máscara alfa sea de tipo uint8 mascara_alpha = cv2.convertScaleAbs(mascara_alpha) # Aplicar el desplazamiento a las coordenadas x e y x_nuevo = x + desplazamiento_x y_nuevo = y + desplazamiento_y # Evitar que la máscara salga del borde de la imagen if x_nuevo < 0: x_nuevo = 0 if y_nuevo < 0: y_nuevo = 0 if x_nuevo + w > frame.shape[1]: x_nuevo = frame.shape[1] - w if y_nuevo + h > frame.shape[0]: y_nuevo = frame.shape[0] - h # Crear una región de interés (ROI) en el frame donde colocaremos la máscara roi = frame[y_nuevo:y_nuevo+h, x_nuevo:x_nuevo+w] # Asegurarse de que la ROI y la máscara tengan el mismo tamaño if roi.shape[:2] == mascara_alpha.shape[:2]: # Invertir la máscara alfa para obtener la parte del rostro donde se aplicará la máscara mascara_alpha_inv = cv2.bitwise_not(mascara_alpha) # Enmascarar la región del rostro en la imagen original fondo = cv2.bitwise_and(roi, roi, mask=mascara_alpha_inv) # Enmascarar la máscara RGB mascara_fg = cv2.bitwise_and(mascara_rgb, mascara_rgb, mask=mascara_alpha) # Combinar el fondo (parte del rostro sin máscara) y la parte con la máscara resultado = cv2.add(fondo, mascara_fg) # Reemplazar la región del rostro con la imagen combinada frame[y_nuevo:y_nuevo+h, x_nuevo:x_nuevo+w] = resultado else: print("Error: El tamaño de la ROI no coincide con la máscara.") # Mostrar el frame con la máscara aplicada cv2.imshow('Video con mascara', frame) # Presionar 'q' para salir del loop if cv2.waitKey(1) & 0xFF == ord('q'): break # Liberar la captura de video y cerrar las ventanas video.release() cv2.destroyAllWindows()
OpenGL
Descripción de la Proyección Isométrica
La proyección isométrica es un tipo de proyección axonométrica que representa un objeto 3D en un espacio 2D sin perspectiva. Esto significa que las líneas paralelas en el espacio 3D siguen siendo paralelas en la proyección 2D, y los objetos mantienen la misma escala independientemente de su distancia. En una proyección isométrica, los tres ejes (x, y, z) aparecen con ángulos de 120° entre sí, creando un efecto visual que se percibe como 3D, aunque solo se trata de una imagen 2D.
Este tipo de proyección es útil en videojuegos, gráficos isométricos y aplicaciones de diseño, ya que proporciona una representación en 3D simple sin distorsión de perspectiva.
Ejemplo de Proyección Isométrica en OpenCV
Este ejemplo en OpenCV muestra cómo proyectar un cubo 3D en 2D utilizando una proyección isométrica. Cada vértice del cubo se proyecta en el plano 2D, y luego se conecta para formar las aristas del cubo.
- Código en Python
import cv2 import numpy as np import math # Dimensiones de la ventana WIDTH, HEIGHT = 800, 600 # Vértices del cubo en coordenadas 3D vertices = np.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] ]) # Conexiones de los vértices para formar las aristas del cubo edges = [ (0, 1), (1, 2), (2, 3), (3, 0), # Base inferior (4, 5), (5, 6), (6, 7), (7, 4), # Base superior (0, 4), (1, 5), (2, 6), (3, 7) # Conexiones entre bases ] def project_isometric(vertex): """Función para proyectar un punto 3D a 2D con proyección isométrica""" x, y, z = vertex x2D = x - z y2D = (x + 2 * y + z) / 2 return int(x2D * 100 + WIDTH / 2), int(-y2D * 100 + HEIGHT / 2) # Crear ventana cv2.namedWindow("Cubo Isométrico") while True: # Crear imagen negra para el fondo frame = np.zeros((HEIGHT, WIDTH, 3), dtype=np.uint8) # Dibujar aristas del cubo for edge in edges: pt1 = project_isometric(vertices[edge[0]]) pt2 = project_isometric(vertices[edge[1]]) cv2.line(frame, pt1, pt2, (255, 255, 255), 2) # Mostrar imagen cv2.imshow("Cubo Isométrico", frame) # Salir si se presiona 'q' if cv2.waitKey(1) & 0xFF == ord('q'): break cv2.destroyAllWindows()
- Explicación del Código
- Definición de Vértices y Aristas:
- `vertices` define los puntos 3D del cubo en coordenadas `(x, y, z)`.
- `edges` define las conexiones entre los vértices para formar las aristas del cubo.
- Función `projectisometric`:
- Convierte cada vértice 3D en coordenadas 2D usando una proyección isométrica. La fórmula aplicada crea un efecto 3D sin perspectiva.
- Bucle de Renderizado:
- Cada fotograma crea una imagen de fondo negro (`frame`) y dibuja las aristas del cubo conectando los puntos proyectados en 2D.
- El bucle continúa hasta que se presiona la tecla `'q'`.
- Definición de Vértices y Aristas:
- Salida Esperada
La ventana mostrará un cubo en perspectiva isométrica. Las aristas se dibujan en blanco y el cubo parece tener profundidad, aunque es solo una representación en 2D.
Este ejemplo simula un efecto 3D mediante una proyección isométrica, útil para aplicaciones de diseño y gráficos 2D en OpenCV.
Opengl Ejemplo
import glfw from OpenGL.GL import glClear, glClearColor, glBegin, GL_QUADS, glEnd, glVertex2f, glColor3f, GL_COLOR_BUFFER_BIT, GL_TRIANGLES def main(): # Inicializar GLFW if not glfw.init(): return # Crear la ventana window = glfw.create_window(500, 500, "OpenGL con GLFW", None, None) if not window: glfw.terminate() return # Hacer el contexto de OpenGL actual glfw.make_context_current(window) # Establecer el color de fondo glClearColor(0.0, 0.0, 0.0, 1.0) # Bucle principal de renderizado while not glfw.window_should_close(window): # Limpiar la pantalla glClear(GL_COLOR_BUFFER_BIT) # Dibujar un triángulo glBegin(GL_TRIANGLES) glColor3f(1.0, 0.0, 0.0) # Rojo glVertex2f(-0.5, -0.5) # Vértice superior izquierdo glColor3f(0.0, 1.0, 0.0) # Rojo glColor3f(0.0, 1.0, 0.0) # Rojo glVertex2f( 0.5, -0.5) # Vértice superior derecho glColor3f(0.0, 0.0, 1.0) # Rojo glVertex2f( -0.5, 0.5) # Vértice inferior derecho glEnd() # Intercambiar buffers y procesar eventos glfw.swap_buffers(window) glfw.poll_events() # Terminar GLFW glfw.terminate() if __name__ == "__main__": main()
import glfw from OpenGL.GL import glClear, glClearColor, glBegin, glEnd, glVertex2f, glColor3f, GL_COLOR_BUFFER_BIT, GL_QUADS, glOrtho def draw_square(): glBegin(GL_QUADS) glColor3f(1.0, 0.0, 0.0) # Color rojo glVertex2f(-0.5, 0.5) # Vértice superior izquierdo glVertex2f( 0.5, 0.5) # Vértice superior derecho glVertex2f( 0.5, -0.5) # Vértice inferior derecho glVertex2f(-0.5, -0.5) # Vértice inferior izquierdo glEnd() def main(): # Inicializar GLFW if not glfw.init(): return # Crear la ventana con un contexto de OpenGL window = glfw.create_window(500, 500, "Cuadrado con GLFW", None, None) if not window: glfw.terminate() return # Hacer que el contexto de OpenGL sea actual para la ventana glfw.make_context_current(window) # Configurar la proyección ortográfica glOrtho(-1, 1, -1, 1, -1, 1) # Configuración para un sistema de coordenadas 2D # Bucle principal while not glfw.window_should_close(window): # Limpiar el buffer de color glClear(GL_COLOR_BUFFER_BIT) # Dibujar el cuadrado draw_square() # Intercambiar buffers y procesar eventos glfw.swap_buffers(window) glfw.poll_events() # Terminar GLFW glfw.terminate() if __name__ == "__main__": main()
Introducción a OpenGL
OpenGL (Open Graphics Library) es una API de gráficos que permite a los desarrolladores crear gráficos en 2D y 3D. Fue desarrollada por Silicon Graphics en 1992 y, desde entonces, se ha utilizado ampliamente en aplicaciones que requieren renderizado en tiempo real, como videojuegos, simulaciones científicas, visualización de datos y entornos de realidad virtual y aumentada.
Características de OpenGL
- Plataforma abierta: OpenGL es multiplataforma y funciona en Windows, macOS, y Linux.
- API de bajo nivel: Proporciona primitivas de gráficos básicas (puntos, líneas, polígonos).
- Hardware acelerado: Aprovecha la GPU para acelerar el renderizado.
- Flexibilidad: Es compatible con diversas aplicaciones, desde gráficos simples en 2D hasta gráficos avanzados en 3D.
Primeros pasos en OpenGL
Para empezar a trabajar con OpenGL, necesitas entender algunos de sus conceptos y configuraciones básicas.
Configuración del Entorno
Para utilizar OpenGL en Python, necesitas instalar las siguientes bibliotecas:
pip install PyOpenGL PyOpenGL_accelerate glfw pip install PyOpenGL glfw PyFreeGLUT
Estructura básica de un programa OpenGL
Un programa básico de OpenGL tiene una estructura general que incluye la configuración de la ventana, la inicialización del contexto de OpenGL, el renderizado de objetos y el bucle principal.
Sistema de Coordenadas en OpenGL
En OpenGL, las coordenadas pasan por una serie de transformaciones para dibujar objetos en pantalla. Aquí están los principales sistemas de coordenadas:
Coordenadas del Objeto
Este es el espacio de coordenadas local de cada objeto. Aquí defines los vértices en relación con el centro del objeto.
Coordenadas del Mundo
Transforman las coordenadas del objeto para situarlo en el mundo virtual.
Coordenadas de Vista
Ajustan la posición del objeto en relación con la cámara u observador.
Coordenadas de Recorte
Aplican la proyección para determinar qué partes del objeto son visibles y cuáles están fuera del campo de visión.
Coordenadas Normalizadas del Dispositivo (NDC)
Las coordenadas se normalizan al rango [-1, 1] en cada eje, para definir la porción visible de la escena.
Coordenadas de Pantalla
Las coordenadas normalizadas se mapean a píxeles en la pantalla según la resolución de la ventana.
Primitivas de Dibujo en OpenGL
OpenGL permite dibujar figuras básicas conocidas como primitivas, entre las que se incluyen:
- GLPOINTS: Puntos individuales.
- GLLINES: Líneas entre pares de vértices.
- GLTRIANGLES: Triángulos entre grupos de tres vértices.
- GLQUADS: Cuadrados entre grupos de cuatro vértices (obsoleto en OpenGL moderno).
Ejemplo de uso de GLTRIANGLES para dibujar un triángulo:
Código en Python con OpenGL y GLUT
Este código utiliza OpenGL y GLUT en Python para dibujar un triángulo. A continuación, se describen en detalle las funciones y líneas de código, así como su propósito y parámetros utilizados.
- Importación de módulos
Se importan los módulos necesarios para trabajar con OpenGL y GLUT en Python.
import sys from OpenGL.GL import * from OpenGL.GLUT import * from OpenGL.GLU import *
- import sys: Se importa el módulo `sys`, que proporciona acceso a algunas variables y funciones específicos del sistema. En este caso, se usa para acceder a los argumentos de la línea de comandos al iniciar el programa.
- from OpenGL.GL import: Se importa todo el módulo `GL` de OpenGL. Este módulo contiene las funciones necesarias para manejar las operaciones gráficas, como la manipulación de la ventana, la configuración de colores, la creación de objetos y más.
- from OpenGL.GLUT import: Se importa el módulo `GLUT` de OpenGL, que es la biblioteca utilizada para crear y gestionar ventanas, manejar la interacción del usuario y ejecutar un bucle de eventos en el sistema. Aquí se utiliza para crear la ventana y controlar la visualización.
- from OpenGL.GLU import: Se importa el módulo `GLU` de OpenGL. GLU (OpenGL Utility Library) ofrece funciones adicionales que simplifican algunas operaciones gráficas complejas, como las proyecciones en perspectiva, que se utilizan en este código.
- Función `init()`
La función `init()` realiza las configuraciones iniciales para la visualización, como establecer el color de fondo, definir la proyección y configurar las matrices.
def init(): glClearColor(0.0, 0.0, 0.0, 1.0) # Establecer color de fondo glMatrixMode(GL_PROJECTION) # Establecer la matriz de proyección glLoadIdentity() # Restablecer la matriz a la identidad gluPerspective(45, 1.0, 0.1, 50.0) # Configuración de perspectiva glMatrixMode(GL_MODELVIEW) # Establecer la matriz de modelo de vista
- `glClearColor(0.0, 0.0, 0.0, 1.0)`: Define el color de fondo de la ventana. Los valores (0.0, 0.0, 0.0) representan el color negro, y el valor 1.0 en el último parámetro significa opacidad total (sin transparencia).
- `glMatrixMode(GLPROJECTION)`: Establece que la matriz activa sea la matriz de proyección, que es la encargada de definir cómo se proyectan los objetos 3D en la pantalla.
- `glLoadIdentity()`: Restablece la matriz activa a la matriz identidad. Este comando es necesario para borrar cualquier transformación previa antes de aplicar nuevas transformaciones.
- `gluPerspective(45, 1.0, 0.1, 50.0)`: Configura una proyección en perspectiva. El primer parámetro (45) es el ángulo de visión en grados. El segundo parámetro (1.0) es la relación de aspecto, que debe ser igual a la relación entre el ancho y la altura de la ventana (en este caso, una ventana cuadrada). El tercer parámetro (0.1) define la distancia mínima (cerca) del plano de corte, y el cuarto parámetro (50.0) define la distancia máxima (lejos) del plano de corte.
- `glMatrixMode(GLMODELVIEW)`: Cambia la matriz activa de nuevo a la de modelo de vista. Esto significa que las siguientes transformaciones (como traslación o rotación de objetos) afectarán a los objetos en el espacio 3D.
- Función `display()`
La función `display()` se encarga de limpiar los buffers y dibujar los objetos en pantalla, en este caso, un triángulo. La función se llama constantemente para actualizar la ventana.
def display(): glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # Limpiar buffers glLoadIdentity() # Restablecer transformaciones previas glTranslatef(0.0, 0.0, -5) # Mover la cámara hacia atrás # Dibujar un triángulo glBegin(GL_TRIANGLES) # Inicia la especificación de los vértices para un triángulo glColor3f(1.0, 0.0, 0.0) # Rojo glVertex3f(-1.0, -1.0, 0.0) # Primer vértice glColor3f(0.0, 1.0, 0.0) # Verde glVertex3f(1.0, -1.0, 0.0) # Segundo vértice glColor3f(0.0, 0.0, 1.0) # Azul glVertex3f(0.0, 1.0, 0.0) # Tercer vértice glEnd() # Finaliza el bloque de dibujo glutSwapBuffers() # Intercambiar buffers
- glClear(GLCOLORBUFFERBIT | GLDEPTHBUFFERBIT): Limpia los buffers de color y profundidad. Esto borra lo que se haya dibujado anteriormente en la ventana, asegurando que cada nueva imagen se dibuje sobre un fondo limpio.
- glLoadIdentity(): Restablece las transformaciones actuales, como traslación, rotación, o escala, para asegurarse de que el triángulo se dibuje en su posición inicial.
- glTranslatef(0.0, 0.0, -5): Aplica una traslación a la cámara. Este comando mueve la vista de la cámara a lo largo del eje Z, alejándola 5 unidades para que el triángulo sea visible en la pantalla.
- glBegin(GLTRIANGLES): Inicia la especificación de los vértices para un objeto en forma de triángulo. Todos los vértices dentro de este bloque serán interpretados como parte de un triángulo.
- glColor3f(1.0, 0.0, 0.0): Establece el color del primer vértice del triángulo a rojo (con valores de 1.0 para rojo y 0.0 para verde y azul).
- glVertex3f(-1.0, -1.0, 0.0): Define las coordenadas (x, y, z) del primer vértice del triángulo.
- glColor3f(0.0, 1.0, 0.0): Establece el color del segundo vértice del triángulo a verde.
- glVertex3f(1.0, -1.0, 0.0): Define las coordenadas del segundo vértice del triángulo.
- glColor3f(0.0, 0.0, 1.0): Establece el color del tercer vértice del triángulo a azul.
- glVertex3f(0.0, 1.0, 0.0): Define las coordenadas del tercer vértice del triángulo.
- glEnd(): Finaliza la especificación del triángulo. Todos los vértices y colores definidos entre `glBegin` y `glEnd` forman un triángulo.
- glutSwapBuffers(): Intercambia los buffers de pantalla en una ventana de doble buffer. Esto garantiza que lo que se dibujó se renderice en la ventana de manera eficiente, evitando parpadeos.
- Función `main()`
La función `main()` inicializa GLUT, configura la ventana y asigna la función de visualización. También lanza el bucle principal de GLUT.
def main(): glutInit(sys.argv) # Inicializar GLUT glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH) # Establecer modo de visualización glutInitWindowSize(800, 600) # Establecer tamaño de la ventana glutCreateWindow("Triángulo con GLUT y Python") # Crear ventana con título init() # Llamar a la función de configuración inicial glutDisplayFunc(display) # Asignar función de visualización glutMainLoop() # Comenzar el bucle principal de GLUT
- glutInit(sys.argv): Inicializa GLUT y permite pasar parámetros desde la línea de comandos.
- glutInitDisplayMode(GLUTDOUBLE | GLUTRGB | GLUTDEPTH): Establece el modo de visualización. Usamos un modo de doble buffer, el modelo RGB para el color y un buffer de profundidad para gestionar la visibilidad de objetos en 3D.
- glutInitWindowSize(800, 600): Define el tamaño de la ventana (800 píxeles de ancho por 600 píxeles de altura).
- glutCreateWindow("Triángulo con GLUT y Python"): Crea una ventana con el título especificado.
- init(): Llama a la función `init()`, que establece las configuraciones iniciales de OpenGL.
- glutDisplayFunc(display): Asocia la función `display()` con el proceso de renderizado de la ventana. Esta función se llamará cada vez que sea necesario actualizar la ventana.
- glutMainLoop(): Inicia el bucle principal de GLUT, que gestiona la ventana, la entrada del usuario y la actualización de la pantalla.
- Ejecución del código
Este bloque final se asegura de que el código se ejecute correctamente cuando se llama al archivo directamente.
if __name__ == "__main__": main() # Llamar a la función principal para ejecutar el programa
Este código completa la configuración para mostrar un triángulo 3D en una ventana utilizando OpenGL y GLUT, con el fondo negro y los colores rojo, verde y azul para los vértices del triángulo.
import sys from OpenGL.GL import * from OpenGL.GLUT import * from OpenGL.GLU import * def init(): glClearColor(0.0, 0.0, 0.0, 1.0) # Establecer color de fondo glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(45, 1.0, 0.1, 50.0) # Configuración de perspectiva glMatrixMode(GL_MODELVIEW) def display(): glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # Limpiar buffers glLoadIdentity() glTranslatef(0.0, 0.0, -5) # Mover la cámara hacia atrás # Dibujar un triángulo glBegin(GL_TRIANGLES) glColor3f(1.0, 0.0, 0.0) # Rojo glVertex3f(-1.0, -1.0, 0.0) glColor3f(0.0, 1.0, 0.0) # Verde glVertex3f(1.0, -1.0, 0.0) glColor3f(0.0, 0.0, 1.0) # Azul glVertex3f(0.0, 1.0, 0.0) glEnd() glutSwapBuffers() # Intercambiar buffers def main(): # Inicializar GLUT glutInit(sys.argv) glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH) glutInitWindowSize(800, 600) glutCreateWindow("Triángulo con GLUT y Python") init() glutDisplayFunc(display) glutMainLoop() if __name__ == "__main__": main()
import glfw from OpenGL.GL import * # Inicializar GLFW if not glfw.init(): raise Exception("No se pudo inicializar GLFW") # Crear ventana window = glfw.create_window(800, 600, "OpenGL Test", None, None) if not window: glfw.terminate() raise Exception("No se pudo crear la ventana GLFW") # Activar el contexto glfw.make_context_current(window) # Obtener versión de OpenGL version = glGetString(GL_VERSION) print("Versión de OpenGL:", version.decode("utf-8")) # Terminar GLFW glfw.terminate()
import glfw from OpenGL.GL import * from OpenGL.GLU import * def draw_triangle(): # Dibuja un triángulo con OpenGL glBegin(GL_TRIANGLES) glColor3f(1.0, 0.0, 0.0) # Rojo glVertex2f(-0.5, -0.5) # Vértice inferior izquierdo glColor3f(0.0, 1.0, 0.0) # Verde glVertex2f(0.5, -0.5) # Vértice inferior derecho glColor3f(0.0, 0.0, 1.0) # Azul glVertex2f(0.0, 0.5) # Vértice superior glEnd() def main(): # Inicializa GLFW if not glfw.init(): return # Crear la ventana window = glfw.create_window(800, 600, "OpenGL Triángulo", None, None) if not window: glfw.terminate() return # Hacer el contexto de OpenGL actual para la ventana glfw.make_context_current(window) # Configurar la proyección (2D simple) glMatrixMode(GL_PROJECTION) glLoadIdentity() glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0) # Proyección ortográfica 2D glMatrixMode(GL_MODELVIEW) # Bucle principal while not glfw.window_should_close(window): glClear(GL_COLOR_BUFFER_BIT) # Limpiar la pantalla con color de fondo draw_triangle() # Dibujar el triángulo glfw.swap_buffers(window) # Intercambiar los buffers glfw.poll_events() # Comprobar eventos # Finalizar GLFW glfw.terminate() if __name__ == "__main__": main()
import glfw from OpenGL.GL import glClearColor, glEnable, glClear, glLoadIdentity, glTranslatef, glRotatef, glMatrixMode from OpenGL.GL import glBegin, glColor3f, glVertex3f, glEnd, glFlush, glViewport from OpenGL.GL import GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT, GL_DEPTH_TEST, GL_QUADS, GL_PROJECTION, GL_MODELVIEW from OpenGL.GLU import gluPerspective import sys # Variables globales window = None angle = 0 # Declaramos angle en el nivel superior def init(): # Configuración inicial de OpenGL glClearColor(0.0, 0.0, 0.0, 1.0) # Color de fondo glEnable(GL_DEPTH_TEST) # Activar prueba de profundidad para 3D # Configuración de proyección glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(45, 1.0, 0.1, 50.0) # Cambiar a la matriz de modelo para los objetos glMatrixMode(GL_MODELVIEW) def draw_cube(): global angle glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # Limpiar pantalla y buffer de profundidad # Configuración de la vista del cubo glLoadIdentity() glTranslatef(0.0, 0.0, -5) # Alejar el cubo para que sea visible glRotatef(angle, 0, 0,1) # Rotar el cubo en todos los ejes glBegin(GL_QUADS) # Iniciar el cubo como un conjunto de caras (quads) # Cada conjunto de cuatro vértices representa una cara del cubo glColor3f(1.0, 0.0, 0.0) # Rojo glVertex3f( 1, 1,-1) glVertex3f(-1, 1,-1) glVertex3f(-1, 1, 1) glVertex3f( 1, 1, 1) glColor3f(0.0, 1.0, 0.0) # Verde glVertex3f( 1,-1, 1) glVertex3f(-1,-1, 1) glVertex3f(-1,-1,-1) glVertex3f( 1,-1,-1) glColor3f(0.0, 0.0, 1.0) # Azul glVertex3f( 1, 1, 1) glVertex3f(-1, 1, 1) glVertex3f(-1,-1, 1) glVertex3f( 1,-1, 1) glColor3f(1.0, 1.0, 0.0) # Amarillo glVertex3f( 1,-1,-1) glVertex3f(-1,-1,-1) glVertex3f(-1, 1,-1) glVertex3f( 1, 1,-1) glColor3f(1.0, 0.0, 1.0) # Magenta glVertex3f(-1, 1, 1) glVertex3f(-1, 1,-1) glVertex3f(-1,-1,-1) glVertex3f(-1,-1, 1) glColor3f(0.0, 1.0, 1.0) # Cyan glVertex3f( 1, 1,-1) glVertex3f( 1, 1, 1) glVertex3f( 1,-1, 1) glVertex3f( 1,-1,-1) glEnd() glFlush() glfw.swap_buffers(window) # Intercambiar buffers para animación suave angle += 1 # Incrementar el ángulo para rotación def main(): global window # Inicializar GLFW if not glfw.init(): sys.exit() # Crear ventana de GLFW width, height = 500, 500 window = glfw.create_window(width, height, "Cubo 3D Rotando con GLFW", None, None) if not window: glfw.terminate() sys.exit() # Configurar el contexto de OpenGL en la ventana glfw.make_context_current(window) # Configuración de viewport y OpenGL glViewport(0, 0, width, height) init() # Bucle principal while not glfw.window_should_close(window): draw_cube() glfw.poll_events() glfw.terminate() # Cerrar GLFW al salir if __name__ == "__main__": main()
import glfw from OpenGL.GL import glClearColor, glEnable, glClear, glLoadIdentity, glTranslatef, glRotatef, glMatrixMode from OpenGL.GL import glBegin, glColor3f, glVertex3f, glEnd, glFlush, glViewport from OpenGL.GL import GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT, GL_DEPTH_TEST, GL_QUADS, GL_PROJECTION, GL_MODELVIEW from OpenGL.GLU import gluPerspective import sys # Variables globales window = None angle_x, angle_y = 0, 0 # Ángulos de rotación en los ejes X e Y last_x, last_y = None, None # Última posición del ratón para calcular la diferencia def init(): # Configuración inicial de OpenGL glClearColor(0.0, 0.0, 0.0, 1.0) # Color de fondo glEnable(GL_DEPTH_TEST) # Activar prueba de profundidad para 3D # Configuración de proyección glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(45, 1.0, 0.1, 50.0) # Cambiar a la matriz de modelo para los objetos glMatrixMode(GL_MODELVIEW) def draw_cube(): global angle_x, angle_y glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # Limpiar pantalla y buffer de profundidad # Configuración de la vista del cubo glLoadIdentity() glTranslatef(0.0, 0.0, -5) # Alejar el cubo para que sea visible glRotatef(angle_x, 1, 0, 0) # Rotar el cubo en el eje X glRotatef(angle_y, 0, 1, 0) # Rotar el cubo en el eje Y glBegin(GL_QUADS) # Iniciar el cubo como un conjunto de caras (quads) # Cada conjunto de cuatro vértices representa una cara del cubo glColor3f(1.0, 0.0, 0.0) # Rojo glVertex3f( 1, 1,-1) glVertex3f(-1, 1,-1) glVertex3f(-1, 1, 1) glVertex3f( 1, 1, 1) glColor3f(0.0, 1.0, 0.0) # Verde glVertex3f( 1,-1, 1) glVertex3f(-1,-1, 1) glVertex3f(-1,-1,-1) glVertex3f( 1,-1,-1) glColor3f(0.0, 0.0, 1.0) # Azul glVertex3f( 1, 1, 1) glVertex3f(-1, 1, 1) glVertex3f(-1,-1, 1) glVertex3f( 1,-1, 1) glColor3f(1.0, 1.0, 0.0) # Amarillo glVertex3f( 1,-1,-1) glVertex3f(-1,-1,-1) glVertex3f(-1, 1,-1) glVertex3f( 1, 1,-1) glColor3f(1.0, 0.0, 1.0) # Magenta glVertex3f(-1, 1, 1) glVertex3f(-1, 1,-1) glVertex3f(-1,-1,-1) glVertex3f(-1,-1, 1) glColor3f(0.0, 1.0, 1.0) # Cyan glVertex3f( 1, 1,-1) glVertex3f( 1, 1, 1) glVertex3f( 1,-1, 1) glVertex3f( 1,-1,-1) glEnd() glFlush() glfw.swap_buffers(window) # Intercambiar buffers para animación suave def mouse_callback(window, xpos, ypos): global angle_x, angle_y, last_x, last_y # Si es la primera vez que movemos el ratón, inicializamos last_x y last_y if last_x is None or last_y is None: last_x, last_y = xpos, ypos # Calcular las diferencias en el movimiento del ratón dx = xpos - last_x dy = ypos - last_y # Ajustar los ángulos de rotación en función del movimiento del ratón angle_x += dy * 0.1 # El factor 0.1 ajusta la sensibilidad angle_y += dx * 0.1 # Actualizar las posiciones anteriores del ratón last_x, last_y = xpos, ypos def main(): global window # Inicializar GLFW if not glfw.init(): sys.exit() # Crear ventana de GLFW width, height = 500, 500 window = glfw.create_window(width, height, "Cubo 3D Controlado por Ratón", None, None) if not window: glfw.terminate() sys.exit() # Configurar el contexto de OpenGL en la ventana glfw.make_context_current(window) glViewport(0, 0, width, height) init() # Configurar el callback de ratón glfw.set_cursor_pos_callback(window, mouse_callback) # Bucle principal while not glfw.window_should_close(window): draw_cube() glfw.poll_events() glfw.terminate() # Cerrar GLFW al salir if __name__ == "__main__": main()
import glfw from OpenGL.GL import * from OpenGL.GLU import gluPerspective import numpy as np import sys # Variables globales para el ángulo de rotación window = None angle_x, angle_y = 0.0, 0.0 # Ángulos de rotación en los ejes X e Y last_x, last_y = None, None # Última posición del ratón para calcular la diferencia # Parámetros del toroide R = 1.0 # Radio mayor (distancia del centro del tubo al centro del toroide) r = 0.4 # Radio menor (radio del tubo) num_major = 30 # Segmentos en el círculo mayor num_minor = 15 # Segmentos en el círculo menor def init(): # Configuración inicial de OpenGL glClearColor(0.0, 0.0, 0.0, 1.0) # Color de fondo glEnable(GL_DEPTH_TEST) # Activar prueba de profundidad para 3D # Configuración de proyección glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(45, 1.0, 0.1, 50.0) # Cambiar a la matriz de modelo para los objetos glMatrixMode(GL_MODELVIEW) def draw_torus(): global angle_x, angle_y glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # Limpiar pantalla y buffer de profundidad # Configuración de la vista del toroide glLoadIdentity() glTranslatef(0.0, 0.0, -5) # Alejar el toroide para que sea visible glRotatef(angle_x, 1, 0, 0) # Rotar el toroide en el eje X glRotatef(angle_y, 0, 1, 0) # Rotar el toroide en el eje Y # Dibujar el toroide utilizando segmentos for i in range(num_major): theta = 2 * np.pi * i / num_major next_theta = 2 * np.pi * (i + 1) / num_major glBegin(GL_QUAD_STRIP) for j in range(num_minor + 1): phi = 2 * np.pi * j / num_minor cos_theta, sin_theta = np.cos(theta), np.sin(theta) cos_next_theta, sin_next_theta = np.cos(next_theta), np.sin(next_theta) cos_phi, sin_phi = np.cos(phi), np.sin(phi) # Color dinámico para variación visual glColor3f((i % 2) * 0.5 + 0.5, (j % 2) * 0.5 + 0.5, 0.5) # Primera esquina x = (R + r * cos_phi) * cos_theta y = r * sin_phi z = (R + r * cos_phi) * sin_theta glVertex3f(x, y, z) # Segunda esquina x = (R + r * cos_phi) * cos_next_theta z = (R + r * cos_phi) * sin_next_theta glVertex3f(x, y, z) glEnd() glfw.swap_buffers(window) # Intercambiar buffers para animación suave def mouse_callback(window, xpos, ypos): global angle_x, angle_y, last_x, last_y # Si es la primera vez que movemos el ratón, inicializamos last_x y last_y if last_x is None or last_y is None: last_x, last_y = xpos, ypos # Calcular las diferencias en el movimiento del ratón dx = xpos - last_x dy = ypos - last_y # Ajustar los ángulos de rotación en función del movimiento del ratón angle_x += dy * 0.2 # Factor de sensibilidad en Y angle_y += dx * 0.2 # Factor de sensibilidad en X # Actualizar las posiciones anteriores del ratón last_x, last_y = xpos, ypos def main(): global window # Inicializar GLFW if not glfw.init(): sys.exit() # Crear ventana de GLFW width, height = 500, 500 window = glfw.create_window(width, height, "Toroide 3D Controlado por Ratón", None, None) if not window: glfw.terminate() sys.exit() # Configurar el contexto de OpenGL en la ventana glfw.make_context_current(window) glViewport(0, 0, width, height) init() # Configurar el callback de ratón glfw.set_cursor_pos_callback(window, mouse_callback) # Bucle principal while not glfw.window_should_close(window): draw_torus() glfw.poll_events() glfw.terminate() # Cerrar GLFW al salir if __name__ == "__main__": main()
import glfw from OpenGL.GL import * from OpenGL.GLU import gluNewQuadric, gluSphere, gluPerspective import sys import math # Variables globales para el ángulo de rotación y posición de la esfera window = None rotation_angle = 0.0 # Ángulo de rotación de la esfera movement_offset = 0.0 # Offset para el movimiento de vaivén movement_speed = 0.05 # Velocidad del movimiento de vaivén movement_direction = 1 # Dirección del movimiento (1 a la derecha, -1 a la izquierda) def init(): glClearColor(0.5, 0.8, 1.0, 1.0) # Fondo negro glEnable(GL_DEPTH_TEST) # Activar prueba de profundidad glEnable(GL_LIGHTING) # Activar iluminación glEnable(GL_LIGHT0) # Activar la luz 0 # Configuración de la perspectiva glMatrixMode(GL_PROJECTION) gluPerspective(45, 1.0, 0.1, 50.0) glMatrixMode(GL_MODELVIEW) # Configuración de la luz light_pos = [1.0, 1.0, 1.0, 0.0] # Posición de la luz light_color = [1.0, 1.0, 1.0, 1.0] # Color de la luz blanca ambient_light = [0.2, 0.2, 0.2, 1.0] # Luz ambiental glLightfv(GL_LIGHT0, GL_POSITION, light_pos) glLightfv(GL_LIGHT0, GL_DIFFUSE, light_color) glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_light) # Configuración de las propiedades de material material_diffuse = [1, 0.2, 1.0, 0.0] # Color difuso (azul claro) glMaterialfv(GL_FRONT, GL_DIFFUSE, material_diffuse) def draw_sphere(radius=1, slices=32, stacks=32): global rotation_angle, movement_offset glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() glTranslatef(movement_offset, 0.0, -50) # Mover la esfera en vaivén glRotatef(rotation_angle, 0, 1, 0) # Rotar la esfera sobre su eje Y quadric = gluNewQuadric() gluSphere(quadric, radius, slices, stacks) # Dibujar la esfera glfw.swap_buffers(window) def update_motion(): global rotation_angle, movement_offset, movement_direction # Actualizar el ángulo de rotación rotation_angle += 1 if rotation_angle >= 360: rotation_angle = 0 # Reiniciar el ángulo después de una vuelta completa # Actualizar el movimiento de vaivén movement_offset += movement_speed * movement_direction if movement_offset > 3.0: # Limite derecho movement_direction = -1 # Cambiar dirección hacia la izquierda elif movement_offset < -3.0: # Limite izquierdo movement_direction = 1 # Cambiar dirección hacia la derecha def main(): global window # Inicializar GLFW if not glfw.init(): sys.exit() # Crear ventana de GLFW width, height = 500, 500 window = glfw.create_window(width, height, "Esfera en Movimiento y Rotación", None, None) if not window: glfw.terminate() sys.exit() glfw.make_context_current(window) glViewport(0, 0, width, height) init() # Bucle principal while not glfw.window_should_close(window): draw_sphere() update_motion() # Actualizar el movimiento y rotación glfw.poll_events() glfw.terminate() if __name__ == "__main__": main()
import glfw from OpenGL.GL import * from OpenGL.GLU import gluNewQuadric, gluSphere, gluPerspective, gluCylinder import sys def init(): glClearColor(0.5, 0.7, 1.0, 1.0) # Fondo de cielo glEnable(GL_DEPTH_TEST) # Configuración de la perspectiva glMatrixMode(GL_PROJECTION) gluPerspective(45, 1.0, 0.1, 50.0) glMatrixMode(GL_MODELVIEW) def draw_sphere(radius=1, x=0, y=0, z=0): glPushMatrix() glTranslatef(x, y, z) quadric = gluNewQuadric() gluSphere(quadric, radius, 32, 32) glPopMatrix() def draw_cone(base=0.1, height=0.5, x=0, y=0, z=0): glPushMatrix() glTranslatef(x, y, z) glRotatef(-90, 1, 0, 0) # Orientar el cono hacia adelante quadric = gluNewQuadric() gluCylinder(quadric, base, 0, height, 32, 32) glPopMatrix() def draw_snowman(): glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() glTranslatef(0.0, -1.5, -8) # Alejar al muñeco de nieve # Cuerpo glColor3f(1, 1, 1) draw_sphere(1.0, 0, 0, 0) # Base draw_sphere(0.75, 0, 1.2, 0) # Cuerpo medio draw_sphere(0.5, 0, 2.2, 0) # Cabeza # Ojos glColor3f(0, 0, 0) draw_sphere(0.05, -0.15, 2.3, 0.4) # Ojo izquierdo draw_sphere(0.05, 0.15, 2.3, 0.4) # Ojo derecho # Nariz (cono) glColor3f(1, 0.5, 0) # Color naranja draw_cone(0.05, 0.2, 0, 2.2, 0.5) # Nariz glfw.swap_buffers(window) def main(): if not glfw.init(): sys.exit() global window window = glfw.create_window(500, 500, "Muñeco de Nieve en OpenGL", None, None) if not window: glfw.terminate() sys.exit() glfw.make_context_current(window) init() while not glfw.window_should_close(window): draw_snowman() glfw.poll_events() glfw.terminate() if __name__ == "__main__": main()
import glfw from OpenGL.GL import * from OpenGL.GLU import gluNewQuadric, gluSphere, gluPerspective, gluCylinder import sys import math # Variables globales window = None jump_offset = 0.0 # Para el movimiento de salto vertical jump_speed = 0.05 # Velocidad del salto jump_direction = 1 # Dirección del salto (1 hacia arriba, -1 hacia abajo) rotation_angle = 0.0 # Ángulo de rotación del muñeco de nieve def init(): glClearColor(0.5, 0.7, 1.0, 1.0) # Fondo de cielo glEnable(GL_DEPTH_TEST) # Configuración de la perspectiva glMatrixMode(GL_PROJECTION) gluPerspective(45, 1.0, 0.1, 50.0) glMatrixMode(GL_MODELVIEW) def draw_sphere(radius=1, x=0, y=0, z=0): glPushMatrix() glTranslatef(x, y, z) quadric = gluNewQuadric() gluSphere(quadric, radius, 32, 32) glPopMatrix() def draw_cone(base=0.1, height=0.5, x=0, y=0, z=0): glPushMatrix() glTranslatef(x, y, z) glRotatef(-90, 1, 0, 0) # Orientar el cono hacia adelante quadric = gluNewQuadric() gluCylinder(quadric, base, 0, height, 32, 32) glPopMatrix() def draw_snowman(): global jump_offset, rotation_angle # Limpiar la pantalla y la profundidad glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() # Configurar posición de la cámara glTranslatef(0.0 + jump_offset, -1.5 , -8) # Posición del muñeco de nieve y altura glRotatef(rotation_angle, 0, 0, 1) # Rotación en el eje Y # Cuerpo glColor3f(1, 1, 1) draw_sphere(1.0, 0, 0, 0) # Base draw_sphere(0.75, 0, 1.2, 0) # Cuerpo medio draw_sphere(0.5, 0, 2.2, 0) # Cabeza # Ojos glColor3f(0, 0, 0) draw_sphere(0.05, -0.15, 2.3, 0.4) # Ojo izquierdo draw_sphere(0.05, 0.15, 2.3, 0.4) # Ojo derecho # Nariz (cono) glColor3f(1, 0.5, 0) # Color naranja draw_cone(0.05, 0.2, 0, 2.2, 0.5) # Nariz glfw.swap_buffers(window) def update_motion(): global jump_offset, jump_direction, rotation_angle # Actualizar el ángulo de rotación rotation_angle += 1 # Incrementa para que el muñeco gire if rotation_angle >= 360: rotation_angle = 0 # Reiniciar el ángulo después de una vuelta completa # Actualizar el movimiento de salto jump_offset += jump_speed * jump_direction if jump_offset > 1.0: # Limite superior del salto jump_direction = -1 # Cambiar dirección hacia abajo elif jump_offset < 0.0: # Limite inferior del salto jump_direction = 1 # Cambiar dirección hacia arriba def main(): global window # Inicializar GLFW if not glfw.init(): sys.exit() # Crear ventana de GLFW width, height = 500, 500 window = glfw.create_window(width, height, "Muñeco de Nieve en Movimiento", None, None) if not window: glfw.terminate() sys.exit() glfw.make_context_current(window) glViewport(0, 0, width, height) init() # Bucle principal while not glfw.window_should_close(window): draw_snowman() update_motion() # Actualizar el movimiento en cada cuadro glfw.poll_events() glfw.terminate() if __name__ == "__main__": main()
Tutorial de gluPerspective en OpenGL
Este tutorial describe cómo utilizar la función gluPerspective
en
OpenGL para definir la proyección en perspectiva en una escena 3D. La
proyección en perspectiva simula cómo los humanos ven el mundo, con
objetos cercanos que se ven más grandes y objetos lejanos que se ven
más pequeños.
¿Qué es gluPerspective?
La función gluPerspective
establece una proyección en perspectiva para
la cámara, creando una "pirámide de visión" o "frustum" en la que los
objetos más lejanos se ven más pequeños, proporcionando una sensación
de profundidad en la escena.
Sintaxis
gluPerspective(fovY, aspect, zNear, zFar)
fovY
: El campo de visión vertical en grados. Un valor mayor defovY
hace que la vista sea más amplia.aspect
: La relación de aspecto de la ventana (ancho/alto). Asegura que la imagen no se vea estirada.zNear
: La distancia mínima visible desde la cámara. Los objetos más cercanos a la cámara quezNear
no se renderizan.zFar
: La distancia máxima visible desde la cámara. Los objetos más lejanos quezFar
no se renderizan.
Ejemplo Básico de Uso de gluPerspective
Este ejemplo muestra cómo configurar gluPerspective
en una escena de OpenGL en Python, utilizando PyOpenGL y GLFW.
import glfw from OpenGL.GL import * from OpenGL.GLU import gluPerspective def init(): glClearColor(0.0, 0.0, 0.0, 1.0) # Fondo negro glEnable(GL_DEPTH_TEST) # Activar prueba de profundidad # Configuración de la proyección en perspectiva glMatrixMode(GL_PROJECTION) # Cambiar a la matriz de proyección glLoadIdentity() # Resetear la matriz de proyección gluPerspective(45, 4/3, 0.1, 50.0) # Configurar perspectiva glMatrixMode(GL_MODELVIEW) # Cambiar a la matriz de modelo/vista def draw(): glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() glTranslatef(0.0, 0.0, -5) # Alejar el objeto de la cámara glBegin(GL_QUADS) glColor3f(1, 0, 0) # Rojo glVertex3f(1, 1, -1) glVertex3f(-1, 1, -1) glVertex3f(-1, -1, -1) glVertex3f(1, -1, -1) glEnd() glfw.swap_buffers(window) def main(): global window if not glfw.init(): return window = glfw.create_window(800, 600, "Ejemplo de gluPerspective", None, None) if not window: glfw.terminate() return glfw.make_context_current(window) glViewport(0, 0, 800, 600) init() while not glfw.window_should_close(window): draw() glfw.poll_events() glfw.terminate() if __name__ == "__main__": main()
Objetos de Glut
- casa
import glfw from OpenGL.GL import * from OpenGL.GLU import gluPerspective, gluLookAt import sys def init(): """Configuración inicial de OpenGL""" glClearColor(0.5, 0.8, 1.0, 1.0) # Fondo azul cielo glEnable(GL_DEPTH_TEST) # Activar prueba de profundidad # Configuración de la perspectiva glMatrixMode(GL_PROJECTION) gluPerspective(60, 1.0, 0.1, 100.0) # Campo de visión más amplio glMatrixMode(GL_MODELVIEW) def draw_cube(): """Dibuja el cubo (base de la casa)""" glBegin(GL_QUADS) glColor3f(0.8, 0.5, 0.2) # Marrón para todas las caras # Frente glVertex3f(-1, 0, 1) glVertex3f(1, 0, 1) glVertex3f(1, 1, 1) glVertex3f(-1, 1, 1) # Atrás glVertex3f(-1, 0, -1) glVertex3f(1, 0, -1) glVertex3f(1, 1, -1) glVertex3f(-1, 1, -1) # Izquierda glVertex3f(-1, 0, -1) glVertex3f(-1, 0, 1) glVertex3f(-1, 1, 1) glVertex3f(-1, 1, -1) # Derecha glVertex3f(1, 0, -1) glVertex3f(1, 0, 1) glVertex3f(1, 1, 1) glVertex3f(1, 1, -1) # Arriba glColor3f(0.9, 0.6, 0.3) # Color diferente para el techo glVertex3f(-1, 1, -1) glVertex3f(1, 1, -1) glVertex3f(1, 1, 1) glVertex3f(-1, 1, 1) # Abajo glColor3f(0.6, 0.4, 0.2) # Suelo más oscuro glVertex3f(-1, 0, -1) glVertex3f(1, 0, -1) glVertex3f(1, 0, 1) glVertex3f(-1, 0, 1) glEnd() def draw_roof(): """Dibuja el techo (pirámide)""" glBegin(GL_TRIANGLES) glColor3f(0.9, 0.1, 0.1) # Rojo brillante # Frente glVertex3f(-1, 1, 1) glVertex3f(1, 1, 1) glVertex3f(0, 2, 0) # Atrás glVertex3f(-1, 1, -1) glVertex3f(1, 1, -1) glVertex3f(0, 2, 0) # Izquierda glVertex3f(-1, 1, -1) glVertex3f(-1, 1, 1) glVertex3f(0, 2, 0) # Derecha glVertex3f(1, 1, -1) glVertex3f(1, 1, 1) glVertex3f(0, 2, 0) glEnd() def draw_ground(): """Dibuja un plano para representar el suelo o calle""" glBegin(GL_QUADS) glColor3f(0.3, 0.3, 0.3) # Gris oscuro para la calle # Coordenadas del plano glVertex3f(-10, 0, 10) glVertex3f(10, 0, 10) glVertex3f(10, 0, -10) glVertex3f(-10, 0, -10) glEnd() def draw_house(): """Dibuja una casa sobre un plano""" glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() # Configuración de la cámara gluLookAt(4, 3, 8, # Posición de la cámara 0, 1, 0, # Punto al que mira 0, 1, 0) # Vector hacia arriba draw_ground() # Dibuja el suelo draw_cube() # Dibuja la base de la casa draw_roof() # Dibuja el techo glfw.swap_buffers(window) def main(): global window # Inicializar GLFW if not glfw.init(): sys.exit() # Crear ventana de GLFW width, height = 800, 600 window = glfw.create_window(width, height, "Casa 3D con Base", None, None) if not window: glfw.terminate() sys.exit() glfw.make_context_current(window) glViewport(0, 0, width, height) init() # Bucle principal while not glfw.window_should_close(window): draw_house() glfw.poll_events() glfw.terminate() if __name__ == "__main__": main()
- casas
import glfw from OpenGL.GL import * from OpenGL.GLU import gluPerspective, gluLookAt import sys def init(): """Configuración inicial de OpenGL""" glClearColor(0.5, 0.8, 1.0, 1.0) # Fondo azul cielo glEnable(GL_DEPTH_TEST) # Activar prueba de profundidad # Configuración de la perspectiva glMatrixMode(GL_PROJECTION) gluPerspective(60, 1, 6, 100.0) # Campo de visión más amplio glMatrixMode(GL_MODELVIEW) def draw_cube(): """Dibuja el cubo (base de la casa)""" glBegin(GL_QUADS) glColor3f(0.8, 0.5, 0.2) # Marrón para todas las caras # Frente glVertex3f(-1, 0, 1) glVertex3f(1, 0, 1) glVertex3f(1, 5, 1) glVertex3f(-1, 5, 1) # Atrás X Y Z glVertex3f(-1, 0, -1) glVertex3f(1, 0, -1) glVertex3f(1, 5, -1) glVertex3f(-1, 5, -1) # Izquierda glVertex3f(-1, 0, -1) glVertex3f(-1, 0, 1) glVertex3f(-1, 5, 1) glVertex3f(-1, 5, -1) # Derecha glVertex3f(1, 0, -1) glVertex3f(1, 0, 1) glVertex3f(1, 5, 1) glVertex3f(1, 5, -1) # Arriba glColor3f(0.9, 0.6, 0.3) # Color diferente para el techo glVertex3f(-1, 5, -1) glVertex3f(1, 5, -1) glVertex3f(1, 5, 1) glVertex3f(-1, 5, 1) # Abajo glColor3f(0.6, 0.4, 0.2) # Suelo más oscuro glVertex3f(-1,0 , -1) glVertex3f(1, 0, -1) glVertex3f(1, 0, 1) glVertex3f(-1, 0, 1) glEnd() def draw_roof(): """Dibuja el techo (pirámide)""" glBegin(GL_TRIANGLES) glColor3f(0.9, 0.1, 0.1) # Rojo brillante # Frente glVertex3f(-1, 5, 1) glVertex3f(1, 5, 1) glVertex3f(0, 9, 0) # Atrás x y z glVertex3f(-1, 5, -1) glVertex3f(1, 5, -1) glVertex3f(0, 9, 0) # Izquierda glVertex3f(-1, 5, -1) glVertex3f(-1, 5, 1) glVertex3f(0, 9, 0) # Derecha glVertex3f(1, 5, -1) glVertex3f(1, 5, 1) glVertex3f(0, 9, 0) glEnd() def draw_ground(): """Dibuja un plano para representar el suelo o calle""" glBegin(GL_QUADS) glColor3f(0.3, 0.3, 0.3) # Gris oscuro para la calle # Coordenadas del plano glVertex3f(-20, 0, 20) glVertex3f(20, 0, 20) glVertex3f(20, 0, -20) glVertex3f(-20, 0, -20) glEnd() def draw_house(): """Dibuja una casa (base + techo)""" draw_cube() # Base de la casa draw_roof() # Techo def draw_scene(): """Dibuja toda la escena con 4 casas""" glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() # Configuración de la cámara gluLookAt(10, 8, 15, # Posición de la cámara 0, 0, 0, # Punto al que mira 0, 1, 0) # Vector hacia arriba # Dibujar el suelo draw_ground() # Dibujar las casas en diferentes posiciones positions = [ (-5, 0, -5), # Casa 1 (5, 0, -5), # Casa 2 (-5, 0, 5), # Casa 3 (5, 0, 5), (0, 0, 0), ] for pos in positions: glPushMatrix() glTranslatef(*pos) # Mover la casa a la posición actual draw_house() # Dibujar la casa glPopMatrix() glfw.swap_buffers(window) def main(): global window # Inicializar GLFW if not glfw.init(): sys.exit() # Crear ventana de GLFW width, height = 800, 600 window = glfw.create_window(width, height, "Escena con 4 casas", None, None) if not window: glfw.terminate() sys.exit() glfw.make_context_current(window) glViewport(0, 0, width, height) init() # Bucle principal while not glfw.window_should_close(window): draw_scene() glfw.poll_events() glfw.terminate() if __name__ == "__main__": main()
- Arbol
import glfw from OpenGL.GL import * from OpenGL.GLU import gluPerspective, gluLookAt, gluNewQuadric, gluCylinder, gluSphere import sys def init(): """Configuración inicial de OpenGL""" glClearColor(0.5, 0.8, 1.0, 1.0) # Fondo azul cielo glEnable(GL_DEPTH_TEST) # Activar prueba de profundidad # Configuración de la perspectiva glMatrixMode(GL_PROJECTION) gluPerspective(60, 1.0, 0.1, 100.0) # Campo de visión más amplio glMatrixMode(GL_MODELVIEW) def draw_trunk(): """Dibuja el tronco del árbol como un cilindro""" glPushMatrix() glColor3f(0.6, 0.3, 0.1) # Marrón para el tronco glTranslatef(0.0, 0.0, 0.0) # Posicionar el tronco glRotatef(-90, 1, 0, 0) # Rota para orientar el cilindro verticalmente quadric = gluNewQuadric() gluCylinder(quadric, 0.3, 0.3, 2.0, 32, 32) # Radio y altura del cilindro glPopMatrix() def draw_foliage(): """Dibuja las hojas del árbol como una esfera""" glPushMatrix() glColor3f(0.1, 0.8, 0.1) # Verde para las hojas glTranslatef(0.0, 2.0, 0.0) # Posicionar las hojas encima del tronco quadric = gluNewQuadric() gluSphere(quadric, 1.0, 32, 32) # Radio de la esfera glPopMatrix() def draw_ground(): """Dibuja un plano para representar el suelo""" glBegin(GL_QUADS) glColor3f(0.3, 0.3, 0.3) # Gris oscuro para el suelo glVertex3f(-10, 0, 10) glVertex3f(10, 0, 10) glVertex3f(10, 0, -10) glVertex3f(-10, 0, -10) glEnd() def draw_tree(): """Dibuja un árbol completo""" glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() # Configuración de la cámara gluLookAt(4, 3, 8, # Posición de la cámara 0, 1, 0, # Punto al que mira 0, 1, 0) # Vector hacia arriba draw_ground() # Dibuja el suelo draw_trunk() # Dibuja el tronco draw_foliage() # Dibuja las hojas glfw.swap_buffers(window) def main(): global window # Inicializar GLFW if not glfw.init(): sys.exit() # Crear ventana de GLFW width, height = 800, 600 window = glfw.create_window(width, height, "Árbol 3D con Tronco y Hojas", None, None) if not window: glfw.terminate() sys.exit() glfw.make_context_current(window) glViewport(0, 0, width, height) init() # Bucle principal while not glfw.window_should_close(window): draw_tree() glfw.poll_events() glfw.terminate() if __name__ == "__main__": main()
import glfw from OpenGL.GL import * import sys # Variables globales para controlar la posición del cuadrado square_x = 0.0 square_y = 0.0 speed = 0.05 # Velocidad de movimiento def init(): """Configuración inicial de OpenGL""" glClearColor(0.0, 0.0, 0.0, 1.0) # Fondo negro glMatrixMode(GL_PROJECTION) glLoadIdentity() glOrtho(-1, 1, -1, 1, -1, 1) # Configurar un sistema de coordenadas 2D def draw_square(): """Dibuja un cuadrado en la posición actual""" global square_x, square_y glClear(GL_COLOR_BUFFER_BIT) glColor3f(0.0, 1.0, 0.0) # Color verde glPushMatrix() glTranslatef(square_x, square_y, 0.0) # Trasladar el cuadrado glBegin(GL_QUADS) glVertex2f(-0.1, -0.1) glVertex2f(0.1, -0.1) glVertex2f(0.1, 0.1) glVertex2f(-0.1, 0.1) glEnd() glPopMatrix() glfw.swap_buffers(window) def key_callback(window, key, scancode, action, mods): """Procesa las entradas de teclado""" global square_x, square_y, speed if action == glfw.PRESS or action == glfw.REPEAT: if key == glfw.KEY_UP: square_y += speed # Mover hacia arriba elif key == glfw.KEY_DOWN: square_y -= speed # Mover hacia abajo elif key == glfw.KEY_LEFT: square_x -= speed # Mover a la izquierda elif key == glfw.KEY_RIGHT: square_x += speed # Mover a la derecha def main(): global window # Inicializar GLFW if not glfw.init(): print("No se pudo inicializar GLFW") sys.exit() # Crear ventana window = glfw.create_window(800, 600, "Prueba de Teclado", None, None) if not window: glfw.terminate() print("No se pudo crear la ventana") sys.exit() glfw.make_context_current(window) glfw.set_key_callback(window, key_callback) # Configurar callback de teclado init() # Bucle principal while not glfw.window_should_close(window): draw_square() glfw.poll_events() glfw.terminate() if __name__ == "__main__": main()
import glfw from OpenGL.GL import * from OpenGL.GLU import gluPerspective, gluLookAt, gluNewQuadric, gluCylinder, gluSphere import sys # Variables globales para la cámara camera_pos = [4.0, 3.0, 8.0] # Posición de la cámara camera_target = [0.0, 1.0, 0.0] # Punto al que mira camera_up = [0.0, 1.0, 0.0] # Vector hacia arriba # Variables para el movimiento camera_speed = 0.2 # Velocidad de movimiento keys = {} # Diccionario para controlar el estado de las teclas def init(): """Configuración inicial de OpenGL""" glClearColor(0.5, 0.8, 1.0, 1.0) # Fondo azul cielo glEnable(GL_DEPTH_TEST) # Activar prueba de profundidad # Configuración de la perspectiva glMatrixMode(GL_PROJECTION) gluPerspective(60, 1.0, 0.1, 100.0) # Campo de visión más amplio glMatrixMode(GL_MODELVIEW) def draw_trunk(): """Dibuja el tronco del árbol como un cilindro""" glPushMatrix() glColor3f(0.6, 0.3, 0.1) # Marrón para el tronco glTranslatef(0.0, 0.0, 0.0) # Posicionar el tronco glRotatef(-90, 1, 0, 0) # Rota para orientar el cilindro verticalmente quadric = gluNewQuadric() gluCylinder(quadric, 0.3, 0.3, 2.0, 32, 32) # Radio y altura del cilindro glPopMatrix() def draw_foliage(): """Dibuja las hojas del árbol como una esfera""" glPushMatrix() glColor3f(0.1, 0.8, 0.1) # Verde para las hojas glTranslatef(0.0, 2.0, 0.0) # Posicionar las hojas encima del tronco quadric = gluNewQuadric() gluSphere(quadric, 1.0, 32, 32) # Radio de la esfera glPopMatrix() def draw_ground(): """Dibuja un plano para representar el suelo""" glBegin(GL_QUADS) glColor3f(0.3, 0.3, 0.3) # Gris oscuro para el suelo glVertex3f(-10, 0, 10) glVertex3f(10, 0, 10) glVertex3f(10, 0, -10) glVertex3f(-10, 0, -10) glEnd() def draw_scene(): """Dibuja la escena completa""" glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() # Configuración de la cámara gluLookAt(camera_pos[0], camera_pos[1], camera_pos[2], # Posición de la cámara camera_target[0], camera_target[1], camera_target[2], # Punto al que mira camera_up[0], camera_up[1], camera_up[2]) # Vector hacia arriba draw_ground() # Dibuja el suelo draw_trunk() # Dibuja el tronco draw_foliage() # Dibuja las hojas glfw.swap_buffers(window) def process_input(): """Procesa el estado de las teclas para mover la cámara""" global camera_pos if keys.get(glfw.KEY_W, False): # Mover hacia adelante camera_pos[2] -= camera_speed if keys.get(glfw.KEY_S, False): # Mover hacia atrás camera_pos[2] += camera_speed if keys.get(glfw.KEY_A, False): # Mover a la izquierda camera_pos[0] -= camera_speed if keys.get(glfw.KEY_D, False): # Mover a la derecha camera_pos[0] += camera_speed if keys.get(glfw.KEY_UP, False): # Subir camera_pos[1] += camera_speed if keys.get(glfw.KEY_DOWN, False): # Bajar camera_pos[1] -= camera_speed def key_callback(window, key, scancode, action, mods): """Actualiza el estado de las teclas""" if action == glfw.PRESS: keys[key] = True elif action == glfw.RELEASE: keys[key] = False def main(): global window # Inicializar GLFW if not glfw.init(): sys.exit() # Crear ventana de GLFW width, height = 800, 600 window = glfw.create_window(width, height, "Mover Escena Completa", None, None) if not window: glfw.terminate() sys.exit() glfw.make_context_current(window) glViewport(0, 0, width, height) init() # Configurar callback de teclado glfw.set_key_callback(window, key_callback) # Bucle principal while not glfw.window_should_close(window): process_input() # Procesar teclas presionadas draw_scene() glfw.poll_events() glfw.terminate() if __name__ == "__main__": main()
import glfw from OpenGL.GL import * from OpenGL.GLU import gluPerspective, gluLookAt, gluNewQuadric, gluCylinder, gluSphere from PIL import Image import sys def init(): """Configuración inicial de OpenGL""" glClearColor(0.5, 0.8, 1.0, 1.0) # Fondo azul cielo glEnable(GL_DEPTH_TEST) # Activar prueba de profundidad glEnable(GL_LIGHTING) # Activar luces glEnable(GL_LIGHT0) # Luz básica glEnable(GL_COLOR_MATERIAL) # Materiales de color para reflejar luz glShadeModel(GL_SMOOTH) # Sombreado suave glEnable(GL_TEXTURE_2D) # Activar texturas # Configuración de la perspectiva glMatrixMode(GL_PROJECTION) gluPerspective(60, 1.0, 0.1, 100.0) # Campo de visión más amplio glMatrixMode(GL_MODELVIEW) # Luz ambiental y difusa light_pos = [10, 10, 10, 1.0] # Posición de la luz light_ambient = [0.3, 0.3, 0.3, 1.0] light_diffuse = [0.8, 0.8, 0.8, 1.0] glLightfv(GL_LIGHT0, GL_POSITION, light_pos) glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient) glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse) def load_texture(filename): """Carga una textura desde un archivo de imagen""" img = Image.open(filename) img_data = img.tobytes("raw", "RGB", 0, -1) texture_id = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, texture_id) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img.width, img.height, 0, GL_RGB, GL_UNSIGNED_BYTE, img_data) return texture_id def draw_trunk(texture_id): """Dibuja el tronco del árbol como un cilindro con textura""" glPushMatrix() glBindTexture(GL_TEXTURE_2D, texture_id) # Vincula la textura glColor3f(1.0, 1.0, 1.0) # Color blanco para mostrar la textura glTranslatef(0.0, 0.0, 0.0) glRotatef(-90, 1, 0, 0) # Orienta el cilindro verticalmente quadric = gluNewQuadric() gluQuadricTexture(quadric, GL_TRUE) gluCylinder(quadric, 0.3, 0.3, 2.0, 32, 32) glPopMatrix() def draw_foliage(texture_id): """Dibuja las hojas del árbol como una esfera con textura""" glPushMatrix() glBindTexture(GL_TEXTURE_2D, texture_id) # Vincula la textura glColor3f(1.0, 1.0, 1.0) # Color blanco para mostrar la textura glTranslatef(0.0, 2.0, 0.0) quadric = gluNewQuadric() gluQuadricTexture(quadric, GL_TRUE) gluSphere(quadric, 1.0, 32, 32) glPopMatrix() def draw_ground(texture_id): """Dibuja un plano para representar el suelo con textura""" glBindTexture(GL_TEXTURE_2D, texture_id) # Vincula la textura glBegin(GL_QUADS) glColor3f(1.0, 1.0, 1.0) # Color blanco para mostrar la textura glTexCoord2f(0.0, 0.0); glVertex3f(-10, 0, 10) glTexCoord2f(1.0, 0.0); glVertex3f(10, 0, 10) glTexCoord2f(1.0, 1.0); glVertex3f(10, 0, -10) glTexCoord2f(0.0, 1.0); glVertex3f(-10, 0, -10) glEnd() def draw_tree(trunk_texture, foliage_texture, ground_texture): """Dibuja un árbol completo""" glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() # Configuración de la cámara gluLookAt(4, 3, 8, # Posición de la cámara 0, 1, 0, # Punto al que mira 0, 1, 0) # Vector hacia arriba draw_ground(ground_texture) # Dibuja el suelo draw_trunk(trunk_texture) # Dibuja el tronco draw_foliage(foliage_texture) # Dibuja las hojas glfw.swap_buffers(window) def main(): global window # Inicializar GLFW if not glfw.init(): sys.exit() # Crear ventana de GLFW width, height = 800, 600 window = glfw.create_window(width, height, "Árbol 3D con Texturas y Luz", None, None) if not window: glfw.terminate() sys.exit() glfw.make_context_current(window) glViewport(0, 0, width, height) init() # Carga de texturas trunk_texture = load_texture("trunk.jpg") # Textura para el tronco foliage_texture = load_texture("foliage.jpg") # Textura para las hojas ground_texture = load_texture("groud.jpg") # Textura para el suelo # Bucle principal while not glfw.window_should_close(window): draw_tree(trunk_texture, foliage_texture, ground_texture) glfw.poll_events() glfw.terminate() if __name__ == "__main__": main()
import glfw from OpenGL.GL import * from OpenGL.GLU import gluPerspective, gluLookAt, gluNewQuadric, gluCylinder, gluSphere import struct # Función para cargar una textura BMP def load_texture(filename): """Carga un archivo BMP y genera una textura de OpenGL""" with open(filename, "rb") as f: header = f.read(54) # Cabecera BMP de 54 bytes width, height = struct.unpack("ii", header[18:26]) data_offset = struct.unpack("I", header[10:14])[0] f.seek(data_offset) data = f.read() # almacena píxeles en orden BGR, los convertimos a RGB image_data = bytearray() for i in range(0, len(data), 3): image_data.extend([data[i + 2], data[i + 1], data[i]]) texture_id = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, texture_id) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image_data) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) return texture_id def init(): """Configuración inicial de OpenGL""" glClearColor(0.5, 0.8, 1.0, 1.0) # Fondo azul cielo glEnable(GL_DEPTH_TEST) # Activar prueba de profundidad glEnable(GL_TEXTURE_2D) # Activar texturas glShadeModel(GL_SMOOTH) # Sombreado suave # Configuración de la perspectiva glMatrixMode(GL_PROJECTION) gluPerspective(60, 1.0, 0.1, 100.0) # Campo de visión glMatrixMode(GL_MODELVIEW) def draw_trunk(texture_id): """Dibuja el tronco del árbol como un cilindro texturizado""" glPushMatrix() glBindTexture(GL_TEXTURE_2D, texture_id) glColor3f(1.0, 1.0, 1.0) # Blanco para mostrar la textura glTranslatef(0.0, 0.0, 0.0) glRotatef(-90, 1, 0, 0) # Rotar para orientar verticalmente quadric = gluNewQuadric() gluQuadricTexture(quadric, GL_TRUE) gluCylinder(quadric, 0.3, 0.3, 2.0, 32, 32) glPopMatrix() def draw_foliage(texture_id): """Dibuja las hojas del árbol como una esfera texturizada""" glPushMatrix() glBindTexture(GL_TEXTURE_2D, texture_id) glColor3f(1.0, 1.0, 1.0) # Blanco para mostrar la textura glTranslatef(0.0, 2.0, 0.0) quadric = gluNewQuadric() gluQuadricTexture(quadric, GL_TRUE) gluSphere(quadric, 1.0, 32, 32) glPopMatrix() def draw_ground(texture_id): """Dibuja un plano para representar el suelo con textura""" glBindTexture(GL_TEXTURE_2D, texture_id) glBegin(GL_QUADS) glColor3f(1.0, 1.0, 1.0) # Blanco para mostrar la textura glTexCoord2f(0.0, 0.0); glVertex3f(-10, 0, 10) glTexCoord2f(1.0, 0.0); glVertex3f(10, 0, 10) glTexCoord2f(1.0, 1.0); glVertex3f(10, 0, -10) glTexCoord2f(0.0, 1.0); glVertex3f(-10, 0, -10) glEnd() def draw_tree(trunk_texture, foliage_texture, ground_texture): """Dibuja un árbol completo""" glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() # Configuración de la cámara gluLookAt(4, 3, 8, # Posición de la cámara 0, 1, 0, # Punto al que mira 0, 1, 0) # Vector hacia arriba draw_ground(ground_texture) # Dibuja el suelo draw_trunk(trunk_texture) # Dibuja el tronco draw_foliage(foliage_texture) # Dibuja las hojas glfw.swap_buffers(window) def main(): global window # Inicializar GLFW if not glfw.init(): sys.exit() # Crear ventana de GLFW width, height = 800, 600 window = glfw.create_window(width, height, "Árbol con Texturas", None, None) if not window: glfw.terminate() sys.exit() glfw.make_context_current(window) glViewport(0, 0, width, height) init() # Carga de texturas BMP trunk_texture = load_texture("trunk.bmp") # Textura para el tronco foliage_texture = load_texture("foliage.bmp") # Textura para las hojas ground_texture = load_texture("groud.bmp") # Textura para el suelo if not trunk_texture or not foliage_texture or not ground_texture: print("Error al cargar texturas.") glfw.terminate() sys.exit() # Bucle principal while not glfw.window_should_close(window): draw_tree(trunk_texture, foliage_texture, ground_texture) glfw.poll_events() glfw.terminate() if __name__ == "__main__": main()
texturas blanco y negro
from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * def load_texture(image_path): texture_id = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, texture_id) # Cargar imagen de textura from PIL import Image img = Image.open(image_path) img_data = img.tobytes("raw", "RGB", 0, -1) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img.width, img.height, 0, GL_RGB, GL_UNSIGNED_BYTE, img_data) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) return texture_id def display(): glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glEnable(GL_TEXTURE_2D) # Crear un objeto cuadrático quadric = gluNewQuadric() gluQuadricTexture(quadric, GL_TRUE) # Habilitar texturas gluQuadricNormals(quadric, GLU_SMOOTH) # Dibujar una esfera con textura glBindTexture(GL_TEXTURE_2D, texture_id) gluSphere(quadric, 1.0, 32, 32) gluDeleteQuadric(quadric) glDisable(GL_TEXTURE_2D) glutSwapBuffers() def main(): global texture_id glutInit() glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH) glutInitWindowSize(800, 600) glutCreateWindow("Textura") glEnable(GL_DEPTH_TEST) texture_id = load_texture("/home/likcos/Materias/Graficacion/code/foliage.bmp") glutDisplayFunc(display) glutMainLoop() if __name__ == "__main__": main()
texturas de color
from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * from PIL import Image #pip install Pillow def load_texture(image_path): # Generar y enlazar una textura texture_id = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, texture_id) # Cargar imagen desde el archivo img = Image.open(image_path) img = img.convert("RGB") # Convertir a RGB explícitamente img_data = img.tobytes("raw", "RGB", 0, -1) # Configurar la textura glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img.width, img.height, 0, GL_RGB, GL_UNSIGNED_BYTE, img_data) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT) return texture_id def init_lighting(): # Configuración de iluminación glEnable(GL_LIGHTING) # Habilitar iluminación glEnable(GL_LIGHT0) # Activar la luz 0 glEnable(GL_COLOR_MATERIAL) # Habilitar el material basado en colores # Configuración de la luz light_pos = [1.0, 1.0, 1.0, 0.0] # Posición de la luz light_ambient = [0.2, 0.2, 0.2, 1.0] # Luz ambiental light_diffuse = [0.8, 0.8, 0.8, 1.0] # Luz difusa light_specular = [1.0, 1.0, 1.0, 1.0] # Luz especular glLightfv(GL_LIGHT0, GL_POSITION, light_pos) glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient) glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse) glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular) # Configuración del material material_specular = [1.0, 1.0, 1.0, 1.0] material_shininess = [50.0] glMaterialfv(GL_FRONT, GL_SPECULAR, material_specular) glMaterialfv(GL_FRONT, GL_SHININESS, material_shininess) def display(): glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glEnable(GL_TEXTURE_2D) # Crear un objeto cuadrático quadric = gluNewQuadric() gluQuadricTexture(quadric, GL_TRUE) # Habilitar texturas para el objeto gluQuadricNormals(quadric, GLU_SMOOTH) glBindTexture(GL_TEXTURE_2D, texture_id) # Enlazar la textura cargada # Dibujar la esfera pequeña glPushMatrix() glColor3f(1.0, 1.0, 1.0) # no altere la textura glTranslatef(0.0, 0.0, -2.0) # Mover la esfera para que sea visible gluSphere(quadric, 0.5, 32, 32) # Esfera con radio 0.5 glPopMatrix() gluDeleteQuadric(quadric) # Liberar el recurso del cuadrático glDisable(GL_TEXTURE_2D) glutSwapBuffers() def reshape(width, height): if height == 0: height = 1 aspect = width / height glViewport(0, 0, width, height) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(45.0, aspect, 0.1, 50.0) glMatrixMode(GL_MODELVIEW) glLoadIdentity() def main(): global texture_id glutInit() glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH) glutInitWindowSize(800, 600) glutCreateWindow("Esfera con textura e iluminación") glEnable(GL_DEPTH_TEST) # Habilitar prueba de profundidad texture_id = load_texture("/home/likcos/Materias/Graficacion/code/tex1.png") init_lighting() # Inicializar iluminación glutDisplayFunc(display) glutReshapeFunc(reshape) glutMainLoop() if __name__ == "__main__": main()
Transformaciones en OpenGL
Las transformaciones de OpenGL son fundamentales para mover, rotar y escalar los objetos en la escena.
Traslación
Mueve un objeto de una posición a otra.
Rotación
Gira un objeto alrededor de un eje.
Escalado
Cambia el tamaño del objeto.
Ejemplo de cómo aplicar una transformación de escalado y rotación:
from OpenGL.GL import * from OpenGL.GLU import * glPushMatrix() # Guardar la matriz actual glTranslatef(1.0, 0.0, 0.0) # Traslación glRotatef(45, 0.0, 0.0, 1.0) # Rotación de 45 grados glScalef(2.0, 2.0, 1.0) # Escalar el objeto draw_triangle() # Dibujar el objeto transformado glPopMatrix() # Restaurar la matriz original
Iluminación y Materiales
En OpenGL, puedes aplicar iluminación y definir materiales para que los objetos interactúen con la luz de manera realista.
Tipos de luces
- Luz ambiental: Luz sin una dirección específica que ilumina toda la escena.
- Luz direccional: Luz que proviene de una dirección específica, como la luz solar.
- Luz de punto: Luz que emana de un punto específico, como una lámpara.
- Luz de foco: Luz que se emite en un ángulo específico, similar a un reflector.
Materiales
Puedes definir propiedades de los materiales, como el color y la reflectividad, para cada objeto.
Sombras y Efectos Avanzados
OpenGL también permite generar sombras y efectos avanzados como transparencia, texturas y sombreado por vértices.
OpenGL Moderno: Shaders y el Pipeline Gráfico
OpenGL moderno (a partir de OpenGL 3.0) ha evolucionado hacia un modelo basado en shaders. Los shaders son pequeños programas que se ejecutan en la GPU y permiten controlar de manera detallada el comportamiento del pipeline gráfico.
Vertex Shader
Procesa cada vértice y aplica transformaciones para determinar su posición en la pantalla.
Fragment Shader
Calcula el color final de cada píxel en la superficie de los objetos.
Geometry Shader
Opcional, permite manipular primitivas completas (como puntos, líneas o triángulos).
# Ejemplo de vertex shader simple en GLSL (OpenGL Shading Language) """ #version 330 core layout(location = 0) in vec3 position; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { gl_Position = projection * view * model * vec4(position, 1.0); } """
Conclusión
OpenGL es una herramienta poderosa y flexible para el renderizado de gráficos en 2D y 3D. Aunque es de bajo nivel y puede requerir trabajo adicional para construir escenas complejas, su compatibilidad multiplataforma y la aceleración por hardware lo convierten en una opción popular en una variedad de aplicaciones.
Con el tiempo, OpenGL ha evolucionado hacia un modelo basado en shaders, lo que permite un control mucho mayor sobre el pipeline gráfico, haciendo posible gráficos de alta calidad y efectos avanzados en tiempo real.
Proyecto Final
El flujo óptico es el patrón del movimiento aparente de los objetos, superficies y bordes en una escena causado por el movimiento relativo entre un observador (un ojo o una cámara) y la escena.23 El concepto de flujo óptico se estudió por primera vez en la década de 1940 y, finalmente, fue publicado por el psicólogo estadounidense James J. Gibson4 como parte de su teoría de la affordance (una acción que un individuo puede potencialmente realizar en su ambiente). Las aplicaciones del flujo óptico tales como la detección de movimiento, la segmentación de objetos, el tiempo hasta la colisión y el enfoque de cálculo de expansiones, la codificación del movimiento compensado y la medición de la disparidad estereoscópica utilizan este movimiento de las superficies y bordes de los objetos.
Actividades
- Desarrollar un programa que utilice el flujo óptico, para aplicar a una imagen o una primitiva de dibujo, las transformaciones geométricas vistas en clase, (Traslación, Escalamiento, Rotación)
- Desarrollar un programa que utilice opengl, para modelar un entorno completo tipo mundo minecraft de alguna ciudad, paisaje, bosque, etc. Donde se deben de incluir por lo menos 15 modelos diferentes, diseñados con primitivas de opengl de igual manera se debe de mover todo el entorno modelado mediante el flujo óptico aplicando las transformaciones geométricas vistas en clase, (Traslación, Escalamiento, Rotación), para esta actividad pueden hacer equipos máximo de 5 personas.