A production-grade, end-to-end modular Python pipeline designed to reconstruct a spatially coherent and temporally consistent 3D environment of downtown Tecate, Baja California, Mexico, utilizing historical Google Street View imagery from circa 2009.
This system leverages advanced planar graph cycle traversal, unauthenticated metadata reverse-engineering, Playwright Chromium automation, vectorized NumPy roofline & height analysis, homography-based perspective rectification, and feature-aligned template-matching similarity blending to compile highly detailed urban blocks.
Downstream stages output procedurally generated textured 3D assets compiled via headless Blender automation with dynamic Apple Silicon Metal and Nvidia OptiX/CUDA hardware GPU-compute auto-configuration into a unified, high-fidelity export/geometry.glb asset.
The pipeline decouples network metadata acquisition and Playwright browser crawling from downstream processing through a tri-table relational caching architecture. If relational caches are warm, the system executes E2E block reconstruction and Blender compilation in under 12 seconds entirely offline.
graph TD
A["data/tecate_osm_cache.json (OSM Road Graph)"] --> B["Cycle Traversal Engine"]
B --> C["Planar CCW Extraction & Block Segmentation"]
C --> D["Safety Radius Filter: controllable via --radius"]
D --> E["Outward Normal Offsetting by 8.0 meters"]
E --> F["Granular Relational Cache Check"]
F -- "Cache Hit (Block/Facade/Pano Cached)" --> G["Instant Skip / Incremental Resume"]
F -- "Cache Miss / --reprocess set" --> H["Pre-pass Metadata Resolution & Scraper Crawl"]
H --> I["Playwright Chromium Screenshot Harvesting (networkidle optimized)"]
I --> J["Normalized Cross-Correlation (NCC) & Cardinal Blending"]
J --> K["Coarse-to-Fine Facade Texturing & Warping"]
G --> L["High-Fidelity 3D Block Geometry Compiler"]
K --> L
L --> M["blender_script.py: Headless Blender Assembler"]
M --> N["GPU Compute Auto-Configuration (Metal / OptiX / CUDA)"]
N --> O["tecate_reconstruction.blend"]
O --> P["export/geometry.glb Compiled Successfully"]
Instead of using arbitrary grid coordinates that divide street corridors, blocks are organically extracted using planar counter-clockwise (CCW) cycle traversal of the pruned road network.
-
Low Park Elevation (Parque Hidalgo): To preserve the spatial prominence of the central park square, the system computes the distance from the centroid of each extracted block
$B$ to the coordinate origin$(0, 0)$ :$$\text{Dist}_{\text{center}} = \sqrt{\text{centroid}_x^2 + \text{centroid}_y^2}$$ - If
$\text{Dist}_{\text{center}} \le 50.0\text{ meters}$ , the block is dynamically identified as Parque Hidalgo and assigned a low-extrusion height of 1.0 meter. - All other surrounding blocks receive standard, multi-story vertical heights between 7.0 and 11.0 meters.
To position the camera in the street looking directly at a building's facade, the system computes the segment's outward normal vector (lat, lon) to query the Street View API at the exact point where a vehicle would have passed.
Adjacent 5-meter facade screenshots overlap due to the camera's movement along the street. To merge them seamlessly into a single panorama, the system dynamically solves the horizontal translation shift
-
Coarse Search: Slide
img2horizontally overimg1checking shifts$s \in [100, 450]$ in steps of 5 pixels. Overlapping grayscale strips are correlated:$$\text{Score}(s) = \frac{\sum (T - \bar{T})(S_s - \bar{S}_s)}{\sqrt{\sum(T - \bar{T})^2 \sum(S_s - \bar{S}_s)^2}}$$ where$T$ represents the template strip ofimg1($s$ to$512$ ) and$S_s$ is the overlapping search strip ofimg2($0$ to$512-s$ ). -
Fine Search: Refine the search at 1-pixel intervals within a
$\pm 4$ pixel window around the coarse peak. If the correlation score falls below$0.35$ , the system falls back to a nominal shift of$350$ pixels.
To erase harsh vertical boundaries and storefront duplication artifacts, a weighted blending mask
-
Left Overlap Region (width
$W_{\text{left}} = 512 - s_{i-1}$ ):$$M(x) = \frac{x}{W_{\text{left}}}, \quad x \in [0, W_{\text{left}}]$$ -
Right Overlap Region (width
$W_{\text{right}} = 512 - s_i$ ):$$M(x) = 1.0 - \frac{x - (512 - W_{\text{right}})}{W_{\text{right}}}, \quad x \in [512 - W_{\text{right}}, 512]$$ -
Central Region:
$M(x) = 1.0$
Overlapping pixels are accumulated in a float32 canvas and normalized by the cumulative sum of weights: $$\text{Pixel}{\text{blended}} = \frac{\sum_i \text{Pixel}{i} \times M_i(x)}{\sum_i M_i(x)}$$
Because the total width of the stitched panorama
-
Bottom-Left:
$\left[\frac{x_i}{W_{\text{final}}}, 0.0\right]$ -
Bottom-Right:
$\left[\frac{x_i + 512}{W_{\text{final}}}, 0.0\right]$ -
Top-Right:
$\left[\frac{x_i + 512}{W_{\text{final}}}, 1.0\right]$ -
Top-Left:
$\left[\frac{x_i}{W_{\text{final}}}, 1.0\right]$
To prevent Python interpreter overhead during pixel search loops, the math operations are fully vectorized using highly optimized NumPy slice arrays, releasing the Python GIL to execute in native C code:
- Vectorized Sky Masking: Replaces sequential row-by-row column searches with vectorized slice ranges and norm comparisons, accelerating roofline search operations 10x to 100x (leveraging ARM Neon on M1 and AVX2 on Ryzen).
- Vectorized Height Sweep: Converts multi-millisecond loops scanning up to 500 rows into a single vectorized sweep executing in microseconds.
To maximize hardware throughput without risking resource congestion, the system dynamically auto-scales the execution pool to saturate host resources:
- Worker threads scale automatically via
os.cpu_count()(saturating 8 threads on M1 MacBooks, and 12 logical threads on Ryzen 5 7600X hosts). - Pre-allocates the UTM coordinate projection and road-distance grid cells sequentially on the main thread prior to spawning workers to guarantee 100% thread-safe, lock-free concurrent lookups.
The repository is organized as follows:
tecate-simulator/
├── data/ # GIS graphs and relational cache tables
│ ├── tecate_osm_cache.json # Static OSM road graph data for Tecate
│ ├── blocks_cache.json # Cached block polygons, heights, and roof colors
│ ├── panoramas_cache.json # Cached georeferenced panorama nodes (coords, dates, links)
│ ├── facades_cache.json # Cached facade observations (pano_id, heading, road details)
│ └── screenshots/
│ └── facades/ # Raw downloaded screenshots (block_X_facade_Y.png)
├── export/ # Procedural compiler exports
│ ├── geometry.glb # Fully compiled and textured 3D environment
│ ├── reconstruction_export.json # Compiled block vertices, materials, and UV data
│ ├── metadata.json # Coverage rates and asset provenance data
│ ├── textures/
│ │ ├── transparent_facade.png # Fallback transparent texture (bypasses redundant saves)
│ │ └── {block_id}_{cardinal}_facade.png # Similarity-blended storefront maps
│ └── debug/
│ ├── global_observation_map.png # Clean, perpendicular TTF coverage diagnostic map (1:1 aspect)
│ └── reconstruction_diagnostics.json # Face-level texturing status logs
├── src/ # Main pipeline source package
│ ├── main.py # Pipeline master orchestrator CLI entrypoint
│ ├── core_io/ # UTM projection conversion and file systems
│ │ ├── coords.py
│ │ └── io_manager.py
│ ├── data_acquisition/ # API clients and Playwright screenshot scraper
│ │ ├── browser_scraper.py # Playwright scraper with networkidle-delay optimization
│ │ └── sv_downloader.py
│ ├── gis_graph/ # OSM road graph builders
│ │ └── graph_builder.py
│ ├── reconstruction/ # Main block compiler & template-matching blending
│ │ └── prism_generator.py # Vectorized block compiler and per-cache tracking system
│ ├── temporal_filter/ # Historical time selectors
│ │ └── classifier.py
│ └── visualization/ # Observation maps
│ └── coverage.py
├── blender_script.py # Procedural assembler with Cycles GPU compute config (Metal/OptiX)
├── requirements.txt # Python dependencies
└── README.md # This file (documentation hub)
The system requires Python 3.10+ and a local installation of Blender (registered in your system path, or placed in standard directories).
# 1. Clone the repository and navigate
cd tecate-simulator
# 2. Initialize virtual environment
python3 -m venv venv
source venv/bin/activate
# 3. Upgrade pip and install core requirements
pip install --upgrade pip
pip install -r requirements.txt
# 4. Install Playwright Chromium browser binaries
./venv/bin/playwright install chromiumThe master script src/main.py orchestrates the entire GIS build, Playwright crawling, cropping, stitching, and Blender compilation:
| CLI Option | Default Value | Description |
|---|---|---|
--headless |
False |
Run Playwright in headless mode (highly recommended for server environments). |
--reprocess |
False |
Forces recalculation of cropping, homography warping, and similarity stitching on cached disk screenshots without querying the network. |
--skip-scraper |
False |
Completely bypasses Playwright browser initialization and crawling, executing the pipeline entirely offline using cached observations. |
--radius |
-1 |
Safety radius from central origin (Parque Hidalgo) in meters. restricts new crawls to this distance. Set to -1 for the entire city of Tecate. |
--parallel |
Dynamic | Number of concurrent execution threads. Defaults to dynamic auto-scaling saturating all host CPU cores (os.cpu_count() or 4). |
Runs the entire pipeline entirely offline, completely bypassing browser crawling and loading observations exclusively from cache:
PYTHONPATH=. ./venv/bin/python src/main.py --skip-scraperRegenerates all horizontal panoramas and UV layouts directly from existing screenshots without firing any browser queries:
PYTHONPATH=. ./venv/bin/python src/main.py --headless --reprocessPYTHONPATH=. ./venv/bin/python src/main.py --headless --radius 350The system implements high-performance caching and crash-resilience strategies to protect NVMe SSD health and optimize execution:
If the process is interrupted via Ctrl+C, the system catches the signal and initiates an advanced synchronous shutdown pipeline to preserve all progress:
- Cache Serialization: Writes only modified cache tables immediately.
- Intermediate Scene Export: Generates
reconstruction_export.json,metadata.json, andreconstruction_diagnostics.jsonup to the last processed block segment. - Coverage Map Generation: Automatically compiles the
export/debug/global_observation_map.pngusing current progress. - Blender glTF Compilation: Triggers the background Blender compiler to build
export/geometry.glbfrom the partially gathered assets. - Instant Force Exit: If the user presses
Ctrl+Ca second time during this cleanup pipeline, the process instantly terminates.
The cache is split into three normalized, relational tables under the data/ directory, achieving a 98.4% reduction in disk size (from 722 MB down to 11.8 MB combined) while avoiding duplicate information:
data/panoramas_cache.json(PK:pano_id): Stores georeferenced sensor parameters (lat/lon coordinates, unauthenticated API elevation, pitch, roll, dates) of individual panorama nodes.data/blocks_cache.json(PK:block_id): Stores building block cycle geometries, centroids, distance metrics, height variables, and dynamic roof colors.data/facades_cache.json(PK:facade_id): Stores individual storefront quad observations, yaw headings, camera rotations, offset points, road relation indices, and foreign keys.
To maximize disk performance and protect NVMe SSD durability, the system implements a highly optimized per-cache change tracking system:
- Tracks separate state flags:
blocks_cache_changed,panoramas_cache_changed,facades_cache_changed, andmetadata_cache_changed. - Employs deep in-memory comparison checking (
_facade_entry_changedand_pano_entry_changed) to only raise dirty flags if values are materially different from their initialized states (ignoring transient volatile fields). - Saves to disk only when a cache's flag is
True, completely bypassing redundant writes and reducing disk I/O cycles up to 100% on repeat runs. - Increases auto-save checkpoint intervals to every
25newly resolved blocks, minimizing CPU serialization pauses.
Generates a clean, perpendicular diagnostic observation map export/debug/global_observation_map.png featuring:
- A mathematically locked 1:1 aspect ratio centered perfectly over the coordinates grid.
- Anti-aliased high-contrast text rendering using clean, dynamic vector TrueType system fonts (
Arial,DejaVuSans) with cross-platform fallback checks. - Legend symbology neatly positioned in the bottom-left corner for maximum visibility and clear progress tracking.
The master script automatically triggers Blender background processing using blender_script.py.
The system injects an advanced hardware configuration tool inside blender_script.py to ensure high viewport frame rates and ultra-fast rendering on all machines:
- Apple Silicon: Programmatically registers and binds cycles rendering to METAL GPU compute.
- Nvidia Desktop: Programmatically registers and binds cycle rendering to OPTIX or CUDA compute engines.
- Automatically scales rendering threads and locks viewport navigation under GPU hardware acceleration on both Mac (M1) and PC platforms.
- Procedural Geometry Compiler: Reads
export/reconstruction_export.jsoncontaining vertices and faces. - Texturing & Material Slotting: Binds cardinally stitched horizontal panoramas to vertical storefront quads and maps exact UV coordinates.
- Bypassed Transparent Fallback Redundancies: Applies a fully transparent fallback texture
transparent_facade.pngto non-street-facing walls. The pipeline checksif not os.path.exists(...)beforehand, eliminating redundant writes. - Dynamic Roof Tinting: Calculates the average RGB value of all storefront textures on a block and applies it as a solid color to the block's roof geometry.
- Unified GLB Asset Compilation: Saves the project as
tecate_reconstruction.blendand exports the fully textured, self-contained scene toexport/geometry.glb(~260MB).
To open the compiled scene visually in Blender:
blender tecate_reconstruction.blend(Toggle viewport shading to Material Preview or Rendered to see the blended storefronts under full GPU compute acceleration).