完整项目架构设计与实现方案
我来帮你梳理一下完整的项目架构和实现方案。作为算法工程师,你需要了解整个数据流转过程,这样能更好地与前后端工程师协作。
1. 整体架构图解
前端(Vue3) → 后端(Java) → Python算法 → 后端(Java) → 前端(Vue3)
↓ ↓ ↓ ↓ ↓
用户选择 接收请求 执行贴图 返回结果 展示模型
船型/日期 调用算法 处理 处理响应
2. 详细流程设计
2.1 前端部分 (Vue3)
// 前端调用示例
async function generateTexturedModel() {
const requestData = {
shipType: selectedShipType, // 用户选择的船型号
textureDate: selectedDate // 用户选择的日期
};
try {
// 调用后端接口
const response = await fetch('/api/ship/generate-textured-model', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestData)
});
const result = await response.json();
if (result.success) {
// 显示生成的模型
displayModel(result.modelUrl);
}
} catch (error) {
console.error('生成模型失败:', error);
}
}
2.2 后端接口设计 (Java Spring Boot)
@RestController
@RequestMapping("/api/ship")
public class ShipModelController {
@Autowired
private ShipModelService shipModelService;
@PostMapping("/generate-textured-model")
public ResponseEntity<ModelGenerationResponse> generateTexturedModel(
@RequestBody ModelGenerationRequest request) {
try {
// 1. 根据船型号获取白模型文件路径
String modelPath = shipModelService.getModelPath(request.getShipType());
// 2. 根据日期获取纹理图片路径
List<String> texturePaths = shipModelService.getTexturePaths(request.getTextureDate());
// 3. 调用Python算法进行贴图处理
String resultPath = pythonAlgorithmService.processModel(modelPath, texturePaths);
// 4. 返回结果
ModelGenerationResponse response = new ModelGenerationResponse();
response.setSuccess(true);
response.setModelUrl("/models/" + resultPath);
return ResponseEntity.ok(response);
} catch (Exception e) {
// 错误处理
ModelGenerationResponse errorResponse = new ModelGenerationResponse();
errorResponse.setSuccess(false);
errorResponse.setMessage("生成模型失败: " + e.getMessage());
return ResponseEntity.status(500).body(errorResponse);
}
}
}
3. Python算法调用服务
3.1 Java调用Python的服务类
@Service
public class PythonAlgorithmService {
@Value("${blender.executable.path}")
private String blenderPath;
@Value("${python.script.path}")
private String scriptPath;
@Value("${work.directory}")
private String workDirectory;
/**
* 调用Python脚本进行模型贴图处理
*/
public String processModel(String modelPath, List<String> texturePaths) throws Exception {
// 1. 准备输出文件路径
String timestamp = String.valueOf(System.currentTimeMillis());
String outputPath = workDirectory + "/output_" + timestamp + ".glb";
// 2. 构建命令行参数
List<String> command = Arrays.asList(
blenderPath, // Blender可执行文件路径
"-b", // 后台模式
"-P", scriptPath, // Python脚本路径
"--", // 参数分隔符
modelPath, // 输入模型路径
texturePaths.get(0), // 纹理1路径
texturePaths.get(1), // 纹理2路径
outputPath // 输出路径
);
// 3. 执行命令
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.directory(new File(workDirectory));
Process process = processBuilder.start();
// 4. 等待执行完成
int exitCode = process.waitFor();
if (exitCode != 0) {
// 读取错误信息
String errorOutput = readStream(process.getErrorStream());
throw new RuntimeException("Python脚本执行失败: " + errorOutput);
}
// 5. 检查输出文件是否存在
File outputFile = new File(outputPath);
if (!outputFile.exists()) {
throw new RuntimeException("输出文件未生成");
}
return outputFile.getName(); // 返回文件名
}
private String readStream(InputStream inputStream) throws IOException {
// 读取流内容的工具方法
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
return reader.lines().collect(Collectors.joining("\n"));
}
}
}
4. 修改后的Python脚本 (支持命令行参数)
import bpy
import os
import sys
import math
import bmesh
from mathutils import Vector, Matrix
import logging
def process_model_with_args(input_ply, texture_top, texture_side, output_glb):
"""
处理3D模型的主函数,支持命令行参数
"""
try:
logger.info(f"开始处理模型: {input_ply}")
# [这里放入你之前写好的所有核心代码]
# 只需要将硬编码的路径替换为参数
# 修改路径配置
ply_path = input_ply
camera_configs = [
{
# ... 其他配置保持不变
"texture_path": texture_top, # 使用传入的纹理路径
},
{
# ... 其他配置保持不变
"texture_path": texture_side, # 使用传入的纹理路径
}
]
# [执行你的核心算法代码]
# 导出到指定路径
bpy.ops.export_scene.gltf(filepath=output_glb, export_format='GLB')
logger.info(f"模型已成功导出到 {output_glb}")
return True
except Exception as e:
logger.error(f"处理模型时出错: {e}")
return False
# 主程序入口
if __name__ == "__main__":
# 设置日志
logging.basicConfig(level=logging.INFO, format='[执行日志] %(message)s')
logger = logging.getLogger(__name__)
# 解析命令行参数
argv = sys.argv
argv = argv[argv.index("--") + 1:] if "--" in argv else []
if len(argv) != 4:
print("用法: blender -b -P script.py -- input.ply texture_top.jpg texture_side.jpg output.glb")
sys.exit(1)
input_ply = argv[0]
texture_top = argv[1]
texture_side = argv[2]
output_glb = argv[3]
# 检查输入文件是否存在
if not os.path.exists(input_ply):
logger.error(f"输入模型文件不存在: {input_ply}")
sys.exit(1)
if not os.path.exists(texture_top):
logger.error(f"纹理文件不存在: {texture_top}")
sys.exit(1)
if not os.path.exists(texture_side):
logger.error(f"纹理文件不存在: {texture_side}")
sys.exit(1)
# 执行处理
success = process_model_with_args(input_ply, texture_top, texture_side, output_glb)
# 返回适当的退出代码
sys.exit(0 if success else 1)
5. 配置文件 (application.yml)
# 后端配置
blender:
executable:
path: "/Applications/Blender.app/Contents/MacOS/Blender"
python:
script:
path: "/path/to/your/blender_script.py"
work:
directory: "/path/to/work/directory"
# 文件服务配置
file:
upload:
path: "/path/to/upload/directory"
model:
path: "/path/to/models/directory"
texture:
path: "/path/to/textures/directory"
6. 数据传输对象 (DTO)
// 请求对象
public class ModelGenerationRequest {
private String shipType;
private String textureDate;
// getters and setters
}
// 响应对象
public class ModelGenerationResponse {
private boolean success;
private String modelUrl;
private String message;
// getters and setters
}
7. 部署和使用流程
7.1 环境准备
- 服务器上安装Blender:确保Blender可执行文件路径正确
- Python脚本部署:将你的脚本放到服务器指定目录
- 工作目录设置:创建临时工作目录用于处理文件
7.2 调用流程
- 用户在前端选择船型号和日期
- 前端发送POST请求到后端API
- 后端根据参数从数据库获取模型和纹理文件路径
- 后端调用Python脚本处理模型
- Python脚本执行完成后返回处理结果
- 后端将结果文件路径返回给前端
- 前端展示处理后的3D模型
8. 错误处理和优化建议
8.1 异步处理
对于大模型处理,建议使用异步处理:
@Async
public CompletableFuture<String> processModelAsync(String modelPath, List<String> texturePaths) {
// 异步执行处理逻辑
}
8.2 进度反馈
可以通过WebSocket向前端实时推送处理进度。
8.3 缓存机制
对于相同的船型号+日期组合,可以缓存结果避免重复处理。
总结
这个架构的核心是:
- 前端:用户界面和交互
- 后端API:业务逻辑和文件管理
- Python算法:你的核心贴图算法
- 数据库:存储模型和纹理文件信息
你需要和团队其他成员协作的部分主要是:
- 确定API接口格式
- 确定文件存储路径规范
- 测试Python脚本的命令行调用
算法部分你已经完成了,现在主要是集成工作。你可以先准备一个支持命令行参数的Python脚本版本,然后与后端工程师一起测试调用流程。