| """Background Difference Segmentation — Free Pixel-Level GT |
| ============================================================= |
| Fixed cameras = static background. Compute per-camera background via |
| median of 100 images, then get goat masks via |frame - background|. |
| |
| Output: Data/Detection_dataset/labels/train_seg/ with binary masks. |
| Runs on CPU. |
| """ |
| import sys,os |
| import numpy as np |
| from PIL import Image |
| from tqdm import tqdm |
| from collections import defaultdict |
|
|
| PROJECT_DIR='/home/user/goat' |
| os.chdir(PROJECT_DIR);sys.path.insert(0,PROJECT_DIR) |
|
|
| def compute_background(camera, img_dir, n_samples=100): |
| """Compute median background for one camera.""" |
| files=[f for f in os.listdir(img_dir) if camera in f and f.endswith('.jpg')] |
| files=sorted(files)[:n_samples] |
| if not files: return None |
|
|
| |
| imgs=[] |
| for f in tqdm(files, desc=f'{camera} bg load'): |
| img=np.array(Image.open(os.path.join(img_dir,f)).convert('L'),dtype=np.float32) |
| imgs.append(img) |
|
|
| |
| stack=np.stack(imgs,axis=0) |
| bg=np.median(stack,axis=0).astype(np.uint8) |
| return bg |
|
|
| def main(): |
| img_dir='Data/Detection_dataset/images/train' |
| cameras=['EastLeft','EastRight','WestLeft','WestRight'] |
|
|
| |
| backgrounds={} |
| for cam in cameras: |
| bg=compute_background(cam, img_dir) |
| if bg is not None: |
| backgrounds[cam]=bg |
| |
| out_path=f'Data/background_{cam}.png' |
| Image.fromarray(bg).save(out_path) |
| print(f'{cam} background saved: {out_path} ({bg.shape})') |
|
|
| |
| out_dir='Data/Detection_dataset/labels/train_seg' |
| os.makedirs(out_dir,exist_ok=True) |
|
|
| all_files=sorted([f for f in os.listdir(img_dir) if f.endswith('.jpg')]) |
| total_masks=0 |
|
|
| for f in tqdm(all_files, desc='Generating masks'): |
| |
| cam=None |
| for c in cameras: |
| if c in f: cam=c;break |
| if cam is None or cam not in backgrounds: continue |
|
|
| img=np.array(Image.open(os.path.join(img_dir,f)).convert('L'),dtype=np.float32) |
| bg=backgrounds[cam].astype(np.float32) |
|
|
| |
| diff=np.abs(img-bg) |
|
|
| |
| thresh=diff.mean()+2.0*diff.std() |
| mask=(diff>thresh).astype(np.uint8)*255 |
|
|
| |
| from scipy import ndimage |
| mask=ndimage.binary_fill_holes(mask).astype(np.uint8)*255 |
| |
| labeled,_=ndimage.label(mask) |
| sizes=ndimage.sum(mask,labeled,range(1,labeled.max()+1)) |
| clean_mask=np.zeros_like(mask) |
| for i,sz in enumerate(sizes,1): |
| if sz>100: |
| clean_mask[labeled==i]=255 |
| mask=clean_mask |
|
|
| |
| out_name=f.replace('.jpg','.png') |
| Image.fromarray(mask).save(os.path.join(out_dir,out_name)) |
|
|
| |
| goat_pixels=(mask>0).sum() |
| total_masks+=1 |
|
|
| print(f'\nSegmentation masks generated: {total_masks} images') |
| print(f'Saved to: {out_dir}') |
| return out_dir |
|
|
| if __name__=='__main__': |
| main() |
|
|