image.py 10.8 KB
Newer Older
D
dzhwinter 已提交
1
#   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve.
D
dzhwinter 已提交
2
#
D
dzhwinter 已提交
3 4 5
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
D
dzhwinter 已提交
6
#
D
dzhwinter 已提交
7
#     http://www.apache.org/licenses/LICENSE-2.0
D
dzhwinter 已提交
8
#
D
dzhwinter 已提交
9 10 11 12 13
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
D
dangqingqing 已提交
14
"""
D
dangqingqing 已提交
15
This file contains some common interfaces for image preprocess.
D
dangqingqing 已提交
16
Many users are confused about the image layout. We introduce
D
dangqingqing 已提交
17
the image layout as follows.
D
dangqingqing 已提交
18 19

- CHW Layout
L
Luo Tao 已提交
20

D
dangqingqing 已提交
21
  - The abbreviations: C=channel, H=Height, W=Width
D
dangqingqing 已提交
22 23 24
  - The default layout of image opened by cv2 or PIL is HWC.
    PaddlePaddle only supports the CHW layout. And CHW is simply
    a transpose of HWC. It must transpose the input image.
D
dangqingqing 已提交
25 26

- Color format: RGB or BGR
L
Luo Tao 已提交
27

D
dangqingqing 已提交
28
  OpenCV use BGR color format. PIL use RGB color format. Both
D
dangqingqing 已提交
29 30
  formats can be used for training. Noted that, the format should
  be keep consistent between the training and inference peroid.
D
dangqingqing 已提交
31
"""
L
Luo Tao 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44 45
import numpy as np
try:
    import cv2
except ImportError:
    cv2 = None
import os
import tarfile
import cPickle

__all__ = [
    "load_image_bytes", "load_image", "resize_short", "to_chw", "center_crop",
    "random_crop", "left_right_flip", "simple_transform", "load_and_transform",
    "batch_images_from_tar"
]
D
dangqingqing 已提交
46 47


48 49 50 51 52 53
def batch_images_from_tar(data_file,
                          dataset_name,
                          img2label,
                          num_per_batch=1024):
    """
    Read images from tar file and batch them into batch file.
L
Luo Tao 已提交
54 55 56 57 58 59

    :param data_file: path of image tar file
    :type data_file: string
    :param dataset_name: 'train','test' or 'valid'
    :type dataset_name: string
    :param img2label: a dic with image file name as key 
60
                    and image's label as value
L
Luo Tao 已提交
61 62 63 64 65
    :type img2label: dic
    :param num_per_batch: image number per batch file
    :type num_per_batch: int
    :return: path of list file containing paths of batch file
    :rtype: string
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
    """
    batch_dir = data_file + "_batch"
    out_path = "%s/%s" % (batch_dir, dataset_name)
    meta_file = "%s/%s.txt" % (batch_dir, dataset_name)

    if os.path.exists(out_path):
        return meta_file
    else:
        os.makedirs(out_path)

    tf = tarfile.open(data_file)
    mems = tf.getmembers()
    data = []
    labels = []
    file_id = 0
    for mem in mems:
        if mem.name in img2label:
            data.append(tf.extractfile(mem).read())
            labels.append(img2label[mem.name])
            if len(data) == num_per_batch:
                output = {}
                output['label'] = labels
                output['data'] = data
                cPickle.dump(
                    output,
                    open('%s/batch_%d' % (out_path, file_id), 'w'),
                    protocol=cPickle.HIGHEST_PROTOCOL)
                file_id += 1
                data = []
                labels = []
    if len(data) > 0:
        output = {}
        output['label'] = labels
        output['data'] = data
        cPickle.dump(
            output,
            open('%s/batch_%d' % (out_path, file_id), 'w'),
            protocol=cPickle.HIGHEST_PROTOCOL)

    with open(meta_file, 'a') as meta:
        for file in os.listdir(out_path):
            meta.write(os.path.abspath("%s/%s" % (out_path, file)) + "\n")
    return meta_file


111 112 113 114 115 116 117
def load_image_bytes(bytes, is_color=True):
    """
    Load an color or gray image from bytes array.

    Example usage:
    
    .. code-block:: python
L
Luo Tao 已提交
118

119
        with open('cat.jpg') as f:
120
            im = load_image_bytes(f.read())
121 122

    :param bytes: the input image bytes array.
L
Luo Tao 已提交
123
    :type bytes: str
124 125 126
    :param is_color: If set is_color True, it will load and
                     return a color image. Otherwise, it will
                     load and return a gray image.
L
Luo Tao 已提交
127
    :type is_color: bool
128 129 130 131 132 133 134
    """
    flag = 1 if is_color else 0
    file_bytes = np.asarray(bytearray(bytes), dtype=np.uint8)
    img = cv2.imdecode(file_bytes, flag)
    return img


D
dangqingqing 已提交
135 136 137 138 139 140 141
def load_image(file, is_color=True):
    """
    Load an color or gray image from the file path.

    Example usage:
    
    .. code-block:: python
L
Luo Tao 已提交
142

D
dangqingqing 已提交
143 144 145 146 147 148 149
        im = load_image('cat.jpg')

    :param file: the input image path.
    :type file: string
    :param is_color: If set is_color True, it will load and
                     return a color image. Otherwise, it will
                     load and return a gray image.
L
Luo Tao 已提交
150
    :type is_color: bool
D
dangqingqing 已提交
151
    """
D
dangqingqing 已提交
152 153 154 155 156 157 158
    # cv2.IMAGE_COLOR for OpenCV3
    # cv2.CV_LOAD_IMAGE_COLOR for older OpenCV Version
    # cv2.IMAGE_GRAYSCALE for OpenCV3
    # cv2.CV_LOAD_IMAGE_GRAYSCALE for older OpenCV Version
    # Here, use constant 1 and 0
    # 1: COLOR, 0: GRAYSCALE
    flag = 1 if is_color else 0
D
dangqingqing 已提交
159 160 161 162 163 164 165 166 167 168 169
    im = cv2.imread(file, flag)
    return im


def resize_short(im, size):
    """ 
    Resize an image so that the length of shorter edge is size.

    Example usage:
    
    .. code-block:: python
L
Luo Tao 已提交
170

D
dangqingqing 已提交
171 172 173 174 175 176 177 178 179 180 181 182 183 184
        im = load_image('cat.jpg')
        im = resize_short(im, 256)
    
    :param im: the input image with HWC layout.
    :type im: ndarray
    :param size: the shorter edge size of image after resizing.
    :type size: int
    """
    h, w = im.shape[:2]
    h_new, w_new = size, size
    if h > w:
        h_new = size * h / w
    else:
        w_new = size * w / h
185
    im = cv2.resize(im, (h_new, w_new), interpolation=cv2.INTER_CUBIC)
D
dangqingqing 已提交
186 187 188 189 190 191
    return im


def to_chw(im, order=(2, 0, 1)):
    """
    Transpose the input image order. The image layout is HWC format
D
dangqingqing 已提交
192 193
    opened by cv2 or PIL. Transpose the input image to CHW layout
    according the order (2,0,1).
D
dangqingqing 已提交
194 195 196 197

    Example usage:
    
    .. code-block:: python
L
Luo Tao 已提交
198

D
dangqingqing 已提交
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
        im = load_image('cat.jpg')
        im = resize_short(im, 256)
        im = to_chw(im)
    
    :param im: the input image with HWC layout.
    :type im: ndarray
    :param order: the transposed order.
    :type order: tuple|list 
    """
    assert len(im.shape) == len(order)
    im = im.transpose(order)
    return im


def center_crop(im, size, is_color=True):
    """
    Crop the center of image with size.

    Example usage:
    
    .. code-block:: python
L
Luo Tao 已提交
220

D
dangqingqing 已提交
221 222 223 224
        im = center_crop(im, 224)
    
    :param im: the input image with HWC layout.
    :type im: ndarray
D
dangqingqing 已提交
225
    :param size: the cropping size.
D
dangqingqing 已提交
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
    :type size: int
    :param is_color: whether the image is color or not.
    :type is_color: bool
    """
    h, w = im.shape[:2]
    h_start = (h - size) / 2
    w_start = (w - size) / 2
    h_end, w_end = h_start + size, w_start + size
    if is_color:
        im = im[h_start:h_end, w_start:w_end, :]
    else:
        im = im[h_start:h_end, w_start:w_end]
    return im


def random_crop(im, size, is_color=True):
    """
    Randomly crop input image with size.

    Example usage:
    
    .. code-block:: python
L
Luo Tao 已提交
248

D
dangqingqing 已提交
249 250 251 252
        im = random_crop(im, 224)
    
    :param im: the input image with HWC layout.
    :type im: ndarray
D
dangqingqing 已提交
253
    :param size: the cropping size.
D
dangqingqing 已提交
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
    :type size: int
    :param is_color: whether the image is color or not.
    :type is_color: bool
    """
    h, w = im.shape[:2]
    h_start = np.random.randint(0, h - size + 1)
    w_start = np.random.randint(0, w - size + 1)
    h_end, w_end = h_start + size, w_start + size
    if is_color:
        im = im[h_start:h_end, w_start:w_end, :]
    else:
        im = im[h_start:h_end, w_start:w_end]
    return im


Q
qingqing01 已提交
269
def left_right_flip(im, is_color=True):
D
dangqingqing 已提交
270 271 272 273 274 275 276
    """
    Flip an image along the horizontal direction.
    Return the flipped image.

    Example usage:
    
    .. code-block:: python
L
Luo Tao 已提交
277

D
dangqingqing 已提交
278 279
        im = left_right_flip(im)
    
Q
qingqing01 已提交
280 281
    :paam im: input image with HWC layout or HW layout for gray image

D
dangqingqing 已提交
282
    :type im: ndarray
Q
qingqing01 已提交
283 284
    :paam is_color: whether color input image or not
    :type is_color: bool
D
dangqingqing 已提交
285
    """
Q
qingqing01 已提交
286
    if len(im.shape) == 3 and is_color:
D
dangqingqing 已提交
287 288
        return im[:, ::-1, :]
    else:
Q
qingqing01 已提交
289
        return im[:, ::-1]
D
dangqingqing 已提交
290 291


D
dangqingqing 已提交
292 293 294 295 296 297
def simple_transform(im,
                     resize_size,
                     crop_size,
                     is_train,
                     is_color=True,
                     mean=None):
D
dangqingqing 已提交
298
    """
D
dangqingqing 已提交
299
    Simply data argumentation for training. These operations include
D
dangqingqing 已提交
300 301
    resizing, croping and flipping.

D
dangqingqing 已提交
302 303 304
    Example usage:
    
    .. code-block:: python
L
Luo Tao 已提交
305

D
dangqingqing 已提交
306 307
        im = simple_transform(im, 256, 224, True)

D
dangqingqing 已提交
308 309 310 311 312 313 314 315
    :param im: The input image with HWC layout.
    :type im: ndarray
    :param resize_size: The shorter edge length of the resized image.
    :type resize_size: int
    :param crop_size: The cropping size.
    :type crop_size: int
    :param is_train: Whether it is training or not.
    :type is_train: bool
L
Luo Tao 已提交
316 317 318 319 320
    :param is_color: whether the image is color or not.
    :type is_color: bool
    :param mean: the mean values, which can be element-wise mean values or 
                 mean values per channel.
    :type mean: numpy array | list
D
dangqingqing 已提交
321 322 323
    """
    im = resize_short(im, resize_size)
    if is_train:
Y
yeyupiaoling 已提交
324
        im = random_crop(im, crop_size, is_color=is_color)
D
dangqingqing 已提交
325
        if np.random.randint(2) == 0:
Q
qingqing01 已提交
326
            im = left_right_flip(im, is_color)
D
dangqingqing 已提交
327
    else:
Q
qingqing01 已提交
328
        im = center_crop(im, crop_size, is_color)
Y
yeyupiaoling 已提交
329
        im = center_crop(im, crop_size, is_color=is_color)
D
dangqingqing 已提交
330 331 332 333 334 335 336
    if len(im.shape) == 3:
        im = to_chw(im)

    im = im.astype('float32')
    if mean is not None:
        mean = np.array(mean, dtype=np.float32)
        # mean value, may be one value per channel 
Q
qingqing01 已提交
337
        if mean.ndim == 1 and is_color:
D
dangqingqing 已提交
338
            mean = mean[:, np.newaxis, np.newaxis]
Q
qingqing01 已提交
339 340
        elif mean.ndim == 1:
            mean = mean
D
dangqingqing 已提交
341 342 343 344
        else:
            # elementwise mean
            assert len(mean.shape) == len(im)
        im -= mean
D
dangqingqing 已提交
345 346 347 348 349 350 351 352

    return im


def load_and_transform(filename,
                       resize_size,
                       crop_size,
                       is_train,
D
dangqingqing 已提交
353 354
                       is_color=True,
                       mean=None):
D
dangqingqing 已提交
355 356
    """
    Load image from the input file `filename` and transform image for
D
dangqingqing 已提交
357 358
    data argumentation. Please refer to the `simple_transform` interface
    for the transform operations.
D
dangqingqing 已提交
359

D
dangqingqing 已提交
360 361 362
    Example usage:
    
    .. code-block:: python
L
Luo Tao 已提交
363

D
dangqingqing 已提交
364 365
        im = load_and_transform('cat.jpg', 256, 224, True)

D
dangqingqing 已提交
366 367 368 369 370 371 372 373
    :param filename: The file name of input image.
    :type filename: string
    :param resize_size: The shorter edge length of the resized image.
    :type resize_size: int
    :param crop_size: The cropping size.
    :type crop_size: int
    :param is_train: Whether it is training or not.
    :type is_train: bool
L
Luo Tao 已提交
374 375 376 377 378
    :param is_color: whether the image is color or not.
    :type is_color: bool
    :param mean: the mean values, which can be element-wise mean values or 
                 mean values per channel.
    :type mean: numpy array | list
D
dangqingqing 已提交
379
    """
Q
qingqing01 已提交
380
    im = load_image(filename, is_color)
D
dangqingqing 已提交
381
    im = simple_transform(im, resize_size, crop_size, is_train, is_color, mean)
D
dangqingqing 已提交
382
    return im