image.py 9.5 KB
Newer Older
D
dangqingqing 已提交
1 2 3
import numpy as np
try:
    import cv2
4 5
except ImportError:
    cv2 = None
6 7 8
import os
import tarfile
import cPickle
D
dangqingqing 已提交
9 10

__all__ = [
11
    "load_image_bytes", "load_image", "resize_short", "to_chw", "center_crop",
12 13
    "random_crop", "left_right_flip", "simple_transform", "load_and_transform",
    "batch_images_from_tar"
D
dangqingqing 已提交
14 15
]
"""
D
dangqingqing 已提交
16
This file contains some common interfaces for image preprocess.
D
dangqingqing 已提交
17
Many users are confused about the image layout. We introduce
D
dangqingqing 已提交
18
the image layout as follows.
D
dangqingqing 已提交
19 20 21

- CHW Layout
  - 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 27

- Color format: RGB or BGR
  OpenCV use BGR color format. PIL use RGB color format. Both
D
dangqingqing 已提交
28 29
  formats can be used for training. Noted that, the format should
  be keep consistent between the training and inference peroid.
D
dangqingqing 已提交
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
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.
    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 
                    and image's label as value
    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
    """
    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


95 96 97 98 99 100 101 102
def load_image_bytes(bytes, is_color=True):
    """
    Load an color or gray image from bytes array.

    Example usage:
    
    .. code-block:: python
        with open('cat.jpg') as f:
103
            im = load_image_bytes(f.read())
104 105 106 107 108 109 110 111 112 113 114 115 116

    :param bytes: the input image bytes array.
    :type file: str
    :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.
    """
    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 已提交
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
def load_image(file, is_color=True):
    """
    Load an color or gray image from the file path.

    Example usage:
    
    .. code-block:: python
        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.
    """
D
dangqingqing 已提交
132 133 134 135 136 137 138
    # 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 已提交
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
    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
        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
    """
    assert im.shape[-1] == 1 or im.shape[-1] == 3
    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
165
    im = cv2.resize(im, (h_new, w_new), interpolation=cv2.INTER_CUBIC)
D
dangqingqing 已提交
166 167 168 169 170 171
    return im


def to_chw(im, order=(2, 0, 1)):
    """
    Transpose the input image order. The image layout is HWC format
D
dangqingqing 已提交
172 173
    opened by cv2 or PIL. Transpose the input image to CHW layout
    according the order (2,0,1).
D
dangqingqing 已提交
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

    Example usage:
    
    .. code-block:: python
        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
        im = center_crop(im, 224)
    
    :param im: the input image with HWC layout.
    :type im: ndarray
D
dangqingqing 已提交
203
    :param size: the cropping size.
D
dangqingqing 已提交
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
    :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
        im = random_crop(im, 224)
    
    :param im: the input image with HWC layout.
    :type im: ndarray
D
dangqingqing 已提交
230
    :param size: the cropping size.
D
dangqingqing 已提交
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
    :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


def left_right_flip(im):
    """
    Flip an image along the horizontal direction.
    Return the flipped image.

    Example usage:
    
    .. code-block:: python
        im = left_right_flip(im)
    
    :paam im: input image with HWC layout
    :type im: ndarray
    """
    if len(im.shape) == 3:
        return im[:, ::-1, :]
    else:
        return im[:, ::-1, :]


D
dangqingqing 已提交
265 266 267 268 269 270
def simple_transform(im,
                     resize_size,
                     crop_size,
                     is_train,
                     is_color=True,
                     mean=None):
D
dangqingqing 已提交
271
    """
D
dangqingqing 已提交
272
    Simply data argumentation for training. These operations include
D
dangqingqing 已提交
273 274
    resizing, croping and flipping.

D
dangqingqing 已提交
275 276 277 278 279
    Example usage:
    
    .. code-block:: python
        im = simple_transform(im, 256, 224, True)

D
dangqingqing 已提交
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
    :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
    """
    im = resize_short(im, resize_size)
    if is_train:
        im = random_crop(im, crop_size)
        if np.random.randint(2) == 0:
            im = left_right_flip(im)
    else:
        im = center_crop(im, crop_size)
D
dangqingqing 已提交
296 297 298 299 300 301 302 303 304 305 306 307 308
    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 
        if mean.ndim == 1:
            mean = mean[:, np.newaxis, np.newaxis]
        else:
            # elementwise mean
            assert len(mean.shape) == len(im)
        im -= mean
D
dangqingqing 已提交
309 310 311 312 313 314 315 316

    return im


def load_and_transform(filename,
                       resize_size,
                       crop_size,
                       is_train,
D
dangqingqing 已提交
317 318
                       is_color=True,
                       mean=None):
D
dangqingqing 已提交
319 320
    """
    Load image from the input file `filename` and transform image for
D
dangqingqing 已提交
321 322
    data argumentation. Please refer to the `simple_transform` interface
    for the transform operations.
D
dangqingqing 已提交
323

D
dangqingqing 已提交
324 325 326 327 328
    Example usage:
    
    .. code-block:: python
        im = load_and_transform('cat.jpg', 256, 224, True)

D
dangqingqing 已提交
329 330 331 332 333 334 335 336 337 338
    :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
    """
    im = load_image(filename)
D
dangqingqing 已提交
339
    im = simple_transform(im, resize_size, crop_size, is_train, is_color, mean)
D
dangqingqing 已提交
340
    return im