image.py 8.42 KB
Newer Older
1
2
3
4
import torch

import os
import os.path as osp
5
import importlib.machinery
6

7
8
from enum import Enum

9
10
11
_HAS_IMAGE_OPT = False

try:
12
    lib_dir = osp.abspath(osp.join(osp.dirname(__file__), ".."))
13
14
15
16
17
18

    loader_details = (
        importlib.machinery.ExtensionFileLoader,
        importlib.machinery.EXTENSION_SUFFIXES
    )

19
    extfinder = importlib.machinery.FileFinder(lib_dir, loader_details)  # type: ignore[arg-type]
20
    ext_specs = extfinder.find_spec("image")
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

    if os.name == 'nt':
        # Load the image extension using LoadLibraryExW
        import ctypes
        import sys

        kernel32 = ctypes.WinDLL('kernel32.dll', use_last_error=True)
        with_load_library_flags = hasattr(kernel32, 'AddDllDirectory')
        prev_error_mode = kernel32.SetErrorMode(0x0001)

        kernel32.LoadLibraryW.restype = ctypes.c_void_p
        if with_load_library_flags:
            kernel32.LoadLibraryExW.restype = ctypes.c_void_p

        if ext_specs is not None:
            res = kernel32.LoadLibraryExW(ext_specs.origin, None, 0x00001100)
            if res is None:
                err = ctypes.WinError(ctypes.get_last_error())
                err.strerror += (f' Error loading "{ext_specs.origin}" or any or '
                                 'its dependencies.')
                raise err

        kernel32.SetErrorMode(prev_error_mode)

45
46
47
48
49
50
51
    if ext_specs is not None:
        torch.ops.load_library(ext_specs.origin)
        _HAS_IMAGE_OPT = True
except (ImportError, OSError):
    pass


52
class ImageReadMode(Enum):
53
54
55
56
57
58
59
60
61
    """
    Support for various modes while reading images.

    Use `ImageReadMode.UNCHANGED` for loading the image as-is,
    `ImageReadMode.GRAY` for converting to grayscale,
    `ImageReadMode.GRAY_ALPHA` for grayscale with transparency,
    `ImageReadMode.RGB` for RGB and `ImageReadMode.RGB_ALPHA` for
    RGB with transparency.
    """
62
63
64
65
66
67
68
    UNCHANGED = 0
    GRAY = 1
    GRAY_ALPHA = 2
    RGB = 3
    RGB_ALPHA = 4


Francisco Massa's avatar
Francisco Massa committed
69
70
71
72
73
74
75
76
77
78
79
80
def read_file(path: str) -> torch.Tensor:
    """
    Reads and outputs the bytes contents of a file as a uint8 Tensor
    with one dimension.

    Arguments:
        path (str): the path to the file to be read

    Returns:
        data (Tensor)
    """
    data = torch.ops.image.read_file(path)
Francisco Massa's avatar
Francisco Massa committed
81
82
83
    return data


Francisco Massa's avatar
Francisco Massa committed
84
85
86
87
88
89
90
91
92
93
94
95
def write_file(filename: str, data: torch.Tensor) -> None:
    """
    Writes the contents of a uint8 tensor with one dimension to a
    file.

    Arguments:
        filename (str): the path to the file to be written
        data (Tensor): the contents to be written to the output file
    """
    torch.ops.image.write_file(filename, data)


96
def decode_png(input: torch.Tensor, mode: ImageReadMode = ImageReadMode.UNCHANGED) -> torch.Tensor:
97
98
    """
    Decodes a PNG image into a 3 dimensional RGB Tensor.
99
    Optionally converts the image to the desired format.
100
101
102
    The values of the output tensor are uint8 between 0 and 255.

    Arguments:
Francisco Massa's avatar
Francisco Massa committed
103
        input (Tensor[1]): a one dimensional uint8 tensor containing
104
    the raw bytes of the PNG image.
105
        mode (ImageReadMode): the read mode used for optionally
106
107
108
    converting the image. Default: `ImageReadMode.UNCHANGED`.
    See `ImageReadMode` class for more information on various
    available modes.
109
110

    Returns:
111
        output (Tensor[image_channels, image_height, image_width])
112
    """
113
    output = torch.ops.image.decode_png(input, mode.value)
114
115
116
    return output


117
118
119
120
def encode_png(input: torch.Tensor, compression_level: int = 6) -> torch.Tensor:
    """
    Takes an input tensor in CHW layout and returns a buffer with the contents
    of its corresponding PNG file.
121
122
123
124
125
126
127
128
129

    Parameters
    ----------
    input: Tensor[channels, image_height, image_width]
        int8 image tensor of `c` channels, where `c` must 3 or 1.
    compression_level: int
        Compression factor for the resulting file, it must be a number
        between 0 and 9. Default: 6

130
    Returns
131
132
133
134
    -------
    output: Tensor[1]
        A one dimensional int8 tensor that contains the raw bytes of the
        PNG file.
135
136
137
138
139
140
141
142
143
    """
    output = torch.ops.image.encode_png(input, compression_level)
    return output


def write_png(input: torch.Tensor, filename: str, compression_level: int = 6):
    """
    Takes an input tensor in CHW layout (or HW in the case of grayscale images)
    and saves it in a PNG file.
144
145
146
147
148
149
150
151
152
153

    Parameters
    ----------
    input: Tensor[channels, image_height, image_width]
        int8 image tensor of `c` channels, where `c` must be 1 or 3.
    filename: str
        Path to save the image.
    compression_level: int
        Compression factor for the resulting file, it must be a number
        between 0 and 9. Default: 6
154
    """
155
156
    output = encode_png(input, compression_level)
    write_file(filename, output)
157
158


159
def decode_jpeg(input: torch.Tensor, mode: ImageReadMode = ImageReadMode.UNCHANGED) -> torch.Tensor:
160
161
    """
    Decodes a JPEG image into a 3 dimensional RGB Tensor.
162
    Optionally converts the image to the desired format.
163
    The values of the output tensor are uint8 between 0 and 255.
164

165
    Arguments:
Francisco Massa's avatar
Francisco Massa committed
166
        input (Tensor[1]): a one dimensional uint8 tensor containing
167
    the raw bytes of the JPEG image.
168
        mode (ImageReadMode): the read mode used for optionally
169
170
171
    converting the image. Default: `ImageReadMode.UNCHANGED`.
    See `ImageReadMode` class for more information on various
    available modes.
172

173
    Returns:
174
        output (Tensor[image_channels, image_height, image_width])
175
    """
176
    output = torch.ops.image.decode_jpeg(input, mode.value)
177
178
179
    return output


180
181
def encode_jpeg(input: torch.Tensor, quality: int = 75) -> torch.Tensor:
    """
182
183
    Takes an input tensor in CHW layout and returns a buffer with the contents
    of its corresponding JPEG file.
184
185
186
187
188
189
190
191
192

    Parameters
    ----------
    input: Tensor[channels, image_height, image_width])
        int8 image tensor of `c` channels, where `c` must be 1 or 3.
    quality: int
        Quality of the resulting JPEG file, it must be a number between
        1 and 100. Default: 75

193
    Returns
194
195
196
197
    -------
    output: Tensor[1]
        A one dimensional int8 tensor that contains the raw bytes of the
        JPEG file.
198
199
200
201
202
203
204
205
206
207
208
    """
    if quality < 1 or quality > 100:
        raise ValueError('Image quality should be a positive number '
                         'between 1 and 100')

    output = torch.ops.image.encode_jpeg(input, quality)
    return output


def write_jpeg(input: torch.Tensor, filename: str, quality: int = 75):
    """
209
    Takes an input tensor in CHW layout and saves it in a JPEG file.
210
211
212
213
214
215
216
217
218
219

    Parameters
    ----------
    input: Tensor[channels, image_height, image_width]
        int8 image tensor of `c` channels, where `c` must be 1 or 3.
    filename: str
        Path to save the image.
    quality: int
        Quality of the resulting JPEG file, it must be a number
        between 1 and 100. Default: 75
220
    """
221
222
    output = encode_jpeg(input, quality)
    write_file(filename, output)
Francisco Massa's avatar
Francisco Massa committed
223
224


225
def decode_image(input: torch.Tensor, mode: ImageReadMode = ImageReadMode.UNCHANGED) -> torch.Tensor:
Francisco Massa's avatar
Francisco Massa committed
226
227
228
229
    """
    Detects whether an image is a JPEG or PNG and performs the appropriate
    operation to decode the image into a 3 dimensional RGB Tensor.

230
    Optionally converts the image to the desired format.
Francisco Massa's avatar
Francisco Massa committed
231
232
    The values of the output tensor are uint8 between 0 and 255.

233
234
235
236
237
    Parameters
    ----------
    input: Tensor
        a one dimensional uint8 tensor containing the raw bytes of the
        PNG or JPEG image.
238
    mode: ImageReadMode
239
240
241
242
        the read mode used for optionally converting the image.
        Default: `ImageReadMode.UNCHANGED`.
        See `ImageReadMode` class for more information on various
        available modes.
243
244
245

    Returns
    -------
246
    output: Tensor[image_channels, image_height, image_width]
Francisco Massa's avatar
Francisco Massa committed
247
    """
248
    output = torch.ops.image.decode_image(input, mode.value)
Francisco Massa's avatar
Francisco Massa committed
249
250
251
    return output


252
def read_image(path: str, mode: ImageReadMode = ImageReadMode.UNCHANGED) -> torch.Tensor:
Francisco Massa's avatar
Francisco Massa committed
253
254
    """
    Reads a JPEG or PNG image into a 3 dimensional RGB Tensor.
255
    Optionally converts the image to the desired format.
Francisco Massa's avatar
Francisco Massa committed
256
    The values of the output tensor are uint8 between 0 and 255.
257
258
259
260
261

    Parameters
    ----------
    path: str
        path of the JPEG or PNG image.
262
    mode: ImageReadMode
263
264
265
266
        the read mode used for optionally converting the image.
        Default: `ImageReadMode.UNCHANGED`.
        See `ImageReadMode` class for more information on various
        available modes.
267
268
269

    Returns
    -------
270
    output: Tensor[image_channels, image_height, image_width]
Francisco Massa's avatar
Francisco Massa committed
271
    """
Francisco Massa's avatar
Francisco Massa committed
272
    data = read_file(path)
273
    return decode_image(data, mode)