Spaces:
Build error
Build error
liuyiyang01
commited on
Commit
·
8eded48
1
Parent(s):
460372d
fix history video display
Browse files- app_utils.py +93 -17
- requirements.txt +2 -1
app_utils.py
CHANGED
|
@@ -13,6 +13,7 @@ from collections import defaultdict
|
|
| 13 |
import shutil
|
| 14 |
from urllib.parse import urljoin
|
| 15 |
import oss2
|
|
|
|
| 16 |
|
| 17 |
TMP_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "tmp")
|
| 18 |
os.makedirs(TMP_ROOT, exist_ok=True)
|
|
@@ -168,7 +169,7 @@ OSS_CONFIG = {
|
|
| 168 |
}
|
| 169 |
|
| 170 |
auth = oss2.Auth(OSS_CONFIG["access_key_id"], OSS_CONFIG["access_key_secret"])
|
| 171 |
-
bucket = oss2.Bucket(auth, OSS_CONFIG["endpoint"], OSS_CONFIG["bucket_name"])
|
| 172 |
|
| 173 |
|
| 174 |
def list_oss_files(folder_path: str) -> List[str]:
|
|
@@ -179,9 +180,15 @@ def list_oss_files(folder_path: str) -> List[str]:
|
|
| 179 |
files.append(obj.key)
|
| 180 |
return sorted(files, key=lambda x: os.path.splitext(x)[0])
|
| 181 |
|
| 182 |
-
def download_oss_file(oss_path: str, local_path: str):
|
| 183 |
-
"""从OSS
|
| 184 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
|
| 186 |
def oss_file_exists(oss_path):
|
| 187 |
try:
|
|
@@ -190,6 +197,7 @@ def oss_file_exists(oss_path):
|
|
| 190 |
except Exception as e:
|
| 191 |
print(f"Error checking if file exists in OSS: {str(e)}")
|
| 192 |
return False
|
|
|
|
| 193 |
def stream_simulation_results(result_folder: str, task_id: str, request: gr.Request, fps: int = 30):
|
| 194 |
"""
|
| 195 |
流式输出仿真结果,从OSS读取图片
|
|
@@ -208,11 +216,11 @@ def stream_simulation_results(result_folder: str, task_id: str, request: gr.Requ
|
|
| 208 |
image_folder = os.path.join(result_folder, "image")
|
| 209 |
os.makedirs(image_folder, exist_ok=True)
|
| 210 |
frame_buffer: List[np.ndarray] = []
|
| 211 |
-
|
| 212 |
processed_files = set()
|
| 213 |
width, height = 0, 0
|
| 214 |
last_status_check = 0
|
| 215 |
-
status_check_interval =
|
| 216 |
max_time = 240
|
| 217 |
|
| 218 |
# 创建临时目录存储下载的图片
|
|
@@ -245,7 +253,6 @@ def stream_simulation_results(result_folder: str, task_id: str, request: gr.Requ
|
|
| 245 |
try:
|
| 246 |
oss_files = list_oss_files(image_folder)
|
| 247 |
new_files = [f for f in oss_files if f not in processed_files]
|
| 248 |
-
has_new_frames = False
|
| 249 |
|
| 250 |
for oss_path in new_files:
|
| 251 |
try:
|
|
@@ -262,15 +269,13 @@ def stream_simulation_results(result_folder: str, task_id: str, request: gr.Requ
|
|
| 262 |
|
| 263 |
frame_buffer.append(frame)
|
| 264 |
processed_files.add(oss_path)
|
| 265 |
-
has_new_frames = True
|
| 266 |
except Exception as e:
|
| 267 |
print(f"Error processing {oss_path}: {e}")
|
| 268 |
|
| 269 |
-
# 如果有新帧且积累够60
|
| 270 |
-
if
|
| 271 |
-
|
| 272 |
-
frame_buffer =
|
| 273 |
-
yield create_video_segment(segment_frames, fps, width, height, request)
|
| 274 |
|
| 275 |
except Exception as e:
|
| 276 |
print(f"Error accessing OSS: {e}")
|
|
@@ -279,7 +284,6 @@ def stream_simulation_results(result_folder: str, task_id: str, request: gr.Requ
|
|
| 279 |
|
| 280 |
if max_time <= 0:
|
| 281 |
raise gr.Error("timeout 240s")
|
| 282 |
-
|
| 283 |
def create_video_segment(frames: List[np.ndarray], fps: int, width: int, height: int, req: gr.Request) -> str:
|
| 284 |
"""创建视频片段"""
|
| 285 |
user_dir = os.path.join(TMP_ROOT, str(req.session_hash))
|
|
@@ -424,6 +428,76 @@ def convert_to_h264(video_path):
|
|
| 424 |
except Exception as e:
|
| 425 |
raise gr.Error(f"转换过程中发生错误: {str(e)}")
|
| 426 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 427 |
def run_simulation(
|
| 428 |
scene: str,
|
| 429 |
prompt: str,
|
|
@@ -488,14 +562,16 @@ def run_simulation(
|
|
| 488 |
status = get_task_status(task_id)
|
| 489 |
print("status: ", status)
|
| 490 |
if status.get("status") == "completed":
|
| 491 |
-
|
| 492 |
oss_video_path = os.path.join(result_folder, "manipulation.mp4")
|
| 493 |
local_video_path = os.path.join(user_dir, task_id, "tasks", "manipulation.mp4")
|
| 494 |
-
download_oss_file(oss_video_path, local_video_path)
|
| 495 |
print("oss_video_path: ", oss_video_path)
|
| 496 |
print("local_video_path: ", local_video_path)
|
| 497 |
|
| 498 |
-
video_path =
|
|
|
|
|
|
|
| 499 |
|
| 500 |
# 创建新的历史记录条目
|
| 501 |
new_entry = {
|
|
|
|
| 13 |
import shutil
|
| 14 |
from urllib.parse import urljoin
|
| 15 |
import oss2
|
| 16 |
+
from natsort import natsorted
|
| 17 |
|
| 18 |
TMP_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "tmp")
|
| 19 |
os.makedirs(TMP_ROOT, exist_ok=True)
|
|
|
|
| 169 |
}
|
| 170 |
|
| 171 |
auth = oss2.Auth(OSS_CONFIG["access_key_id"], OSS_CONFIG["access_key_secret"])
|
| 172 |
+
bucket = oss2.Bucket(auth, OSS_CONFIG["endpoint"], OSS_CONFIG["bucket_name"], enable_crc=False)
|
| 173 |
|
| 174 |
|
| 175 |
def list_oss_files(folder_path: str) -> List[str]:
|
|
|
|
| 180 |
files.append(obj.key)
|
| 181 |
return sorted(files, key=lambda x: os.path.splitext(x)[0])
|
| 182 |
|
| 183 |
+
def download_oss_file(oss_path: str, local_path: str) -> bool:
|
| 184 |
+
"""从OSS下载文件到本地,返回是否成功"""
|
| 185 |
+
try:
|
| 186 |
+
result = bucket.get_object_to_file(oss_path, local_path)
|
| 187 |
+
print(f"下载: {oss_path}, {result.status}")
|
| 188 |
+
return result.status == 200
|
| 189 |
+
except Exception as e:
|
| 190 |
+
print(f"下载失败: {e}")
|
| 191 |
+
return False
|
| 192 |
|
| 193 |
def oss_file_exists(oss_path):
|
| 194 |
try:
|
|
|
|
| 197 |
except Exception as e:
|
| 198 |
print(f"Error checking if file exists in OSS: {str(e)}")
|
| 199 |
return False
|
| 200 |
+
|
| 201 |
def stream_simulation_results(result_folder: str, task_id: str, request: gr.Request, fps: int = 30):
|
| 202 |
"""
|
| 203 |
流式输出仿真结果,从OSS读取图片
|
|
|
|
| 216 |
image_folder = os.path.join(result_folder, "image")
|
| 217 |
os.makedirs(image_folder, exist_ok=True)
|
| 218 |
frame_buffer: List[np.ndarray] = []
|
| 219 |
+
min_frames_per_segment = fps * 1 # 至少30帧才输出
|
| 220 |
processed_files = set()
|
| 221 |
width, height = 0, 0
|
| 222 |
last_status_check = 0
|
| 223 |
+
status_check_interval = 5 # 每5秒检查一次后端状态
|
| 224 |
max_time = 240
|
| 225 |
|
| 226 |
# 创建临时目录存储下载的图片
|
|
|
|
| 253 |
try:
|
| 254 |
oss_files = list_oss_files(image_folder)
|
| 255 |
new_files = [f for f in oss_files if f not in processed_files]
|
|
|
|
| 256 |
|
| 257 |
for oss_path in new_files:
|
| 258 |
try:
|
|
|
|
| 269 |
|
| 270 |
frame_buffer.append(frame)
|
| 271 |
processed_files.add(oss_path)
|
|
|
|
| 272 |
except Exception as e:
|
| 273 |
print(f"Error processing {oss_path}: {e}")
|
| 274 |
|
| 275 |
+
# 如果有新帧且积累够60帧以上,输出所有当前帧
|
| 276 |
+
if len(frame_buffer) >= min_frames_per_segment:
|
| 277 |
+
yield create_video_segment(frame_buffer, fps, width, height, request)
|
| 278 |
+
frame_buffer = [] # 清空缓冲区
|
|
|
|
| 279 |
|
| 280 |
except Exception as e:
|
| 281 |
print(f"Error accessing OSS: {e}")
|
|
|
|
| 284 |
|
| 285 |
if max_time <= 0:
|
| 286 |
raise gr.Error("timeout 240s")
|
|
|
|
| 287 |
def create_video_segment(frames: List[np.ndarray], fps: int, width: int, height: int, req: gr.Request) -> str:
|
| 288 |
"""创建视频片段"""
|
| 289 |
user_dir = os.path.join(TMP_ROOT, str(req.session_hash))
|
|
|
|
| 428 |
except Exception as e:
|
| 429 |
raise gr.Error(f"转换过程中发生错误: {str(e)}")
|
| 430 |
|
| 431 |
+
|
| 432 |
+
def generate_whole_video(task_id: str, request: gr.Request, fps: int = 30) -> str:
|
| 433 |
+
"""
|
| 434 |
+
从图片序列生成完整视频
|
| 435 |
+
|
| 436 |
+
Args:
|
| 437 |
+
task_id: 任务ID
|
| 438 |
+
fps: 视频帧率,默认为30
|
| 439 |
+
|
| 440 |
+
Returns:
|
| 441 |
+
生成的视频文件路径
|
| 442 |
+
"""
|
| 443 |
+
frame_buffer: List[np.ndarray] = []
|
| 444 |
+
user_dir = os.path.join(TMP_ROOT, str(request.session_hash))
|
| 445 |
+
image_folder = os.path.join(user_dir, task_id, "tasks", "images")
|
| 446 |
+
|
| 447 |
+
|
| 448 |
+
# 确保输出目录存在
|
| 449 |
+
result_folder = os.path.join(user_dir, task_id, "tasks", "video")
|
| 450 |
+
os.makedirs(result_folder, exist_ok=True)
|
| 451 |
+
|
| 452 |
+
# 获取所有图片文件并按自然顺序排序
|
| 453 |
+
image_files = [f for f in os.listdir(image_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg'))]
|
| 454 |
+
image_files = natsorted(image_files) # 自然排序处理类似 'frame1, frame2, ..., frame10' 的情况
|
| 455 |
+
|
| 456 |
+
if not image_files:
|
| 457 |
+
raise ValueError(f"No image files found in {image_folder}")
|
| 458 |
+
|
| 459 |
+
# 初始化视频尺寸
|
| 460 |
+
width, height = 0, 0
|
| 461 |
+
|
| 462 |
+
for img_file in image_files:
|
| 463 |
+
img_path = os.path.join(image_folder, img_file)
|
| 464 |
+
try:
|
| 465 |
+
frame = cv2.imread(img_path)
|
| 466 |
+
if frame is not None:
|
| 467 |
+
if width == 0: # 第一次获取图像尺寸
|
| 468 |
+
height, width = frame.shape[:2]
|
| 469 |
+
frame_buffer.append(frame)
|
| 470 |
+
except Exception as e:
|
| 471 |
+
print(f"Error processing {img_path}: {e}")
|
| 472 |
+
continue
|
| 473 |
+
|
| 474 |
+
if not frame_buffer:
|
| 475 |
+
raise ValueError("No valid frames found to create video")
|
| 476 |
+
|
| 477 |
+
# 生成视频文件名
|
| 478 |
+
output_video_path = os.path.join(result_folder, f"manipulation.mp4")
|
| 479 |
+
|
| 480 |
+
# 创建视频
|
| 481 |
+
fourcc = cv2.VideoWriter_fourcc(*'mp4v') # 或使用 'avc1' 更好的兼容性
|
| 482 |
+
video_writer = cv2.VideoWriter(
|
| 483 |
+
output_video_path,
|
| 484 |
+
fourcc,
|
| 485 |
+
fps,
|
| 486 |
+
(width, height)
|
| 487 |
+
)
|
| 488 |
+
|
| 489 |
+
for frame in frame_buffer:
|
| 490 |
+
video_writer.write(frame)
|
| 491 |
+
|
| 492 |
+
video_writer.release()
|
| 493 |
+
|
| 494 |
+
# 验证视频是否成功创建
|
| 495 |
+
if not os.path.exists(output_video_path) or os.path.getsize(output_video_path) == 0:
|
| 496 |
+
raise RuntimeError(f"Failed to create video at {output_video_path}")
|
| 497 |
+
|
| 498 |
+
return output_video_path
|
| 499 |
+
|
| 500 |
+
|
| 501 |
def run_simulation(
|
| 502 |
scene: str,
|
| 503 |
prompt: str,
|
|
|
|
| 562 |
status = get_task_status(task_id)
|
| 563 |
print("status: ", status)
|
| 564 |
if status.get("status") == "completed":
|
| 565 |
+
time.sleep(3)
|
| 566 |
oss_video_path = os.path.join(result_folder, "manipulation.mp4")
|
| 567 |
local_video_path = os.path.join(user_dir, task_id, "tasks", "manipulation.mp4")
|
| 568 |
+
# download_oss_file(oss_video_path, local_video_path)
|
| 569 |
print("oss_video_path: ", oss_video_path)
|
| 570 |
print("local_video_path: ", local_video_path)
|
| 571 |
|
| 572 |
+
video_path = generate_whole_video(task_id, request)
|
| 573 |
+
|
| 574 |
+
# video_path = convert_to_h264(local_video_path)
|
| 575 |
|
| 576 |
# 创建新的历史记录条目
|
| 577 |
new_entry = {
|
requirements.txt
CHANGED
|
@@ -4,4 +4,5 @@ jsonlib-python3
|
|
| 4 |
opencv-python
|
| 5 |
numpy
|
| 6 |
python-dateutil
|
| 7 |
-
oss2
|
|
|
|
|
|
| 4 |
opencv-python
|
| 5 |
numpy
|
| 6 |
python-dateutil
|
| 7 |
+
oss2
|
| 8 |
+
natsort
|