AIP-AD / src /data_d3.py
Nabidnur's picture
AIP-AD full artifact: code, results, figures, video, Kaggle notebook, research report
acc1556 verified
Raw History Blame Contribute Delete
1.96 kB
"""Real-IAD D3 (CVPR 2025) adapter: RGB / PS / XYZ / PCD as a 4-agent swarm.
Each D3 sample directory holds one file per modality:
RGBL05 (jpg) - color image
PS (jpg) - photometric-stereo surface map (pseudo-3D normals)
XYZ (tiff) - surface height/coordinate map
PCD (pcd) - registered point cloud
Splits per category: train=200, test=150, validation=150 (all-labeled).
The fusion layer treats the four modality scores exactly like camera views:
agents that "lie" (miss the defect or cry wolf) are inverted into information.
"""
from __future__ import annotations
import json
import os
from dataclasses import dataclass, field
MODALITIES = ("rgb", "ps", "xyz", "pcd")
@dataclass
class D3Sample:
sample_id: str
category: str
label: int
anomaly_class: str
paths: dict = field(default_factory=dict) # modality -> abs path
@property
def n_views(self):
return len(self.paths)
def load_d3_samples(category: str, image_root: str, json_root: str, split: str,
modalities=MODALITIES) -> list:
"""modalities: subset of MODALITIES; pcd requires open3d at scoring time."""
path = os.path.join(json_root, f"{category}.json")
with open(path) as f:
d = json.load(f)
base = os.path.join(image_root, category)
keymap = {"rgb": "image_path", "ps": "ps_path", "xyz": "xyz_path", "pcd": "pcd_path"}
samples = {}
for rec in d[split]:
parent = os.path.dirname(rec["image_path"])
key = f"{category}/{parent}"
s = samples.setdefault(key, D3Sample(sample_id=key, category=category,
label=0 if rec["anomaly_class"] == "OK" else 1,
anomaly_class=rec["anomaly_class"]))
for m in modalities:
rel = rec.get(keymap[m])
if rel:
s.paths[m] = os.path.join(base, rel)
return list(samples.values())