Spaces:
Running
Running
Create modules/lut.py
#2
by Jacky2305 - opened
- .gitattributes +35 -0
- Dockerfile +5 -36
- README.md +5 -83
- app.py +141 -0
- modules/lut.py +47 -0
- requirements.txt +8 -0
- run.sh +0 -21
.gitattributes
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
Dockerfile
CHANGED
|
@@ -1,37 +1,6 @@
|
|
| 1 |
-
FROM
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
# buildcpu.sh: cmake -DGGML_BLAS=ON). No CUDA/Vulkan/Metal.
|
| 5 |
-
# pkg-config is REQUIRED: ggml-blas/CMakeLists.txt calls find_package(PkgConfig)
|
| 6 |
-
# and fails the configure step without it (build died here on the first attempt).
|
| 7 |
-
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
| 8 |
-
git cmake build-essential ca-certificates curl python3 \
|
| 9 |
-
pkg-config \
|
| 10 |
-
libblas-dev liblapack-dev libgomp1 libcurl4 \
|
| 11 |
-
&& rm -rf /var/lib/apt/lists/*
|
| 12 |
-
|
| 13 |
-
WORKDIR /build
|
| 14 |
-
|
| 15 |
-
# clone source + ggml submodule
|
| 16 |
-
RUN git clone --depth 1 --recurse-submodules \
|
| 17 |
-
https://github.com/ServeurpersoCom/acestep.cpp.git . \
|
| 18 |
-
&& rm -rf build && mkdir build && cd build \
|
| 19 |
-
&& cmake .. -DGGML_BLAS=ON -DCMAKE_BUILD_TYPE=Release \
|
| 20 |
-
&& cmake --build . --config Release -j "$(getconf _NPROCESSORS_ONLN)"
|
| 21 |
-
|
| 22 |
-
# weights: 0.6B LM + Q8_0 DiT turbo + Q8_0 text encoder + BF16 VAE (~4.2 GB total)
|
| 23 |
-
RUN mkdir -p /app/models \
|
| 24 |
-
&& curl -fL -o /app/models/acestep-5Hz-lm-0.6B-Q8_0.gguf \
|
| 25 |
-
https://huggingface.co/Serveurperso/ACE-Step-1.5-GGUF/resolve/main/acestep-5Hz-lm-0.6B-Q8_0.gguf \
|
| 26 |
-
&& curl -fL -o /app/models/acestep-v15-turbo-Q8_0.gguf \
|
| 27 |
-
https://huggingface.co/Serveurperso/ACE-Step-1.5-GGUF/resolve/main/acestep-v15-turbo-Q8_0.gguf \
|
| 28 |
-
&& curl -fL -o /app/models/Qwen3-Embedding-0.6B-Q8_0.gguf \
|
| 29 |
-
https://huggingface.co/Serveurperso/ACE-Step-1.5-GGUF/resolve/main/Qwen3-Embedding-0.6B-Q8_0.gguf \
|
| 30 |
-
&& curl -fL -o /app/models/vae-BF16.gguf \
|
| 31 |
-
https://huggingface.co/Serveurperso/ACE-Step-1.5-GGUF/resolve/main/vae-BF16.gguf
|
| 32 |
-
|
| 33 |
WORKDIR /app
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
EXPOSE 7860
|
| 37 |
-
CMD ["bash", "/app/run.sh"]
|
|
|
|
| 1 |
+
FROM python:3.10-slim
|
| 2 |
+
RUN apt-get update && apt-get install -y ffmpeg fonts-noto-cjk
|
| 3 |
+
COPY . /app
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
WORKDIR /app
|
| 5 |
+
RUN pip install -r requirements.txt
|
| 6 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
|
|
|
|
|
|
README.md
CHANGED
|
@@ -1,88 +1,10 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: docker
|
| 7 |
-
app_port: 7860
|
| 8 |
pinned: false
|
| 9 |
---
|
| 10 |
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
AI music generation on cpu-basic. Text + lyrics in, stereo 48kHz audio out.
|
| 14 |
-
Built from source (`acestep.cpp` GGML) with the **0.6B** model set:
|
| 15 |
-
|
| 16 |
-
| 部件 | 文件 | 大小 |
|
| 17 |
-
|---|---|---|
|
| 18 |
-
| LM(歌词+音频编码) | `acestep-5Hz-lm-0.6B-Q8_0.gguf` | 0.68 GB |
|
| 19 |
-
| DiT(flow-matching 扩散,turbo 8 步) | `acestep-v15-turbo-Q8_0.gguf` | 2.4 GB |
|
| 20 |
-
| 文本编码器 | `Qwen3-Embedding-0.6B-Q8_0.gguf` | 0.75 GB |
|
| 21 |
-
| VAE | `vae-BF16.gguf` | 0.32 GB |
|
| 22 |
-
|
| 23 |
-
合计约 **4.2 GB**,16 GB 内存装得下。占用会随生成过程浮动,峰值约 6 GB。
|
| 24 |
-
|
| 25 |
-
## 使用
|
| 26 |
-
|
| 27 |
-
网页 UI 在 `/`(`ace-server` 内嵌的 WebUI,无需 npm)。写 caption → 设时长 → 生成 → 下载。
|
| 28 |
-
|
| 29 |
-
## REST API(给 pipeline 调用)
|
| 30 |
-
|
| 31 |
-
异步作业模型:POST 拿 id → 轮询 `/job?id=` → `result=1` 取文件。
|
| 32 |
-
|
| 33 |
-
**请求体(`examples/dit-only.json` 格式):**
|
| 34 |
-
```json
|
| 35 |
-
{
|
| 36 |
-
"lm_model": "acestep-5Hz-lm-0.6B-Q8_0.gguf",
|
| 37 |
-
"synth_model": "acestep-v15-turbo-Q8_0.gguf",
|
| 38 |
-
"caption": "Ambient electronic soundscape with warm analog pads",
|
| 39 |
-
"lyrics": "",
|
| 40 |
-
"bpm": 90,
|
| 41 |
-
"duration": 30,
|
| 42 |
-
"keyscale": "C minor",
|
| 43 |
-
"timesignature": "4",
|
| 44 |
-
"vocal_language": "en",
|
| 45 |
-
"inference_steps": 8,
|
| 46 |
-
"guidance_scale": 1.0,
|
| 47 |
-
"shift": 3.0
|
| 48 |
-
}
|
| 49 |
-
```
|
| 50 |
-
|
| 51 |
-
### 出音乐(两步)
|
| 52 |
-
```bash
|
| 53 |
-
# 1) LM: caption -> lyrics + audio codes
|
| 54 |
-
LM_ID=$(curl -sX POST https://jacky3102-platform.hf.space/lm \
|
| 55 |
-
-H 'Content-Type: application/json' -d @req.json | jq -r '.id')
|
| 56 |
-
|
| 57 |
-
# 2) 轮询直到 done
|
| 58 |
-
curl -s "https://jacky3102-platform.hf.space/job?id=$LM_ID&result=1" | jq '.[0]' > out.json
|
| 59 |
-
|
| 60 |
-
# 3) synth: codes -> audio (WAV or MP3)
|
| 61 |
-
SY_ID=$(curl -sX POST https://jacky3102-platform.hf.space/synth \
|
| 62 |
-
-H 'Content-Type: application/json' -d @out.json | jq -r '.id')
|
| 63 |
-
curl -s "https://jacky3102-platform.hf.space/job?id=$SY_ID&result=1" -o track.wav
|
| 64 |
-
```
|
| 65 |
-
|
| 66 |
-
### 可用模型 / 默认参数
|
| 67 |
-
```bash
|
| 68 |
-
curl https://jacky3102-platform.hf.space/props
|
| 69 |
-
```
|
| 70 |
-
|
| 71 |
-
### 跳过 LM(直接用已知歌词)
|
| 72 |
-
POST 同一个 `/synth`,带 `lyrics` + `lm_model` 即可(dit-only 模式)。
|
| 73 |
-
|
| 74 |
-
## 已知限制
|
| 75 |
-
|
| 76 |
-
- **CPU 生成慢**:30 秒音乐在 2 vCPU 上预计几分钟到十几分钟,未实测。第一次请求会额外等模型加载(`--keep-loaded` 已开,之后不再重复加载)。
|
| 77 |
-
- HF Space 有请求超时,长生成要用 `/job` 异步轮询,别在线等。
|
| 78 |
-
- 官方 Space 用 ZeroGPU;这是 CPU 移植版,慢但免费。
|
| 79 |
-
|
| 80 |
-
## 模型质量档位(之后可换)
|
| 81 |
-
|
| 82 |
-
| LM | 大小 | 说明 |
|
| 83 |
-
|---|---|---|
|
| 84 |
-
| `acestep-5Hz-lm-0.6B-Q8_0.gguf` | 0.68 GB | 最快(当前) |
|
| 85 |
-
| `acestep-5Hz-lm-1.7B-Q8_0.gguf` | 1.9 GB | 折中 |
|
| 86 |
-
| `acestep-5Hz-lm-4B-Q8_0.gguf` | 4.2 GB | 官方默认,音质最好 |
|
| 87 |
-
|
| 88 |
-
DiT 也可换 `sft`(50 步,音质更高但慢一倍)。
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Video Gen
|
| 3 |
+
emoji: 📊
|
| 4 |
+
colorFrom: green
|
| 5 |
+
colorTo: red
|
| 6 |
sdk: docker
|
|
|
|
| 7 |
pinned: false
|
| 8 |
---
|
| 9 |
|
| 10 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, File, UploadFile, HTTPException, Form
|
| 2 |
+
from fastapi.responses import Response
|
| 3 |
+
import tempfile
|
| 4 |
+
import os
|
| 5 |
+
import traceback
|
| 6 |
+
import sys
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
from typing import Optional, List
|
| 9 |
+
|
| 10 |
+
from modules.video_processor import generate_video
|
| 11 |
+
from modules.lut import LUTS
|
| 12 |
+
from modules.particles import EFFECTS
|
| 13 |
+
from modules.titles import TITLE_STYLES, TITLE_ANIMATIONS, TITLE_POSITIONS
|
| 14 |
+
|
| 15 |
+
app = FastAPI(title="Video Generator with Effects API")
|
| 16 |
+
|
| 17 |
+
@app.get("/")
|
| 18 |
+
async def root():
|
| 19 |
+
return {
|
| 20 |
+
"message": "Video Generator API",
|
| 21 |
+
"effects": EFFECTS,
|
| 22 |
+
"luts": {k: v["name"] for k, v in LUTS.items()},
|
| 23 |
+
"title_styles": TITLE_STYLES,
|
| 24 |
+
"title_animations": TITLE_ANIMATIONS,
|
| 25 |
+
"title_positions": TITLE_POSITIONS,
|
| 26 |
+
"features": ["ken_burns", "typewriter", "cinemascope", "title", "video_input"],
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
@app.get("/health")
|
| 30 |
+
async def health():
|
| 31 |
+
return {"status": "healthy"}
|
| 32 |
+
|
| 33 |
+
@app.post("/generate")
|
| 34 |
+
async def generate(
|
| 35 |
+
images: List[UploadFile] = File(None),
|
| 36 |
+
video: UploadFile = File(None),
|
| 37 |
+
audio: UploadFile = File(...),
|
| 38 |
+
srt_text: str = Form(""),
|
| 39 |
+
bgm: Optional[UploadFile] = File(None),
|
| 40 |
+
effect: str = Form("snow"),
|
| 41 |
+
lut: str = Form("none"),
|
| 42 |
+
ken_burns: bool = Form(False),
|
| 43 |
+
typewriter: bool = Form(False),
|
| 44 |
+
cinemascope: bool = Form(False),
|
| 45 |
+
title_text: str = Form(""),
|
| 46 |
+
title_style: str = Form("3d"),
|
| 47 |
+
title_color: str = Form("white"),
|
| 48 |
+
title_animation: str = Form("fade_in"),
|
| 49 |
+
title_duration: float = Form(4.0),
|
| 50 |
+
title_position: str = Form("center"),
|
| 51 |
+
title_start: float = Form(0.0),
|
| 52 |
+
title_font_size: int = Form(0),
|
| 53 |
+
typewriter_font_size: int = Form(0),
|
| 54 |
+
typewriter_color: str = Form("white"),
|
| 55 |
+
subtitle_color: str = Form("white"),
|
| 56 |
+
subtitle_effect: str = Form("none"),
|
| 57 |
+
fps: int = Form(25),
|
| 58 |
+
):
|
| 59 |
+
# 验证
|
| 60 |
+
if not images and not video:
|
| 61 |
+
raise HTTPException(400, "必须提供 images 或 video 中的一个")
|
| 62 |
+
if images and video:
|
| 63 |
+
raise HTTPException(400, "不能同时提供 images 和 video")
|
| 64 |
+
|
| 65 |
+
# 创建临时目录
|
| 66 |
+
tmp_dir = tempfile.mkdtemp()
|
| 67 |
+
try:
|
| 68 |
+
# 保存文件
|
| 69 |
+
image_paths = None
|
| 70 |
+
video_path = None
|
| 71 |
+
if images:
|
| 72 |
+
image_paths = []
|
| 73 |
+
for i, img in enumerate(images):
|
| 74 |
+
suffix = Path(img.filename).suffix if img.filename else ".jpg"
|
| 75 |
+
path = os.path.join(tmp_dir, f"image_{i}{suffix}")
|
| 76 |
+
with open(path, "wb") as f:
|
| 77 |
+
f.write(await img.read())
|
| 78 |
+
image_paths.append(path)
|
| 79 |
+
elif video:
|
| 80 |
+
suffix = Path(video.filename).suffix if video.filename else ".mp4"
|
| 81 |
+
video_path = os.path.join(tmp_dir, f"video{suffix}")
|
| 82 |
+
with open(video_path, "wb") as f:
|
| 83 |
+
f.write(await video.read())
|
| 84 |
+
|
| 85 |
+
audio_path = os.path.join(tmp_dir, "audio" + (Path(audio.filename).suffix or ".wav"))
|
| 86 |
+
with open(audio_path, "wb") as f:
|
| 87 |
+
f.write(await audio.read())
|
| 88 |
+
|
| 89 |
+
bgm_path = None
|
| 90 |
+
if bgm:
|
| 91 |
+
bgm_path = os.path.join(tmp_dir, "bgm" + (Path(bgm.filename).suffix or ".mp3"))
|
| 92 |
+
with open(bgm_path, "wb") as f:
|
| 93 |
+
f.write(await bgm.read())
|
| 94 |
+
|
| 95 |
+
output_path = os.path.join(tmp_dir, "output.mp4")
|
| 96 |
+
|
| 97 |
+
# 调用主合成函数
|
| 98 |
+
generate_video(
|
| 99 |
+
image_paths=image_paths,
|
| 100 |
+
video_path=video_path,
|
| 101 |
+
audio_path=audio_path,
|
| 102 |
+
srt_text=srt_text,
|
| 103 |
+
bgm_path=bgm_path,
|
| 104 |
+
effect=effect,
|
| 105 |
+
output_path=output_path,
|
| 106 |
+
lut=lut,
|
| 107 |
+
ken_burns=ken_burns,
|
| 108 |
+
typewriter=typewriter,
|
| 109 |
+
cinemascope=cinemascope,
|
| 110 |
+
title_text=title_text,
|
| 111 |
+
title_style=title_style,
|
| 112 |
+
title_color=title_color,
|
| 113 |
+
title_animation=title_animation,
|
| 114 |
+
title_duration=title_duration,
|
| 115 |
+
title_position=title_position,
|
| 116 |
+
title_start=title_start,
|
| 117 |
+
title_font_size=title_font_size,
|
| 118 |
+
typewriter_font_size=typewriter_font_size,
|
| 119 |
+
typewriter_color=typewriter_color,
|
| 120 |
+
subtitle_color=subtitle_color,
|
| 121 |
+
subtitle_effect=subtitle_effect,
|
| 122 |
+
fps=fps,
|
| 123 |
+
)
|
| 124 |
+
|
| 125 |
+
with open(output_path, "rb") as f:
|
| 126 |
+
video_bytes = f.read()
|
| 127 |
+
|
| 128 |
+
import shutil
|
| 129 |
+
shutil.rmtree(tmp_dir, ignore_errors=True)
|
| 130 |
+
return Response(content=video_bytes, media_type="video/mp4",
|
| 131 |
+
headers={"Content-Disposition": "attachment; filename=output.mp4"})
|
| 132 |
+
|
| 133 |
+
except Exception as e:
|
| 134 |
+
print(traceback.format_exc(), file=sys.stderr)
|
| 135 |
+
import shutil
|
| 136 |
+
shutil.rmtree(tmp_dir, ignore_errors=True)
|
| 137 |
+
raise HTTPException(500, detail=str(e))
|
| 138 |
+
|
| 139 |
+
if __name__ == "__main__":
|
| 140 |
+
import uvicorn
|
| 141 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|
modules/lut.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
|
| 3 |
+
LUTS = {
|
| 4 |
+
"cinematic": {
|
| 5 |
+
"name": "🎬 电影橙青",
|
| 6 |
+
"matrix": np.array([[1.1, 0.0, -0.1], [0.0, 1.05, 0.0], [-0.05, 0.0, 1.15]]),
|
| 7 |
+
"warmth": (5, 0, -5), "contrast": 1.15,
|
| 8 |
+
},
|
| 9 |
+
"bw": {
|
| 10 |
+
"name": "⬛ 经典黑白",
|
| 11 |
+
"matrix": np.array([[0.3, 0.59, 0.11], [0.3, 0.59, 0.11], [0.3, 0.59, 0.11]]),
|
| 12 |
+
"warmth": (0, 0, 0), "contrast": 1.3,
|
| 13 |
+
},
|
| 14 |
+
"cyberpunk": {
|
| 15 |
+
"name": "🌆 赛博朋克",
|
| 16 |
+
"matrix": np.array([[0.8, 0.0, 0.3], [0.0, 0.9, 0.1], [0.1, 0.0, 1.3]]),
|
| 17 |
+
"warmth": (-10, 0, 15), "contrast": 1.2,
|
| 18 |
+
},
|
| 19 |
+
"warm": {
|
| 20 |
+
"name": "☀️ 日系暖阳",
|
| 21 |
+
"matrix": np.array([[1.1, 0.05, 0.0], [0.0, 1.0, -0.05], [-0.05, -0.05, 0.9]]),
|
| 22 |
+
"warmth": (15, 5, -10), "contrast": 0.95,
|
| 23 |
+
},
|
| 24 |
+
"noir": {
|
| 25 |
+
"name": "🌙 夜景蓝调",
|
| 26 |
+
"matrix": np.array([[0.7, 0.0, 0.2], [0.0, 0.8, 0.1], [0.1, 0.0, 1.2]]),
|
| 27 |
+
"warmth": (-15, -5, 10), "contrast": 1.25,
|
| 28 |
+
},
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
def apply_lut(frame, lut_name):
|
| 32 |
+
lut = LUTS.get(lut_name)
|
| 33 |
+
if not lut:
|
| 34 |
+
return frame
|
| 35 |
+
frame_float = frame.astype(np.float32)
|
| 36 |
+
h, w = frame.shape[:2]
|
| 37 |
+
pixels = frame_float.reshape(-1, 3)
|
| 38 |
+
transformed = pixels @ lut["matrix"].T
|
| 39 |
+
transformed = np.clip(transformed, 0, 255).reshape(h, w, 3)
|
| 40 |
+
b, g, r = cv2.split(transformed.astype(np.uint8))
|
| 41 |
+
r = cv2.add(r, lut["warmth"][0])
|
| 42 |
+
g = cv2.add(g, lut["warmth"][1])
|
| 43 |
+
b = cv2.add(b, lut["warmth"][2])
|
| 44 |
+
frame = cv2.merge([b, g, r])
|
| 45 |
+
frame = frame.astype(np.float32)
|
| 46 |
+
frame = (frame - 128) * lut["contrast"] + 128
|
| 47 |
+
return np.clip(frame, 0, 255).astype(np.uint8)
|
requirements.txt
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi==0.104.1
|
| 2 |
+
uvicorn==0.24.0
|
| 3 |
+
python-multipart==0.0.6
|
| 4 |
+
numpy>=1.24.0
|
| 5 |
+
opencv-python-headless>=4.8.0
|
| 6 |
+
Pillow>=10.0.0
|
| 7 |
+
pydub>=0.25.0
|
| 8 |
+
librosa>=0.10.0
|
run.sh
DELETED
|
@@ -1,21 +0,0 @@
|
|
| 1 |
-
#!/bin/bash
|
| 2 |
-
# Boot diagnostics + ace-server on the port HF expects (7860).
|
| 3 |
-
# On a crash the container stays alive so the traceback stays readable in the
|
| 4 |
-
# runtime logs (this is what finally exposed the gradio/huggingface_hub bug
|
| 5 |
-
# on Platform).
|
| 6 |
-
echo "=== boot $(date -u) ==="
|
| 7 |
-
echo "--- binaries ---"; ls /build/build/ | grep -E "^ace-" | head
|
| 8 |
-
echo "--- models ---"; ls -la /app/models/
|
| 9 |
-
echo "--- nproc / mem ---"; nproc; free -m | head -2
|
| 10 |
-
echo "--- starting ace-server on 7860 ---"
|
| 11 |
-
/build/build/ace-server \
|
| 12 |
-
--host 0.0.0.0 \
|
| 13 |
-
--port "${PORT:-7860}" \
|
| 14 |
-
--models /app/models \
|
| 15 |
-
--adapters /build/adapters \
|
| 16 |
-
--max-batch 1 \
|
| 17 |
-
--keep-loaded \
|
| 18 |
-
--device CPU
|
| 19 |
-
echo "=== ace-server exited code=$? at $(date -u) ==="
|
| 20 |
-
echo "--- holding container alive for log capture ---"
|
| 21 |
-
sleep infinity
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|