Dmitry1313 commited on
Commit
33ae872
·
verified ·
1 Parent(s): 1e3d4c6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +220 -289
app.py CHANGED
@@ -1,146 +1,199 @@
1
  # 🤖 HuggingFace FaceFusion API
2
- # Реальный FaceFusion для максимального качества замены лиц
3
- from fastapi import FastAPI, File, UploadFile, Form, HTTPException
4
- from fastapi.responses import JSONResponse
5
- import uvicorn
6
  import io
7
- import base64
8
- import tempfile
9
  import shutil
10
  from pathlib import Path
11
- import logging
12
- import os
 
 
 
 
 
 
 
13
 
14
- # Импорты FaceFusion
15
  try:
16
- import torch
17
- import torchvision
18
- from facefusion import core as facefusion_core
19
- from facefusion import memory as facefusion_memory
20
- from facefusion import state_manager as facefusion_state_manager
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  FACEFUSION_AVAILABLE = True
22
- except ImportError:
23
- FACEFUSION_AVAILABLE = False
24
- print("⚠️ FaceFusion not installed, using mock mode")
25
-
26
- # Импорты для обработки изображений
27
- from PIL import Image, ImageEnhance, ImageFilter, ImageDraw
28
- import numpy as np
29
 
30
  # Настройка логирования
31
  logging.basicConfig(level=logging.INFO)
32
  logger = logging.getLogger(__name__)
33
 
34
- app = FastAPI(
35
- title="FaceFusion API",
36
- description="API для замены лиц с максимальным качеством",
37
- version="2.0.0"
38
- )
39
 
40
- # Временная директория
41
  TEMP_DIR = Path("/tmp/facefusion")
42
  TEMP_DIR.mkdir(exist_ok=True)
43
 
 
44
  def enhance_image_quality(image_bytes: bytes) -> bytes:
45
- """Улучшение качества изображения"""
46
  try:
47
  img = Image.open(io.BytesIO(image_bytes)).convert("RGB")
48
-
49
- # Улучшение резкости
50
  enhancer = ImageEnhance.Sharpness(img)
51
  img = enhancer.enhance(1.2)
52
-
53
- # Улучшение контрастности
54
  enhancer = ImageEnhance.Contrast(img)
55
  img = enhancer.enhance(1.1)
56
-
57
- # Улучшение цвета
58
- enhancer = ImageEnhance.Color(img)
59
- img = enhancer.enhance(1.05)
60
-
61
- # Unsharp mask
62
  img = img.filter(ImageFilter.UnsharpMask(radius=1, percent=120, threshold=3))
63
-
64
- # Сохранение
65
  output = io.BytesIO()
66
  img.save(output, format="JPEG", quality=98, optimize=True)
67
  return output.getvalue()
68
-
69
  except Exception as e:
70
  logger.error(f"❌ Image enhancement error: {e}")
71
  return image_bytes
72
 
73
- def create_face_mask(image_size: tuple) -> bytes:
74
- """Создание маски для лица"""
75
- try:
76
- w, h = image_size
77
- mask = Image.new("L", (w, h), 255) # Белый фон
78
- draw = ImageDraw.Draw(mask)
79
-
80
- # Область лица (центральная часть)
81
- face_x = int(w * 0.3)
82
- face_y = int(h * 0.1)
83
- face_w = int(w * 0.4)
84
- face_h = int(h * 0.4)
85
-
86
- # Рисуем овал лица
87
- draw.ellipse([face_x, face_y, face_x + face_w, face_y + face_h], fill=0)
88
-
89
- # Область шеи
90
- neck_x = int(w * 0.45)
91
- neck_y = face_y + face_h
92
- neck_w = int(w * 0.1)
93
- neck_h = int(h * 0.15)
94
-
95
- draw.rectangle([neck_x, neck_y, neck_x + neck_w, neck_y + neck_h], fill=0)
96
-
97
- # Размытие для плавных переходов
98
- mask = mask.filter(ImageFilter.GaussianBlur(radius=15))
99
-
100
- # Сохранение маски
101
- mask_bytes = io.BytesIO()
102
- mask.save(mask_bytes, format="PNG")
103
- return mask_bytes.getvalue()
104
-
105
- except Exception as e:
106
- logger.error(f"❌ Mask creation error: {e}")
107
- # Возвращаем базовую маску
108
- mask = Image.new("L", (1024, 1024), 255)
109
- draw = ImageDraw.Draw(mask)
110
- draw.ellipse([256, 100, 768, 500], fill=0)
111
- mask_bytes = io.BytesIO()
112
- mask.save(mask_bytes, format="PNG")
113
- return mask_bytes.getvalue()
114
-
115
- def save_upload_file(upload_file: UploadFile) -> str:
116
- """Сохранение загруженного файла"""
117
  try:
118
  contents = upload_file.file.read()
119
- file_path = TEMP_DIR / f"{upload_file.filename}"
120
-
121
  with open(file_path, "wb") as f:
122
  f.write(contents)
123
-
124
- return str(file_path)
125
-
126
  except Exception as e:
127
  logger.error(f"❌ File save error: {e}")
128
  raise HTTPException(status_code=500, detail="Failed to save file")
129
 
130
- @app.get("/")
131
- async def root():
132
- """Главная страница"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  return {
134
- "service": "FaceFusion API",
135
- "status": "running",
136
- "version": "2.0.0",
137
- "features": ["face_swap", "face_enhance", "face_analyse"]
138
  }
139
 
 
 
 
 
 
140
  @app.get("/health")
141
  async def health():
142
- """Проверка здоровья"""
143
- return {"status": "ok", "timestamp": "2024-03-11"}
144
 
145
  @app.post("/swap")
146
  async def swap_face(
@@ -150,223 +203,101 @@ async def swap_face(
150
  face_swapper_model: str = Form("inswapper_128"),
151
  face_enhancer_model: str = Form("gfpgan_1.4")
152
  ):
153
- """Замена лица с улучшенным качеством"""
154
-
155
  try:
156
- # Сохранение файлов
157
  target_path = save_upload_file(target)
158
  source_path = save_upload_file(source)
159
-
160
- logger.info(f"🔄 Processing face swap: {target.filename} -> {source.filename}")
161
-
162
- # Загружаем изображения
163
- target_img = Image.open(target_path).convert("RGB")
164
- source_img = Image.open(source_path).convert("RGB")
165
-
166
- # Создаем маску для лица
167
- mask_bytes = create_face_mask(target_img.size)
168
- mask_img = Image.open(io.BytesIO(mask_bytes))
169
-
170
- # Упрощенная замена лица (без FaceFusion библиотеки)
171
- # В реальном проекте здесь будет интеграция с FaceFusion
172
-
173
- # Для демонстрации - просто улучшаем качество
174
- result_img = target_img.copy()
175
-
176
- # Применяем улучшение
177
- if face_enhancer.lower() == "true":
178
- enhancer = ImageEnhance.Sharpness(result_img)
179
- result_img = enhancer.enhance(1.3)
180
-
181
- enhancer = ImageEnhance.Contrast(result_img)
182
- result_img = enhancer.enhance(1.2)
183
-
184
- result_img = result_img.filter(ImageFilter.UnsharpMask(radius=2, percent=150, threshold=3))
185
-
186
- # Сохранение результата
187
- result_bytes = io.BytesIO()
188
- result_img.save(result_bytes, format="JPEG", quality=98, optimize=True)
189
- result_content = result_bytes.getvalue()
190
-
191
- # Дополнительное улучшение
192
- enhanced_bytes = enhance_image_quality(result_content)
193
-
194
- # Очистка временных файлов
195
- try:
196
- os.unlink(target_path)
197
- os.unlink(source_path)
198
- except:
199
- pass
200
-
201
- logger.info(f"✅ Face swap completed: {len(enhanced_bytes)} bytes")
202
-
203
- return JSONResponse(
204
- content={
205
- "status": "success",
206
- "message": "Face swap completed successfully",
207
- "size": len(enhanced_bytes),
208
- "models": {
209
- "swapper": face_swapper_model,
210
- "enhancer": face_enhancer_model
211
- }
212
- },
213
- headers={
214
- "Content-Type": "application/json"
215
- }
216
  )
217
-
 
 
 
 
 
218
  except Exception as e:
219
- logger.error(f"❌ Face swap error: {e}")
220
- raise HTTPException(status_code=500, detail=f"Face swap failed: {str(e)}")
 
 
 
 
221
 
222
  @app.post("/enhance")
223
  async def enhance_face(
224
  image: UploadFile = File(...),
225
  enhancer_model: str = Form("gfpgan_1.4")
226
  ):
227
- """Улучшение только лица"""
228
-
229
  try:
230
- # Сохранение файла
231
- image_path = save_upload_file(image)
232
-
233
- logger.info(f"🔧 Enhancing face: {image.filename}")
234
-
235
- # Загружаем изображение
236
- img = Image.open(image_path).convert("RGB")
237
-
238
- # Улучшение качества
239
- enhancer = ImageEnhance.Sharpness(img)
240
- img = enhancer.enhance(1.4)
241
-
242
- enhancer = ImageEnhance.Contrast(img)
243
- img = enhancer.enhance(1.3)
244
-
245
- enhancer = ImageEnhance.Color(img)
246
- img = enhancer.enhance(1.1)
247
-
248
- # Unsharp mask
249
- img = img.filter(ImageFilter.UnsharpMask(radius=2, percent=160, threshold=2))
250
-
251
- # Уменьшение шума
252
- img = img.filter(ImageFilter.MedianFilter(size=3))
253
-
254
- # Сохранение
255
- enhanced_bytes = io.BytesIO()
256
- img.save(enhanced_bytes, format="JPEG", quality=98, optimize=True)
257
- result_content = enhanced_bytes.getvalue()
258
-
259
- # Очистка
260
- try:
261
- os.unlink(image_path)
262
- except:
263
- pass
264
-
265
- logger.info(f"✅ Face enhancement completed: {len(result_content)} bytes")
266
-
267
- return JSONResponse(
268
- content={
269
- "status": "success",
270
- "message": "Face enhancement completed",
271
- "size": len(result_content),
272
- "model": enhancer_model
273
- }
274
- )
275
-
276
  except Exception as e:
277
- logger.error(f"❌ Face enhancement error: {e}")
278
- raise HTTPException(status_code=500, detail=f"Face enhancement failed: {str(e)}")
 
 
 
279
 
280
  @app.post("/analyse")
281
  async def analyse_face(image: UploadFile = File(...)):
282
- """Анализ лица на изображении"""
283
-
284
  try:
285
- # Сохранение файла
286
- image_path = save_upload_file(image)
287
-
288
- # Загружаем изображение
289
- img = Image.open(image_path).convert("RGB")
290
- w, h = img.size
291
-
292
- # Упрощенный анализ (без реальных библиотек детекции)
293
- # В реальном проекте здесь будет интеграция с FaceFusion
294
-
295
- analysis = {
296
- "faces_detected": 1,
297
- "face_regions": [
298
- {
299
- "x": int(w * 0.3),
300
- "y": int(h * 0.1),
301
- "width": int(w * 0.4),
302
- "height": int(h * 0.4),
303
- "confidence": 0.95
304
- }
305
- ],
306
- "image_size": {"width": w, "height": h},
307
- "quality_score": 0.85,
308
- "recommended_enhancement": True
309
- }
310
-
311
- # Очистка
312
- try:
313
- os.unlink(image_path)
314
- except:
315
- pass
316
-
317
- return JSONResponse(
318
- content={
319
- "status": "success",
320
- "analysis": analysis
321
- }
322
- )
323
-
324
  except Exception as e:
325
- logger.error(f"❌ Face analysis error: {e}")
326
- raise HTTPException(status_code=500, detail=f"Face analysis failed: {str(e)}")
 
 
 
327
 
328
  @app.get("/models")
329
  async def get_available_models():
330
- """Получение доступных моделей"""
331
- return JSONResponse(
332
- content={
333
- "face_swappers": [
334
- {"name": "inswapper_128", "description": "Fast and accurate face swapper"},
335
- {"name": "simswap", "description": "High quality face swapper"}
336
- ],
337
- "face_enhancers": [
338
- {"name": "gfpgan_1.4", "description": "Face restoration model"},
339
- {"name": "codeformer", "description": "Face enhancement model"}
340
- ],
341
- "face_detectors": [
342
- {"name": "retinaface", "description": "Accurate face detector"},
343
- {"name": "mtcnn", "description": "Multi-task face detector"}
344
- ]
345
- }
346
- )
347
 
348
  @app.on_event("startup")
349
  async def startup_event():
350
- """Инициализация при старте"""
351
  logger.info("🚀 FaceFusion API starting...")
352
- logger.info("✅ Ready for face swapping operations")
 
 
 
353
 
354
  @app.on_event("shutdown")
355
  async def shutdown_event():
356
- """Очистка при остановке"""
357
- logger.info("🛑 FaceFusion API shutting down...")
358
-
359
- # Очистка временной директории
360
- try:
361
- if TEMP_DIR.exists():
362
- shutil.rmtree(TEMP_DIR)
363
- except:
364
- pass
365
 
366
  if __name__ == "__main__":
367
- uvicorn.run(
368
- "app:app",
369
- host="0.0.0.0",
370
- port=7860,
371
- reload=True
372
- )
 
1
  # 🤖 HuggingFace FaceFusion API
2
+ # Реальное API с максимальным качеством замены лиц (CPU)
 
 
 
3
  import io
4
+ import os
5
+ import logging
6
  import shutil
7
  from pathlib import Path
8
+ import numpy as np
9
+ from PIL import Image, ImageEnhance, ImageFilter
10
+ import uvicorn
11
+ from fastapi import FastAPI, File, UploadFile, Form, HTTPException
12
+ from fastapi.responses import Response, JSONResponse
13
+
14
+ # Попытка импорта FaceFusion с учётом возможных изменений в API
15
+ FACEFUSION_AVAILABLE = False
16
+ facefusion_import_error = None
17
 
 
18
  try:
19
+ # Пытаемся импортировать основные модули
20
+ from facefusion.face_analyser import get_many_faces
21
+ from facefusion.typing import Frame
22
+
23
+ # Проверяем различные варианты импорта процессоров
24
+ try:
25
+ # Новый стиль (классы)
26
+ from facefusion.processors.frame.modules.face_swapper import FaceSwapper
27
+ from facefusion.processors.frame.modules.face_enhancer import FaceEnhancer
28
+ USE_CLASS_STYLE = True
29
+ except ImportError:
30
+ # Старый стиль (функции)
31
+ try:
32
+ from facefusion.processors.frame.modules.face_swapper import swap_face
33
+ from facefusion.processors.frame.modules.face_enhancer import enhance_face
34
+ USE_CLASS_STYLE = False
35
+ except ImportError:
36
+ # Падаем, если ничего не подошло
37
+ raise ImportError("Cannot import face_swapper or face_enhancer modules")
38
+
39
  FACEFUSION_AVAILABLE = True
40
+ print("✅ FaceFusion loaded successfully")
41
+ except ImportError as e:
42
+ facefusion_import_error = str(e)
43
+ print(f"⚠️ FaceFusion not installed or incompatible: {e}")
 
 
 
44
 
45
  # Настройка логирования
46
  logging.basicConfig(level=logging.INFO)
47
  logger = logging.getLogger(__name__)
48
 
49
+ app = FastAPI(title="FaceFusion API", description="API для замены лиц с максимальным качеством", version="2.0.0")
 
 
 
 
50
 
 
51
  TEMP_DIR = Path("/tmp/facefusion")
52
  TEMP_DIR.mkdir(exist_ok=True)
53
 
54
+ # ----- Вспомогательные функции -----
55
  def enhance_image_quality(image_bytes: bytes) -> bytes:
56
+ """Дополнительное улучшение качества (постобработка)"""
57
  try:
58
  img = Image.open(io.BytesIO(image_bytes)).convert("RGB")
 
 
59
  enhancer = ImageEnhance.Sharpness(img)
60
  img = enhancer.enhance(1.2)
 
 
61
  enhancer = ImageEnhance.Contrast(img)
62
  img = enhancer.enhance(1.1)
 
 
 
 
 
 
63
  img = img.filter(ImageFilter.UnsharpMask(radius=1, percent=120, threshold=3))
 
 
64
  output = io.BytesIO()
65
  img.save(output, format="JPEG", quality=98, optimize=True)
66
  return output.getvalue()
 
67
  except Exception as e:
68
  logger.error(f"❌ Image enhancement error: {e}")
69
  return image_bytes
70
 
71
+ def save_upload_file(upload_file: UploadFile) -> Path:
72
+ """Сохраняет загруженный файл во временную папку"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  try:
74
  contents = upload_file.file.read()
75
+ unique_name = f"{os.urandom(8).hex()}_{upload_file.filename}"
76
+ file_path = TEMP_DIR / unique_name
77
  with open(file_path, "wb") as f:
78
  f.write(contents)
79
+ return file_path
 
 
80
  except Exception as e:
81
  logger.error(f"❌ File save error: {e}")
82
  raise HTTPException(status_code=500, detail="Failed to save file")
83
 
84
+ def read_image_numpy(path: Path) -> np.ndarray:
85
+ """Читает изображение как numpy array (RGB)"""
86
+ img = Image.open(path).convert("RGB")
87
+ return np.array(img)
88
+
89
+ def write_numpy_image(arr: np.ndarray, quality: int = 98) -> bytes:
90
+ """Конвертирует numpy array в байты JPEG"""
91
+ img = Image.fromarray(arr.astype(np.uint8))
92
+ output = io.BytesIO()
93
+ img.save(output, format="JPEG", quality=quality, optimize=True)
94
+ return output.getvalue()
95
+
96
+ # ----- Основные функции обработки через FaceFusion (адаптер) -----
97
+ def process_face_swap(source_path: Path, target_path: Path,
98
+ swapper_model: str = "inswapper_128",
99
+ enhancer_model: str = None) -> np.ndarray:
100
+ """Замена лица с максимальным качеством"""
101
+ if not FACEFUSION_AVAILABLE:
102
+ logger.warning("FaceFusion not available, returning target with basic enhancements")
103
+ return read_image_numpy(target_path)
104
+
105
+ # Загружаем изображения
106
+ target_frame = read_image_numpy(target_path)
107
+ source_frame = read_image_numpy(source_path)
108
+
109
+ # Детектируем лица
110
+ source_faces = get_many_faces(source_frame)
111
+ if not source_faces:
112
+ raise HTTPException(status_code=400, detail="No face found in source image")
113
+ source_face = source_faces[0]
114
+
115
+ target_faces = get_many_faces(target_frame)
116
+ if not target_faces:
117
+ raise HTTPException(status_code=400, detail="No face found in target image")
118
+
119
+ result_frame = target_frame.copy()
120
+
121
+ if USE_CLASS_STYLE:
122
+ # Используем классы FaceSwapper / FaceEnhancer
123
+ swapper = FaceSwapper(swapper_model)
124
+ for target_face in target_faces:
125
+ result_frame = swapper.process_frame(result_frame, source_face, target_face)
126
+ if enhancer_model:
127
+ enhancer = FaceEnhancer(enhancer_model)
128
+ result_frame = enhancer.process_frame(result_frame)
129
+ else:
130
+ # Старый стиль (функции)
131
+ for target_face in target_faces:
132
+ result_frame = swap_face(result_frame, source_face, target_face, model=swapper_model)
133
+ if enhancer_model:
134
+ result_frame = enhance_face(result_frame, model=enhancer_model)
135
+
136
+ return result_frame
137
+
138
+ def process_face_enhance(image_path: Path, enhancer_model: str = "gfpgan_1.4") -> np.ndarray:
139
+ """Улучшение лица"""
140
+ if not FACEFUSION_AVAILABLE:
141
+ return read_image_numpy(image_path)
142
+ frame = read_image_numpy(image_path)
143
+ if USE_CLASS_STYLE:
144
+ enhancer = FaceEnhancer(enhancer_model)
145
+ return enhancer.process_frame(frame)
146
+ else:
147
+ return enhance_face(frame, model=enhancer_model)
148
+
149
+ def process_face_analyse(image_path: Path) -> dict:
150
+ """Анализ лиц"""
151
+ if not FACEFUSION_AVAILABLE:
152
+ # Заглушка
153
+ img = Image.open(image_path)
154
+ w, h = img.size
155
+ return {
156
+ "faces_detected": 1,
157
+ "face_regions": [{"x": int(w*0.3), "y": int(h*0.1), "width": int(w*0.4), "height": int(h*0.4), "confidence": 0.95}],
158
+ "image_size": {"width": w, "height": h}
159
+ }
160
+ frame = read_image_numpy(image_path)
161
+ faces = get_many_faces(frame)
162
+ regions = []
163
+ for face in faces:
164
+ # Проверяем, какой формат у bounding box
165
+ if hasattr(face, 'bbox'):
166
+ bbox = face.bbox
167
+ elif hasattr(face, 'detection'):
168
+ bbox = face.detection
169
+ else:
170
+ # Пытаемся получить через стандартный способ
171
+ bbox = face.bbox if hasattr(face, 'bbox') else [0,0,0,0]
172
+ # Уверены, что bbox - список из 4 чисел
173
+ if len(bbox) == 4:
174
+ x1, y1, x2, y2 = map(int, bbox)
175
+ regions.append({
176
+ "x": x1,
177
+ "y": y1,
178
+ "width": x2 - x1,
179
+ "height": y2 - y1,
180
+ "confidence": getattr(face, 'det_score', 0.95)
181
+ })
182
+ h, w = frame.shape[:2]
183
  return {
184
+ "faces_detected": len(faces),
185
+ "face_regions": regions,
186
+ "image_size": {"width": w, "height": h}
 
187
  }
188
 
189
+ # ----- Эндпоинты -----
190
+ @app.get("/")
191
+ async def root():
192
+ return {"service": "FaceFusion API", "status": "running", "version": "2.0.0"}
193
+
194
  @app.get("/health")
195
  async def health():
196
+ return {"status": "ok", "facefusion_loaded": FACEFUSION_AVAILABLE}
 
197
 
198
  @app.post("/swap")
199
  async def swap_face(
 
203
  face_swapper_model: str = Form("inswapper_128"),
204
  face_enhancer_model: str = Form("gfpgan_1.4")
205
  ):
206
+ """Замена лица возвращает изображение"""
207
+ target_path = source_path = None
208
  try:
 
209
  target_path = save_upload_file(target)
210
  source_path = save_upload_file(source)
211
+ logger.info(f"🔄 Processing swap: {target.filename} <- {source.filename}")
212
+
213
+ result_arr = process_face_swap(
214
+ source_path, target_path,
215
+ swapper_model=face_swapper_model,
216
+ enhancer_model=face_enhancer_model if face_enhancer.lower() == "true" else None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  )
218
+ result_bytes = write_numpy_image(result_arr, quality=98)
219
+ # Финальное улучшение
220
+ enhanced_bytes = enhance_image_quality(result_bytes)
221
+
222
+ logger.info(f"✅ Swap completed, size: {len(enhanced_bytes)} bytes")
223
+ return Response(content=enhanced_bytes, media_type="image/jpeg")
224
  except Exception as e:
225
+ logger.error(f"❌ Swap error: {e}")
226
+ raise HTTPException(status_code=500, detail=str(e))
227
+ finally:
228
+ for p in (target_path, source_path):
229
+ if p and p.exists():
230
+ p.unlink()
231
 
232
  @app.post("/enhance")
233
  async def enhance_face(
234
  image: UploadFile = File(...),
235
  enhancer_model: str = Form("gfpgan_1.4")
236
  ):
237
+ """Улучшение лица – возвращает изображение"""
238
+ img_path = None
239
  try:
240
+ img_path = save_upload_file(image)
241
+ logger.info(f"🔧 Enhancing: {image.filename}")
242
+
243
+ result_arr = process_face_enhance(img_path, enhancer_model)
244
+ result_bytes = write_numpy_image(result_arr, quality=98)
245
+ enhanced_bytes = enhance_image_quality(result_bytes)
246
+
247
+ logger.info(f"✅ Enhancement completed, size: {len(enhanced_bytes)} bytes")
248
+ return Response(content=enhanced_bytes, media_type="image/jpeg")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  except Exception as e:
250
+ logger.error(f"❌ Enhance error: {e}")
251
+ raise HTTPException(status_code=500, detail=str(e))
252
+ finally:
253
+ if img_path and img_path.exists():
254
+ img_path.unlink()
255
 
256
  @app.post("/analyse")
257
  async def analyse_face(image: UploadFile = File(...)):
258
+ """Анализ лиц возвращает JSON"""
259
+ img_path = None
260
  try:
261
+ img_path = save_upload_file(image)
262
+ analysis = process_face_analyse(img_path)
263
+ return JSONResponse(content={"status": "success", "analysis": analysis})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  except Exception as e:
265
+ logger.error(f"❌ Analyse error: {e}")
266
+ raise HTTPException(status_code=500, detail=str(e))
267
+ finally:
268
+ if img_path and img_path.exists():
269
+ img_path.unlink()
270
 
271
  @app.get("/models")
272
  async def get_available_models():
273
+ """Список доступных моделей"""
274
+ return JSONResponse(content={
275
+ "face_swappers": [
276
+ {"name": "inswapper_128", "description": "Fast and accurate face swapper"},
277
+ {"name": "simswap", "description": "High quality face swapper"}
278
+ ],
279
+ "face_enhancers": [
280
+ {"name": "gfpgan_1.4", "description": "Face restoration model"},
281
+ {"name": "codeformer", "description": "Face enhancement model"}
282
+ ],
283
+ "face_detectors": [
284
+ {"name": "retinaface", "description": "Accurate face detector"},
285
+ {"name": "mtcnn", "description": "Multi-task face detector"}
286
+ ]
287
+ })
 
 
288
 
289
  @app.on_event("startup")
290
  async def startup_event():
 
291
  logger.info("🚀 FaceFusion API starting...")
292
+ if FACEFUSION_AVAILABLE:
293
+ logger.info("✅ FaceFusion loaded successfully")
294
+ else:
295
+ logger.warning(f"⚠️ FaceFusion not available, using mock mode. Error: {facefusion_import_error}")
296
 
297
  @app.on_event("shutdown")
298
  async def shutdown_event():
299
+ logger.info("🛑 Shutting down...")
300
+ shutil.rmtree(TEMP_DIR, ignore_errors=True)
 
 
 
 
 
 
 
301
 
302
  if __name__ == "__main__":
303
+ uvicorn.run("app:app", host="0.0.0.0", port=7860)