Spaces:
Sleeping
Sleeping
Download legacy/b4.py from ansarzeinulla/9OCR: direct link, hf CLI and curl.
- Browser
- Download file 5 kB
-
https://huggingface.co/spaces/ansarzeinulla/9OCR/resolve/1b69166ff3b566e4c64f3a39f6730abc742e6bf4/legacy/b4.py
- Command line
-
hf download hf://spaces/ansarzeinulla/9OCR@1b69166ff3b566e4c64f3a39f6730abc742e6bf4/legacy/b4.py
-
curl -L -o b4.py https://huggingface.co/spaces/ansarzeinulla/9OCR/resolve/1b69166ff3b566e4c64f3a39f6730abc742e6bf4/legacy/b4.py
5 kB
| import os | |
| import random | |
| import glob | |
| import numpy as np | |
| from PIL import Image, ImageFilter | |
| import cv2 | |
| # --- CONFIGURATION --- | |
| INGREDIENTS_PATH = "ingredients" | |
| OUTPUT_PATH = "train_data5" | |
| BOX_HEIGHT = 40 | |
| BOX_WIDTH = 80 | |
| SAMPLES_PER_CLASS = 300 | |
| # 1. Generate the 163 Class Names | |
| move_classes = [] | |
| for start_hole in range(1, 10): | |
| for end_hole in range(1, 10): | |
| move_classes.append(f"{start_hole}{end_hole}") | |
| move_classes.append(f"{start_hole}{end_hole}x") | |
| classes = move_classes + ['empty'] | |
| os.makedirs(OUTPUT_PATH, exist_ok=True) | |
| def get_random_ingredient(char): | |
| files = glob.glob(os.path.join(INGREDIENTS_PATH, char, "*.png")) | |
| if not files: | |
| raise ValueError(f"No images found for character: {char}") | |
| # Convert to grayscale to act perfectly as an alpha mask | |
| return Image.open(random.choice(files)).convert('L') | |
| def add_camera_noise(img_array): | |
| """Simulates rough paper texture and camera sensor noise""" | |
| noise = np.random.randint(0, 25, img_array.shape, dtype='uint8') | |
| # Subtracting noise makes random pixels slightly darker, like paper grain | |
| noisy_img = np.clip(img_array.astype(int) - noise, 0, 255).astype('uint8') | |
| return noisy_img | |
| def create_move_image(class_name): | |
| # --- PHILOSOPHY 1: IMPERFECT PAPER BACKGROUND --- | |
| # Random RGB values mimicking paper under different lighting (off-white, warm, cool) | |
| bg_gray = random.randint(200, 255) | |
| img = Image.new('L', (BOX_WIDTH, BOX_HEIGHT), color=bg_gray) | |
| if class_name == 'empty': | |
| img_arr = add_camera_noise(np.array(img)) | |
| img = Image.fromarray(img_arr) | |
| return img.filter(ImageFilter.GaussianBlur(radius=random.uniform(0.1, 0.5))) | |
| # --- PHILOSOPHY 2: REAL PEN INK COLORS --- | |
| # Randomly pick black, dark blue, or bright blue ink | |
| ink_color = random.choice([0,15,30,45]) | |
| chars_to_draw = list(class_name) | |
| prepared_chars = [] | |
| total_width = 0 | |
| gaps = [] | |
| for i, char in enumerate(chars_to_draw): | |
| char_img = get_random_ingredient(char) | |
| # Sizing | |
| size = random.randint(14, 18) if char == 'x' else random.randint(20, 26) | |
| char_img = char_img.resize((size, size), Image.Resampling.LANCZOS) | |
| # Rotation | |
| char_img = char_img.rotate(random.randint(-15, 15), expand=True, fillcolor=0) | |
| # --- PHILOSOPHY 3: VARIABLE PEN PRESSURE --- | |
| # Randomly thicken or thin the stroke using morphology | |
| char_arr = np.array(char_img) | |
| kernel = np.ones((2, 2), np.uint8) | |
| thickness_op = random.choice(['dilate', 'erode', 'none', 'dilate']) # Bias slightly towards thicker | |
| if thickness_op == 'dilate': | |
| char_arr = cv2.dilate(char_arr, kernel, iterations=1) | |
| elif thickness_op == 'erode': | |
| char_arr = cv2.erode(char_arr, kernel, iterations=1) | |
| char_img = Image.fromarray(char_arr) | |
| # Crop tight around the character | |
| bbox = char_img.getbbox() | |
| if bbox: | |
| char_img = char_img.crop(bbox) | |
| prepared_chars.append(char_img) | |
| total_width += char_img.width | |
| # Gaps (Allowing negative numbers means strokes might overlap naturally!) | |
| if i < len(chars_to_draw) - 1: | |
| gap = random.randint(-6, 1) | |
| gaps.append(gap) | |
| total_width += gap | |
| # Translation | |
| max_start_x = BOX_WIDTH - total_width | |
| start_x = random.randint(2, max(2, max_start_x - 2)) | |
| # Paste using the EMNIST mask | |
| current_x = start_x | |
| for i, char_mask in enumerate(prepared_chars): | |
| # Create a solid block of our chosen ink color | |
| ink_layer = Image.new('RGB', char_mask.size, color=ink_color) | |
| max_y = BOX_HEIGHT - char_mask.height | |
| paste_y = random.randint(1, max(1, max_y - 1)) | |
| # Paste the ink onto the paper, using the white EMNIST digit as the stencil | |
| img.paste(ink_layer, (current_x, paste_y), mask=char_mask) | |
| if i < len(gaps): | |
| current_x += char_mask.width + gaps[i] | |
| # --- PHILOSOPHY 4: THE "DIRTY" REALITY --- | |
| # 1. Add noise | |
| img_arr = np.array(img) | |
| img_arr = add_camera_noise(img_arr) | |
| final_img = Image.fromarray(img_arr) | |
| # 2. Add random camera blur | |
| blur_radius = random.uniform(0.1, 0.8) | |
| final_img = final_img.filter(ImageFilter.GaussianBlur(radius=blur_radius)) | |
| return final_img | |
| # --- EXECUTION --- | |
| print(f"Generating {len(classes)} classes with Real-World Domain Shift...") | |
| for cls in classes: | |
| class_dir = os.path.join(OUTPUT_PATH, cls) | |
| os.makedirs(class_dir, exist_ok=True) | |
| for i in range(SAMPLES_PER_CLASS): | |
| box_img = create_move_image(cls) | |
| box_img.save(os.path.join(class_dir, f"{cls}_{i}.png")) | |
| if (classes.index(cls) + 1) % 10 == 0: | |
| print(f"Generated {classes.index(cls) + 1}/{len(classes)} classes...") | |
| print(f"\nSuccess! Generated highly robust dataset in {OUTPUT_PATH}") |