A Python-based forensic facial approximation tool that estimates a 3D face surface from skull measurements. Built using real forensic anthropology data — the same tissue depth methodology used by forensic artists in criminal investigations.
⚠️ Demographic limitation: Tissue depth values are sourced from De Greef et al. (2006), Table 2a: Caucasian adult females, age 18–29, BMI 20–25. 17 of 18 landmarks map directly to DeGreef measurement points. One landmark (cheek zygomatic prominence) uses DeGreef's nearest available point — lateral orbit (pt 46/25, 10.0 mm) — as an anatomical proxy, documented in the source code. Results for other demographic groups (different sex, age, ancestry, or BMI) will be inaccurate. Expanding demographic coverage is a planned future improvement.
Enter skull measurements → get an interactive 3D face approximation.
The tool uses published forensic tissue depth data to estimate where the skin surface sits above each skull landmark, interpolates a smooth surface using Radial Basis Function (RBF) interpolation, and applies Gaussian deformations to sculpt facial features (eye sockets, brow ridge, nose, cheekbones, chin, jaw hollows).
Run app.py and open http://127.0.0.1:8050 — adjust the sliders to see the face update live.
git clone https://github.com/saornek/facial-approximation.git
cd facial-approximation
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
python app.pymacOS note: Flask and Dash run on port 8050. If you see a blank page on port 5000, macOS Monterey+ reserves that port for AirPlay.
| Measurement | Description | Default (avg) |
|---|---|---|
| Bizygomatic Width | Cheekbone to cheekbone | 130 mm |
| Face Height | Nasion to chin | 120 mm |
| Nasal Height | Nasion to nasal base | 50 mm |
| Mandible Width | Jaw width at gonion | 90 mm |
- Tissue depth data (
data/tissue_depths.py) — 18 skull landmarks with average skin-to-bone distances in mm - Landmark coordinates (
core/landmarks.py) — 3D positions of each landmark on a normalized skull - Surface computation (
core/surface.py) — pushes each landmark outward along its surface normal by the tissue depth - Scaling (
core/scaler.py) — adjusts landmark positions based on user-entered measurements vs. population averages - RBF interpolation (
core/smooth_mesh.py) — fits a smooth continuous surface through the sparse landmark points - Sculpting (
core/sculpt.py) — applies Gaussian bump/depression functions to carve in facial features - Dashboard (
app.py) — Plotly Dash interface with live-updating 3D surface plot
face_approximation/
├── core/
│ ├── landmarks.py # 3D skull landmark coordinates
│ ├── surface.py # Skull → skin surface computation
│ ├── scaler.py # Measurement-based scaling
│ ├── smooth_mesh.py # RBF interpolation
│ └── sculpt.py # Gaussian facial feature sculpting
├── data/
│ └── tissue_depths.py # Forensic tissue depth table
├── app.py # Dash dashboard (main entrypoint)
└── requirements.txt
- Demographic variation — tissue depth adjustment by sex, age, and ancestry
- Head wrapping — map the flat face surface onto a sphere
- Export to
.obj/.stlfor use in 3D software - Hair approximation
- More skull landmarks for higher resolution
- De Greef, S. et al. (2006). Large-scale in-vivo Caucasian facial soft tissue thickness database for craniofacial reconstruction. Forensic Science International 159S, S126–S146. https://doi.org/10.1016/j.forsciint.2006.02.031 — primary source for tissue depth values used in this project. Values taken from Table 2a (Caucasian adult females, age 18–29, BMI 20–25, n=127–149). 17 of 18 landmarks map directly to DeGreef measurement points; the cheek zygomatic prominence uses DeGreef's lateral orbit (pt 46/25) as the nearest anatomical proxy.
Python · NumPy · SciPy · Plotly Dash · Flask
