feat: implement batch composition core

This commit is contained in:
2026-06-15 17:55:55 +08:00
parent 4b008a0dc8
commit 29523f9ac2
2 changed files with 150 additions and 14 deletions
+138 -2
View File
@@ -1,2 +1,138 @@
def run_batch(*args, **kwargs): import logging
raise NotImplementedError("Batch composition is not implemented yet.") from pathlib import Path
from typing import Callable, Iterable, List, Optional, Tuple, Union
from core.composer import compose
from core.models import (
BatchMode,
BatchOptions,
BatchResult,
ComposeResult,
ImageAsset,
TransformState,
)
from services.file_service import get_output_dir, make_safe_output_path
logger = logging.getLogger(__name__)
PathLike = Union[str, Path]
ProgressCallback = Callable[[int, int, ComposeResult], None]
def run_batch(
garments: Iterable[Union[ImageAsset, PathLike]],
prints: Iterable[Union[ImageAsset, PathLike]],
transform: TransformState,
options: BatchOptions,
progress_callback: Optional[ProgressCallback] = None,
) -> BatchResult:
"""Run batch composition tasks.
The function only orchestrates pair generation and delegates all image
processing to core.composer.compose(). A failed item is recorded and does
not stop later tasks.
"""
garment_paths = _selected_paths(garments)
print_paths = _selected_paths(prints)
pairs = _build_pairs(garment_paths, print_paths, options.mode)
results = []
export_options = options.export_options
output_dir = Path(export_options.output_dir) if export_options.output_dir else get_output_dir()
logger.info(
"Batch started: mode=%s, garments=%d, prints=%d, tasks=%d",
options.mode.value,
len(garment_paths),
len(print_paths),
len(pairs),
)
total = len(pairs)
for index, (garment_path, print_path) in enumerate(pairs, start=1):
try:
output_path = make_safe_output_path(
output_dir,
garment_path,
print_path,
export_options.output_format,
)
result = compose(
garment_path=garment_path,
print_path=print_path,
transform=transform,
options=export_options,
output_path=output_path,
)
except Exception as exc:
logger.exception("Batch item failed unexpectedly: %s + %s", garment_path, print_path)
result = ComposeResult(
success=False,
garment_path=garment_path,
print_path=print_path,
error=str(exc),
)
results.append(result)
if progress_callback is not None:
progress_callback(index, total, result)
batch_result = _summarize(results)
logger.info(
"Batch finished: total=%d, success=%d, failure=%d",
batch_result.total,
batch_result.success_count,
batch_result.failure_count,
)
return batch_result
def _selected_paths(items: Iterable[Union[ImageAsset, PathLike]]) -> List[Path]:
paths = []
for item in items:
if isinstance(item, ImageAsset):
if not item.selected:
continue
paths.append(Path(item.path))
else:
paths.append(Path(item))
return paths
def _build_pairs(
garment_paths: List[Path],
print_paths: List[Path],
mode: BatchMode,
) -> List[Tuple[Path, Path]]:
if mode == BatchMode.MANY_GARMENTS:
if not print_paths:
return []
return [(garment_path, print_paths[0]) for garment_path in garment_paths]
if mode == BatchMode.MANY_PRINTS:
if not garment_paths:
return []
return [(garment_paths[0], print_path) for print_path in print_paths]
if mode == BatchMode.ONE_TO_ONE:
return list(zip(garment_paths, print_paths))
if mode == BatchMode.FULL_COMBO:
return [
(garment_path, print_path)
for garment_path in garment_paths
for print_path in print_paths
]
raise ValueError("Unsupported batch mode: {}".format(mode))
def _summarize(results: List[ComposeResult]) -> BatchResult:
success_count = sum(1 for result in results if result.success)
failure_count = len(results) - success_count
return BatchResult(
results=results,
total=len(results),
success_count=success_count,
failure_count=failure_count,
)
+12 -12
View File
@@ -238,21 +238,21 @@
任务: 任务:
- [ ] 先读取 `src/core/batch.py` 现有内容 - [x] 先读取 `src/core/batch.py` 现有内容
- [ ] 完善 `src/core/batch.py` - [x] 完善 `src/core/batch.py`
- [ ] 支持多衣服 × 单印花 - [x] 支持多衣服 × 单印花
- [ ] 支持单衣服 × 多印花 - [x] 支持单衣服 × 多印花
- [ ] 支持一一匹配 - [x] 支持一一匹配
- [ ] 支持全组合 - [x] 支持全组合
- [ ] 复用 `core/composer.py` - [x] 复用 `core/composer.py`
- [ ] 单个任务失败时继续处理剩余任务 - [x] 单个任务失败时继续处理剩余任务
- [ ] 汇总成功数量、失败数量和失败原因 - [x] 汇总成功数量、失败数量和失败原因
验收: 验收:
- [ ] 不复制第二套合成算法 - [x] 不复制第二套合成算法
- [ ] 单张失败不会中断整个批量任务 - [x] 单张失败不会中断整个批量任务
- [ ] 失败原因可供 UI 和日志使用 - [x] 失败原因可供 UI 和日志使用
## 7. 主界面布局 ## 7. 主界面布局