Skip to content

Feature/face anonymization#166

Open
Mah-diaa wants to merge 43 commits into
ibs-lab:devfrom
Mah-diaa:feature/face-anonymization
Open

Feature/face anonymization#166
Mah-diaa wants to merge 43 commits into
ibs-lab:devfrom
Mah-diaa:feature/face-anonymization

Conversation

@Mah-diaa

Copy link
Copy Markdown

This work was developed as part of my thesis on facial anonymization for photogrammetry scans. I tried to abide as much as possible by the rules of contribution in the wiki. I will also attempt to explain how the more "vague" parts of this works. Hope this helps!

Summary

This PR adds a deletion-based face anonymization pipeline for photogrammetry scans used in fNIRS workflows. The pipeline takes a surface plus five anatomical landmarks (Nz, Iz, Cz, LPA, RPA), isolates the head from surrounding scan artifacts/body geometry, removes the facial/ear region while preserving landmarks and cap-relevant geometry, and exports an anonymized OBJ bundle.

What changed

  • Added cedalion.geometry.photogrammetry.anonymization
  • Added anonymize_scan(...) as the main public entry point
  • Added helpers for axis normalization, head isolation, CTF alignment, cap-boundary detection, face-mask construction, vertex
    deletion, frame reversion, and anonymized OBJ export
  • Added texture sanitization during export so removed face regions are not still present in the JPG texture
  • Added examples/head_models/51_manual_5pt_anonymization.ipynb for the manual 5-landmark workflow
  • Added synthetic unit tests in tests/test_anonymization.py

Design notes

  • The workflow is landmark-based: the user provides Nz, Iz, Cz, LPA, and RPA.
  • The scan is first aligned into a CTF-style anatomical frame, anonymized there, and then transformed back to the original digitized frame by default. I did this because I was unsure whether downstream cedalion workflows require the anonymized surface to keep the same frame convention as the original scan.
  • Head isolation uses position-based seam merging for connectivity analysis. During experimentation I found that some photogrammetry heads are not represented as one clean connected component, but as several overlapping UV/geometry islands. This caused problems when selecting the largest connected region to remove scan artifacts.
  • The anonymization mask tries to stop before it reaches the optode/cap region. It estimates where the front edge of the cap starts and uses that as an upper limit for face removal. (theres also a failsafe if the subject happens to wear a cap with no optodes)
  • Several mask parameters are exposed on anonymize_scan(...), so the deletion region can be made more conservative or more aggressive if the default mask is too generous or too greedy for a given scan.
  • The pipeline preserves UVs where possible and sanitizes texture output during saving.

Validation

Added synthetic mesh tests covering the exported API and the end-to-end anonymization path:

  • missing landmark handling
  • axis normalization
  • CTF alignment
  • frame reversion
  • cap-boundary fallback
  • face-mask semantics
  • vertex deletion
  • geometry-only save path
  • full anonymization pipeline

Example Jupyter notebook

The example workflow is in:

examples/head_models/51_manual_5pt_anonymization.ipynb

It demonstrates the intended manual 5-landmark workflow:

  • load an Einstar photogrammetry scan
  • select Nz, Iz, Cz, LPA, and RPA
  • run anonymize_scan(surface, landmarks)
  • inspect the before/after result
  • save the anonymized OBJ bundle with save_anonymized_scan(...)

Before running it, update the local input/output paths near the top of the notebook:

  • SCANS_FOLDER: folder containing the subject scan folders
  • SUBJECT_ID: subject folder or scan identifier to process
  • OUT_DIR: destination folder for the anonymized OBJ/MTL/JPG output

Reviewer notes

  • The implementation is isolated under src/cedalion/geometry/photogrammetry/anonymization/.
  • The README currently contains thesis-related context because it is needed for my thesis submission. I expect to revise and clean it up once the PR is closer to being ready to merge.

Mah-diaa added 30 commits March 9, 2026 16:10
…-10 comparison

- Strip validator.py to practical sanity check (face removed, mesh valid, protected points intact)
- Remove fake metrics that just confirmed a==a (curvature, non-facial displacement, landmark deviation)
- Add notebook 52: end-to-end validation comparing 10-10 system on original vs anonymized
- Update __init__.py, ui.py, and tests to use new ValidationResult API
Implements automatic and semi-manual face removal for GDPR-compliant
anonymization of 3D photogrammetry scans:
- MediaPipe-based nasion detection with manual click fallback
- Geometric landmark estimation (Iz, Cz, LPA, RPA) from nasion
- Face mask via spherical projection of MediaPipe face contour
- Interactive refinement with click-to-delete circles
- Vertex deletion preserving all landmarks and optodes
Refinement step now supports:
- T key to toggle between add and remove mode
- +/- keys to adjust brush radius (5-100mm)
- Color-coded feedback (yellow=add, cyan=remove)
- Status text updates live with mode and radius
Move controls text to lower-right, increase font size to 14, set color
to black for better visibility. Add detailed keyboard shortcut
instructions in the markdown cell. Clear all cell outputs.
…_nasion and get_facial_region_mask_from_nasion
# Conflicts:
#	environment_dev.yml
#	examples/head_models/43_crs_and_headmodel.ipynb
…ared utils, remove dead parameters and never-firing guards
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant