Apuntes Graficación

Índice

Manual de Instalación de Git

Instalación en Windows

  • 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

  1. Git Bash: En Windows, utilizas Git Bash como terminal para ejecutar los comandos, en lugar de la terminal estándar de Linux o macOS.
  2. 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

  1. Abre el navegador web y visita el sitio: https://www.python.org.
  2. Ve a la pestaña "Downloads" y selecciona la versión más reciente para Windows.
  3. Haz clic en el botón que dice "Download Python (versión actual)" para descargar el instalador de Python.

Paso 2: Ejecutar el instalador

  1. Ejecuta el archivo descargado (.exe).
  2. Asegúrate de marcar la opción Add Python to PATH antes de proceder con la instalación.
  3. Haz clic en Install Now para instalar con la configuración predeterminada.
  4. Espera a que finalice el proceso de instalación y verifica el mensaje de éxito.

Paso 3: Verificación de la instalación

  1. Abre la línea de comandos (cmd) escribiendo "cmd" en el menú de inicio.
  2. Verifica que Python se instaló correctamente ejecutando el siguiente comando:

          python --version
    
  3. 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 >>>).

  4. Para salir del intérprete, escribe exit() o presiona Ctrl + Z seguido de Enter.

Paso 4: Instalar pip y otros paquetes

  1. Verifica si pip está instalado escribiendo en la línea de comandos:

          pip --version
    
  2. Si necesitas instalar paquetes, usa pip con el siguiente comando:

          pip install nombre_paquete
    

Paso 5: Configuración del entorno de desarrollo

  1. Instala un editor de código como:
  2. 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

  1. Asegúrate de tener Python instalado en tu sistema. Puedes verificarlo con:

       python --version
    
  2. Descarga e instala [Visual Studio Code](https://code.visualstudio.com/).
  3. 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:

  1. Abre la terminal integrada en VS Code (menú Terminal > New Terminal).
  2. 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:

  1. Presiona `Ctrl+Shift+P` (o `Cmd+Shift+P` en macOS) para abrir la Command Palette.
  2. Busca y selecciona `Python: Select Interpreter`.
  3. 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:

  1. Ejecuta:

       pip freeze > requirements.txt
    
  2. 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

  1. Generación de imágenes: Crear imágenes digitales a partir de descripciones matemáticas y geométricas de objetos, en 2D y 3D.
  2. Modelado de objetos: Crear representaciones matemáticas de objetos y escenas usando técnicas como polígonos, mallas y curvas.
  3. Renderizado: Convertir representaciones matemáticas en imágenes visuales, simulando iluminación, sombras y texturas.
  4. Transformaciones geométricas: Aplicar traslaciones, rotaciones, escalados y proyecciones a objetos en sistemas de coordenadas 2D y 3D.
  5. Iluminación y sombreado: Simular cómo la luz interactúa con los objetos para crear efectos realistas.
  6. Animación: Generar secuencias de imágenes que cambian a lo largo del tiempo, incluyendo movimientos y simulaciones físicas.
  7. Texturizado: Aplicar imágenes (texturas) sobre superficies para dar detalles visuales sin aumentar la complejidad geométrica.
  8. 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

  1. Matemáticas: Uso de geometría y álgebra lineal para realizar transformaciones geométricas y modelar objetos.
  2. Programación: Conocimiento de lenguajes como C++, Python, GLSL y HLSL para implementar algoritmos gráficos.
  3. Algoritmos gráficos: Estudio de algoritmos como el trazado de rayos (ray tracing), rasterización y z-buffering.
  4. Herramientas gráficas: Uso de APIs como OpenGL, DirectX o Vulkan para crear gráficos y animaciones.
  5. 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)

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)

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

  1. Independencia espacial: Los operadores puntuales solo modifican cada píxel basado en su valor original, sin tener en cuenta su entorno.
  2. Simplicidad computacional: Dado que no se necesita información de los píxeles vecinos, estas operaciones suelen ser más rápidas y eficientes.
  3. 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

  1. Convertir las coordenadas \((x, y)\) a coordenadas homogéneas \((x, y, 1)\).
  2. Definir la matriz de transformación \(T\).
  3. Multiplicar la matriz \(T\) por las coordenadas homogéneas.
  4. 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

  1. Se inicializa MediaPipe Hands con una confianza mínima de detección y seguimiento del 50%.
  2. Se usa OpenCV para capturar video en tiempo real.
  3. Se convierte el fotograma de BGR a RGB, ya que MediaPipe trabaja con imágenes en formato RGB.
  4. Se procesan los fotogramas y se extraen las coordenadas de la mano si se detecta.
  5. Se dibujan los puntos clave de la mano y sus conexiones en la imagen original.
  6. 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:
    1. `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.
    2. `imagen`:
      • Es la imagen original en color. Se utiliza en las posiciones donde la máscara detecta el color rojo (condición `True`).
    3. `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.
  • 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
    1. 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.
    2. 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.
    3. 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'`.
  • 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 de fovY 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 que zNear no se renderizan.
  • zFar: La distancia máxima visible desde la cámara. Los objetos más lejanos que zFar 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.2​3​ 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

  1. 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)
  1. 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.

Autor: Eduardo Alcaraz

Created: 2025-04-01 mar 12:31

Validate