by3axap commited on
Commit
ef9045e
·
verified ·
1 Parent(s): 9d199da

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +88 -28
app.py CHANGED
@@ -1,19 +1,34 @@
1
  import os
2
  import io
 
3
  import numpy as np
4
  from flask import Flask, render_template, request, send_file
5
  from PIL import Image
6
  import xml.etree.ElementTree as ET
7
 
8
- # Попытка импортировать moviepy для обработки видео
9
  try:
10
- from moviepy.editor import VideoFileClip, ImageSequenceClip
11
  HAS_MOVIEPY = True
12
  except ImportError:
13
  HAS_MOVIEPY = False
14
 
15
  app = Flask(__name__)
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  @app.route('/')
18
  def index():
19
  return render_template('index.html')
@@ -27,13 +42,14 @@ def download_svg():
27
  invert = request.form.get('invert') == 'true'
28
  bg_color = request.form.get('bg_color', '#000000')
29
 
 
 
 
30
  fill_colors = request.form.getlist('fill_colors[]')
31
  svg_files = request.files.getlist('shape_svg[]')
32
  raster = request.files.get('raster_image')
33
 
34
- canvas_w, canvas_h = (1400, 900)
35
  img_data = None
36
-
37
  if raster:
38
  img = Image.open(raster).convert('L').resize((density, int(canvas_h / (canvas_w / density))))
39
  img_data = np.array(img)
@@ -50,12 +66,20 @@ def download_svg():
50
  parsed_patterns = []
51
  for i, f in enumerate(svg_files):
52
  try:
53
- svg_root = ET.fromstring(f.read().decode('utf-8'))
 
 
 
 
 
 
 
54
  parsed_patterns.append({
55
  'elements': list(svg_root),
56
  'color': fill_colors[i] if i < len(fill_colors) else '#ffffff'
57
  })
58
- except:
 
59
  continue
60
 
61
  step = canvas_w / density
@@ -91,7 +115,7 @@ def download_svg():
91
  else:
92
  ET.SubElement(dwg, 'rect', {
93
  'x': str(x - size/2), 'y': str(y - size/2),
94
- 'width': str(size), 'height': str(size), 'fill': fill_colors[0] if fill_colors else '#fff'
95
  })
96
 
97
  svg_io = io.BytesIO(ET.tostring(dwg))
@@ -104,47 +128,83 @@ def download_svg():
104
  @app.route('/process_video', methods=['POST'])
105
  def process_video():
106
  if not HAS_MOVIEPY:
107
- return "MoviePy not installed on server. Run: pip install moviepy", 400
108
 
109
  try:
 
 
 
 
 
 
 
 
 
 
 
 
110
  video_file = request.files.get('video_file')
111
  if not video_file:
112
- return "No video file uploaded", 400
113
 
114
- # Сохраняем временно загруженное видео
115
  in_path = "temp_in.mp4"
116
  out_path = "temp_out.mp4"
117
  video_file.save(in_path)
118
-
119
- # Читаем параметры как для SVG, но применяем к каждому кадру
120
- density = int(request.form.get('density', 76))
121
- invert = request.form.get('invert') == 'true'
122
-
123
  clip = VideoFileClip(in_path)
124
 
125
- # --- Тут логика рендера кадров.
126
- # Чтобы сервер не завис, я генерирую упрощенную версию видео (эффект пикселизации/порога),
127
- # так как рендерить сотни SVG-фигур через Pillow для каждого кадра — убьет процессор.
128
- def process_frame(frame):
129
- # Пример базовой стилизации кадров (можно дописать рендер паттернов через PIL)
130
- # Перевод в ЧБ и инверсия
131
- gray = np.dot(frame[...,:3], [0.2989, 0.5870, 0.1140])
132
- if invert:
133
- gray = 255 - gray
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
- # Возвращаем черно-белый массив как 3-канальный (чтобы MoviePy съел)
136
- return np.stack((gray,)*3, axis=-1).astype('uint8')
137
 
138
- new_clip = clip.fl_image(process_frame)
139
  new_clip.write_videofile(out_path, codec="libx264", audio=False)
140
 
141
  clip.close()
142
  new_clip.close()
143
  os.remove(in_path)
144
 
145
- return send_file(out_path, mimetype='video/mp4', as_attachment=True, download_name='rendered.mp4')
146
 
147
  except Exception as e:
 
 
148
  return str(e), 500
149
 
150
  if __name__ == '__main__':
 
1
  import os
2
  import io
3
+ import re
4
  import numpy as np
5
  from flask import Flask, render_template, request, send_file
6
  from PIL import Image
7
  import xml.etree.ElementTree as ET
8
 
 
9
  try:
10
+ from moviepy.editor import VideoFileClip
11
  HAS_MOVIEPY = True
12
  except ImportError:
13
  HAS_MOVIEPY = False
14
 
15
  app = Flask(__name__)
16
 
17
+ # Рекурсивная функция для удаления жестко заданных цветов внутри SVG-файлов
18
+ def strip_fill_colors(elem):
19
+ # Удаляем fill если он не "none" (none обычно используется для обводки)
20
+ if 'fill' in elem.attrib and elem.attrib['fill'].lower() != 'none':
21
+ del elem.attrib['fill']
22
+
23
+ # Удаляем fill из инлайн-стилей (style="fill: #000; ...")
24
+ if 'style' in elem.attrib:
25
+ style_str = elem.attrib['style']
26
+ style_str = re.sub(r'fill:\s*(?!none)[^;]+;?', '', style_str, flags=re.IGNORECASE)
27
+ elem.attrib['style'] = style_str
28
+
29
+ for child in elem:
30
+ strip_fill_colors(child)
31
+
32
  @app.route('/')
33
  def index():
34
  return render_template('index.html')
 
42
  invert = request.form.get('invert') == 'true'
43
  bg_color = request.form.get('bg_color', '#000000')
44
 
45
+ canvas_w = float(request.form.get('canvas_w', 1400))
46
+ canvas_h = float(request.form.get('canvas_h', 900))
47
+
48
  fill_colors = request.form.getlist('fill_colors[]')
49
  svg_files = request.files.getlist('shape_svg[]')
50
  raster = request.files.get('raster_image')
51
 
 
52
  img_data = None
 
53
  if raster:
54
  img = Image.open(raster).convert('L').resize((density, int(canvas_h / (canvas_w / density))))
55
  img_data = np.array(img)
 
66
  parsed_patterns = []
67
  for i, f in enumerate(svg_files):
68
  try:
69
+ # Читаем файл, убираем теги неймспейсов для корректной работы
70
+ content = f.read().decode('utf-8')
71
+ content = re.sub(r'\sxmlns="[^"]+"', '', content, count=1)
72
+ svg_root = ET.fromstring(content)
73
+
74
+ # ОЧИЩАЕМ ИСХОДНЫЕ ЦВЕТА ПАТТЕРНА
75
+ strip_fill_colors(svg_root)
76
+
77
  parsed_patterns.append({
78
  'elements': list(svg_root),
79
  'color': fill_colors[i] if i < len(fill_colors) else '#ffffff'
80
  })
81
+ except Exception as e:
82
+ print("SVG Parse Error:", e)
83
  continue
84
 
85
  step = canvas_w / density
 
115
  else:
116
  ET.SubElement(dwg, 'rect', {
117
  'x': str(x - size/2), 'y': str(y - size/2),
118
+ 'width': str(size), 'height': str(size), 'fill': fill_colors[0] if fill_colors else '#000'
119
  })
120
 
121
  svg_io = io.BytesIO(ET.tostring(dwg))
 
128
  @app.route('/process_video', methods=['POST'])
129
  def process_video():
130
  if not HAS_MOVIEPY:
131
+ return "MoviePy not installed", 400
132
 
133
  try:
134
+ density = int(request.form.get('density', 76))
135
+ gap_x = float(request.form.get('gap_x', 0.2))
136
+ base_scale = float(request.form.get('base_scale', 1.0))
137
+ invert = request.form.get('invert') == 'true'
138
+ bg_color = request.form.get('bg_color', '#000000')
139
+ fallback_fill = request.form.get('fallback_fill', '#000000')
140
+
141
+ # Получаем готовые цветные PNG паттерны с фронтенда!
142
+ png_files = request.files.getlist('pattern_pngs')
143
+ pil_patterns = [Image.open(p).convert("RGBA") for p in png_files]
144
+ num_p = len(pil_patterns)
145
+
146
  video_file = request.files.get('video_file')
147
  if not video_file:
148
+ return "No video file", 400
149
 
 
150
  in_path = "temp_in.mp4"
151
  out_path = "temp_out.mp4"
152
  video_file.save(in_path)
 
 
 
 
 
153
  clip = VideoFileClip(in_path)
154
 
155
+ w, h = clip.size
156
+ step = w / density
157
+ rows = int(h / step)
158
+ cell_size = int(step * (1 - gap_x) * base_scale)
159
+
160
+ # Ресайзим паттерны заранее под размер ячейки для скорости рендера
161
+ resized_patterns = [p.resize((cell_size, cell_size), Image.Resampling.LANCZOS) for p in pil_patterns]
162
+
163
+ def make_frame(get_frame, t):
164
+ frame = get_frame(t)
165
+ # Переводим кадр в ЧБ для анализа яркости
166
+ gray = Image.fromarray(frame).convert("L")
167
+ # Сжимаем кадр до размера сетки для ультра-быстрого анализа
168
+ gray_small = np.array(gray.resize((density, rows)))
169
+
170
+ # Создаем чистый кадр с цветом фона
171
+ canvas_img = Image.new("RGB", (w, h), bg_color)
172
+
173
+ from PIL import ImageDraw
174
+ draw = ImageDraw.Draw(canvas_img)
175
+
176
+ for r in range(rows):
177
+ for c in range(density):
178
+ bright = gray_small[r, c] / 255.0
179
+ if invert: bright = 1.0 - bright
180
+ if bright > 0.96: continue
181
+
182
+ x = int(c * step + step/2 - cell_size/2)
183
+ y = int(r * step + step/2 - cell_size/2)
184
+
185
+ if num_p > 0:
186
+ p_idx = int(np.floor(bright * num_p))
187
+ p_idx = max(0, min(num_p - 1, p_idx))
188
+ pat = resized_patterns[p_idx]
189
+ # Накладываем цветной PNG паттерн с поддержкой прозрачности
190
+ canvas_img.paste(pat, (x, y), mask=pat)
191
+ else:
192
+ draw.rectangle([x, y, x + cell_size, y + cell_size], fill=fallback_fill)
193
 
194
+ return np.array(canvas_img)
 
195
 
196
+ new_clip = clip.fl(make_frame)
197
  new_clip.write_videofile(out_path, codec="libx264", audio=False)
198
 
199
  clip.close()
200
  new_clip.close()
201
  os.remove(in_path)
202
 
203
+ return send_file(out_path, mimetype='video/mp4', as_attachment=True, download_name='cell_clipper_render.mp4')
204
 
205
  except Exception as e:
206
+ import traceback
207
+ traceback.print_exc()
208
  return str(e), 500
209
 
210
  if __name__ == '__main__':