← Back to résumé

Research Assistant — Automated Heritage Damage Detection

"Ancient" project, KMUTT · grant-funded, paid in installments

Dec 2025 – PresentIndependent contribution
MetricBeforeAfter
Overall accuracy90.08%99.52%
Crack IoU78.25%83.25%
Spalling IoU89.34%97.28%
Decay IoU54.58%98.43%
Unseen surface—0.0048%

What the project does

Automated detection of cracks, spalling, and decay on Thai heritage structures from real terrestrial-laser-scan (TLS) point clouds — real scans of Wat Chaiwatthanaram and Wat Yai Chai Mongkhon, both Ayutthaya-era sites, some over 5 million points per scan.

Two parallel tracks — and which one is his

The project's git-committed src/ package (a RandLA-Net/PointNet++ semantic-segmentation track, plus a FastAPI backend) is teammates' work. His own contribution is a separate, self-directed track: a full multi-view 3D→2D rendering and consensus-classification pipeline, built and iterated across many solo engineering sessions using real Colab GPU runs rather than guesswork.

EWA Surface Splatting

The renderer projects the point cloud into 2D from 32 camera views, then reaches a per-pixel consensus across views to classify damage. The original isotropic point-splat renderer left visible gaps at oblique camera angles. Rewrote it around EWA (Elliptical Weighted Average) surface splatting — anisotropic covariance-based projection adapted from the same math family used in 3D Gaussian Splatting (Zwicker et al., 2001) — applied as a closed-form, single-pass render, with no training involved. Result: the rate of surface points never seen by any camera view dropped to 0.0048%, and every IoU metric improved substantially (see table).

  1. TLS point cloudReal scans of Ayutthaya-era temples, some over 5M points
  2. 32 camera viewsThe cloud is projected into 2D from 32 viewpoints
  3. EWA splatting renderAnisotropic, covariance-based projection in one pass
  4. Per-pixel consensusThe views are combined to classify each surface point
  5. Damage labelsCrack, spalling and decay

Real bugs found and fixed

Along the way, diagnosed and fixed several real rendering and reliability bugs on live Colab GPU runs, not in the abstract:

  • Point-splat size asymmetry across damage classes was causing large classes to silently overwrite small ones — fixing it cut false positives by 76–89%.
  • Replaced hard pixel-overwrite rendering with proper Gaussian-weighted alpha blending, closing black holes at splat boundaries.
  • Switched label decoding from hard one-hot to SVLS-style soft decoding, so ambiguous boundary pixels aren't forced into a false-confident class.
  • Root-caused and fixed a Google Drive FUSE I/O hang (a native-level block that a plain SIGINT can't interrupt) with a signal-based timeout guard.
  • Found and fixed a missing pcd.normals reassignment bug that silently broke Ball Pivoting mesh reconstruction whenever cached normals were reused.

Point-cloud simplification pipeline

Built independently: density-based clustering (DBSCAN/HDBSCAN) to segment the point cloud, curvature/roughness saliency scoring to identify which points carry structurally important detail, then adaptive voxel downsampling applied only to the non-salient points. On a real 5.49-million-point scan, this cut the cloud to 1.53 million points — a 72.14% reduction — while preserving the fine detail that damage detection depends on.

  1. 5.49M-point scanA real heritage scan
  2. DBSCAN / HDBSCANDensity-based segmentation of the cloud
  3. Saliency scoringCurvature and roughness mark the points that matter
  4. Adaptive voxel downsamplingApplied only to the non-salient points
  5. 1.53M points−72.14%, damage-relevant detail kept

A documented decision not to pursue 3D Gaussian Splatting

Evaluated whether 3D Gaussian Splatting could be used directly for the semantic-segmentation task itself (not just as the projection math EWA borrows from). Root-caused four compounding bugs in a teammate's existing 3DGS implementation, scoped what a real fix would take, and made a reasoned decision not to pursue it — documented with the actual reasoning rather than left as an unexplained dead end.

Code

Private research code; it is not published on GitHub.