Views
No views yet
1from datasets import load_dataset
2
3# dataset_name = "dim/nfs_pix2pix_1920_1080_v5"
4# dataset_name = "dim/nfs_pix2pix_1920_1080_v5_upscale_2x_raw"
5# dataset_name = "dim/nfs_pix2pix_1920_1080_v6"
6dataset_name = "dim/render_nfs_4screens_6_sdxl_1_wan_mix"
7# dataset_name = "dim/render_nfs_4screens_5_sdxl_1_wan_mix"
8dataset = load_dataset(
9 dataset_name,
10 cache_dir=f"/code/dataset/{dataset_name.split('/')[-1]}",
11)
12dataset["train"] = dataset["train"].shuffle(seed=2025)
13dataset = dataset["train"]
14
15from PIL import Image
16import diffusers
17from diffusers import (
18 AutoencoderKL,
19 DDPMScheduler,
20 StableDiffusionPipeline,
21 UNet2DConditionModel,
22 StableDiffusionImg2ImgPipeline,
23 AutoencoderTiny,
24 UNet2DModel,
25 FlowMatchEulerDiscreteScheduler,
26)
27import numpy as np
28import torch
29from torchvision import transforms
30from auto_remaster.sandbox.flux2_tiny_autoencoder import Flux2TinyAutoEncoder
31
32noise_scheduler = FlowMatchEulerDiscreteScheduler()
33
34# 1. Загрузка VAE (Tiny Autoencoder для скорости и экономии памяти)
35# vae_val = AutoencoderTiny.from_pretrained(
36# "madebyollin/taesd",
37# torch_device="cuda",
38# torch_dtype=weight_dtype,
39# ).to(accelerator.device)
40# vae_val.decoder.ignore_skip = False
41weight_dtype = torch.bfloat16
42# weight_dtype = torch.float32
43device = "cuda"
44# resolution = 512
45resolution = 480
46# resolution = 512 * 2
47# checkpoint_path = "checkpoints/auto_remaster/lbm_repae_gan/checkpoint-28800"
48# checkpoint_path = "checkpoints/auto_remaster/lbm_repae_gan_v2/checkpoint-30000"
49# checkpoint_path = "/code/checkpoints/auto_remaster/lbm_v6_wan_mix/checkpoint-105600"
50checkpoint_path = (
51 "dim/lbm_train_test_gap_struct_noise_6_sdxl_1_wan_mix_177600"
52)
53vae_val = AutoencoderKL.from_pretrained(
54 # "black-forest-labs/FLUX.1-dev",
55 "black-forest-labs/FLUX.2-dev",
56 # checkpoint_path,
57 subfolder="vae",
58 torch_device="cuda",
59 torch_dtype=weight_dtype,
60).to(device)
61# vae_val = Flux2TinyAutoEncoder.from_pretrained(
62# "fal/FLUX.2-Tiny-AutoEncoder",
63# torch_dtype=weight_dtype,
64# ).to(device)
65vae_val.requires_grad_(False)
66vae_val.eval()
67# checkpoint_path = "checkpoints/auto_remaster/lbm/checkpoint-28800"
68# 2. Загрузка UNet из чекпоинта
69unet_val = UNet2DModel.from_pretrained(
70 checkpoint_path,
71 subfolder="unet",
72 torch_dtype=weight_dtype,
73).to(device)
74unet_val.eval()
75
76# 3. Подготовка трансформаций
77valid_transforms = transforms.Compose(
78 [
79 transforms.Resize(
80 resolution,
81 interpolation=transforms.InterpolationMode.LANCZOS,
82 ),
83 transforms.CenterCrop(resolution),
84 ]
85)
86train_transforms = transforms.Compose(
87 [
88 transforms.Resize(
89 resolution,
90 interpolation=transforms.InterpolationMode.LANCZOS,
91 ),
92 transforms.CenterCrop(resolution),
93 transforms.ToTensor(),
94 transforms.Normalize(
95 (0.5, 0.5, 0.5),
96 (0.5, 0.5, 0.5),
97 ),
98 ]
99)
100
101
102# Вспомогательная функция для получения сигм (как в основном скрипте)
103def _get_sigmas_val(
104 scheduler,
105 timesteps,
106 n_dim=4,
107 dtype=torch.float32,
108 device="cpu",
109):
110 sigmas = scheduler.sigmas.to(device=device, dtype=dtype)
111 schedule_timesteps = scheduler.timesteps.to(device)
112 timesteps = timesteps.to(device)
113 step_indices = [(schedule_timesteps == t).nonzero().item() for t in timesteps]
114 sigma = sigmas[step_indices].flatten()
115 while len(sigma.shape) < n_dim:
116 sigma = sigma.unsqueeze(-1)
117 return sigma
118
119
120def create_frequency_soft_cutoff_mask(
121 height: int,
122 width: int,
123 cutoff_radius: float,
124 transition_width: float = 5.0,
125 device: torch.device = None,
126) -> torch.Tensor:
127 """
128 Create a smooth frequency cutoff mask for low-pass filtering.
129
130 Args:
131 height: Image height
132 width: Image width
133 cutoff_radius: Frequency cutoff radius (0 = no structure, max_radius = full structure)
134 transition_width: Width of smooth transition (smaller = sharper cutoff)
135 device: Device to create tensor on
136
137 Returns:
138 torch.Tensor: Frequency mask of shape (height, width)
139 """
140 if device is None:
141 device = torch.device("cpu")
142
143 # Create frequency coordinates
144 u = torch.arange(height, device=device)
145 v = torch.arange(width, device=device)
146 u, v = torch.meshgrid(u, v, indexing="ij")
147
148 # Calculate distance from center
149 center_u, center_v = height // 2, width // 2
150 frequency_radius = torch.sqrt((u - center_u) ** 2 + (v - center_v) ** 2)
151
152 # Create smooth transition mask
153 mask = torch.exp(
154 -((frequency_radius - cutoff_radius) ** 2) / (2 * transition_width**2)
155 )
156 mask = torch.where(frequency_radius <= cutoff_radius, torch.ones_like(mask), mask)
157
158 return mask
159
160
161def clip_frequency_magnitude(noise_magnitudes, clip_percentile=0.95):
162 """Clip frequency domain magnitude to prevent large values."""
163
164 # Calculate clipping threshold
165 clip_threshold = torch.quantile(noise_magnitudes, clip_percentile)
166
167 # Clip large values
168 clipped_magnitudes = torch.clamp(noise_magnitudes, max=clip_threshold)
169
170 return clipped_magnitudes
171
172
173def generate_structured_noise_batch_vectorized(
174 image_batch: torch.Tensor,
175 noise_std: float = 1.0,
176 pad_factor: float = 1.5,
177 cutoff_radius: float = None,
178 transition_width: float = 2.0,
179 input_noise: torch.Tensor = None,
180 sampling_method: str = "fft",
181) -> torch.Tensor:
182 """
183 Generate structured noise for a batch of images using frequency soft cutoff.
184 Reduces boundary artifacts by padding images before FFT processing.
185
186 Args:
187 image_batch: Batch of image tensors of shape (B, C, H, W)
188 noise_std: Standard deviation for Gaussian noise
189 pad_factor: Padding factor (1.5 = 50% padding, 2.0 = 100% padding)
190 cutoff_radius: Frequency cutoff radius (None = auto-calculate)
191 transition_width: Width of smooth transition for frequency cutoff
192 input_noise: Optional input noise tensor to use instead of generating new noise.
193 sampling_method: Method to sample noise magnitude ('fft', 'cdf', 'two-gaussian')
194
195 Returns:
196 torch.Tensor: Batch of structured noise tensors of shape (B, C, H, W)
197 """
198 assert sampling_method in ["fft", "cdf", "two-gaussian"]
199 # Ensure tensor is on the correct device
200 batch_size, channels, height, width = image_batch.shape
201 dtype = image_batch.dtype
202 device = image_batch.device
203 image_batch = image_batch.float()
204
205 # Calculate padding size for overlap-add method
206 pad_h = int(height * (pad_factor - 1))
207 pad_h = pad_h // 2 * 2 # make it even
208 pad_w = int(width * (pad_factor - 1))
209 pad_w = pad_w // 2 * 2 # make it even
210
211 # Pad images with reflection to reduce boundary artifacts
212 padded_images = torch.nn.functional.pad(
213 image_batch,
214 (pad_w // 2, pad_w // 2, pad_h // 2, pad_h // 2),
215 mode="reflect", # Mirror edges for natural transitions
216 )
217
218 # Calculate padded dimensions
219 padded_height = height + pad_h
220 padded_width = width + pad_w
221
222 # Create frequency soft cutoff mask only if cutoff_radius is provided
223 if cutoff_radius is not None:
224 cutoff_radius = min(min(padded_height / 2, padded_width / 2), cutoff_radius)
225 freq_mask = create_frequency_soft_cutoff_mask(
226 padded_height, padded_width, cutoff_radius, transition_width, device
227 )
228 else:
229 # No cutoff - preserve all frequencies (full structure preservation)
230 freq_mask = torch.ones(padded_height, padded_width, device=device)
231
232 # Apply 2D FFT to padded images
233 fft = torch.fft.fft2(padded_images, dim=(-2, -1))
234
235 # Shift zero frequency to center
236 fft_shifted = torch.fft.fftshift(fft, dim=(-2, -1))
237
238 # Extract phase and magnitude for all images
239 image_phases = torch.angle(fft_shifted)
240 image_phases = clip_frequency_magnitude(image_phases)
241 image_magnitudes = torch.abs(fft_shifted)
242
243 if input_noise is not None:
244 # Use provided noise
245 noise_batch = torch.nn.functional.pad(
246 input_noise,
247 (pad_w // 2, pad_w // 2, pad_h // 2, pad_h // 2),
248 mode="reflect", # Mirror edges for natural transitions
249 )
250 noise_batch = noise_batch.float()
251 else:
252 # Generate Gaussian noise for the padded size
253 noise_batch = torch.randn_like(padded_images)
254
255 # Extract noise magnitude and phase
256 if sampling_method == "fft":
257 # Apply 2D FFT to noise batch
258 noise_fft = torch.fft.fft2(noise_batch, dim=(-2, -1))
259 noise_fft_shifted = torch.fft.fftshift(noise_fft, dim=(-2, -1))
260
261 noise_magnitudes = torch.abs(noise_fft_shifted)
262 noise_phases = torch.angle(noise_fft_shifted)
263 elif sampling_method == "cdf":
264 # The magnitude of FFT of Gaussian noise follows a Rayleigh distribution.
265 # We can sample it directly.
266 # The scale of the Rayleigh distribution is related to the std of the Gaussian noise
267 # and the size of the FFT.
268 # For an N-point FFT of Gaussian noise with variance sigma^2, the variance of
269 # the real and imaginary parts of the FFT coefficients is N*sigma^2.
270 # The scale parameter for the Rayleigh distribution is sqrt(N*sigma^2 / 2).
271 # Here, N = padded_height * padded_width.
272
273 N = padded_height * padded_width
274 rayleigh_scale = (N / 2) ** 0.5
275
276 ## Sample from a standard Rayleigh distribution (scale=1) and then scale it.
277 uu = torch.rand(size=image_magnitudes.shape, device=device)
278 noise_magnitudes = rayleigh_scale * torch.sqrt(-2.0 * torch.log(uu))
279 if input_noise is not None:
280 noise_fft = torch.fft.fft2(noise_batch, dim=(-2, -1))
281 noise_fft_shifted = torch.fft.fftshift(noise_fft, dim=(-2, -1))
282
283 noise_magnitudes = torch.abs(noise_fft_shifted)
284 noise_phases = torch.angle(noise_fft_shifted)
285 else:
286 noise_phases = (
287 torch.rand(size=image_magnitudes.shape, device=device) * 2 * torch.pi
288 - torch.pi
289 )
290 elif sampling_method == "two-gaussian":
291 N = padded_height * padded_width
292 rayleigh_scale = (N / 2) ** 0.5
293 # A standard Rayleigh can be generated from two standard normal distributions.
294 u1 = torch.randn_like(image_magnitudes)
295 u2 = torch.randn_like(image_magnitudes)
296 noise_magnitudes = rayleigh_scale * torch.sqrt(u1**2 + u2**2)
297 if input_noise is not None:
298 noise_fft = torch.fft.fft2(noise_batch, dim=(-2, -1))
299 noise_fft_shifted = torch.fft.fftshift(noise_fft, dim=(-2, -1))
300
301 noise_magnitudes = torch.abs(noise_fft_shifted)
302 noise_phases = torch.angle(noise_fft_shifted)
303 else:
304 noise_phases = (
305 torch.rand(size=image_magnitudes.shape, device=device) * 2 * torch.pi
306 - torch.pi
307 )
308 else:
309 raise ValueError(f"Unknown sampling method: {sampling_method}")
310
311 noise_magnitudes = clip_frequency_magnitude(noise_magnitudes)
312
313 # Scale noise magnitude by standard deviation
314 noise_magnitudes = noise_magnitudes * noise_std
315
316 # Apply frequency soft cutoff to mix phases
317 # Low frequencies (within cutoff) use image phase, high frequencies use noise phase
318 mixed_phases = (
319 freq_mask.unsqueeze(0).unsqueeze(0) * image_phases
320 + (1 - freq_mask.unsqueeze(0).unsqueeze(0)) * noise_phases
321 )
322
323 # Combine magnitude and mixed phase for all images
324 fft_combined = noise_magnitudes * torch.exp(1j * mixed_phases)
325 # Shift zero frequency back to corner
326 fft_unshifted = torch.fft.ifftshift(fft_combined, dim=(-2, -1))
327 # Apply inverse FFT
328 structured_noise_padded = torch.fft.ifft2(fft_unshifted, dim=(-2, -1))
329 # Take real part
330 structured_noise_padded = torch.real(structured_noise_padded)
331
332 clamp_mask = (structured_noise_padded < -5) + (structured_noise_padded > 5)
333 clamp_mask = (clamp_mask > 0).float()
334
335 structured_noise_padded = (
336 structured_noise_padded * (1 - clamp_mask) + noise_batch * clamp_mask
337 )
338
339 # Crop back to original size (remove padding)
340 structured_noise_batch = structured_noise_padded[
341 :, :, pad_h // 2 : pad_h // 2 + height, pad_w // 2 : pad_w // 2 + width
342 ]
343 return structured_noise_batch.to(dtype)1# ---------------------------------------------------------
2# НАСТРОЙКА ШЕДУЛЕРА
3# ---------------------------------------------------------
4from accelerate.utils import ProjectConfiguration, set_seed
5from auto_remaster.train_auto_remaster_lbm_train_test_gap_struct_noise import (
6 generate_structured_noise_batch_vectorized,
7)
8
9# num_steps = 1
10num_steps = 8
11# num_steps = 40
12# num_steps = 1
13bridge_noise_sigma = 0.001
14# bridge_noise_sigma = 0.01
15# bridge_noise_sigma = 0.0
16sigmas = np.linspace(1.0, 1 / num_steps, num_steps)
17
18noise_scheduler.set_timesteps(sigmas=sigmas, device=device)
19
20# pos = 10000
21# pos = 10100
22# pos = 1727
23# pos = 727
24# pos = 527
25# pos = 327
26pos = 227
27# pos = 170
28# pos = 0
29import random
30
31test_images_ids = list(range(0, len(dataset), 30))
32rng = random.Random(2025)
33amount = min(30, len(test_images_ids))
34selected_ids = rng.sample(test_images_ids, amount)
35
36# pos = 84
37# pos = selected_ids[0]
38# pos = selected_ids[24]
39pos = selected_ids[28]
40# pos = selected_ids[7]
41# pos = 0
42item = dataset[pos]
43source_image_name = "input_image"
44target_image_name = "edited_image"
45# Подготовка исходных изображений для визуализации и метрик
46orig_source_pil = item[source_image_name].convert("RGB")
47target_pil = item[target_image_name].convert("RGB")
48
49noise_scheduler = FlowMatchEulerDiscreteScheduler()
50
51
52# ---------------------------------------------------------
53# НАСТРОЙКА ШЕДУЛЕРА
54# ---------------------------------------------------------
55# num_steps = diffusion_args.num_inference_steps
56
57sigmas = np.linspace(1.0, 1 / num_steps, num_steps)
58
59noise_scheduler.set_timesteps(sigmas=sigmas, device="cuda")
60
61set_seed(2025)
62item = dataset[pos]
63
64# Подготовка исходных изображений для визуализации и метрик
65orig_source_pil = item[source_image_name].convert("RGB")
66target_pil = item[target_image_name].convert("RGB")
67
68source_tensor = valid_transforms(orig_source_pil)
69target_tensor = valid_transforms(target_pil)
70
71# Подготовка латента source
72# Используем train_transforms для кодирования, как в обучении
73c_t = (
74 train_transforms(orig_source_pil).unsqueeze(0).to(vae_val.dtype).to(vae_val.device)
75)
76
77
78with torch.no_grad():
79 # Encode source image
80 z_source = (
81 # vae_val.encode(c_t, return_dict=False)[0]
82 vae_val.encode(c_t, return_dict=False)[0].sample()
83 * vae_val.config.scaling_factor
84 )
85 structured_noise = generate_structured_noise_batch_vectorized(
86 z_source.float(), # float обязателен для FFT
87 noise_std=1.0,
88 pad_factor=1.5,
89 cutoff_radius=20, # Фиксированный радиус для валидации
90 input_noise=torch.randn_like(z_source.float()),
91 sampling_method="fft",
92 ).to(dtype=z_source.dtype, device=z_source.device)
93
94 # sample = z_source
95 sample = z_source + structured_noise * bridge_noise_sigma
96
97 # ---------------------------------------------------------
98 # ЦИКЛ СЭМПЛИНГА (Адаптировано из sample())
99 # ---------------------------------------------------------
100 # for i, t in enumerate(noise_scheduler.timesteps):
101 for i in range(num_steps):
102 t = noise_scheduler.timesteps[i]
103 # 1. Масштабирование входа (если требуется шедулером)
104 if hasattr(noise_scheduler, "scale_model_input"):
105 denoiser_input = noise_scheduler.scale_model_input(sample, t)
106 else:
107 denoiser_input = sample
108 denoiser_input = torch.cat([denoiser_input, z_source], dim=1)
109 # 2. Предсказание направления (UNet)
110 # unet_val(x, t) -> output
111 # print(i, t, noise_scheduler.timesteps)
112 pred = unet_val(
113 denoiser_input,
114 t.to(z_source.device).repeat(denoiser_input.shape[0]),
115 return_dict=False,
116 )[0]
117
118 # 3. Шаг диффузии (Reverse Process)
119 sample = noise_scheduler.step(pred, t, sample, return_dict=False)[0]
120
121 # 4. Добавление стохастичности (Bridge Noise)
122 # Не добавляем шум после последнего шага
123 if i < len(noise_scheduler.timesteps) - 1:
124 # Получаем таймстемп следующего шага
125 next_timestep = (
126 noise_scheduler.timesteps[i + 1]
127 .to(z_source.device)
128 .repeat(sample.shape[0])
129 )
130
131 # Получаем сигму для следующего шага
132 sigmas_next = _get_sigmas_val(
133 noise_scheduler,
134 next_timestep,
135 n_dim=4,
136 dtype=weight_dtype,
137 device=z_source.device,
138 )
139
140 # Формула Bridge Matching: шум пропорционален sqrt(sigma * (1-sigma))
141 # noise = torch.randn_like(sample)
142 structured_noise = generate_structured_noise_batch_vectorized(
143 z_source.float(), # float обязателен для FFT
144 noise_std=1.0,
145 pad_factor=1.5,
146 cutoff_radius=10.0, # Фиксированный радиус для валидации
147 input_noise=torch.randn_like(z_source.float()),
148 sampling_method="fft",
149 ).to(dtype=sample.dtype, device=sample.device)
150 noise = structured_noise
151 bridge_factor = (sigmas_next * (1.0 - sigmas_next)) ** 0.5
152
153 sample = sample + bridge_noise_sigma * bridge_factor * noise
154 sample = sample.to(z_source.dtype)
155
156 # ---------------------------------------------------------
157
158 # Декодирование результата
159 output_image = (
160 vae_val.decode(
161 sample / vae_val.config.scaling_factor,
162 return_dict=False,
163 )[0]
164 ).clamp(-1, 1)
165
166 pred_image_pil = transforms.ToPILImage()(output_image[0].cpu().float() * 0.5 + 0.5)
167
168many_steps = pred_image_pil.convert("RGB")
169num_steps = 1
170sigmas = np.linspace(1.0, 1 / num_steps, num_steps)
171
172noise_scheduler.set_timesteps(sigmas=sigmas, device="cuda")
173c_t = (
174 train_transforms(orig_source_pil).unsqueeze(0).to(vae_val.dtype).to(vae_val.device)
175)
176
177with torch.no_grad():
178 # Encode source image
179 z_source = (
180 # vae_val.encode(c_t, return_dict=False)[0]
181 vae_val.encode(c_t, return_dict=False)[0].sample()
182 * vae_val.config.scaling_factor
183 )
184 structured_noise = generate_structured_noise_batch_vectorized(
185 z_source.float(), # float обязателен для FFT
186 noise_std=1.0,
187 pad_factor=1.5,
188 cutoff_radius=20, # Фиксированный радиус для валидации
189 input_noise=torch.randn_like(z_source.float()),
190 sampling_method="fft",
191 ).to(dtype=z_source.dtype, device=z_source.device)
192
193 # sample = z_source
194 sample = z_source + structured_noise * bridge_noise_sigma
195
196 # ---------------------------------------------------------
197 # ЦИКЛ СЭМПЛИНГА (Адаптировано из sample())
198 # ---------------------------------------------------------
199 # for i, t in enumerate(noise_scheduler.timesteps):
200 for i in range(num_steps):
201 t = noise_scheduler.timesteps[i]
202 # 1. Масштабирование входа (если требуется шедулером)
203 if hasattr(noise_scheduler, "scale_model_input"):
204 denoiser_input = noise_scheduler.scale_model_input(sample, t)
205 else:
206 denoiser_input = sample
207 denoiser_input = torch.cat([denoiser_input, z_source], dim=1)
208 # 2. Предсказание направления (UNet)
209 # unet_val(x, t) -> output
210 # print(i, t, noise_scheduler.timesteps)
211 pred = unet_val(
212 denoiser_input,
213 t.to(z_source.device).repeat(denoiser_input.shape[0]),
214 return_dict=False,
215 )[0]
216
217 # 3. Шаг диффузии (Reverse Process)
218 sample = noise_scheduler.step(pred, t, sample, return_dict=False)[0]
219
220 # 4. Добавление стохастичности (Bridge Noise)
221 # Не добавляем шум после последнего шага
222 if i < len(noise_scheduler.timesteps) - 1:
223 # Получаем таймстемп следующего шага
224 next_timestep = (
225 noise_scheduler.timesteps[i + 1]
226 .to(z_source.device)
227 .repeat(sample.shape[0])
228 )
229
230 # Получаем сигму для следующего шага
231 sigmas_next = _get_sigmas_val(
232 noise_scheduler,
233 next_timestep,
234 n_dim=4,
235 dtype=weight_dtype,
236 device=z_source.device,
237 )
238
239 # Формула Bridge Matching: шум пропорционален sqrt(sigma * (1-sigma))
240 noise = torch.randn_like(sample)
241 bridge_factor = (sigmas_next * (1.0 - sigmas_next)) ** 0.5
242
243 sample = sample + bridge_noise_sigma * bridge_factor * noise
244 sample = sample.to(z_source.dtype)
245
246 # ---------------------------------------------------------
247
248 # Декодирование результата
249 output_image = (
250 vae_val.decode(
251 sample / vae_val.config.scaling_factor,
252 return_dict=False,
253 )[0]
254 ).clamp(-1, 1)
255
256 pred_image_pil = transforms.ToPILImage()(output_image[0].cpu().float() * 0.5 + 0.5)
257
258steps_1 = pred_image_pil.convert("RGB")
259
260from jupyter_compare_view import compare
261
262# compare(img, grayscale_img, cmap="gray", start_mode="horizontal", start_slider_pos=0.73)
263# grayscale_img
264Image.fromarray(
265 np.hstack(
266 (
267 # np.array(source_tensor),
268 np.array(steps_1),
269 np.array(many_steps),
270 np.array(target_tensor),
271 )
272 )
273)