import csv import hashlib from pathlib import Path import numpy as np from PIL import Image def sha(path): return hashlib.sha256(Path(path).read_bytes()).hexdigest() def read_manifest(path): path=Path(path) with path.open(newline='') as stream: rows=list(csv.DictReader(stream)) if not rows or not {'id','image','mask','split','group'} <= set(rows[0]): raise ValueError('Manifest requires id,image,mask,split,group') ids=set(); groups={}; hashes={} for r in rows: if r['id'] in ids or not r['id'] or '/' in r['id'] or '\\' in r['id'] or r['id'] in ('.','..'): raise ValueError('Duplicate/unsafe id') ids.add(r['id']) if r['split'] not in ('train','dev','cal','test') or not r['group']: raise ValueError('Invalid split/group') if r['group'] in groups and groups[r['group']]!=r['split']: raise ValueError('Scene group crosses splits') groups[r['group']]=r['split'] for k in ('image','mask'): r[k]=str((path.parent/r[k]).resolve()) if not Path(r[k]).is_file(): raise FileNotFoundError(r[k]) h=sha(r['image']) if h in hashes and hashes[h]!=r['split']: raise ValueError('Identical image bytes cross splits') hashes[h]=r['split'] return rows def read_pair(row): with Image.open(row['image']) as img: image=np.asarray(img).copy() with Image.open(row['mask']) as img: mask=np.asarray(img).copy() if image.ndim!=2 or image.dtype!=np.uint8: raise ValueError('Prototype requires native-resolution 8-bit grayscale; no implicit conversion') if mask.ndim!=2 or mask.shape!=image.shape: raise ValueError('Mask shape/format mismatch') return image.astype(np.float32)/255., mask>0