File size: 8,229 Bytes
e6edcfa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""

These functions are work on a set of images in a directory.

"""
import cv2
import copy
import glob
import os
import re
import sys
import numpy as np
from PIL import Image
from subprocess import check_output


def minify(datadir, destdir, factors=[], resolutions=[], extend='png'):
    """Using mogrify to resize rgb image



    Args:

        datadir(str): source data path

        destdir(str): save path

        factor(int): ratio of original width or height

        resolutions(int): new width or height

    """
    imgs = [os.path.join(datadir, f) for f in sorted(os.listdir(datadir))]
    imgs = [f for f in imgs if any([f.endswith(ex) for ex in ['JPG', 'jpg', 'png', 'jpeg', 'PNG']])]

    wd = os.getcwd()

    for r in factors + resolutions:
        if isinstance(r, int):
            name = 'images_{}'.format(r)
            resizearg = '{}%'.format(int(r))
        else:
            name = 'images_{}x{}'.format(r[1], r[0])
            resizearg = '{}x{}'.format(r[1], r[0])
        if os.path.exists(destdir):
            continue

        print('Minifying', r, datadir)

        os.makedirs(destdir)
        check_output('cp {}/* {}'.format(datadir, destdir), shell=True)

        ext = imgs[0].split('.')[-1]
        args = ' '.join(['mogrify', '-resize', resizearg, '-format', extend, '*.{}'.format(ext)])

        print(args)
        os.chdir(destdir)
        check_output(args, shell=True)
        os.chdir(wd)

        if ext != extend:
            check_output('rm {}/*.{}'.format(destdir, ext), shell=True)
            print('Removed duplicates')
        print('Done')


def resizemask(datadir, destdir, factors=[], resolutions=[]):
    """Using PIL.Image.resize to resize binary images with nearest-neighbor



    Args:

        datadir(str): source data path

        destdir(str): save path

        factor(float): 1/N original width or height

        resolutions(int): new width or height

    """
    mask_paths = sorted([p for p in glob.glob(os.path.join(datadir, '*'))
                         if re.search('/*\.(jpg|jpeg|png|gif|bmp)', str(p))])
    old_size = np.array(Image.open(mask_paths[0])).shape
    if len(old_size) != 2:
        old_size = old_size[:2]

    for r in factors + resolutions:
        if isinstance(r, int):
            width = int(old_size[0] / r)
            height = int(old_size[1] / r)
        else:
            width = r[0]
            height = r[1]
        if os.path.exists(destdir):
            continue
        else:
            os.makedirs(destdir)

        for i, mask_path in enumerate(mask_paths):
            mask = Image.open(mask_path)
            new_mask = mask.resize((width, height))

            base_filename = mask_path.split('/')[-1]
            new_mask.save(os.path.join(destdir, base_filename))

        print('Done')


def getbbox(mask, exponent=1):
    """Computing bboxes of foreground in the masks



    Args:

        mask: binary image

        exponent(int): the size (width or height) should be a multiple of exponent

    """

    x_center = mask.shape[0] // 2
    y_center = mask.shape[1] // 2

    x, y = (mask != 0).nonzero()  # x:height; y:width
    bbox = [min(x), max(x), min(y), max(y)]

    # nearest rectangle box that height/width is the multipler of a factor
    x_min = np.max([bbox[1] - x_center, x_center - bbox[0]]) * 2
    y_min = np.max([bbox[3] - y_center, y_center - bbox[2]]) * 2
    new_x = int(np.ceil(x_min / exponent) * exponent)
    new_y = int(np.ceil(y_min / exponent) * exponent)
    # print("A rectangle to bound the object with width and height:", (new_y, new_x))

    bbox = [x_center - new_x // 2, x_center + new_x // 2,
            y_center - new_y // 2, y_center + new_y // 2]
    return bbox


def centercrop(img, new_size):
    """Computing bboxes of foreground in the masks



    Args:

        img: PIL image

        exponent(int): the size (width or height) should be a multiple of exponent

    """
    if len(new_size) == 2:
        new_width = new_size[0]
        new_height = new_size[1]
    else:
        print('ERROR: Valid size not found. Aborting')
        sys.exit()

    width, height = img.size
    left = (width - new_width) // 2
    top = (height - new_height) // 2
    right = (width + new_width) // 2
    bottom = (height + new_height) // 2

    new_img = img.crop((left, top, right, bottom))

    return new_img


def invertmask(img, mask):
    # mask only has 0 and 1, extract the foreground
    fg = cv2.bitwise_and(img, img, mask=mask)

    # create white background
    black_bg = np.zeros(img.shape, np.uint8)
    white_bg = ~black_bg

    # masking the white background
    white_bg = cv2.bitwise_and(white_bg, white_bg, mask=mask)
    white_bg = ~white_bg

    # foreground will be added to the black area
    new_img = cv2.add(white_bg, img)

    # invert mask to 0 for foreground and 255 for background
    new_mask = np.where(mask == 0, 255, 0)

    return new_img, new_mask
    
def gen_square_crops(img, bbox, padding_color=(255, 255, 255), upscale_quality=Image.LANCZOS):
    """

    Generate square crops from an image based on a bounding box.

    

    Args:

        img: PIL Image object

        bbox: Tuple of (x0, y0, x1, y1) coordinates

        padding_color: Color for padding (default white)

        upscale_quality: Resampling method for upscaling (default LANCZOS)

    

    Returns:

        PIL Image object with square crop

    """
    img_width, img_height = img.size
    x0, y0, x1, y1 = bbox
    
    # Calculate original width and height of the bbox
    bbox_width = x1 - x0
    bbox_height = y1 - y0
    
    # Determine the size of the square crop
    new_size = max(bbox_width, bbox_height)
    
    # Calculate center of the original bbox
    center_x = x0 + bbox_width // 2
    center_y = y0 + bbox_height // 2
    
    # Calculate new coordinates that maintain the square aspect ratio
    half_size = new_size // 2
    
    # Adjust coordinates to stay within image boundaries
    new_x0 = max(0, center_x - half_size)
    new_y0 = max(0, center_y - half_size)
    new_x1 = min(img_width, center_x + half_size)
    new_y1 = min(img_height, center_y + half_size)
    
    # If we're at the edges, adjust the other side to maintain square size
    if new_x0 == 0 and new_x1 < img_width:
        new_x1 = min(img_width, new_x0 + new_size)
    elif new_x1 == img_width and new_x0 > 0:
        new_x0 = max(0, new_x1 - new_size)
        
    if new_y0 == 0 and new_y1 < img_height:
        new_y1 = min(img_height, new_y0 + new_size)
    elif new_y1 == img_height and new_y0 > 0:
        new_y0 = max(0, new_y1 - new_size)
    
    # Crop the image
    cropped_img = img.crop((new_x0, new_y0, new_x1, new_y1))
    
    # Create a new square image
    square_img = Image.new('RGB', (new_size, new_size), padding_color)
    
    # Calculate paste position (centered)
    paste_x = (new_size - (new_x1 - new_x0)) // 2
    paste_y = (new_size - (new_y1 - new_y0)) // 2
    
    # Paste the cropped image onto the square canvas
    square_img.paste(cropped_img, (paste_x, paste_y))
    
    # If the original crop was smaller than new_size, we need to resize with anti-aliasing
    if (new_x1 - new_x0) < new_size or (new_y1 - new_y0) < new_size:
        # Calculate the scale factor
        scale = new_size / max(bbox_width, bbox_height)
        
        # Resize the original crop with anti-aliasing
        resized_crop = img.crop((x0, y0, x1, y1)).resize(
            (int(bbox_width * scale), int(bbox_height * scale)),
            resample=upscale_quality
        )
        
        # Create new square image
        square_img = Image.new('RGB', (new_size, new_size), padding_color)
        
        # Calculate centered position
        paste_x = (new_size - resized_crop.width) // 2
        paste_y = (new_size - resized_crop.height) // 2
        
        # Paste the resized image
        square_img.paste(resized_crop, (paste_x, paste_y))
    
    return square_img