|
13 | 13 | import pickle
|
14 | 14 | import random
|
15 | 15 | from copy import deepcopy
|
| 16 | +from itertools import product |
16 | 17 | from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
17 | 18 |
|
18 | 19 | import numpy as np
|
@@ -2549,6 +2550,92 @@ def skew(
|
2549 | 2550 | return imutils.ret_and_save_image(aug_image, output_path, src_mode)
|
2550 | 2551 |
|
2551 | 2552 |
|
| 2553 | +def split_and_shuffle( |
| 2554 | + image: Union[str, Image.Image], |
| 2555 | + output_path: Optional[str] = None, |
| 2556 | + n_columns: int = 3, |
| 2557 | + n_rows: int = 3, |
| 2558 | + seed: int = 10, |
| 2559 | + metadata: Optional[List[Dict[str, Any]]] = None, |
| 2560 | + bboxes: Optional[List[Tuple]] = None, |
| 2561 | + bbox_format: Optional[str] = None, |
| 2562 | +) -> Image.Image: |
| 2563 | + """ |
| 2564 | + Splits the image into a grid of tiles (determined by n_columns and n_rows) and |
| 2565 | + shuffles the tiles randomly. The resulting image is the concatenation of the |
| 2566 | + shuffled tiles into the same grid format (resulting in an image of the same size) |
| 2567 | +
|
| 2568 | + @param image: the path to an image or a variable of type PIL.Image.Image |
| 2569 | + to be augmented |
| 2570 | +
|
| 2571 | + @param output_path: the path in which the resulting image will be stored. |
| 2572 | + If None, the resulting PIL Image will still be returned |
| 2573 | +
|
| 2574 | + @param n_columns: number of columns to split the image into |
| 2575 | +
|
| 2576 | + @param n_rows: number of rows to split the image into |
| 2577 | +
|
| 2578 | + @param seed: seed for numpy random generator to select random order for shuffling |
| 2579 | +
|
| 2580 | + @param metadata: if set to be a list, metadata about the function execution |
| 2581 | + including its name, the source & dest width, height, etc. will be appended |
| 2582 | + to the inputted list. If set to None, no metadata will be appended or returned |
| 2583 | +
|
| 2584 | + @param bboxes: a list of bounding boxes can be passed in here if desired. If |
| 2585 | + provided, this list will be modified in place such that each bounding box is |
| 2586 | + transformed according to this function |
| 2587 | +
|
| 2588 | + @param bbox_format: signifies what bounding box format was used in `bboxes`. Must |
| 2589 | + specify `bbox_format` if `bboxes` is provided. Supported bbox_format values are |
| 2590 | + "pascal_voc", "pascal_voc_norm", "coco", and "yolo" |
| 2591 | +
|
| 2592 | + @returns: the augmented PIL Image |
| 2593 | + """ |
| 2594 | + np.random.seed(seed) |
| 2595 | + |
| 2596 | + image = imutils.validate_and_load_image(image) |
| 2597 | + |
| 2598 | + assert n_columns > 0, "Expected 'n_columns' to be a positive integer" |
| 2599 | + assert n_rows > 0, "Expected 'n_rows' to be a positive integer" |
| 2600 | + |
| 2601 | + func_kwargs = imutils.get_func_kwargs(metadata, locals()) |
| 2602 | + src_mode = image.mode |
| 2603 | + |
| 2604 | + width, height = image.size |
| 2605 | + width_per_tile = width // n_columns |
| 2606 | + height_per_tile = height // n_rows |
| 2607 | + |
| 2608 | + grid = product( |
| 2609 | + range(0, height - height % height_per_tile, height_per_tile), |
| 2610 | + range(0, width - width % width_per_tile, width_per_tile), |
| 2611 | + ) |
| 2612 | + |
| 2613 | + sub_images = [] |
| 2614 | + for y0, x0 in grid: |
| 2615 | + bbox = (x0, y0, x0 + width_per_tile, y0 + height_per_tile) |
| 2616 | + sub_images.append(image.crop(bbox)) |
| 2617 | + |
| 2618 | + if len(sub_images) == 2: |
| 2619 | + sub_images[0], sub_images[1] = sub_images[1], sub_images[0] |
| 2620 | + else: |
| 2621 | + np.random.shuffle(sub_images) |
| 2622 | + |
| 2623 | + aug_image = Image.new("RGB", (width_per_tile * n_columns, height_per_tile * n_rows)) |
| 2624 | + for i, sub_image in enumerate(sub_images): |
| 2625 | + x = i % n_columns |
| 2626 | + y = i // n_columns |
| 2627 | + aug_image.paste(sub_image, (x * width_per_tile, y * height_per_tile)) |
| 2628 | + |
| 2629 | + imutils.get_metadata( |
| 2630 | + metadata=metadata, |
| 2631 | + function_name="split_and_shuffle", |
| 2632 | + aug_image=aug_image, |
| 2633 | + **func_kwargs, |
| 2634 | + ) |
| 2635 | + |
| 2636 | + return imutils.ret_and_save_image(aug_image, output_path, src_mode) |
| 2637 | + |
| 2638 | + |
2552 | 2639 | def vflip(
|
2553 | 2640 | image: Union[str, Image.Image],
|
2554 | 2641 | output_path: Optional[str] = None,
|
|
0 commit comments