This commit is contained in:
i
2026-04-22 11:35:40 +08:00
parent c204293162
commit ce36785600
+110 -41
View File
@@ -51,7 +51,7 @@ from PyQt5.QtWidgets import (
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer
from PyQt5.QtGui import QImage, QPixmap, QFont
from d2c_batch import batch_convert
from d2c_batch import batch_convert, software_d2c, software_d2c_inv
PARAMS_FILE = "camera_params.json"
MIN_DEPTH_MM, MAX_DEPTH_MM = 200, 5000
@@ -435,12 +435,17 @@ class CameraWorker(QThread):
self._disconnect_emitted = False
self._sync_wait_count = 0 # 等待同步帧的计数器
self._splat_radius = 0
self._d2c_method = "sdk"
self._capture_opts: dict = {}
def set_d2c(self, enabled: bool):
with self._lock:
self._d2c = enabled
def set_d2c_method(self, method: str):
with self._lock:
self._d2c_method = method
def set_splat(self, radius: int):
with self._lock:
self._splat_radius = radius
@@ -586,11 +591,12 @@ class CameraWorker(QThread):
self._consec_timeouts = 0
with self._lock:
d2c = self._d2c
do_cap = self._do_capture
cap_dir = self._capture_dir
splat_r = self._splat_radius
cap_opts = self._capture_opts
d2c = self._d2c
d2c_method = self._d2c_method
do_cap = self._do_capture
cap_dir = self._capture_dir
splat_r = self._splat_radius
cap_opts = self._capture_opts
# 首帧提取相机参数
if self._camera_params is None:
@@ -636,14 +642,8 @@ class CameraWorker(QThread):
continue
# 预览
view = frames
if d2c:
af = align_filter.process(frames)
if af:
view = af.as_frame_set()
cf = view.get_color_frame()
df = view.get_depth_frame()
cf = frames.get_color_frame()
df = frames.get_depth_frame()
if not cf or not df:
continue
@@ -651,11 +651,31 @@ class CameraWorker(QThread):
if color_bgr is None:
continue
depth_data = np.frombuffer(df.get_data(), dtype=np.uint16).reshape(
df.get_height(), df.get_width())
if splat_r > 0:
depth_data = _splat_depth(depth_data, splat_r)
depth_vis = depth_to_vis(depth_data, df.get_depth_scale())
if d2c:
if d2c_method == "forward" and self._camera_params is not None:
raw = np.frombuffer(df.get_data(), dtype=np.uint16).reshape(
df.get_height(), df.get_width())
depth_data = software_d2c(
raw, self._camera_params, splat_radius=splat_r)
depth_scale = df.get_depth_scale()
else:
af = align_filter.process(frames)
if af:
view = af.as_frame_set()
df2 = view.get_depth_frame()
if df2:
df = df2
depth_data = np.frombuffer(df.get_data(), dtype=np.uint16).reshape(
df.get_height(), df.get_width())
depth_scale = df.get_depth_scale()
else:
depth_data = np.frombuffer(df.get_data(), dtype=np.uint16).reshape(
df.get_height(), df.get_width())
if splat_r > 0:
depth_data = _splat_depth(depth_data, splat_r)
depth_scale = df.get_depth_scale()
depth_vis = depth_to_vis(depth_data, depth_scale)
now = time.monotonic()
if now - self._last_emit_ts >= 0.033:
@@ -717,40 +737,59 @@ class CameraWorker(QThread):
imwrite_utf8(str(base / "depth_vis.png"),
depth_to_vis(raw, df.get_depth_scale()))
need_aligned = (capture_opts.get("d2c", True) or
d2c_method = capture_opts.get("d2c_method", "sdk")
need_aligned = ((d2c_method == "sdk" and capture_opts.get("d2c", True)) or
capture_opts.get("pc", False) or
capture_opts.get("pc_color", False))
aset = None
if need_aligned:
aligned = align_filter.process(frames)
if aligned:
aset = aligned.as_frame_set()
adf = aset.get_depth_frame()
if adf and capture_opts.get("d2c", True):
# D2C 深度图保存
if capture_opts.get("d2c", True):
if d2c_method == "sdk" and aset:
adf = aset.get_depth_frame()
if adf:
ad = np.frombuffer(adf.get_data(), dtype=np.uint16).reshape(
adf.get_height(), adf.get_width())
imwrite_utf8(str(base / "depth_d2c.png"), ad)
imwrite_utf8(str(base / "depth_d2c_vis.png"),
depth_to_vis(ad, adf.get_depth_scale()))
elif self._camera_params is not None:
raw = np.frombuffer(df.get_data(), dtype=np.uint16).reshape(
df.get_height(), df.get_width())
if d2c_method == "inverse":
aligned_img = software_d2c_inv(raw, self._camera_params)
else: # forward
splat_r = capture_opts.get("splat_radius", 1)
aligned_img = software_d2c(raw, self._camera_params, splat_radius=splat_r)
imwrite_utf8(str(base / "depth_d2c.png"), aligned_img)
imwrite_utf8(str(base / "depth_d2c_vis.png"),
depth_to_vis(aligned_img, df.get_depth_scale()))
if capture_opts.get("pc", False):
try:
point_cloud_filter.set_create_point_format(OBFormat.POINT)
pc = point_cloud_filter.process(aligned)
if pc:
save_ply_utf8(str(base / "point_cloud.ply"), pc,
save_point_cloud_to_ply)
except Exception as exc:
self.log_msg.emit(f"点云警告:{exc}")
# 点云保存(始终使用 SDK AlignFilter 结果)
if aset:
if capture_opts.get("pc", False):
try:
point_cloud_filter.set_create_point_format(OBFormat.POINT)
pc = point_cloud_filter.process(aligned)
if pc:
save_ply_utf8(str(base / "point_cloud.ply"), pc,
save_point_cloud_to_ply)
except Exception as exc:
self.log_msg.emit(f"点云警告:{exc}")
if capture_opts.get("pc_color", False):
try:
point_cloud_filter.set_create_point_format(OBFormat.RGB_POINT)
pc = point_cloud_filter.process(aligned)
if pc:
save_ply_utf8(str(base / "point_cloud_color.ply"), pc,
save_point_cloud_to_ply)
except Exception as exc:
self.log_msg.emit(f"彩色点云警告:{exc}")
if capture_opts.get("pc_color", False):
try:
point_cloud_filter.set_create_point_format(OBFormat.RGB_POINT)
pc = point_cloud_filter.process(aligned)
if pc:
save_ply_utf8(str(base / "point_cloud_color.ply"), pc,
save_point_cloud_to_ply)
except Exception as exc:
self.log_msg.emit(f"彩色点云警告:{exc}")
self.capture_done.emit(save_dir)
@@ -1062,6 +1101,15 @@ class MainWindow(QMainWindow):
for key, cb in self._capture_checks.items():
cb.setChecked(getattr(self._app_settings, f"capture_{key}", cb.isChecked()))
def _on_capture_d2c_method_changed(self):
method = self.capture_d2c_method_combo.currentData()
if self._worker:
self._worker.set_d2c_method(method)
is_forward = method == "forward"
if not is_forward:
self.capture_splat_check.setChecked(False)
self.capture_splat_check.setEnabled(is_forward)
def _on_capture_splat_changed(self, state: int):
radius = max(1, self._app_settings.splat_radius) if state == Qt.Checked else 0
if self._worker:
@@ -1147,7 +1195,7 @@ class MainWindow(QMainWindow):
layout.addWidget(grp2)
# ── 第 3 步:保存目录 & 采集内容 ──
grp3 = self._step_group(3, "保存目录 & 采集内容")
grp3 = self._step_group(3, "保存目录 && 采集内容")
vg3 = QVBoxLayout(grp3)
vg3.setContentsMargins(8, 4, 8, 8)
vg3.setSpacing(5)
@@ -1223,6 +1271,25 @@ class MainWindow(QMainWindow):
row_cap.addStretch()
vg3.addLayout(row_cap)
# 行 4:D2C 转换方式
row_d2c_method = QHBoxLayout()
row_d2c_method.setSpacing(8)
lbl_d2c_method = QLabel("D2C 方式:")
lbl_d2c_method.setFixedWidth(_sz(58))
row_d2c_method.addWidget(lbl_d2c_method)
self.capture_d2c_method_combo = QComboBox()
self.capture_d2c_method_combo.setSizeAdjustPolicy(QComboBox.AdjustToContents)
self.capture_d2c_method_combo.addItem("逆向投影(SDK 同等,推荐)", "sdk")
self.capture_d2c_method_combo.addItem("正向投影 + Splat 填充", "forward")
self.capture_d2c_method_combo.currentIndexChanged.connect(
self._on_capture_d2c_method_changed)
row_d2c_method.addWidget(self.capture_d2c_method_combo)
# 默认选中 "sdk",Splat 应取消勾选并禁用
self.capture_splat_check.setChecked(False)
self.capture_splat_check.setEnabled(False)
row_d2c_method.addStretch()
vg3.addLayout(row_d2c_method)
layout.addWidget(grp3)
# ── 预览区 ──
@@ -1611,6 +1678,8 @@ class MainWindow(QMainWindow):
self.capture_btn.setEnabled(False)
self.capture_btn.setText("拍摄中…")
capture_opts = {k: cb.isChecked() for k, cb in self._capture_checks.items()}
capture_opts["d2c_method"] = self.capture_d2c_method_combo.currentData()
capture_opts["splat_radius"] = self._app_settings.splat_radius
self._worker.trigger_capture(str(self._session_dir / ts), capture_opts)
def _on_capture_done(self, save_dir: str):