comdoleger commited on
Commit
b56482c
·
verified ·
1 Parent(s): 49dcca3

Upload extensions_built_in/diffusion_models/omnigen2/src/pipelines/image_processor.py with huggingface_hub

Browse files
extensions_built_in/diffusion_models/omnigen2/src/pipelines/image_processor.py ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import math
16
+ import warnings
17
+ from typing import List, Optional, Tuple, Union
18
+
19
+ import numpy as np
20
+ import PIL.Image
21
+ import torch
22
+
23
+ from diffusers.image_processor import PipelineImageInput, VaeImageProcessor, is_valid_image_imagelist
24
+ from diffusers.configuration_utils import register_to_config
25
+
26
+ class OmniGen2ImageProcessor(VaeImageProcessor):
27
+ """
28
+ Image processor for PixArt image resize and crop.
29
+
30
+ Args:
31
+ do_resize (`bool`, *optional*, defaults to `True`):
32
+ Whether to downscale the image's (height, width) dimensions to multiples of `vae_scale_factor`. Can accept
33
+ `height` and `width` arguments from [`image_processor.VaeImageProcessor.preprocess`] method.
34
+ vae_scale_factor (`int`, *optional*, defaults to `8`):
35
+ VAE scale factor. If `do_resize` is `True`, the image is automatically resized to multiples of this factor.
36
+ resample (`str`, *optional*, defaults to `lanczos`):
37
+ Resampling filter to use when resizing the image.
38
+ do_normalize (`bool`, *optional*, defaults to `True`):
39
+ Whether to normalize the image to [-1,1].
40
+ do_binarize (`bool`, *optional*, defaults to `False`):
41
+ Whether to binarize the image to 0/1.
42
+ do_convert_rgb (`bool`, *optional*, defaults to be `False`):
43
+ Whether to convert the images to RGB format.
44
+ do_convert_grayscale (`bool`, *optional*, defaults to be `False`):
45
+ Whether to convert the images to grayscale format.
46
+ """
47
+
48
+ @register_to_config
49
+ def __init__(
50
+ self,
51
+ do_resize: bool = True,
52
+ vae_scale_factor: int = 16,
53
+ resample: str = "lanczos",
54
+ max_pixels: Optional[int] = None,
55
+ max_side_length: Optional[int] = None,
56
+ do_normalize: bool = True,
57
+ do_binarize: bool = False,
58
+ do_convert_grayscale: bool = False,
59
+ ):
60
+ super().__init__(
61
+ do_resize=do_resize,
62
+ vae_scale_factor=vae_scale_factor,
63
+ resample=resample,
64
+ do_normalize=do_normalize,
65
+ do_binarize=do_binarize,
66
+ do_convert_grayscale=do_convert_grayscale,
67
+ )
68
+
69
+ self.max_pixels = max_pixels
70
+ self.max_side_length = max_side_length
71
+
72
+ def get_new_height_width(
73
+ self,
74
+ image: Union[PIL.Image.Image, np.ndarray, torch.Tensor],
75
+ height: Optional[int] = None,
76
+ width: Optional[int] = None,
77
+ max_pixels: Optional[int] = None,
78
+ max_side_length: Optional[int] = None,
79
+ ) -> Tuple[int, int]:
80
+ r"""
81
+ Returns the height and width of the image, downscaled to the next integer multiple of `vae_scale_factor`.
82
+
83
+ Args:
84
+ image (`Union[PIL.Image.Image, np.ndarray, torch.Tensor]`):
85
+ The image input, which can be a PIL image, NumPy array, or PyTorch tensor. If it is a NumPy array, it
86
+ should have shape `[batch, height, width]` or `[batch, height, width, channels]`. If it is a PyTorch
87
+ tensor, it should have shape `[batch, channels, height, width]`.
88
+ height (`Optional[int]`, *optional*, defaults to `None`):
89
+ The height of the preprocessed image. If `None`, the height of the `image` input will be used.
90
+ width (`Optional[int]`, *optional*, defaults to `None`):
91
+ The width of the preprocessed image. If `None`, the width of the `image` input will be used.
92
+
93
+ Returns:
94
+ `Tuple[int, int]`:
95
+ A tuple containing the height and width, both resized to the nearest integer multiple of
96
+ `vae_scale_factor`.
97
+ """
98
+
99
+ if height is None:
100
+ if isinstance(image, PIL.Image.Image):
101
+ height = image.height
102
+ elif isinstance(image, torch.Tensor):
103
+ height = image.shape[2]
104
+ else:
105
+ height = image.shape[1]
106
+
107
+ if width is None:
108
+ if isinstance(image, PIL.Image.Image):
109
+ width = image.width
110
+ elif isinstance(image, torch.Tensor):
111
+ width = image.shape[3]
112
+ else:
113
+ width = image.shape[2]
114
+
115
+ if max_side_length is None:
116
+ max_side_length = self.max_side_length
117
+
118
+ if max_pixels is None:
119
+ max_pixels = self.max_pixels
120
+
121
+ ratio = 1.0
122
+ if max_side_length is not None:
123
+ if height > width:
124
+ max_side_length_ratio = max_side_length / height
125
+ else:
126
+ max_side_length_ratio = max_side_length / width
127
+
128
+ cur_pixels = height * width
129
+ max_pixels_ratio = (max_pixels / cur_pixels) ** 0.5
130
+ ratio = min(max_pixels_ratio, max_side_length_ratio, 1.0) # do not upscale input image
131
+
132
+ new_height, new_width = int(height * ratio) // self.config.vae_scale_factor * self.config.vae_scale_factor, int(width * ratio) // self.config.vae_scale_factor * self.config.vae_scale_factor
133
+ return new_height, new_width
134
+
135
+ def preprocess(
136
+ self,
137
+ image: PipelineImageInput,
138
+ height: Optional[int] = None,
139
+ width: Optional[int] = None,
140
+ max_pixels: Optional[int] = None,
141
+ max_side_length: Optional[int] = None,
142
+ resize_mode: str = "default", # "default", "fill", "crop"
143
+ crops_coords: Optional[Tuple[int, int, int, int]] = None,
144
+ ) -> torch.Tensor:
145
+ """
146
+ Preprocess the image input.
147
+
148
+ Args:
149
+ image (`PipelineImageInput`):
150
+ The image input, accepted formats are PIL images, NumPy arrays, PyTorch tensors; Also accept list of
151
+ supported formats.
152
+ height (`int`, *optional*):
153
+ The height in preprocessed image. If `None`, will use the `get_default_height_width()` to get default
154
+ height.
155
+ width (`int`, *optional*):
156
+ The width in preprocessed. If `None`, will use get_default_height_width()` to get the default width.
157
+ resize_mode (`str`, *optional*, defaults to `default`):
158
+ The resize mode, can be one of `default` or `fill`. If `default`, will resize the image to fit within
159
+ the specified width and height, and it may not maintaining the original aspect ratio. If `fill`, will
160
+ resize the image to fit within the specified width and height, maintaining the aspect ratio, and then
161
+ center the image within the dimensions, filling empty with data from image. If `crop`, will resize the
162
+ image to fit within the specified width and height, maintaining the aspect ratio, and then center the
163
+ image within the dimensions, cropping the excess. Note that resize_mode `fill` and `crop` are only
164
+ supported for PIL image input.
165
+ crops_coords (`List[Tuple[int, int, int, int]]`, *optional*, defaults to `None`):
166
+ The crop coordinates for each image in the batch. If `None`, will not crop the image.
167
+
168
+ Returns:
169
+ `torch.Tensor`:
170
+ The preprocessed image.
171
+ """
172
+ supported_formats = (PIL.Image.Image, np.ndarray, torch.Tensor)
173
+
174
+ # Expand the missing dimension for 3-dimensional pytorch tensor or numpy array that represents grayscale image
175
+ if self.config.do_convert_grayscale and isinstance(image, (torch.Tensor, np.ndarray)) and image.ndim == 3:
176
+ if isinstance(image, torch.Tensor):
177
+ # if image is a pytorch tensor could have 2 possible shapes:
178
+ # 1. batch x height x width: we should insert the channel dimension at position 1
179
+ # 2. channel x height x width: we should insert batch dimension at position 0,
180
+ # however, since both channel and batch dimension has same size 1, it is same to insert at position 1
181
+ # for simplicity, we insert a dimension of size 1 at position 1 for both cases
182
+ image = image.unsqueeze(1)
183
+ else:
184
+ # if it is a numpy array, it could have 2 possible shapes:
185
+ # 1. batch x height x width: insert channel dimension on last position
186
+ # 2. height x width x channel: insert batch dimension on first position
187
+ if image.shape[-1] == 1:
188
+ image = np.expand_dims(image, axis=0)
189
+ else:
190
+ image = np.expand_dims(image, axis=-1)
191
+
192
+ if isinstance(image, list) and isinstance(image[0], np.ndarray) and image[0].ndim == 4:
193
+ warnings.warn(
194
+ "Passing `image` as a list of 4d np.ndarray is deprecated."
195
+ "Please concatenate the list along the batch dimension and pass it as a single 4d np.ndarray",
196
+ FutureWarning,
197
+ )
198
+ image = np.concatenate(image, axis=0)
199
+ if isinstance(image, list) and isinstance(image[0], torch.Tensor) and image[0].ndim == 4:
200
+ warnings.warn(
201
+ "Passing `image` as a list of 4d torch.Tensor is deprecated."
202
+ "Please concatenate the list along the batch dimension and pass it as a single 4d torch.Tensor",
203
+ FutureWarning,
204
+ )
205
+ image = torch.cat(image, axis=0)
206
+
207
+ if not is_valid_image_imagelist(image):
208
+ raise ValueError(
209
+ f"Input is in incorrect format. Currently, we only support {', '.join(str(x) for x in supported_formats)}"
210
+ )
211
+ if not isinstance(image, list):
212
+ image = [image]
213
+
214
+ if isinstance(image[0], PIL.Image.Image):
215
+ if crops_coords is not None:
216
+ image = [i.crop(crops_coords) for i in image]
217
+ if self.config.do_resize:
218
+ height, width = self.get_new_height_width(image[0], height, width, max_pixels, max_side_length)
219
+ image = [self.resize(i, height, width, resize_mode=resize_mode) for i in image]
220
+ if self.config.do_convert_rgb:
221
+ image = [self.convert_to_rgb(i) for i in image]
222
+ elif self.config.do_convert_grayscale:
223
+ image = [self.convert_to_grayscale(i) for i in image]
224
+ image = self.pil_to_numpy(image) # to np
225
+ image = self.numpy_to_pt(image) # to pt
226
+
227
+ elif isinstance(image[0], np.ndarray):
228
+ image = np.concatenate(image, axis=0) if image[0].ndim == 4 else np.stack(image, axis=0)
229
+
230
+ image = self.numpy_to_pt(image)
231
+
232
+ height, width = self.get_new_height_width(image, height, width, max_pixels, max_side_length)
233
+ if self.config.do_resize:
234
+ image = self.resize(image, height, width)
235
+
236
+ elif isinstance(image[0], torch.Tensor):
237
+ image = torch.cat(image, axis=0) if image[0].ndim == 4 else torch.stack(image, axis=0)
238
+
239
+ if self.config.do_convert_grayscale and image.ndim == 3:
240
+ image = image.unsqueeze(1)
241
+
242
+ channel = image.shape[1]
243
+ # don't need any preprocess if the image is latents
244
+ if channel == self.config.vae_latent_channels:
245
+ return image
246
+
247
+ height, width = self.get_new_height_width(image, height, width, max_pixels, max_side_length)
248
+ if self.config.do_resize:
249
+ image = self.resize(image, height, width)
250
+
251
+ # expected range [0,1], normalize to [-1,1]
252
+ do_normalize = self.config.do_normalize
253
+ if do_normalize and image.min() < 0:
254
+ warnings.warn(
255
+ "Passing `image` as torch tensor with value range in [-1,1] is deprecated. The expected value range for image tensor is [0,1] "
256
+ f"when passing as pytorch tensor or numpy Array. You passed `image` with value range [{image.min()},{image.max()}]",
257
+ FutureWarning,
258
+ )
259
+ do_normalize = False
260
+ if do_normalize:
261
+ image = self.normalize(image)
262
+
263
+ if self.config.do_binarize:
264
+ image = self.binarize(image)
265
+
266
+ return image