Counterfactual PGD attack on MedGS / 3DGS Gaussian parameters using gradients from the Sybil lung-cancer risk classifier.
- Linux + NVIDIA GPU
- Conda installed
- This repository cloned with submodules available
Run from repository root:
bash setup.shThis script:
- updates MedGS submodules,
- creates/updates the
sybil_interpretabilityconda env, - installs PyTorch + CUDA packages,
- builds MedGS CUDA extensions (
diff-gaussian-rasterization,fused-ssim,simple-knn).
The Sybil config expects checkpoints under:
data/weights/sybil/checkpoints
Required files include:
sybil_0.pt...sybil_4.ptsybil_ensemble_simple_calibrator.json
conda activate sybil_interpretability
export MEDGS_ROOT="$PWD/submodules/MedGS"
export PYTHONPATH="$PWD/submodules/Sybil/src:$PWD/submodules/Sybil/src_sybil:$PWD/submodules/MedGS:$PYTHONPATH"Example command used in this workspace:
python main.py \
--model_path data/1/1 \
--source_path data/1 \
--masks data/1/masks \
--output_ply data/1/1/attacked_clf.ply \
--sigma 0.5 --remove_top_pct 10.0 \
--props f_dc_0 f_dc_1 f_dc_2 \
--eps 0.5 --steps 10 \
--attack_mode untargeted \
--classifier_config_dir "$PWD/submodules/Sybil/configs" \
--classifier_config_name nlst_sybil_ensemble_inference \
--target_year 5 \
--max_slices 60The attacked Gaussian point cloud is written to:
data/1/1/attacked_clf.ply
The analysis compares attacked renders vs clean renders and extracts connected components inside lungs.
cp -r data/1/1 data/1/1_attacked_clf
cp data/1/1/attacked_clf.ply data/1/1_attacked_clf/point_cloud/iteration_30000/point_cloud.ply
python submodules/MedGS/render.py \
--model_path data/1/1_attacked_clf \
--source_path data/1 \
--iteration 30000 \
--camera mirror \
--pipeline imgClean renders are read from data/1/1/render_img and attacked renders from
data/1/1_attacked_clf/render_img.
python -c "from analysis.image_diff import generate_image_diffs as f; f('data/1/1_attacked_clf/render_img','data/1/1/render_img','analysis/run1/diffs')"python -c "from analysis.component_analysis import analyze_connected_components as f; f(diff_folder='analysis/run1/diffs', output_folder='analysis/run1/components', lung_masks_folder='data/1/lung_masks', threshold=None, min_area=20, closing_radius=5)"Outputs:
analysis/run1/components/centroids.csvanalysis/run1/components/masks/analysis/run1/components/visualizations/
python -c "from analysis.overlay_circles import draw_circles_on_originals as f; f(csv_path='analysis/run1/components/centroids.csv', originals_folder='data/1/original', output_folder='analysis/run1/annotated', radius_mode='area', scale=1.5, min_radius=10, thickness=2, color=(255,0,0))"alternatively the full pipeline can by run via
$ bash run_patient_pipeline.sh <patient_id>.
├── attack/ attack pipeline
│ ├── main.py entry point
│ ├── cli.py command-line arguments
│ ├── constants.py Sybil / MedGS constants
│ ├── medgs_bootstrap.py finds MedGS on sys.path
│ ├── ply_io.py raw PLY reading + SH-degree detection
│ ├── geometry.py camera matrices, 3D covariances, projection
│ ├── camera_loader.py matches cameras.json with mask files
│ ├── mask_filtering.py selects Gaussians lying inside masks
│ ├── attribute_mapping.py PLY fields <-> GaussianModel tensor slices
│ ├── classifier_loader.py Sybil ensemble (frozen weights)
│ ├── volume_rendering.py renders slices into a Sybil-shaped volume
│ ├── pgd_attack.py PGD loop on Gaussian attributes
│ └── scene_setup.py MedGS Scene initialization
│
└── analysis/ post-attack analysis
├── image_diff.py per-pixel absolute diffs between two folders
├── component_analysis.py connected components inside the lung masks
└── overlay_circles.py draws circles on originals from centroid CSV
- Train MedGS on a CT volume.
- Run
python main.py ...to generate an attacked PLY. - Render attacked slices with
submodules/MedGS/render.py. - Run
analysis/image_diff.pyto create attacked-vs-clean diffs. - Run
analysis/component_analysis.pyto keep components inside lungs. - Run
analysis/overlay_circles.pyto annotate originals.

Figure 1. 3D representation of lungs with a tumor.

Figure 2. Comparison of nodules before and after the attack, including physician annotations (a plus sign indicates positive changes regarding prognosis, a minus sign indicates negative changes, and the number indicates severity).