X commited on
Commit
0bb2363
·
verified ·
1 Parent(s): 9730a19

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +108 -164
app.py CHANGED
@@ -7,37 +7,32 @@ from PIL import Image
7
  import imageio
8
  import os
9
  import tempfile
10
- from datetime import datetime
11
 
12
- # ============ ПОЛНОСТЬЮ НЕЙРОСЕТЕВАЯ АРХИТЕКТУРА ============
13
  class FullNeuralAnimator(nn.Module):
14
  """
15
- Одна нейросеть делает ВСЁ:
16
- 1. Анализирует изображение
17
- 2. Предсказывает последовательность кадров
18
- 3. Генерирует анимацию
19
  """
20
  def __init__(self):
21
  super().__init__()
22
 
23
- # === Encoder (понимает структуру) ===
24
  self.enc1 = self._block(3, 32)
25
  self.enc2 = self._block(32, 64)
26
  self.enc3 = self._block(64, 128)
27
  self.enc4 = self._block(128, 256)
28
  self.pool = nn.MaxPool2d(2)
29
 
30
- # === LSTM для временной последовательности ===
31
- # Запоминает как меняется анимация во времени
32
  self.lstm = nn.LSTM(
33
- input_size=256 * 16 * 16, # 256 каналов * 16x16
34
  hidden_size=512,
35
  num_layers=2,
36
  batch_first=True,
37
  dropout=0.2
38
  )
39
 
40
- # === Декодер (создаёт кадры) ===
41
  self.dec4 = self._block(512, 256)
42
  self.dec3 = self._block(256, 128)
43
  self.dec2 = self._block(128, 64)
@@ -56,9 +51,6 @@ class FullNeuralAnimator(nn.Module):
56
  nn.Tanh()
57
  )
58
 
59
- # === Контроль времени ===
60
- self.time_encoder = nn.Linear(1, 128) # Кодируем время
61
-
62
  def _block(self, in_ch, out_ch):
63
  return nn.Sequential(
64
  nn.Conv2d(in_ch, out_ch, 3, padding=1),
@@ -70,10 +62,6 @@ class FullNeuralAnimator(nn.Module):
70
  )
71
 
72
  def forward(self, x, num_frames=20):
73
- """
74
- x: входное изображение [B, 3, H, W]
75
- num_frames: сколько кадров сгенерировать
76
- """
77
  batch_size = x.size(0)
78
 
79
  # === 1. Кодируем изображение ===
@@ -86,45 +74,45 @@ class FullNeuralAnimator(nn.Module):
86
  skips = [e1, e2, e3, e4]
87
 
88
  # === 2. Подготовка для LSTM ===
89
- # [B, 256, 16, 16] -> [B, 256*16*16]
90
- bottleneck = e4.view(batch_size, -1)
91
 
92
- # === 3. Генерируем последовательность во времени ===
93
  frames = []
94
  hidden = None
95
 
96
- # Начальное состояние
97
  lstm_input = bottleneck.unsqueeze(1) # [B, 1, features]
98
 
99
  for t in range(num_frames):
100
- # Кодируем время
101
- time_tensor = torch.tensor([t / num_frames], device=x.device)
102
- time_embed = self.time_encoder(time_tensor).unsqueeze(0).unsqueeze(1) # [1, 1, 128]
103
-
104
- # Добавляем информацию о времени
105
- lstm_input_with_time = torch.cat([lstm_input, time_embed.repeat(batch_size, 1, 1)], dim=-1)
106
-
107
  # LSTM предсказывает следующее состояние
108
- lstm_out, hidden = self.lstm(lstm_input_with_time, hidden)
109
 
110
  # === 4. Декодируем в кадр ===
111
- # [B, 512] -> [B, 256, 16, 16]
112
- h = lstm_out.squeeze(1).view(batch_size, 256, 16, 16)
113
 
114
  # Декодер с skip connections
115
  d4 = self.up4(h)
 
 
 
116
  d4 = torch.cat([d4, skips[3]], dim=1)
117
  d4 = self.dec4(d4)
118
 
119
  d3 = self.up3(d4)
 
 
120
  d3 = torch.cat([d3, skips[2]], dim=1)
121
  d3 = self.dec3(d3)
122
 
123
  d2 = self.up2(d3)
 
 
124
  d2 = torch.cat([d2, skips[1]], dim=1)
125
  d2 = self.dec2(d2)
126
 
127
  d1 = self.up1(d2)
 
 
128
  d1 = torch.cat([d1, skips[0]], dim=1)
129
  d1 = self.dec1(d1)
130
 
@@ -132,34 +120,28 @@ class FullNeuralAnimator(nn.Module):
132
  frame = self.frame_generator(d1)
133
  frames.append(frame)
134
 
135
- # Обновляем вход для LSTM (авторегрессия)
136
- # Берём bottleneck следующего кадра
137
- next_bottleneck = self.enc4(self.pool(self.enc3(self.pool(self.enc2(self.pool(self.enc1(frame)))))))
138
- next_bottleneck = next_bottleneck.view(batch_size, -1)
139
- lstm_input = next_bottleneck.unsqueeze(1)
140
-
141
- # Собираем все кадры
142
- return torch.stack(frames, dim=1) # [B, T, 3, H, W]
143
 
144
- # ============ ЕЩЁ ОДНА НЕЙРОСЕТЬ ДЛЯ РАЗНООБРАЗИЯ ============
145
  class StyleTransferAnimator(nn.Module):
146
- """
147
- Генерирует разные стили анимации
148
- """
149
  def __init__(self):
150
  super().__init__()
151
 
152
- # Стили анимации (обучаемые векторы)
153
  self.style_embeddings = nn.ParameterDict({
154
  'wave': nn.Parameter(torch.randn(64)),
155
  'pulse': nn.Parameter(torch.randn(64)),
156
  'glitch': nn.Parameter(torch.randn(64)),
157
  'melt': nn.Parameter(torch.randn(64)),
158
  'twist': nn.Parameter(torch.randn(64)),
159
- 'dream': nn.Parameter(torch.randn(64)),
160
  })
161
 
162
- # Основная сеть
163
  self.encoder = nn.Sequential(
164
  nn.Conv2d(3, 32, 4, stride=2, padding=1),
165
  nn.ReLU(),
@@ -171,53 +153,58 @@ class StyleTransferAnimator(nn.Module):
171
  nn.ReLU(),
172
  )
173
 
174
- # Генератор кадров с учётом стиля
 
 
 
 
 
 
 
 
 
175
  self.decoder = nn.Sequential(
176
- nn.ConvTranspose2d(256 + 64, 128, 4, stride=2, padding=1),
177
  nn.ReLU(),
178
- nn.ConvTranspose2d(128, 64, 4, stride=2, padding=1),
179
  nn.ReLU(),
180
- nn.ConvTranspose2d(64, 32, 4, stride=2, padding=1),
181
  nn.ReLU(),
182
- nn.ConvTranspose2d(32, 3, 4, stride=2, padding=1),
183
  nn.Tanh()
184
  )
185
-
186
- # LSTM для времени
187
- self.temporal_lstm = nn.LSTMCell(256, 512)
188
- self.time_proj = nn.Linear(1, 128)
189
 
190
  def forward(self, x, style='wave', num_frames=20):
191
  batch_size = x.size(0)
192
 
193
- # Кодируем изображение
194
- features = self.encoder(x) # [B, 256, 16, 16]
195
- features_flat = features.view(batch_size, -1) # [B, 256*16*16]
196
 
197
- # Получаем стиль
198
- style_vector = self.style_embeddings[style] # [64]
199
- style_vector = style_vector.unsqueeze(0).repeat(batch_size, 1) # [B, 64]
200
 
201
  frames = []
202
- h = None
203
- c = None
204
 
205
  for t in range(num_frames):
206
- # Время
207
- t_norm = torch.tensor([t / num_frames], device=x.device)
208
- t_embed = self.time_proj(t_norm).unsqueeze(0).repeat(batch_size, 1)
209
-
210
  # LSTM
211
- lstm_input = torch.cat([features_flat, t_embed, style_vector], dim=1)
212
- h, c = self.temporal_lstm(lstm_input, (h, c))
213
 
214
- # Декодируем
215
- h_reshaped = h.view(batch_size, 256, 16, 16)
216
- style_reshaped = style_vector.view(batch_size, 64, 1, 1).repeat(1, 1, 16, 16)
217
- decoder_input = torch.cat([h_reshaped, style_reshaped], dim=1)
218
 
219
  frame = self.decoder(decoder_input)
220
  frames.append(frame)
 
 
 
 
 
221
 
222
  return torch.stack(frames, dim=1)
223
 
@@ -227,34 +214,34 @@ class NeuralAnimator:
227
  self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
228
  print(f"🔥 Устройство: {self.device}")
229
 
230
- # Загружаем модели
231
  self.animator = FullNeuralAnimator().to(self.device)
232
  self.styler = StyleTransferAnimator().to(self.device)
233
 
234
- # Пробуем загрузить обученные модели
235
  self.load_models()
236
 
237
  self.animator.eval()
238
  self.styler.eval()
239
 
240
  def load_models(self):
241
- """Загружает или создаёт модели"""
242
  models_dir = 'neural_models'
243
  os.makedirs(models_dir, exist_ok=True)
244
 
245
- # Если нет моделей - используем случайные (но они будут работать!)
246
  if os.path.exists(f'{models_dir}/animator.pth'):
247
  self.animator.load_state_dict(torch.load(f'{models_dir}/animator.pth', map_location=self.device))
248
  print("✅ Аниматор загружен")
249
  else:
250
- print("⚠️ Модель не найдена, используется случайная (всё равно работает!)")
251
 
252
  if os.path.exists(f'{models_dir}/styler.pth'):
253
  self.styler.load_state_dict(torch.load(f'{models_dir}/styler.pth', map_location=self.device))
254
  print("✅ Стилизатор загружен")
255
 
256
- def generate_animation(self, image, style='wave', num_frames=25, size=256):
257
- """Генерирует анимацию полностью нейросетью"""
 
 
258
 
259
  # Подготовка
260
  if isinstance(image, np.ndarray):
@@ -266,7 +253,7 @@ class NeuralAnimator:
266
  img_tensor = torch.from_numpy(np.array(img)).float() / 127.5 - 1
267
  img_tensor = img_tensor.permute(2, 0, 1).unsqueeze(0).to(self.device)
268
 
269
- # === ВСЁ ДЕЛАЕТ НЕЙРОСЕТЬ ===
270
  with torch.no_grad():
271
  if style in ['wave', 'pulse', 'glitch', 'melt', 'twist']:
272
  frames_tensor = self.styler(img_tensor, style=style, num_frames=num_frames)
@@ -286,99 +273,75 @@ class NeuralAnimator:
286
 
287
  return temp_file.name
288
 
289
- # ============ ОБУЧЕНИЕ НЕЙРОСЕТИ ============
290
  def train_neural_animator():
291
- """Полностью нейросетевое обучение"""
292
- print("🧠 Обучаем нейросеть делать ВСЁ...")
293
 
294
  device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
295
 
296
- # Создаём модели
297
  animator = FullNeuralAnimator().to(device)
298
  styler = StyleTransferAnimator().to(device)
299
 
300
- # Оптимизаторы
301
  opt_anim = torch.optim.Adam(animator.parameters(), lr=0.0001)
302
  opt_style = torch.optim.Adam(styler.parameters(), lr=0.0001)
303
 
304
- # Функция потерь
305
  mse = nn.MSELoss()
306
 
307
  print("🚀 Начинаем обучение...")
308
 
309
  for epoch in range(10):
310
- # Генерируем случайные данные
311
- batch_size = 4
312
-
313
- # 1. Создаём случайные изображения
314
  fake_images = torch.randn(batch_size, 3, 128, 128, device=device)
315
 
316
- # 2. Обучаем FullNeuralAnimator
317
  frames = animator(fake_images, num_frames=15)
318
-
319
- # Потери: плавность + согласованность
320
- loss_smooth = mse(frames[:, 1:], frames[:, :-1]) # Соседние кадры похожи
321
- loss_consistency = mse(frames.mean(dim=1), fake_images) # Среднее похоже на оригинал
322
  loss_anim = loss_smooth + 0.5 * loss_consistency
323
 
324
  opt_anim.zero_grad()
325
  loss_anim.backward()
326
  opt_anim.step()
327
 
328
- # 3. Обучаем StyleTransferAnimator
329
- for style in ['wave', 'pulse', 'glitch', 'melt', 'twist']:
330
- style_frames = styler(fake_images, style=style, num_frames=15)
331
-
332
- loss_style_smooth = mse(style_frames[:, 1:], style_frames[:, :-1])
333
- loss_style_consistency = mse(style_frames.mean(dim=1), fake_images)
334
- loss_style = loss_style_smooth + 0.5 * loss_style_consistency
335
-
336
- opt_style.zero_grad()
337
- loss_style.backward()
338
- opt_style.step()
339
 
340
  print(f"Epoch {epoch+1}/10 | Loss: {loss_anim.item():.4f} | Style: {loss_style.item():.4f}")
341
 
342
- # Сохраняем модели
343
  os.makedirs('neural_models', exist_ok=True)
344
  torch.save(animator.state_dict(), 'neural_models/animator.pth')
345
  torch.save(styler.state_dict(), 'neural_models/styler.pth')
346
 
347
- print("✅ Обучение завершено! Модели сохранены.")
348
- return animator, styler
349
 
350
  # ============ GRADIO ИНТЕРФЕЙС ============
351
  animator = NeuralAnimator()
352
 
353
- def generate_distortion(image, style, frames, size):
354
- """Функция для Gradio"""
355
  if image is None:
356
  return None
357
-
358
  try:
359
- gif_path = animator.generate_animation(
360
- image,
361
- style=style,
362
- num_frames=int(frames),
363
- size=int(size)
364
- )
365
- return gif_path
366
  except Exception as e:
367
  print(f"Ошибка: {e}")
368
  return None
369
 
370
  # Создаём интерфейс
371
- with gr.Blocks(theme=gr.themes.Soft(), title="🧠 Нейросетевая анимация") as demo:
372
  gr.Markdown("""
373
  # 🧠 ПОЛНОСТЬЮ НЕЙРОСЕТЕВАЯ АНИМАЦИЯ
374
 
375
- ### Нейросеть делает ВСЁ:
376
- - 🎨 Анализирует структуру изображения
377
- - 🧮 Предсказывает движение
378
- - 🎬 Генерирует каждый кадр
379
- - ⏱️ Создаёт временную последовательность
380
-
381
- **Никаких ручных алгоритмов — только нейросеть!**
382
  """)
383
 
384
  with gr.Row():
@@ -386,7 +349,7 @@ with gr.Blocks(theme=gr.themes.Soft(), title="🧠 Нейросетевая ан
386
  input_image = gr.Image(
387
  label="📸 Загрузи фото",
388
  type="numpy",
389
- height=400
390
  )
391
 
392
  style = gr.Dropdown(
@@ -396,7 +359,6 @@ with gr.Blocks(theme=gr.themes.Soft(), title="🧠 Нейросетевая ан
396
  ("Глитч 📺", "glitch"),
397
  ("Плавление 🕯️", "melt"),
398
  ("Скручивание 🌀", "twist"),
399
- ("Сюрреализм 🎭", "dream"),
400
  ("Нейросетевой 🧠", "neural")
401
  ],
402
  label="🎨 Стиль анимации",
@@ -405,28 +367,31 @@ with gr.Blocks(theme=gr.themes.Soft(), title="🧠 Нейросетевая ан
405
 
406
  frames = gr.Slider(
407
  minimum=10,
408
- maximum=40,
409
  value=20,
410
  step=5,
411
  label="Количество кадров"
412
  )
413
 
414
  size = gr.Slider(
415
- minimum=128,
416
- maximum=512,
417
- value=256,
418
  step=64,
419
- label="Размер (качество/скорость)"
420
  )
421
 
422
- generate_btn = gr.Button("🧠 Запустить нейросеть!", variant="primary", size="lg")
423
- train_btn = gr.Button("🎓 Обучить нейросеть", variant="secondary", size="sm")
 
 
 
424
 
425
  with gr.Column(scale=1):
426
  output_gif = gr.Image(
427
- label="🎬 Нейросеть сгенерировала!",
428
  type="filepath",
429
- height=500
430
  )
431
 
432
  download_btn = gr.DownloadButton(
@@ -436,7 +401,7 @@ with gr.Blocks(theme=gr.themes.Soft(), title="🧠 Нейросетевая ан
436
 
437
  # Логика
438
  generate_btn.click(
439
- fn=generate_distortion,
440
  inputs=[input_image, style, frames, size],
441
  outputs=[output_gif]
442
  ).then(
@@ -448,34 +413,13 @@ with gr.Blocks(theme=gr.themes.Soft(), title="🧠 Нейросетевая ан
448
  train_btn.click(
449
  fn=train_neural_animator,
450
  inputs=[],
451
- outputs=[]
452
- ).then(
453
- fn=lambda: "✅ Модель обучена! Перезапустите анимацию.",
454
- inputs=[],
455
- outputs=[gr.Textbox(label="Статус")]
456
  )
457
-
458
- gr.Markdown("""
459
- ### 🔬 Как это работает
460
-
461
- 1. **Нейросеть-кодировщик** понимает структуру изображения
462
- 2. **LSTM-слой** запоминает как меняется анимация во времени
463
- 3. **Нейросеть-декодер** генерирует каждый кадр
464
- 4. **Векторы стиля** управляют типом анимации
465
-
466
- **ВСЁ ОБУЧАЕТСЯ НЕЙРОСЕТЬЮ!**
467
- """)
468
 
469
  if __name__ == "__main__":
470
  print("""
471
- 🧠 ЗАПУСКАЕМ ПОЛНОСТЬЮ НЕЙРОСЕТЕВУЮ АНИМАЦИЮ!
472
  📱 Открой браузер: http://localhost:7860
473
-
474
- Нейросеть делает ВСЁ:
475
- - Анализ фото
476
- - Предсказание движения
477
- - Генерация кадров
478
- - Создание анимации
479
  """)
480
 
481
  demo.launch(
 
7
  import imageio
8
  import os
9
  import tempfile
 
10
 
11
+ # ============ ИСПРАВЛЕННАЯ ПОЛНОСТЬЮ НЕЙРОСЕТЕВАЯ АРХИТЕКТУРА ============
12
  class FullNeuralAnimator(nn.Module):
13
  """
14
+ Одна нейросеть делает ВСЁ
 
 
 
15
  """
16
  def __init__(self):
17
  super().__init__()
18
 
19
+ # === Encoder ===
20
  self.enc1 = self._block(3, 32)
21
  self.enc2 = self._block(32, 64)
22
  self.enc3 = self._block(64, 128)
23
  self.enc4 = self._block(128, 256)
24
  self.pool = nn.MaxPool2d(2)
25
 
26
+ # === LSTM ===
 
27
  self.lstm = nn.LSTM(
28
+ input_size=256 * 8 * 8, # 256 каналов * 8x8 (после 3х пулингов)
29
  hidden_size=512,
30
  num_layers=2,
31
  batch_first=True,
32
  dropout=0.2
33
  )
34
 
35
+ # === Декодер ===
36
  self.dec4 = self._block(512, 256)
37
  self.dec3 = self._block(256, 128)
38
  self.dec2 = self._block(128, 64)
 
51
  nn.Tanh()
52
  )
53
 
 
 
 
54
  def _block(self, in_ch, out_ch):
55
  return nn.Sequential(
56
  nn.Conv2d(in_ch, out_ch, 3, padding=1),
 
62
  )
63
 
64
  def forward(self, x, num_frames=20):
 
 
 
 
65
  batch_size = x.size(0)
66
 
67
  # === 1. Кодируем изображение ===
 
74
  skips = [e1, e2, e3, e4]
75
 
76
  # === 2. Подготовка для LSTM ===
77
+ # После пулингов: 256 -> 8x8
78
+ bottleneck = e4.view(batch_size, -1) # [B, 256*8*8]
79
 
80
+ # === 3. Генерируем последовательность ===
81
  frames = []
82
  hidden = None
83
 
 
84
  lstm_input = bottleneck.unsqueeze(1) # [B, 1, features]
85
 
86
  for t in range(num_frames):
 
 
 
 
 
 
 
87
  # LSTM предсказывает следующее состояние
88
+ lstm_out, hidden = self.lstm(lstm_input, hidden)
89
 
90
  # === 4. Декодируем в кадр ===
91
+ h = lstm_out.squeeze(1).view(batch_size, 256, 8, 8)
 
92
 
93
  # Декодер с skip connections
94
  d4 = self.up4(h)
95
+ # Resize skip connection если нужно
96
+ if d4.size(-1) != skips[3].size(-1):
97
+ skips[3] = F.interpolate(skips[3], size=d4.size(-2:), mode='bilinear')
98
  d4 = torch.cat([d4, skips[3]], dim=1)
99
  d4 = self.dec4(d4)
100
 
101
  d3 = self.up3(d4)
102
+ if d3.size(-1) != skips[2].size(-1):
103
+ skips[2] = F.interpolate(skips[2], size=d3.size(-2:), mode='bilinear')
104
  d3 = torch.cat([d3, skips[2]], dim=1)
105
  d3 = self.dec3(d3)
106
 
107
  d2 = self.up2(d3)
108
+ if d2.size(-1) != skips[1].size(-1):
109
+ skips[1] = F.interpolate(skips[1], size=d2.size(-2:), mode='bilinear')
110
  d2 = torch.cat([d2, skips[1]], dim=1)
111
  d2 = self.dec2(d2)
112
 
113
  d1 = self.up1(d2)
114
+ if d1.size(-1) != skips[0].size(-1):
115
+ skips[0] = F.interpolate(skips[0], size=d1.size(-2:), mode='bilinear')
116
  d1 = torch.cat([d1, skips[0]], dim=1)
117
  d1 = self.dec1(d1)
118
 
 
120
  frame = self.frame_generator(d1)
121
  frames.append(frame)
122
 
123
+ # Обновляем вход для LSTM
124
+ next_features = self.enc4(self.pool(self.enc3(self.pool(self.enc2(self.pool(self.enc1(frame)))))))
125
+ next_features = next_features.view(batch_size, -1)
126
+ lstm_input = next_features.unsqueeze(1)
127
+
128
+ return torch.stack(frames, dim=1)
 
 
129
 
130
+ # ============ СТИЛЕВАЯ НЕЙРОСЕТЬ ============
131
  class StyleTransferAnimator(nn.Module):
 
 
 
132
  def __init__(self):
133
  super().__init__()
134
 
135
+ # Стили
136
  self.style_embeddings = nn.ParameterDict({
137
  'wave': nn.Parameter(torch.randn(64)),
138
  'pulse': nn.Parameter(torch.randn(64)),
139
  'glitch': nn.Parameter(torch.randn(64)),
140
  'melt': nn.Parameter(torch.randn(64)),
141
  'twist': nn.Parameter(torch.randn(64)),
 
142
  })
143
 
144
+ # Encoder
145
  self.encoder = nn.Sequential(
146
  nn.Conv2d(3, 32, 4, stride=2, padding=1),
147
  nn.ReLU(),
 
153
  nn.ReLU(),
154
  )
155
 
156
+ # LSTM
157
+ self.lstm = nn.LSTM(
158
+ input_size=256 * 8 * 8,
159
+ hidden_size=512,
160
+ num_layers=2,
161
+ batch_first=True,
162
+ dropout=0.2
163
+ )
164
+
165
+ # Decoder
166
  self.decoder = nn.Sequential(
167
+ nn.ConvTranspose2d(512 + 64, 256, 4, stride=2, padding=1),
168
  nn.ReLU(),
169
+ nn.ConvTranspose2d(256, 128, 4, stride=2, padding=1),
170
  nn.ReLU(),
171
+ nn.ConvTranspose2d(128, 64, 4, stride=2, padding=1),
172
  nn.ReLU(),
173
+ nn.ConvTranspose2d(64, 3, 4, stride=2, padding=1),
174
  nn.Tanh()
175
  )
 
 
 
 
176
 
177
  def forward(self, x, style='wave', num_frames=20):
178
  batch_size = x.size(0)
179
 
180
+ # Кодируем
181
+ features = self.encoder(x) # [B, 256, 8, 8]
182
+ features_flat = features.view(batch_size, -1)
183
 
184
+ # Стиль
185
+ style_vector = self.style_embeddings[style]
186
+ style_vector = style_vector.unsqueeze(0).repeat(batch_size, 1)
187
 
188
  frames = []
189
+ hidden = None
190
+ lstm_input = features_flat.unsqueeze(1)
191
 
192
  for t in range(num_frames):
 
 
 
 
193
  # LSTM
194
+ lstm_out, hidden = self.lstm(lstm_input, hidden)
 
195
 
196
+ # Декодируем со стилем
197
+ h = lstm_out.squeeze(1).view(batch_size, 256, 8, 8)
198
+ style_expanded = style_vector.view(batch_size, 64, 1, 1).repeat(1, 1, 8, 8)
199
+ decoder_input = torch.cat([h, style_expanded], dim=1)
200
 
201
  frame = self.decoder(decoder_input)
202
  frames.append(frame)
203
+
204
+ # Обновляем вход
205
+ next_features = self.encoder(frame)
206
+ next_features = next_features.view(batch_size, -1)
207
+ lstm_input = next_features.unsqueeze(1)
208
 
209
  return torch.stack(frames, dim=1)
210
 
 
214
  self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
215
  print(f"🔥 Устройство: {self.device}")
216
 
217
+ # Создаём модели
218
  self.animator = FullNeuralAnimator().to(self.device)
219
  self.styler = StyleTransferAnimator().to(self.device)
220
 
221
+ # Пробуем загрузить
222
  self.load_models()
223
 
224
  self.animator.eval()
225
  self.styler.eval()
226
 
227
  def load_models(self):
 
228
  models_dir = 'neural_models'
229
  os.makedirs(models_dir, exist_ok=True)
230
 
 
231
  if os.path.exists(f'{models_dir}/animator.pth'):
232
  self.animator.load_state_dict(torch.load(f'{models_dir}/animator.pth', map_location=self.device))
233
  print("✅ Аниматор загружен")
234
  else:
235
+ print("⚠️ Модель не найдена, используем случайную")
236
 
237
  if os.path.exists(f'{models_dir}/styler.pth'):
238
  self.styler.load_state_dict(torch.load(f'{models_dir}/styler.pth', map_location=self.device))
239
  print("✅ Стилизатор загружен")
240
 
241
+ def generate_animation(self, image, style='wave', num_frames=20, size=128):
242
+ """Генерирует анимацию"""
243
+ if image is None:
244
+ return None
245
 
246
  # Подготовка
247
  if isinstance(image, np.ndarray):
 
253
  img_tensor = torch.from_numpy(np.array(img)).float() / 127.5 - 1
254
  img_tensor = img_tensor.permute(2, 0, 1).unsqueeze(0).to(self.device)
255
 
256
+ # ВСЁ ДЕЛАЕТ НЕЙРОСЕТЬ
257
  with torch.no_grad():
258
  if style in ['wave', 'pulse', 'glitch', 'melt', 'twist']:
259
  frames_tensor = self.styler(img_tensor, style=style, num_frames=num_frames)
 
273
 
274
  return temp_file.name
275
 
276
+ # ============ ОБУЧЕНИЕ ============
277
  def train_neural_animator():
278
+ """Обучение нейросети"""
279
+ print("🧠 Обучаем нейросеть...")
280
 
281
  device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
282
 
 
283
  animator = FullNeuralAnimator().to(device)
284
  styler = StyleTransferAnimator().to(device)
285
 
 
286
  opt_anim = torch.optim.Adam(animator.parameters(), lr=0.0001)
287
  opt_style = torch.optim.Adam(styler.parameters(), lr=0.0001)
288
 
 
289
  mse = nn.MSELoss()
290
 
291
  print("🚀 Начинаем обучение...")
292
 
293
  for epoch in range(10):
294
+ batch_size = 2
 
 
 
295
  fake_images = torch.randn(batch_size, 3, 128, 128, device=device)
296
 
297
+ # Обучаем аниматор
298
  frames = animator(fake_images, num_frames=15)
299
+ loss_smooth = mse(frames[:, 1:], frames[:, :-1])
300
+ loss_consistency = mse(frames.mean(dim=1), fake_images)
 
 
301
  loss_anim = loss_smooth + 0.5 * loss_consistency
302
 
303
  opt_anim.zero_grad()
304
  loss_anim.backward()
305
  opt_anim.step()
306
 
307
+ # Обучаем стилизатор
308
+ style = 'wave'
309
+ style_frames = styler(fake_images, style=style, num_frames=15)
310
+ loss_style_smooth = mse(style_frames[:, 1:], style_frames[:, :-1])
311
+ loss_style_consistency = mse(style_frames.mean(dim=1), fake_images)
312
+ loss_style = loss_style_smooth + 0.5 * loss_style_consistency
313
+
314
+ opt_style.zero_grad()
315
+ loss_style.backward()
316
+ opt_style.step()
 
317
 
318
  print(f"Epoch {epoch+1}/10 | Loss: {loss_anim.item():.4f} | Style: {loss_style.item():.4f}")
319
 
 
320
  os.makedirs('neural_models', exist_ok=True)
321
  torch.save(animator.state_dict(), 'neural_models/animator.pth')
322
  torch.save(styler.state_dict(), 'neural_models/styler.pth')
323
 
324
+ print("✅ Обучение завершено!")
325
+ return "✅ Модель обучена!"
326
 
327
  # ============ GRADIO ИНТЕРФЕЙС ============
328
  animator = NeuralAnimator()
329
 
330
+ def generate_wrapper(image, style, frames, size):
 
331
  if image is None:
332
  return None
 
333
  try:
334
+ return animator.generate_animation(image, style, int(frames), int(size))
 
 
 
 
 
 
335
  except Exception as e:
336
  print(f"Ошибка: {e}")
337
  return None
338
 
339
  # Создаём интерфейс
340
+ with gr.Blocks(title="🧠 Нейросетевая анимация") as demo:
341
  gr.Markdown("""
342
  # 🧠 ПОЛНОСТЬЮ НЕЙРОСЕТЕВАЯ АНИМАЦИЯ
343
 
344
+ Нейросеть делает ВСЁ: анализ, предсказание движения, генерацию кадров!
 
 
 
 
 
 
345
  """)
346
 
347
  with gr.Row():
 
349
  input_image = gr.Image(
350
  label="📸 Загрузи фото",
351
  type="numpy",
352
+ height=300
353
  )
354
 
355
  style = gr.Dropdown(
 
359
  ("Глитч 📺", "glitch"),
360
  ("Плавление 🕯️", "melt"),
361
  ("Скручивание 🌀", "twist"),
 
362
  ("Нейросетевой 🧠", "neural")
363
  ],
364
  label="🎨 Стиль анимации",
 
367
 
368
  frames = gr.Slider(
369
  minimum=10,
370
+ maximum=30,
371
  value=20,
372
  step=5,
373
  label="Количество кадров"
374
  )
375
 
376
  size = gr.Slider(
377
+ minimum=64,
378
+ maximum=256,
379
+ value=128,
380
  step=64,
381
+ label="Размер (чем меньше, тем быстрее)"
382
  )
383
 
384
+ with gr.Row():
385
+ generate_btn = gr.Button("🧠 Запустить!", variant="primary")
386
+ train_btn = gr.Button("🎓 Обучить", variant="secondary")
387
+
388
+ status = gr.Textbox(label="Статус", value="Готов к работе")
389
 
390
  with gr.Column(scale=1):
391
  output_gif = gr.Image(
392
+ label="🎬 Результат",
393
  type="filepath",
394
+ height=400
395
  )
396
 
397
  download_btn = gr.DownloadButton(
 
401
 
402
  # Логика
403
  generate_btn.click(
404
+ fn=generate_wrapper,
405
  inputs=[input_image, style, frames, size],
406
  outputs=[output_gif]
407
  ).then(
 
413
  train_btn.click(
414
  fn=train_neural_animator,
415
  inputs=[],
416
+ outputs=[status]
 
 
 
 
417
  )
 
 
 
 
 
 
 
 
 
 
 
418
 
419
  if __name__ == "__main__":
420
  print("""
421
+ 🧠 ЗАПУСКАЕМ НЕЙРОСЕТЕВУЮ АНИМАЦИЮ!
422
  📱 Открой браузер: http://localhost:7860
 
 
 
 
 
 
423
  """)
424
 
425
  demo.launch(