Spaces:
Runtime error
Runtime error
| import json | |
| import copy | |
| from typing import Optional | |
| from PIL import Image | |
| bbox = [float, float, float, float] | |
| annotation = { | |
| "id": int, | |
| "image_id": int, | |
| "category_id": int, | |
| "bbox": bbox, | |
| "ignore": int, | |
| "iscrowd": int, | |
| "area": float, | |
| } | |
| small_image = { | |
| "image": Image, | |
| "area": bbox | |
| } | |
| def split_image(image: Image, | |
| hint_size_min: tuple[int, int], | |
| hint_size_max: tuple[int, int], | |
| overlap: float = 0.1) -> list[small_image]: | |
| """ | |
| Given an image and a hint size, split the image into a list of images. | |
| New images are overlapped with other images by the overlap ratio. | |
| :param image: The image to split. typically a large image. 1kx1k ~ 10kx10k | |
| :param hint_size_min: The minimum size of the output image. | |
| :param hint_size_max: The maximum size of the output image. | |
| :param overlap: The overlap ratio of the output image. | |
| :return: A list of images. | |
| """ | |
| Wi, Hi = image.size | |
| Wmin, Hmin = hint_size_min | |
| Wmax, Hmax = hint_size_max | |
| assert Wmin <= Wmax <= Wi | |
| assert Hmin <= Hmax <= Hi | |
| w_search = search(Wi, Wmin, Wmax, overlap) | |
| h_search = search(Hi, Hmin, Hmax, overlap) | |
| if w_search is None or h_search is None: | |
| raise ValueError('The image is too small to split.') | |
| w_count, output_width, last_output_width, width_overlap = w_search | |
| h_count, output_height, last_output_height, height_overlap = h_search | |
| images = [] | |
| for h_index in range(h_count): | |
| h = h_index * (output_height - height_overlap) | |
| for w_index in range(w_count): | |
| w = w_index * (output_width - width_overlap) | |
| small = { | |
| "image": image.crop((w, h, w + output_width, h + output_height)), | |
| "area": (w, h, output_width, output_height) | |
| } | |
| images.append(small) | |
| if last_output_width > 0: | |
| w = Wi - output_width | |
| small = { | |
| "image": image.crop((w, h, w + output_width, h + output_height)), | |
| "area": (w, h, output_width, output_height) | |
| } | |
| images.append(small) | |
| return images | |
| def search(input: int, | |
| output_min: int, | |
| output_max: int, | |
| overlap: float) -> Optional[tuple[int, int, int, int]]: | |
| """ | |
| example 1: | |
| input: 8000, output: 1000, overlap: 0.1 | |
| 8000 // (1000 - 100) = 8 | |
| 8000 % (1000 - 100) = 800 | |
| count = 8, output = 1000, last_output = 800, overlap_pixels = 100 | |
| example 2: | |
| input: 7200, output: 800, overlap: 0.1 | |
| 7200 // (800 - 80) = 10 | |
| 7200 % (800 - 80) = 0 | |
| count = 10, output = 800, last_output = 0, overlap_pixels = 80 | |
| :param input: The length of the input image. | |
| :param output_min: The minimum length of the output image. | |
| :param output_max: The maximum length of the output image. | |
| :param overlap: The overlap ratio of the output image. | |
| :return: A tuple of (count, output, last_output, overlap_pixels). | |
| """ | |
| for output in range(output_max, output_min - 1, -1): | |
| overlap_pixels = int(output * overlap) | |
| last_output = input % (output - overlap_pixels) | |
| if last_output == 0 or output_min <= last_output <= output_max: | |
| count = input // (output - overlap_pixels) | |
| return count, output, last_output, overlap_pixels | |
| return None | |
| def box_intersected(box1: bbox, box2: bbox) -> bool: | |
| """ | |
| Check if two boxes are intersected. | |
| :param box1: The first box. | |
| :param box2: The second box. | |
| :return: True if the two boxes are intersected. | |
| """ | |
| x1, y1, w1, h1 = box1 | |
| x2, y2, w2, h2 = box2 | |
| return x1 < x2 + w2 and x2 < x1 + w1 and y1 < y2 + h2 and y2 < y1 + h1 | |
| def fit_in_area(annotations: list[annotation], in_area: bbox) -> list[annotation]: | |
| result = [] | |
| for old in annotations: | |
| ann = copy.deepcopy(old) | |
| result.append(ann) | |
| x, y, w, h = ann["bbox"] | |
| if x < in_area[0]: | |
| ann["bbox"][0] = 0 | |
| else: | |
| ann["bbox"][0] -= in_area[0] | |
| if y < in_area[1]: | |
| ann["bbox"][1] = 0 | |
| else: | |
| ann["bbox"][1] -= in_area[1] | |
| if x + w > in_area[0] + in_area[2]: | |
| ann["bbox"][2] = in_area[2] - ann["bbox"][0] | |
| if y + h > in_area[1] + in_area[3]: | |
| ann["bbox"][3] = in_area[3] - ann["bbox"][1] | |
| return result | |
| small_image_with_labels = { | |
| "image": Image, | |
| "area": bbox, | |
| "labels": list[annotation] | |
| } | |
| def split_image_with_labels(image: Image, | |
| labels: list[annotation], | |
| hint_size_min: tuple[int, int], | |
| hint_size_max: tuple[int, int], | |
| overlap: float = 0.1) -> list[small_image]: | |
| small_imgs = split_image(image, hint_size_min, hint_size_max, overlap) | |
| result = [] | |
| for small_img in small_imgs: | |
| small_labels = [ann for ann in labels if box_intersected(ann["bbox"], small_img["area"])] | |
| small_labels = fit_in_area(small_labels, small_img["area"]) | |
| result.append({ | |
| "image": small_img["image"], | |
| "area": small_img["area"], | |
| "labels": small_labels | |
| }) | |
| return result | |
| def main(): | |
| image = Image.open('../datasets/Das3300161.jpg') | |
| small_imgs = split_image(image, (800, 800), (1000, 1000), 0.1) | |
| labels = json.load(open('../datasets/result.json')) | |
| annotations = list(filter(lambda ann: ann["image_id"] == 28, labels["annotations"])) | |
| for small_img in small_imgs: | |
| small_labels = [ann for ann in annotations if box_intersected(ann["bbox"], small_img["area"])] | |
| small_labels = fit_in_area(small_labels, small_img["area"]) | |
| # save small_labels to json | |
| json.dump(small_labels, open('datasets/' + str(small_img["area"]) + '.json', 'w')) | |
| # save small_image["image"] to file | |
| small_img["image"].save('datasets/' + str(small_img["area"]) + '.jpg') | |
| if __name__ == '__main__': | |
| main() | |