| import os |
| import cv2 |
| import json |
| import numpy as np |
| import albumentations as A |
| from barcode_scanner import scan_all_barcodes |
|
|
| BARCODE_DIR = "images/barcode" |
| CHASSIS_DIR = "images/chassis" |
| OUTPUT_DIR = "images/chassis_augmented" |
| GT_PATH = "ground_truth.json" |
| AUGMENTS_PER_IMAGE = 25 |
|
|
|
|
| def get_augmentation_pipeline(): |
| return A.Compose([ |
|
|
| A.OneOf([ |
| A.RandomBrightnessContrast( |
| brightness_limit=0.4, |
| contrast_limit=0.4, |
| p=1.0 |
| ), |
| A.RandomGamma(gamma_limit=(60, 140), p=1.0), |
| A.CLAHE(clip_limit=4.0, p=1.0), |
| ], p=0.9), |
|
|
| A.OneOf([ |
| A.RandomShadow( |
| shadow_roi=(0, 0, 1, 1), |
| num_shadows_lower=1, |
| num_shadows_upper=2, |
| shadow_dimension=4, |
| p=1.0 |
| ), |
| A.RandomSunFlare( |
| flare_roi=(0, 0, 1, 0.5), |
| angle_lower=0, |
| src_radius=80, |
| p=1.0 |
| ), |
| ], p=0.5), |
|
|
| A.OneOf([ |
| A.MotionBlur(blur_limit=(3, 7), p=1.0), |
| A.GaussianBlur(blur_limit=(3, 5), p=1.0), |
| A.MedianBlur(blur_limit=3, p=1.0), |
| ], p=0.4), |
|
|
| A.OneOf([ |
| A.GaussNoise(var_limit=(10, 50), p=1.0), |
| A.ISONoise(color_shift=(0.01, 0.05), intensity=(0.1, 0.5), p=1.0), |
| A.MultiplicativeNoise(multiplier=(0.9, 1.1), p=1.0), |
| ], p=0.6), |
|
|
| A.OneOf([ |
| A.Perspective(scale=(0.02, 0.08), p=1.0), |
| A.ShiftScaleRotate( |
| shift_limit=0.05, |
| scale_limit=0.1, |
| rotate_limit=10, |
| border_mode=cv2.BORDER_REPLICATE, |
| p=1.0 |
| ), |
| A.ElasticTransform( |
| alpha=30, |
| sigma=5, |
| alpha_affine=5, |
| border_mode=cv2.BORDER_REPLICATE, |
| p=1.0 |
| ), |
| ], p=0.7), |
|
|
| A.OneOf([ |
| A.ImageCompression(quality_lower=60, quality_upper=95, p=1.0), |
| A.Downscale(scale_min=0.5, scale_max=0.9, p=1.0), |
| ], p=0.3), |
|
|
| A.OneOf([ |
| A.CoarseDropout( |
| max_holes=8, |
| max_height=2, |
| max_width=30, |
| min_holes=2, |
| fill_value=128, |
| p=1.0 |
| ), |
| A.GridDistortion(num_steps=5, distort_limit=0.1, p=1.0), |
| ], p=0.4), |
|
|
| ]) |
|
|
|
|
| def augment_dataset(): |
| os.makedirs(OUTPUT_DIR, exist_ok=True) |
|
|
| print("[1/3] Loading ground truth from barcodes...") |
| ground_truths = scan_all_barcodes(BARCODE_DIR) |
| ground_truths = {k: v for k, v in ground_truths.items() if v} |
| print(f" Got {len(ground_truths)} labeled pairs") |
|
|
| chassis_files = sorted([ |
| f for f in os.listdir(CHASSIS_DIR) |
| if f.lower().endswith(('.jpg', '.jpeg', '.png')) |
| and os.path.splitext(f)[0] in ground_truths |
| ]) |
| print(f" Found {len(chassis_files)} chassis images with labels") |
|
|
| pipeline = get_augmentation_pipeline() |
| augmented_gt = {} |
| total = 0 |
|
|
| print(f"\n[2/3] Augmenting — {AUGMENTS_PER_IMAGE} variations per image...") |
|
|
| for fname in chassis_files: |
| key = os.path.splitext(fname)[0] |
| label = ground_truths[key] |
| img_path = os.path.join(CHASSIS_DIR, fname) |
| img = cv2.imread(img_path) |
|
|
| if img is None: |
| print(f" [SKIP] Could not read {fname}") |
| continue |
|
|
| img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) |
|
|
| orig_name = f"{key}_orig.jpg" |
| cv2.imwrite(os.path.join(OUTPUT_DIR, orig_name), img) |
| augmented_gt[orig_name] = label |
| total += 1 |
|
|
| for i in range(AUGMENTS_PER_IMAGE): |
| try: |
| augmented = pipeline(image=img_rgb)["image"] |
| aug_bgr = cv2.cvtColor(augmented, cv2.COLOR_RGB2BGR) |
| aug_name = f"{key}_aug{i:03d}.jpg" |
| cv2.imwrite(os.path.join(OUTPUT_DIR, aug_name), aug_bgr) |
| augmented_gt[aug_name] = label |
| total += 1 |
| except Exception as e: |
| print(f" [WARN] Augmentation failed for {fname} variation {i}: {e}") |
|
|
| print(f" {key} -> {AUGMENTS_PER_IMAGE + 1} images (label: {label})") |
|
|
| with open(GT_PATH, "w") as f: |
| json.dump(augmented_gt, f, indent=2) |
|
|
| print(f"\n[3/3] Done!") |
| print(f" Total images generated : {total}") |
| print(f" Saved to : {OUTPUT_DIR}/") |
| print(f" Ground truth saved to : {GT_PATH}") |
| print(f"\nNext step: use these images to fine-tune PaddleOCR") |
|
|
|
|
| if __name__ == "__main__": |
| augment_dataset() |