- 算法包替换为 delivery_v1.2(coupler_panel 新增 autotune/layer_select,underframe_deflection 新增 single_beam 管线),删除 delivery_v1.1 - manifests/adapters 的 PYTHONPATH 与版本说明同步更新到 v1.2 - 新增任务级 CAD 文件支持(task_cad_files 表、上传/读取路由、TaskManager 全选与加载显示) - 新增 train-measurement 自定义前端面板(YAML 编辑器 + 可视化 ROI 配置 + 3D 取点) - PointCloudViewer 支持 PLY 叠加层渲染与 ROI 拾取 - Dockerfile 改用 Miniforge 安装 pythonocc-core,修复 libgomp/libstdc++ 依赖
177 lines
5.5 KiB
Python
177 lines
5.5 KiB
Python
"""Command-line entry point installed by ``pyproject.toml``.
|
|
|
|
The DEFAULT mode is single-beam measurement: the ROI(s) already isolate one
|
|
beam each, the cloud is cropped and measured directly (no registration, no
|
|
dual-beam tracking). Provide ``--roi`` for one beam, or ``--left-roi`` and
|
|
``--right-roi`` to measure both in one invocation.
|
|
|
|
The legacy dual-beam pipeline (registration + left/right tracking on a
|
|
car-bottom slab) is still available via ``--mode dual``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(
|
|
description="Measure underframe longitudinal beam deflection from a point cloud.",
|
|
)
|
|
parser.add_argument(
|
|
"--mode",
|
|
default="single",
|
|
choices=["single", "dual"],
|
|
help="'single' (default): ROI isolates one beam, measure directly. "
|
|
"'dual': legacy car-bottom slab with left+right tracking + registration.",
|
|
)
|
|
parser.add_argument(
|
|
"--input-cloud",
|
|
required=True,
|
|
help="Path to input scan point cloud (.ply or .xyz). Large binary PLYs "
|
|
"are stream-cropped by the ROI in single mode.",
|
|
)
|
|
parser.add_argument(
|
|
"--out-dir",
|
|
required=True,
|
|
help="Output directory for report.json/.xlsx, CSV, and PNG.",
|
|
)
|
|
# --- single mode ---
|
|
parser.add_argument(
|
|
"--roi",
|
|
default=None,
|
|
help="Single mode: ROI JSON isolating ONE beam (native frame "
|
|
"x=length, y=height, z=width).",
|
|
)
|
|
parser.add_argument(
|
|
"--left-roi",
|
|
default=None,
|
|
help="Single mode: ROI JSON for the left beam (use with --right-roi).",
|
|
)
|
|
parser.add_argument(
|
|
"--right-roi",
|
|
default=None,
|
|
help="Single mode: ROI JSON for the right beam (use with --left-roi).",
|
|
)
|
|
parser.add_argument(
|
|
"--side",
|
|
default=None,
|
|
help="Single mode label for --roi (left/right); inferred from the file name if omitted.",
|
|
)
|
|
parser.add_argument(
|
|
"--enable-registration",
|
|
action="store_true",
|
|
help="Single mode: rigidly align the cloud to --cad before measuring. "
|
|
"Default OFF (cloud assumed already registered). Requires --cad as a "
|
|
"sampled .ply/.stl/.obj (.stp must be converted first).",
|
|
)
|
|
# --- shared ---
|
|
parser.add_argument(
|
|
"--config",
|
|
default=None,
|
|
help="Optional JSON config override file.",
|
|
)
|
|
parser.add_argument(
|
|
"--skip-excel",
|
|
action="store_true",
|
|
help="Skip writing the .xlsx report.",
|
|
)
|
|
parser.add_argument(
|
|
"--skip-plot",
|
|
action="store_true",
|
|
help="Single mode: skip the PNG plot.",
|
|
)
|
|
# --- dual (legacy) mode ---
|
|
parser.add_argument(
|
|
"--cad",
|
|
default=None,
|
|
help="Dual mode: CAD reference (.ply/.xyz/.stl/.obj) for registration.",
|
|
)
|
|
parser.add_argument(
|
|
"--profile",
|
|
default="auto",
|
|
choices=["auto", "M", "TC", "TP", "custom"],
|
|
help="Dual mode: profile for scale-related tuning.",
|
|
)
|
|
parser.add_argument(
|
|
"--skip-registration",
|
|
action="store_true",
|
|
help="Dual mode: bypass ICP even when --cad is supplied.",
|
|
)
|
|
parser.add_argument(
|
|
"--no-bilateral",
|
|
action="store_true",
|
|
help="Dual mode: disable bilateral denoising.",
|
|
)
|
|
parser.add_argument(
|
|
"--input-axis",
|
|
default="y_up",
|
|
choices=["z_up", "y_up"],
|
|
help="Dual mode: height axis convention (default y_up).",
|
|
)
|
|
return parser.parse_args()
|
|
|
|
|
|
def _load_config(path):
|
|
if not path:
|
|
return None
|
|
import json
|
|
from pathlib import Path
|
|
return json.loads(Path(path).read_text(encoding="utf-8"))
|
|
|
|
|
|
def main() -> None:
|
|
args = parse_args()
|
|
|
|
if args.mode == "single":
|
|
from .single_beam_pipeline import run_single_beam_pipeline, run_both_beams
|
|
config = _load_config(args.config)
|
|
if args.left_roi and args.right_roi:
|
|
run_both_beams(
|
|
input_cloud=args.input_cloud,
|
|
left_roi=args.left_roi,
|
|
right_roi=args.right_roi,
|
|
out_dir=args.out_dir,
|
|
config=config,
|
|
cad_path=args.cad,
|
|
enable_registration=args.enable_registration,
|
|
skip_excel=args.skip_excel,
|
|
skip_plot=args.skip_plot,
|
|
)
|
|
elif args.roi:
|
|
run_single_beam_pipeline(
|
|
input_cloud=args.input_cloud,
|
|
roi_path=args.roi,
|
|
out_dir=args.out_dir,
|
|
side=args.side,
|
|
config=config,
|
|
cad_path=args.cad,
|
|
enable_registration=args.enable_registration,
|
|
skip_excel=args.skip_excel,
|
|
skip_plot=args.skip_plot,
|
|
)
|
|
else:
|
|
raise SystemExit(
|
|
"single mode requires --roi (one beam) or both --left-roi and --right-roi."
|
|
)
|
|
return
|
|
|
|
# dual (legacy) mode
|
|
from .pipeline import run_measurement_pipeline
|
|
run_measurement_pipeline(
|
|
input_cloud=args.input_cloud,
|
|
out_dir=args.out_dir,
|
|
config_path=args.config,
|
|
profile=args.profile,
|
|
cad_path=args.cad,
|
|
roi_path=args.roi,
|
|
skip_registration=args.skip_registration,
|
|
enable_bilateral=not args.no_bilateral,
|
|
skip_excel=args.skip_excel,
|
|
input_axis=args.input_axis,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|