Gaussian Splatting desde cero en Linux - Combinar vídeo 360 y vuelo orbital
Gaussian Splatting ha revolucionado el renderizado neuronal logrando calidad fotorrealista a más de 90 FPS. En este artículo te muestro el flujo completo para convertir vídeos 360° en entornos navegables usando Linux: desde la configuración de Docker y CUDA hasta el entrenamiento final, pasando por COLMAP y la extracción optimizada de vistas con ffmpeg. No es un proceso sencillo, te toparás con errores de memoria GPU, reconstrucciones fallidas y nubes de puntos caóticas, pero aquí te explico cómo solucionarlo todo. Incluye scripts probados, parámetros refinados y las lecciones aprendidas tras decenas de intentos fallidos. Contenido:- Intro
- El vídeo
- El Proceso
- Parte 1 - Configurar Docker
- Parte 2 - Flujo de trabajo, conjunto de pruebas y funcionamiento de Gaussian Splatting
- Parte 3 - Flujo de trabajo y test con conjuntos propios
- Parte 4 - Configuración de entorno para simplificar y acelerar el proceso en Linux
- Interludio 1 - Sí se puede, así que no cunda el pánico
- Parte 5 - Gaussian Splatting a partir de imágenes sin Flow State
- Parte 6 - THE LAST DANCE (Gaussian Dance)
- Conclusiones y lecciones aprendidas
Intro
La revolución del renderizado neuronal ha llegado no hace mucho con Gaussian Splatting, una técnica que permite crear reconstrucciones fotorrealistas navegables en tiempo real. A diferencia de NeRF, utiliza rasterización en lugar de ray tracing, logrando renderizado a más de 90 FPS con calidad excepcional. En este artículo te muestro el flujo de trabajo completo para convertir vídeos 360° en entornos Gaussian Splatting en Linux, incluyendo todos los errores con los que te vas a topar y cómo solucionarlos.
Para qué sirve realmente:
- Realidad virtual/aumentada - explorar escenas fotorrealistas
- Cinematografía - crear tomas de cámara imposibles de escenas reales
- Documentación patrimonial - preservar espacios históricos navegables
- Visualización arquitectónica - recorridos inmersivos de espacios
- Gaming/metaversos - entornos fotorrealistas en tiempo real
Para qué NO sirve:
- Modelado 3D tradicional
- Animación de personajes
- Impresión 3D
- Modificación de geometría
- Workflows de Blender/Maya tradicionales
Pero antes…
…nada de esto empieza aquí ni mucho menos. Sin embargo, la técnica es relativamente reciente y se está poniendo muy de moda, sobre todo entre las personas dedicadas a los entornos interactivos. Entre las fuentes que considero fundamentales mencionaré tres canales de YouTube donde encontraréis muchos más detalles de los que podrías encontrar aquí.
- EL PAPER FUNDAMENTAL EXPLICADO
- Olli Huttunen - El capo que lo experimenta todo
- Pixel Reconstruct - El primero que me enseñó la técnica en Linux
Una vez hechas las menciones de honor vamos al lío.
El vídeo
Como siempre, el proceso completo de trabajo se muestra en este vídeo.
El Proceso
De forma general, los bloques y parámetros de código ya están explicados en el vídeo y no merece la pena pararse en este blog post a explicar cada línea. Muchos de ellos son autoexplicativos con el comentario previo. Y si no los entendéis lo más sencillo es que los peguéis en vuestra IA favoríta y le digáis que os los explique. Esto es algo que yo hago muy a menudo ahora para aprender estas cosas.
Parte 1 - Configurar Docker
Instalación completa de Docker y NVIDIA Container Toolkit
# Instalar Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# Agregar tu usuario al grupo docker
sudo usermod -aG docker $USER
# Reiniciar sesión o ejecutar:
newgrp docker
# Instalar NVIDIA Container Toolkit
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-container-runtime/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-container-runtime/$distribution/nvidia-container-runtime.list | sudo tee /etc/apt/sources.list.d/nvidia-container-runtime.list
sudo apt update
sudo apt install -y nvidia-container-toolkit
sudo systemctl restart docker
Verificar disponibilidad de GPU’s en Docker
Antes de empezar, necesitas verificar que Docker reconoce tu GPU NVIDIA correctamente:
# Verificar Docker + GPU con CUDA (debería mostrar la misma salida que nvidia-smi)
docker run --rm --gpus all nvidia/cuda:13.0.1-runtime-ubuntu22.04 nvidia-smi
Si esto funciona, estás listo. Si no, necesitas instalar los componentes necesarios.

Lanzar contenedor Docker para Gaussian Splatting
# Preparar estructura de datos
mkdir -p ~/gaussian_data
mkdir -p ~/gaussian_data/outputs
cd ~/gaussian_data
# Ejecutar contenedor con implementación original INRIA
docker run -it -v ~/gaussian_data:/workspace/data --gpus all \
nvcr.io/nvidia/pytorch:24.02-py3 bash
# Esto mapea:
# ~/gaussian_data (tu máquina) ↔ /workspace/data (dentro del contenedor)
Clave: Todo lo que guardes en /workspace/data dentro del contenedor será accesible en ~/gaussian_data en tu máquina.
Hasta aquí no hemos ejecutado nada, solo hemos configurado las cosas y probado que podemos lanzar docker y gaussian splatting sin errores. Vamos con esto a la siguiente parte: las pruebas con un conjunto de datos ajeno.
Parte 2 - Flujo de trabajo, conjunto de pruebas y funcionamiento de Gaussian Splatting
Preparar el entorno dentro del contenedor
Una vez dentro del contenedor Docker:
# Clonar el repositorio oficial
cd /workspace
git clone https://github.com/graphdeco-inria/gaussian-splatting.git --recursive
cd gaussian-splatting
# Instalar dependencias Python
pip install plyfile tqdm
# Compilar las extensiones CUDA (crítico para el rendimiento)
pip install submodules/diff-gaussian-rasterization --no-build-isolation
pip install submodules/simple-knn --no-build-isolation
Descargar conjunto de datos de prueba
# Volver al directorio de datos
cd /workspace/data
# Descargar dataset de ejemplo oficial
wget https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/datasets/input/tandt_db.zip
unzip tandt_db.zip
Este dataset ya viene preprocesado con COLMAP, lo que significa que incluye los archivos cameras.txt, images.txt y points3D.txt necesarios para el entrenamiento.
Estructura de carpetas que necesita Gaussian Splatting
gaussian_data/
├── tandt/
│ ├── train/
│ │ ├── images/ # Imágenes de entrada
│ │ └── sparse/ # Datos de COLMAP
│ │ └── 0/
│ │ ├── cameras.txt
│ │ ├── images.txt
│ │ └── points3D.txt
│ └── truck/
│ └── ...
└── outputs/ # Resultados del entrenamiento
Primeros entrenamientos - Test de Gaussian Splatting
cd /workspace/gaussian-splatting
# Entrenar con el conjunto "train"
python train.py -s /workspace/data/tandt/train \
--model_path /workspace/data/outputs/train_model \
--iterations 7000
# Entrenar con el conjunto "truck"
python train.py -s /workspace/data/tandt/truck \
--model_path /workspace/data/outputs/truck_model \
--iterations 7000
El entrenamiento genera un archivo .ply que contiene millones de “gaussians” (elipses 3D orientadas). No es un modelo 3D tradicional con mallas y texturas.
Vista previa de los resultados
Lo más sencillo para explorar y moverte por los modelos generadores es usar uno de estos dos navegadores lanzables desde terminal. Puedes moverte en ellos con el ratón o con la cruceta y las teclas W, S, A, D, Q, E.
# En tu navegador (fuera del contenedor)
firefox https://antimatter15.com/splat/
# O usa: https://playcanvas.com/supersplat/editor
Arrastra el archivo: ~/gaussian_data/outputs/train_model/point_cloud/iteration_7000/point_cloud.ply
Importante: Gaussian Splatting es para síntesis de vistas nuevas, no para modelado 3D tradicional. Es perfecto para VR, exploración inmersiva y documentación patrimonial, pero no sirve para editar geometría en Blender o imprimir en 3D.
Parte 3 - Flujo de trabajo y test con conjuntos propios
Experimento: ¿Se puede convertir un vídeo 360° en un entorno Splatting?
Respuesta corta: Sí, pero con matices importantes.
El proceso requiere:
- Extraer frames del vídeo 360°
- Convertir cada frame equirrectangular en múltiples vistas rectilineares
- Usar COLMAP para reconstruir la estructura 3D
- Entrenar el modelo Gaussian Splatting
Ejemplo de extracción de frames del vídeo 360°
VIDEO="./raw/video360.mp4"
FRAMES_DIR="./frames"
FPS=0.5 # 1 frame cada 2 segundos
mkdir -p "$FRAMES_DIR"
ffmpeg -i "$VIDEO" \
-vf "fps=$FPS" \
-q:v 1 \
"$FRAMES_DIR/frame_%04d.png" \
-v error
Clave: No extraigas demasiados frames. Para un vídeo de 2 minutos, 60 frames (1 cada 2s) son suficientes. Más frames = más tiempo de procesamiento sin mejora significativa.
Extracción de vistas con ffmpeg
El filtro v360 de ffmpeg permite convertir imágenes equirrectangulares a vistas rectilineares. Necesitamos mucho solape entre vistas para que COLMAP funcione correctamente.
Script de extracción optimizado (8 vistas por frame):
#!/bin/bash
FRAMES_DIR="./frames"
OUTPUT_DIR="./frames_extract"
FOV=90
QUALITY=3
WIDTH=1000
HEIGHT=1000
YAWS=(0 45 90 135 180 -135 -90 -45)
mkdir -p "$OUTPUT_DIR"
echo "=== Generando vistas rectilineares ==="
for frame in "$FRAMES_DIR"/*.png; do
[ ! -f "$frame" ] && continue
basename=$(basename "$frame" .png)
echo "Procesando: $basename"
# Pitch 0° (horizonte) - 8 vistas
for yaw in "${YAWS[@]}"; do
ffmpeg -y -i "$frame" \
-vf "v360=e:rectilinear:h_fov=${FOV}:v_fov=${FOV}:yaw=${yaw}:pitch=0,scale=${WIDTH}:${HEIGHT}" \
-q:v "$QUALITY" \
"${OUTPUT_DIR}/${basename}_y${yaw}_p0.jpg" \
-v error
done
# Pitch +30° (arriba) - 8 vistas adicionales
for yaw in "${YAWS[@]}"; do
ffmpeg -y -i "$frame" \
-vf "v360=e:rectilinear:h_fov=${FOV}:v_fov=${FOV}:yaw=${yaw}:pitch=30,scale=${WIDTH}:${HEIGHT}" \
-q:v "$QUALITY" \
"${OUTPUT_DIR}/${basename}_y${yaw}_p1.jpg" \
-v error
done
# Pitch -30° (abajo) - solo vista frontal
ffmpeg -y -i "$frame" \
-vf "v360=e:rectilinear:h_fov=${FOV}:v_fov=${FOV}:yaw=0:pitch=-30,scale=${WIDTH}:${HEIGHT}" \
-q:v "$QUALITY" \
"${OUTPUT_DIR}/${basename}_y0_p-1.jpg" \
-v error
done
echo "✓ Completado: 17 vistas por frame"
Por qué estos ángulos:
- 8 vistas horizontales con 45° de separación = ~45° de solape con FOV 90°
- 3 niveles verticales (arriba, horizonte, abajo) = cobertura esférica completa
- Total: 17 vistas por frame con excelente solape
COLMAP - Instalación y conceptos básicos
¿Qué es COLMAP? COLMAP es un software de Structure-from-Motion (SfM) que:
- Detecta características visuales en las imágenes
- Encuentra coincidencias entre imágenes
- Reconstruye la posición de cámaras y puntos 3D
Instalación en Ubuntu:
sudo apt update
sudo apt install colmap
COLMAP - Configuración y parámetros
Script completo de procesamiento con COLMAP:
#!/bin/bash
BASE="/tu/directorio/proyecto"
IMGS="$BASE/frames_extract"
WORK="$BASE/colmap_workspace"
# Crear estructura
mkdir -p "$WORK/input"
# Copiar imágenes
cp "$IMGS"/*.jpg "$WORK/input/"
num_imgs=$(ls "$WORK/input"/*.jpg 2>/dev/null | wc -l)
echo "✓ $num_imgs imágenes copiadas"
cd "$WORK"
# Paso 1: Feature extraction
echo "=== Extrayendo características ==="
colmap feature_extractor \
--database_path "$WORK/database.db" \
--image_path "$WORK/input" \
--ImageReader.camera_model SIMPLE_PINHOLE \
--ImageReader.single_camera 1 \
--SiftExtraction.max_image_size 2000 \
--SiftExtraction.max_num_features 4096 \
--SiftExtraction.use_gpu 1 \
--SiftExtraction.gpu_index 0
# Paso 2: Sequential matching
echo "=== Matching secuencial ==="
colmap sequential_matcher \
--database_path "$WORK/database.db" \
--SiftMatching.guided_matching 1 \
--SiftMatching.max_ratio 0.8 \
--SiftMatching.use_gpu 1 \
--SiftMatching.gpu_index 0 \
--SequentialMatching.overlap 100
# Paso 3: Mapper (reconstrucción 3D)
echo "=== Reconstrucción 3D ==="
mkdir -p "$WORK/distorted/sparse"
colmap mapper \
--database_path "$WORK/database.db" \
--image_path "$WORK/input" \
--output_path "$WORK/distorted/sparse" \
--Mapper.init_min_tri_angle 2 \
--Mapper.init_min_num_inliers 20 \
--Mapper.abs_pose_max_error 15 \
--Mapper.filter_max_reproj_error 4 \
--Mapper.min_num_matches 15
# Verificar resultado
if [ ! -d "$WORK/distorted/sparse/0" ]; then
echo "✗ Mapper falló"
exit 1
fi
registered=$(grep -c ".jpg" "$WORK/distorted/sparse/0/images.txt" 2>/dev/null || echo 0)
echo "✓ $registered/$num_imgs imágenes registradas"
# Paso 4: Image undistortion (crucial para Gaussian Splatting)
echo "=== Undistortion ==="
colmap image_undistorter \
--image_path "$WORK/input" \
--input_path "$WORK/distorted/sparse/0" \
--output_path "$WORK" \
--output_type COLMAP
# Organizar estructura final
mkdir -p "$WORK/sparse/0"
for file in "$WORK/sparse"/*; do
fname=$(basename "$file")
if [ "$fname" != "0" ] && [ -f "$file" ]; then
mv "$file" "$WORK/sparse/0/"
fi
done
echo "✓ COLMAP completado"
echo "Estructura lista para Gaussian Splatting en: $WORK"
Parámetros críticos explicados:
SIMPLE_PINHOLE: Modelo de cámara sin distorsión (fundamental para GS)single_camera 1: Todas las imágenes de la misma cámara virtualSequentialMatching.overlap 100: Compara cada imagen con las 100 siguientes (ajusta según tu caso)Mapper.init_min_num_inliers 20: Mínimo de coincidencias para iniciar reconstrucción
COLMAP - Primeros resultados y problemas comunes
Problema 1: “Mapper falló - no se creó sparse/0”
- Causa: Pocas coincidencias entre imágenes
- Solución: Aumenta el overlap, reduce
min_num_matchesa 10
Problema 2: “Nube de puntos extraña envuelta en sí misma”
- Causa: Configuración incorrecta del modelo de cámara
- Solución: Usa
SIMPLE_PINHOLEy asegúrate del undistort
Problema 3: “Solo se registran 20% de las imágenes”
- Causa: Movimiento de cámara demasiado errático o imágenes borrosas
- Solución: Filtra frames manualmente, evita duplicados
Parte 4 - Configuración de entorno para simplificar y acelerar el proceso en Linux
Trabajar con Docker es útil para pruebas, pero para trabajo serio es mejor crear un entorno virtual Python nativo. Esto elimina la sobrecarga de contenedores y simplifica el flujo.
Instalación completa en entorno virtual
#!/bin/bash
echo "=== Instalando Gaussian Splatting en Ubuntu 24.04 ==="
# Verificar CUDA
if ! command -v nvcc &> /dev/null; then
echo "⚠️ CUDA no encontrado. Instala CUDA toolkit primero"
exit 1
fi
# Crear entorno virtual
python3 -m venv gaussian_env
source gaussian_env/bin/activate
# Actualizar pip
pip install --upgrade pip setuptools wheel
# Detectar versión CUDA
CUDA_VERSION=$(nvcc --version | grep -oP 'release \K[0-9]+\.[0-9]+')
CUDA_MAJOR=$(echo $CUDA_VERSION | cut -d. -f1)
echo "CUDA detectado: $CUDA_VERSION"
# Instalar PyTorch según versión CUDA
if [[ $CUDA_MAJOR -eq 11 ]]; then
TORCH_INDEX="cu118"
elif [[ $CUDA_MAJOR -eq 12 ]]; then
TORCH_INDEX="cu121"
else
TORCH_INDEX="cu121"
fi
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/$TORCH_INDEX
# Verificar PyTorch
python -c "
import torch
print(f'PyTorch: {torch.__version__}')
print(f'CUDA disponible: {torch.cuda.is_available()}')
if torch.cuda.is_available():
print(f'GPU: {torch.cuda.get_device_name(0)}')
"
# Clonar Gaussian Splatting
git clone https://github.com/graphdeco-inria/gaussian-splatting.git --recursive
cd gaussian-splatting
# Instalar dependencias
pip install plyfile tqdm ninja opencv-python
# Compilar módulos CUDA
pip install ./submodules/diff-gaussian-rasterization --no-build-isolation
pip install ./submodules/simple-knn --no-build-isolation
echo "✅ Instalación completada"
Lanzamiento simplificado desde entorno
# Activar entorno
source ~/gaussian_env/bin/activate
cd ~/gaussian-splatting
# Entrenar modelo
python train.py -s /ruta/a/tus/datos \
--model_path /ruta/salida \
--iterations 7000
Primeros resultados: desastrosos (pero educativos)
Si intentas entrenar directamente con todas las vistas extraídas del 360°, probablemente obtengas:
- Una nube de puntos caótica sin forma reconocible
- Mensajes de error de memoria GPU
- Un modelo que no converge
¿Por qué?
- Demasiadas imágenes (>400) saturan la GPU
- Imágenes con mucho ruido o desenfoque
- Falta de limpieza manual del dataset
No te preocupes. En las siguientes secciones solucionamos esto.
Interludio 1 - Sí se puede, así que no cunda el pánico
Antes de seguir con la pesadilla del 360°, hagamos un ejercicio de cordura: entrenar un modelo simple que funcione. Esto te ayudará a verificar que tu instalación funciona correctamente.
Reconstrucción simple: vuelo orbital de dron
Para este ejemplo, usarás un conjunto simple de imágenes:
- 50-100 fotos de un objeto/edificio desde diferentes ángulos
- Capturadas con dron o cámara normal
- Buena iluminación, sin desenfoque
Proceso COLMAP simplificado
#!/bin/bash
IMAGES="/ruta/a/fotos/dron"
WORK="./proyecto_dron"
mkdir -p "$WORK/input"
cp "$IMAGES"/*.jpg "$WORK/input/"
cd "$WORK"
# Feature extraction
colmap feature_extractor \
--database_path database.db \
--image_path input \
--ImageReader.camera_model SIMPLE_PINHOLE \
--SiftExtraction.use_gpu 1
# Exhaustive matching (funciona bien con <150 imágenes)
colmap exhaustive_matcher \
--database_path database.db \
--SiftMatching.use_gpu 1
# Mapper
mkdir -p sparse
colmap mapper \
--database_path database.db \
--image_path input \
--output_path sparse
# Undistort
colmap image_undistorter \
--image_path input \
--input_path sparse/0 \
--output_path . \
--output_type COLMAP
# Organizar
mkdir -p sparse/0
mv sparse/*.bin sparse/*.txt sparse/0/ 2>/dev/null
echo "✓ Listo para Gaussian Splatting"
Entrenamiento del modelo
source ~/gaussian_env/bin/activate
cd ~/gaussian-splatting
python train.py -s /ruta/proyecto_dron \
--model_path /ruta/proyecto_dron/output \
--iterations 7000 \
--resolution 1
Resultados esperados
Con este conjunto simple deberías obtener:
- Entrenamiento completo en 10-20 minutos (GPU)
- Modelo navegable con buen detalle
- Archivo
.plyfuncional en el viewer
Si esto funciona, tu instalación está correcta y puedes volver al desafío del 360°.

Parte 5 - Gaussian Splatting a partir de imágenes sin Flow State
Aquí está el truco que cambia todo para vídeos 360°.
El problema del Flow State (nivelación de horizonte)
Las cámaras 360° como Insta360 tienen una función de “estabilización de horizonte” que rota la imagen para mantenerla nivelada. Esto es terrible para reconstrucción 3D porque:
- La persona que lleva la cámara aparece en lugares diferentes de cada frame
- El “suelo” de la imagen cambia constantemente
- COLMAP se confunde con elementos que deberían ser estáticos
Exportar desde Insta360 Studio SIN Flow State
- Abre tu vídeo en Insta360 Studio
- Ve a Configuración de exportación
- Desactiva “Flow State Stabilization”
- Exporta en formato equirrectangular
- La persona que lleva la cámara estará siempre en la misma zona (típicamente abajo)
Extracción de frames y vistas (versión mejorada)
Ya casi al final del vídeo habrás visto que se puede generar el modelo con las imágenes extraidas de un vídeo 360. La clave ya la veía en los vídeos de los demás, cuando se dedicaban cierto tiempo a limpiar las fotografías basura y a enmascarar elementos que sobran para la reconstrucción. No es un capricho es un paso imprescindible, aunque si sois como yo aborrezcáis las tareas no sistemáticas y tontorronas de este tipo.
Lo más importante ha resultado ser la eliminación de vosotros en el vídeo. Y esta técnica creo que es nueva y me siento orgulloso de haber llegado a esta solución por mis propios medios. Estoy seguro de que ya hay gente que la utiliza pero si no, espero ser como una semilla que germina en los que vengan después.

El proceso consiste en:
- Exportar el vídeo 360 sin nivelar el horizonte y sin flow state.
- Generar las vistas solo de las secciones del vídeo donde no estáis. Tranquilos habrá superposición suficiente en las otras zonas para la reconstrucción.

Después de haber exportado esto con Insta360 Studio, ya podéis hacer la extracción especial.
Aquí extraigo 17 frames:
- 8 en horizontal
- 8 en horizontal +30 grados arriba.
- 1 central abajo (donde no suelo estar yo).
#!/bin/bash
VIDEO="./raw/video360_sinflow.mp4"
FRAMES_DIR="./frames_sinflow"
OUTPUT_DIR="./frames_sinflow_extract"
FPS=0.5
FOV=90
QUALITY=3
WIDTH=1000
HEIGHT=1000
# Extraer frames
mkdir -p "$FRAMES_DIR"
ffmpeg -i "$VIDEO" -vf "fps=$FPS" -q:v 1 "$FRAMES_DIR/frame_%04d.png" -v error
# Configuración de ángulos (evitando la zona del operador)
YAWS=(0 45 90 135 180 -135 -90 -45) # 8 direcciones horizontales
mkdir -p "$OUTPUT_DIR"
for frame in "$FRAMES_DIR"/*.png; do
[ ! -f "$frame" ] && continue
basename=$(basename "$frame" .png)
# Pitch 0° (horizonte)
for yaw in "${YAWS[@]}"; do
ffmpeg -y -i "$frame" \
-vf "v360=e:rectilinear:h_fov=${FOV}:v_fov=${FOV}:yaw=${yaw}:pitch=0,scale=${WIDTH}:${HEIGHT}" \
-q:v "$QUALITY" \
"${OUTPUT_DIR}/${basename}_y${yaw}_p0.jpg" \
-v error
done
# Pitch +30° (cielo)
for yaw in "${YAWS[@]}"; do
ffmpeg -y -i "$frame" \
-vf "v360=e:rectilinear:h_fov=${FOV}:v_fov=${FOV}:yaw=${yaw}:pitch=30,scale=${WIDTH}:${HEIGHT}" \
-q:v "$QUALITY" \
"${OUTPUT_DIR}/${basename}_y${yaw}_p1.jpg" \
-v error
done
# Pitch -30° SOLO frontal (evitamos la zona del operador en otros ángulos)
ffmpeg -y -i "$frame" \
-vf "v360=e:rectilinear:h_fov=${FOV}:v_fov=${FOV}:yaw=0:pitch=-30,scale=${WIDTH}:${HEIGHT}" \
-q:v "$QUALITY" \
"${OUTPUT_DIR}/${basename}_y0_p-1.jpg" \
-v error
done
echo "✓ 17 vistas por frame (evitando operador)"
Limpieza manual del dataset - CRÍTICO
Hazte un favor: antes de lanzar COLMAP, elimina manualmente las imágenes basura:
# Abre el directorio de vistas extraídas
nautilus ./frames_sinflow_extract
# Elimina manualmente:
# - Imágenes borrosas
# - Frames donde aparece tu mano/palo selfie
# - Vistas duplicadas o con poca información
# - Transiciones entre escenas
Regla de oro: Es mejor tener 200 imágenes buenas que 600 imágenes mediocres.
COLMAP + Gaussian Splatting con el conjunto limpio
#!/bin/bash
BASE="/tu/proyecto"
IMGS="$BASE/frames_sinflow_extract"
WORK="$BASE/gaussian_splatting_sinflow"
# Preparar
mkdir -p "$WORK/input"
cp "$IMGS"/*.jpg "$WORK/input/"
cd "$WORK"
# COLMAP completo
colmap feature_extractor \
--database_path database.db \
--image_path input \
--ImageReader.camera_model SIMPLE_PINHOLE \
--ImageReader.single_camera 1 \
--SiftExtraction.max_image_size 2000 \
--SiftExtraction.max_num_features 4096 \
--SiftExtraction.use_gpu 1
colmap sequential_matcher \
--database_path database.db \
--SiftMatching.use_gpu 1 \
--SequentialMatching.overlap 100
mkdir -p distorted/sparse
colmap mapper \
--database_path database.db \
--image_path input \
--output_path distorted/sparse
colmap image_undistorter \
--image_path input \
--input_path distorted/sparse/0 \
--output_path . \
--output_type COLMAP
mkdir -p sparse/0
mv sparse/*.bin sparse/*.txt sparse/0/ 2>/dev/null
echo "✓ Listo para entrenar"
Entrenar Gaussian Splatting
source ~/gaussian_env/bin/activate
cd ~/gaussian-splatting
# Configuración para datasets grandes (>200 imágenes)
export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
export CUDA_VISIBLE_DEVICES=0
python train.py \
-s /ruta/gaussian_splatting_sinflow \
--model_path /ruta/gaussian_splatting_sinflow/output \
--iterations 10000 \
--resolution 1 \
--data_device cpu \
--test_iterations 1000 3000 7000 10000 \
--save_iterations 3000 7000 10000
Parámetros clave para datasets grandes:
data_device cpu: Mantiene imágenes en RAM, libera VRAMresolution 1: Resolución completa (usar 2 o 4 si hay problemas de memoria)iterations 10000: Más iteraciones = mejor calidad (pero más tiempo)
Resultados esperados
Con el flujo sin Flow State y limpieza manual deberías conseguir:
- Reconstrucción coherente del entorno
- Buena definición en zonas con buena iluminación
- Algunas zonas borrosas donde falta información (normal)
- Ausencia de artefactos del operador
Parte 6 - THE LAST DANCE (Gaussian Dance)
Para obtener el mejor resultado posible, hay que aplicar todas las lecciones aprendidas.
Criterios finales de selección manual:
- ✓ Nitidez perfecta
- ✓ Buena iluminación (evita sombras extremas)
- ✓ Sin elementos móviles (personas caminando, coches)
- ✓ Evita transiciones entre zonas
- ✓ Mantén solape del 60% entre vistas consecutivas
Objetivo: Reducir el conjunto final a 100-200 imágenes de máxima calidad.
COLMAP con Vocab Tree Matcher
Lo siento, pero podía haberte puesto esta solución de COLMAP al principio y ya está. Pero no hubieras aprendido lo que suponen algunos de los procesos de COLMAP. La fuerza bruta con “exhaustive matcher” funciona muy bien siempre, pero si tienes muchas imágenes perderás valiosas horas. Con “sequential matcher” no serás capaz de controlar exactamente la relación entre imagenes ya que la cámara 360 sin flow state podrá estar bailando demasiado y no sabrás el número perfecto en la que de forma secuencias se encuentran semejanzas. Al final, y esto me ha costado mis horas de trasteo, el sistema “vocab_tree_matcher” ha resultado ser la salvación. Hacer primero un indexado de imágenes para segmentarlas por semejanzas y después ya si busca los puntos de control y sus relaciones. Una pasada.
Para datasets con imágenes muy dispersas temporalmente, el Vocab Tree Matcher funciona mejor que el sequential:
#!/bin/bash
WORK="/tu/proyecto/final"
cd "$WORK"
# Descargar vocabulario preentrenado
wget https://demuc.de/colmap/vocab_tree_flickr100K_words32K.bin
# Feature extraction (igual que antes)
colmap feature_extractor \
--database_path database.db \
--image_path input \
--ImageReader.camera_model SIMPLE_PINHOLE \
--ImageReader.single_camera 1 \
--SiftExtraction.max_image_size 4000 \
--SiftExtraction.max_num_features 8192 \
--SiftExtraction.use_gpu 1
# Vocab Tree Matcher (mejor para imágenes no secuenciales)
colmap vocab_tree_matcher \
--database_path database.db \
--VocabTreeMatching.vocab_tree_path ./vocab_tree_flickr100K_words32K.bin \
--SiftMatching.use_gpu 1
# Mapper con parámetros más permisivos
mkdir -p distorted/sparse
colmap mapper \
--database_path database.db \
--image_path input \
--output_path distorted/sparse \
--Mapper.init_min_tri_angle 2 \
--Mapper.init_min_num_inliers 10 \
--Mapper.abs_pose_max_error 25 \
--Mapper.filter_max_reproj_error 5 \
--Mapper.min_num_matches 10 \
--Mapper.ba_global_function_tolerance 0.00001
# Undistort
colmap image_undistorter \
--image_path input \
--input_path distorted/sparse/0 \
--output_path . \
--output_type COLMAP
# Organizar
mkdir -p sparse/0
mv sparse/*.bin sparse/*.txt sparse/0/ 2>/dev/null
registered=$(grep -c ".jpg" sparse/0/images.txt 2>/dev/null)
total=$(ls input/*.jpg | wc -l)
echo "✓ $registered/$total imágenes registradas"
Entrenamiento final optimizado
source ~/gaussian_env/bin/activate
cd ~/gaussian-splatting
# Limpiar GPU antes de empezar
pkill -f train.py
sleep 2
# Variables de entorno optimizadas
export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
export CUDA_VISIBLE_DEVICES=0
# Entrenamiento con parámetros refinados
python train.py \
-s /ruta/proyecto/final \
--model_path /ruta/proyecto/final/output \
--iterations 30000 \
--resolution 1 \
--data_device cpu \
--test_iterations 7000 15000 30000 \
--save_iterations 7000 15000 30000 \
--position_lr_init 0.00016 \
--position_lr_final 0.0000016 \
--feature_lr 0.0025 \
--opacity_lr 0.05 \
--scaling_lr 0.001 \
--rotation_lr 0.001 \
--densify_from_iter 500 \
--densify_until_iter 15000 \
--densification_interval 100 \
--opacity_reset_interval 3000
Parámetros refinados explicados:
iterations 30000: Entrenamiento largo para máxima calidaddensify_until_iter 15000: Permite refinar la geometría durante más tiempoposition_lr: Learning rates ajustados para convergencia suave
Resultados finales
Con este proceso completo deberías conseguir:
- ✓ Reconstrucción fotorrealista del entorno
- ✓ Navegación fluida sin artefactos
- ✓ Detalle comparable a fotogrametría tradicional
- ✓ Archivo
.plyoptimizado para viewers web
Visualización:
firefox https://playcanvas.com/supersplat/editor
# Arrastra: output/point_cloud/iteration_30000/point_cloud.ply
Conclusiones y lecciones aprendidas
Lo que funciona
✓ Vídeos 360° SÍ sirven para Gaussian Splatting, pero requieren:
- Exportación sin Flow State
- Limpieza agresiva de imágenes
- Extracción con solape del 60%+
- Paciencia en COLMAP
✓ Resolución de 1000x1000px es suficiente para vistas extraídas de 360°
✓ Vocab Tree matching es mejor para datasets dispersos y 360º
Lo que NO funciona
✗ Entrenar directamente con 500+ imágenes sin filtrar ✗ Usar exhaustive matching con >200 imágenes (tarda eternamente) ✗ Exportar 360° con Flow State activo ✗ FOV >100° en vistas rectilineares (genera distorsión) ✗ Ignorar el paso de undistort en COLMAP
Flujo de trabajo recomendado final
1. Captura → Vídeo 360° sin Flow State (2-3 min máximo)
↓
2. Extracción → 1 frame cada 2s = 60-90 frames
↓
3. Vistas → 17 vistas por frame = 1000-1500 imágenes
↓
4. Limpieza manual → Reducir a 200-300 mejores imágenes
↓
5. COLMAP → Sequential o Vocab Tree según caso
↓
6. Gaussian Splatting → 10000-30000 iteraciones
↓
7. Resultado → Entorno 3D navegable fotorrealista
Recursos adicionales
- Repositorio oficial Gaussian Splatting
- COLMAP Documentation
- SuperSplat Viewer
- Paper original: “3D Gaussian Splatting for Real-Time Radiance Field Rendering”
Nota final: Todo el código de este artículo está probado en Ubuntu 24.04 con GPU NVIDIA RTX 4060. Ajusta los parámetros según tu hardware y no te desanimes si los primeros intentos no salen perfectos. La reconstrucción 3D es iterativa por naturaleza.
Espero que hayas apreciado el esfuerzo en todo esto. Podrías apoyarme de muchas formas: contratándome, contactándome o simplemente compartiéndolo.










