17 bone samples (“locations”), each photographed 4 times by a multi-LED rig. Depth was recovered per sample with Woodham photometric stereo (per-pixel surface normals from 3 single-LED images) + Frankot–Chellappa FFT integration.
Capture convention (verified on all 17 locations)
| File | Lighting | Notes |
|---|---|---|
pN-1.jpg | All LEDs on | Uniform reference; NOT used for the depth solve |
pN-2.jpg | RIGHT LED | Light azimuth ~0° |
pN-3.jpg | TOP/upper LED | Light azimuth ~40–90° |
pN-4.jpg | LEFT LED | Light azimuth ~180° |
Files in this folder
depth_summary— Google Sheet, one row per location (see columns below)bone_depth_batch.py— the program that produced everything (re-runnable)- Image bundle (.zip) — 17× depth maps + 17× normal maps + 17× panel figures (drag the zip in, then right-click and extract, or just open it — the PNGs are inside)
depth_summary columns
| Column | Meaning |
|---|---|
location | Sample number 1..17 |
width, height | Working resolution used (640×360) |
n_lights | LEDs used for the solve (3) |
azimuths_deg | Light directions used (0; 90; 180 = right; top; left) |
depth_min/max | Relative depth extremes |
depth_mean/std | Mean is ~0 by construction; std = overall roughness |
relief_p2_p98 | Headline number: robust peak-to-valley surface relief (bigger = more pronounced 3-D structure on that sample) |
What the numbers mean
Photometric stereo recovers SHAPE up to an unknown offset and scale, so depth is RELATIVE (working-pixel units), not absolute millimetres. The shape and the relative height differences are meaningful; the absolute value is not. To get true mm later you would add one coaxial point-light image and rescale — see the note at the bottom of bone_depth_batch.py.
How to re-run
pip install numpy opencv-python scipy matplotlib
# put the pN-M.jpg photos in a folder, set INPUT_FOLDER at the top of the
# script to point at it, then:
python bone_depth_batch.py
Outputs land in ./depth_outputs (the PNGs and the CSV in this folder).
Caveat
With 3 oblique, uncalibrated LEDs on a glossy specimen, fine relief is reliable but the very-low-frequency “overall tilt” of each map is approximate — inherent to photometric stereo, not a bug. Specular-glint removal is on by default.
Per-location depth estimates
One row per sample location. depth_px is the robust peak-to-valley relief in working-resolution pixels; depth_mm_at_30mm_FOV is the same relief converted to millimetres assuming a 30 mm field of view.
| location | image_width_px | image_height_px | depth_px | depth_mm_at_30mm_FOV | max_relief_px | roughness_px |
|---|---|---|---|---|---|---|
| 1 | 640 | 360 | 64.48 | 3.023 | 73.19 | 19.35 |
| 2 | 640 | 360 | 61.28 | 2.872 | 66.83 | 17.67 |
| 3 | 640 | 360 | 59.19 | 2.774 | 68.28 | 15.59 |
| 4 | 640 | 360 | 57.88 | 2.713 | 65.37 | 17.55 |
| 5 | 640 | 360 | 56.94 | 2.669 | 65.25 | 17.73 |
| 6 | 640 | 360 | 52.73 | 2.472 | 61.34 | 17.48 |
| 7 | 640 | 360 | 51.93 | 2.434 | 57.16 | 16.00 |
| 8 | 640 | 360 | 50.48 | 2.366 | 60.13 | 13.58 |
| 9 | 640 | 360 | 45.61 | 2.138 | 56.37 | 11.69 |
| 10 | 640 | 360 | 48.49 | 2.273 | 55.87 | 12.99 |
| 11 | 640 | 360 | 53.44 | 2.505 | 59.52 | 15.33 |
| 12 | 640 | 360 | 56.31 | 2.64 | 64.65 | 17.59 |
| 13 | 640 | 360 | 55.95 | 2.623 | 62.28 | 18.23 |
| 14 | 640 | 360 | 55.79 | 2.615 | 62.69 | 17.44 |
| 15 | 640 | 360 | 49.82 | 2.335 | 55.88 | 14.91 |
| 16 | 640 | 360 | 42.61 | 1.997 | 52.88 | 11.36 |
| 17 | 640 | 360 | 46.01 | 2.157 | 55.33 | 11.37 |
Across the 17 locations, mean peak-to-valley relief is roughly 2.5 mm (range 2.0–3.0 mm) under the 30 mm field-of-view assumption. These millimetre figures inherit the relative-scale caveat above: the pixel-to-mm conversion assumes the stated FOV rather than a metric calibration.