缩短 Studio 叠字与重复说明,色标只保留量程

去掉 depth_empty 与无用选区峰值文案,并修正「视图 → 分析视频」。已用 locale 单测与 depth_scale_text 核对;含证据录制的 wiring 用例本机跳过。
This commit is contained in:
ykcheungab
2026-10-08 15:41:30 +08:00
parent 48624dec99
commit f2bbab76a6
5 changed files with 56 additions and 69 deletions
+18 -22
View File
@@ -38,7 +38,7 @@
"plot_settings": "Plot settings…", "plot_settings": "Plot settings…",
"rotate_arrows": "Rotate arrows 90°", "rotate_arrows": "Rotate arrows 90°",
"recompute": "Analyze video", "recompute": "Analyze video",
"recompute_tip": "On: serve a stored analysis result for this video, or run the pass if there is none. Off: leave analysis and read the recording’s own forces.csv when it has one. To change profile, calibration or reference frame use Analysis ▸ Analysis setup…", "recompute_tip": "Use a result if there is one; otherwise analyse. Change settings in Analysis setup.",
"analysis_setup": "Analysis setup…", "analysis_setup": "Analysis setup…",
"analysis_setup_tip": "Choose the profile, calibration and reference frame for this video, then run (or re-run) the analysis pass.", "analysis_setup_tip": "Choose the profile, calibration and reference frame for this video, then run (or re-run) the analysis pass.",
"run": "Run / Stop", "run": "Run / Stop",
@@ -78,8 +78,8 @@
"reset_defaults": "Reset to defaults", "reset_defaults": "Reset to defaults",
"plot_orientation_group": "Finger 2D presentation (2D only; the 3D view keeps the design frame)", "plot_orientation_group": "Finger 2D presentation (2D only; the 3D view keeps the design frame)",
"plot_orientation": "Orientation", "plot_orientation": "Orientation",
"plot_orientation_tip": "How the finger's 2D canvas (the unrolled domain) is presented. The 180° default is derived from the unroll map and the design frame: the canvas's +x is the frame's +X and its screen up is −Z, while the 3D default view puts −X right and +Z up, so both axes are reversed and a half turn is what makes the arrows agree left-to-right. Display only — the record and the stored arrays are untouched", "plot_orientation_tip": "Affects the 2D view only; default rotation is 180°.",
"plot_orientation_note": "Affects the 2D finger canvas only: the stored flow, valid mask and recorded data keep the domain's own orientation. The 3D view is not affected (its frame is the design export's)", "plot_orientation_note": "Affects the 2D view only; default rotation is 180°.",
"orientation_identity": "As drawn (no rotation)", "orientation_identity": "As drawn (no rotation)",
"orientation_rotate180": "Rotate 180° (default)", "orientation_rotate180": "Rotate 180° (default)",
"orientation_mirror_h": "Mirror horizontally", "orientation_mirror_h": "Mirror horizontally",
@@ -89,7 +89,7 @@
"filter_preset_tip": "A ready-made combination; editing any value below switches to Custom", "filter_preset_tip": "A ready-made combination; editing any value below switches to Custom",
"filter_preset_custom": "Custom", "filter_preset_custom": "Custom",
"filter_flow_ema": "Flow EMA α", "filter_flow_ema": "Flow EMA α",
"filter_flow_ema_tip": "Temporal smoothing of the optical flow. 1.00 = off. It changes what is *measured* (contact statistics and forces along with it), so two recordings with different α are not comparable — only use it when the flow jitter really has to go", "filter_flow_ema_tip": "1.00 is off. Changing it changes the measured force.",
"filter_median": "Median window", "filter_median": "Median window",
"filter_median_tip": "Sliding-median window over the force readings (samples). 1 = off. Removes single-sample spikes at the cost of about half a window of delay", "filter_median_tip": "Sliding-median window over the force readings (samples). 1 = off. Removes single-sample spikes at the cost of about half a window of delay",
"filter_lowpass": "Low-pass cutoff", "filter_lowpass": "Low-pass cutoff",
@@ -116,7 +116,7 @@
"choose_video": "Choose a recorded video…", "choose_video": "Choose a recorded video…",
"choose_video_tip": "Open a recorded video: the 2D panel plays it and the charts read the forces.csv beside it", "choose_video_tip": "Open a recorded video: the 2D panel plays it and the charts read the forces.csv beside it",
"recompute": "Analyze video…", "recompute": "Analyze video…",
"recompute_tip": "Serve a stored analysis result for this video, or run the pass if there is none. The 2D panel can then draw arrows and depth; the charts may show force from the result. Stop cancels a pass that is still running. To change profile, calibration or reference frame use Analysis ▸ Analysis setup…", "recompute_tip": "Use a result if there is one; otherwise analyse. Change settings in Analysis setup.",
"replay_view_only": "Open a recording from the History replay view", "replay_view_only": "Open a recording from the History replay view",
"preview": "Preview", "preview": "Preview",
"preview_stop": "Stop preview", "preview_stop": "Stop preview",
@@ -143,7 +143,7 @@
"reset_display": "Reset display", "reset_display": "Reset display",
"reset_display_tip": "Clear the accumulated display: the curves, cursor and value cards go back to their defaults and the replay returns to the start of the recording. There is no device session in this view, so nothing of the device is touched; the 3D camera is View ▸ Reset views (F8).", "reset_display_tip": "Clear the accumulated display: the curves, cursor and value cards go back to their defaults and the replay returns to the start of the recording. There is no device session in this view, so nothing of the device is touched; the 3D camera is View ▸ Reset views (F8).",
"pause": "⏸ Pause", "pause": "⏸ Pause",
"pause_tip": "Pause the computation and the display (F7): no flow, depth or contact is computed, the picture holds its last frame, and the device stays connected for an immediate resume. A recording that spans a pause has a gap.", "pause_tip": "Pause computation; the picture holds and the device stays connected (F7).",
"resume_tip": "Resume the computation and the display (F7), from the newest frame.", "resume_tip": "Resume the computation and the display (F7), from the newest frame.",
"resume_label": "▶ Resume", "resume_label": "▶ Resume",
"resume_label_tip": "Resume the computation and the display (F7): it carries on from the newest frame with the device still connected.", "resume_label_tip": "Resume the computation and the display (F7): it carries on from the newest frame with the device still connected.",
@@ -210,8 +210,8 @@
"view_mode_tip": "Image display mode while running", "view_mode_tip": "Image display mode while running",
"arrow_settings_tip": "Finger 2D presentation (orientation; display only)", "arrow_settings_tip": "Finger 2D presentation (orientation; display only)",
"replay_pipeline": "Recorded video (raw camera image)", "replay_pipeline": "Recorded video (raw camera image)",
"replay_pipeline_tip": "Replay shows the recorded raw camera image with no live arrow overlay; to get arrows and depth from analysis, use View → Analyze video", "replay_pipeline_tip": "This is the recorded camera image. For arrows and depth use Analysis ▸ Analyze video.",
"replay_raw_note": "No analysis result yet. Opening a recording runs analysis, or choose Analyze video… above.", "replay_raw_note": "No analysis result yet; use Analysis ▸ Analyze video.",
"frame_missing_note": "This frame carries no {fields}: there is nothing for this view to draw", "frame_missing_note": "This frame carries no {fields}: there is nothing for this view to draw",
"field_flow": "flow", "field_flow": "flow",
"field_depth": "depth", "field_depth": "depth",
@@ -222,9 +222,8 @@
"arrow_empty_tip": "The deformation arrow image appears here while running or previewing", "arrow_empty_tip": "The deformation arrow image appears here while running or previewing",
"arrow_render_failed": "Flow arrows could not be drawn for this frame\nTry another view, or reload the video", "arrow_render_failed": "Flow arrows could not be drawn for this frame\nTry another view, or reload the video",
"arrow_capture_unavailable": "The captured frame is not available for this overlay\nThe raw camera view is still one selection away", "arrow_capture_unavailable": "The captured frame is not available for this overlay\nThe raw camera view is still one selection away",
"depth_unavailable": "No depth data for this frame\n(not a depth of zero, and no silent fallback to the processed image)", "depth_unavailable": "No depth data for this frame",
"depth_scale": "Depth scale {low} – {high}", "depth_scale": "Depth scale {low} – {high}",
"depth_empty": "This frame's depth is all zeros (0 of {pixels} px non-zero): the ramp's low colour is a real zero response, not missing data",
"arrow_scale": "Arrows: displacement ×{factor}", "arrow_scale": "Arrows: displacement ×{factor}",
"arrow_scale_tip": "Display magnification of the relative displacement: the arrows are drawn from the algorithm output of the captured image, with the measured displacement multiplied by this factor and capped at 0.8 of the grid spacing (a capped arrow no longer shows its true factor).", "arrow_scale_tip": "Display magnification of the relative displacement: the arrows are drawn from the algorithm output of the captured image, with the measured displacement multiplied by this factor and capped at 0.8 of the grid spacing (a capped arrow no longer shows its true factor).",
"arrow_pipeline": "Captured image → algorithm output", "arrow_pipeline": "Captured image → algorithm output",
@@ -246,9 +245,9 @@
"mesh3d_unavailable_gl_reason": "3D is not available on this machine: {reason}\nMeasuring, recording and the charts work as usual.", "mesh3d_unavailable_gl_reason": "3D is not available on this machine: {reason}\nMeasuring, recording and the charts work as usual.",
"mesh3d_settings_tip": "3D display settings (view, housing, colour scale, frame rate)", "mesh3d_settings_tip": "3D display settings (view, housing, colour scale, frame rate)",
"mesh3d_reset_tip": "Reset view: back to the framing the scene itself uses", "mesh3d_reset_tip": "Reset view: back to the framing the scene itself uses",
"replay_analysis_running": "this recording is being analysed: there are no arrows or depth to draw until it finishes. Progress and Cancel are on the analysis panel.", "replay_analysis_running": "Analysing; arrows and depth appear when it finishes.",
"replay_analysis_failed": "there is no analysis result to draw: {reason}. Only the raw camera image can be shown here; once that is dealt with, Analyze video runs the pass again by hand.", "replay_analysis_failed": "Analysis failed: {reason}.",
"replay_analysis_unavailable": "This video has no analysis setup. Choose Analyze video… above to select one and generate results." "replay_analysis_unavailable": "No analysis setup yet; use Analysis ▸ Analyze video."
}, },
"chart": { "chart": {
"fn": "FN", "fn": "FN",
@@ -267,7 +266,7 @@
"mode_replay": "Replaying a recording (browsing)", "mode_replay": "Replaying a recording (browsing)",
"mode_replay_tip": "The charts read the open recording; capture and recording continue", "mode_replay_tip": "The charts read the open recording; capture and recording continue",
"mode_recomputed": "Recomputed from this video (browsing)", "mode_recomputed": "Recomputed from this video (browsing)",
"mode_recomputed_tip": "The charts read the force the analysis pass recomputed from this video and stored in its result. This is not the recorded forces.csv — no device measured these numbers", "mode_recomputed_tip": "Curves come from this analysis, not forces.csv.",
"mode_no_trace": "No force trace — {reason}", "mode_no_trace": "No force trace — {reason}",
"mode_no_trace_tip": "There is nothing to plot, and the reason is a fact about the input rather than a loading state", "mode_no_trace_tip": "There is nothing to plot, and the reason is a fact about the input rather than a loading state",
"trace_not_analysed": "this video has not been analysed yet", "trace_not_analysed": "this video has not been analysed yet",
@@ -280,10 +279,7 @@
"box_zoom_tip": "Switch to rubber-band zoom (drag a region on the chart)", "box_zoom_tip": "Switch to rubber-band zoom (drag a region on the chart)",
"settings": "Chart settings", "settings": "Chart settings",
"settings_tip": "Time window, axis ranges, and the acquisition filters (screen and record use the same data)", "settings_tip": "Time window, axis ranges, and the acquisition filters (screen and record use the same data)",
"selection": "Selection {duration}s / {samples} samples — {detail}",
"selection_summary": "Selection {duration}s / {samples} samples", "selection_summary": "Selection {duration}s / {samples} samples",
"selection_detail": "mean {mean} · σ {std}",
"selection_peak_tip": "The large number is the selection's peak; the line under it is its mean",
"selection_empty": "No valid samples in the selection", "selection_empty": "No valid samples in the selection",
"value_unavailable": "n/a", "value_unavailable": "n/a",
"value_unavailable_tip": "No curve is loaded for {channel}, so it has no value in this unit (raw counts are never shown in its place)", "value_unavailable_tip": "No curve is loaded for {channel}, so it has no value in this unit (raw counts are never shown in its place)",
@@ -298,7 +294,7 @@
"video_filter": "Recorded video (*.mp4 *.avi *.mov *.mkv);;All files (*)", "video_filter": "Recorded video (*.mp4 *.avi *.mov *.mkv);;All files (*)",
"no_video": "This recording has no usable video segment", "no_video": "This recording has no usable video segment",
"open_failed": "Could not open that recording: {error}", "open_failed": "Could not open that recording: {error}",
"not_a_recording": "“{name}” is not a recording: its folder has no forces.csv (a recording is the video plus forces.csv and session.json). Replay needs a recording; to just watch this video, open it as a video or use Analysis ▸ Analyze video.", "not_a_recording": "No forces.csv beside it; cannot open as a recording.",
"video_only_opened": "Opened “{name}”; no analysis result yet. Choose Analyze video… above to generate one.", "video_only_opened": "Opened “{name}”; no analysis result yet. Choose Analyze video… above to generate one.",
"video_only_notice": "This video has no recording beside it (no forces.csv): it plays, but the force charts and the recording details are empty.", "video_only_notice": "This video has no recording beside it (no forces.csv): it plays, but the force charts and the recording details are empty.",
"video_only_panel": "This is not a recording, and this video cannot be decoded (no forces.csv beside it, and it does not read as playable video)\nYou can still try Analysis ▸ Analyze video", "video_only_panel": "This is not a recording, and this video cannot be decoded (no forces.csv beside it, and it does not read as playable video)\nYou can still try Analysis ▸ Analyze video",
@@ -325,7 +321,7 @@
}, },
"video_analysis": { "video_analysis": {
"title": "Analyse this video", "title": "Analyse this video",
"video": "{name} — {frames} frames at {fps} fps ({seconds} s). It has no recording beside it (no forces.csv, no frames.csv), so the pass will work from a frame index generated for it.", "video": "{name} — {frames} frames at {fps} fps",
"profile": "Processing profile", "profile": "Processing profile",
"calibration": "Calibration", "calibration": "Calibration",
"calibration_default": "Profile default — {name}", "calibration_default": "Profile default — {name}",
@@ -487,7 +483,7 @@
"run_failed": "Run failed: {error}", "run_failed": "Run failed: {error}",
"filters_saved": "Filter settings saved: {describe} (applies to the next session)", "filters_saved": "Filter settings saved: {describe} (applies to the next session)",
"rail_camera_fps": "camera {fps}", "rail_camera_fps": "camera {fps}",
"capture_slow": "The camera delivers less than the configured frame rate: {detail}. The recording keeps the real rate. This is a capture-side (camera/driver) matter that software cannot change: use a camera that really does deliver 30 fps — after checking that the cable or port is not slowing this one down — and measure again.", "capture_slow": "Camera frame rate is below the configured value: {detail}.",
"video_ended": "Video ended", "video_ended": "Video ended",
"camera_disconnected": "Camera disconnected", "camera_disconnected": "Camera disconnected",
"reset_ok": "Sensor tracker reset", "reset_ok": "Sensor tracker reset",
@@ -510,7 +506,7 @@
"deform_tip": "Display-only magnification of the measured deformation. It changes the picture and nothing else: the recorded data, the calibration and the camera are not touched.", "deform_tip": "Display-only magnification of the measured deformation. It changes the picture and nothing else: the recorded data, the calibration and the camera are not touched.",
"deform_unavailable": "This data source provides no 3D displacement: the pane can only colour the surface, so exaggeration is unavailable.", "deform_unavailable": "This data source provides no 3D displacement: the pane can only colour the surface, so exaggeration is unavailable.",
"field": "Colour by", "field": "Colour by",
"field_tip": "Which quantity colours the surface: the depth response (the chain's relative deformation response, unitless) or the optical-flow magnitude (the tracker's pixel displacement). Neither is a length in millimetres — both are approximate. When the chosen one cannot be drawn for a frame, the other is used and the line below says so.", "field_tip": "Colour the surface by depth or optical flow.",
"field_depth": "Depth response", "field_depth": "Depth response",
"field_flow": "Optical flow magnitude", "field_flow": "Optical flow magnitude",
"field_substituted": "This frame shows {shown}, not the chosen {chosen}.", "field_substituted": "This frame shows {shown}, not the chosen {chosen}.",
@@ -537,7 +533,7 @@
"max_fps_tip": "How many frames per second the 3D view draws. A display budget only: the camera and the recording are unaffected, and a dropped frame here is never a dropped sample.", "max_fps_tip": "How many frames per second the 3D view draws. A display budget only: the camera and the recording are unaffected, and a dropped frame here is never a dropped sample.",
"reset_view": "Reset view", "reset_view": "Reset view",
"settings_title": "3D display settings", "settings_title": "3D display settings",
"settings_hint": "Per-frame adaptive applies to both the 2D depth view and the 3D surface. Every other item affects the 3D display only. Measuring, recording and the charts are untouched, and each change applies at once.", "settings_hint": "Per-frame adaptive applies to both 2D and 3D.",
"legend": "{quantity} [{unit}] · {quality}", "legend": "{quantity} [{unit}] · {quality}",
"legend_none": "No measurement on screen — the surface model is shown uncoloured", "legend_none": "No measurement on screen — the surface model is shown uncoloured",
"quality_calibrated": "calibrated", "quality_calibrated": "calibrated",
+18 -22
View File
@@ -38,7 +38,7 @@
"plot_settings": "绘图设置…", "plot_settings": "绘图设置…",
"rotate_arrows": "旋转箭头图 90°", "rotate_arrows": "旋转箭头图 90°",
"recompute": "分析视频", "recompute": "分析视频",
"recompute_tip": "打开:有兼容的分析结果就直接用,没有就跑一遍分析。关闭:离开分析,有 forces.csv 时曲线读录制值。要改配置、标定或参考帧请用「分析 ▸ 分析设置…」。", "recompute_tip": "有结果就用,没有就分析。改配置用分析设置。",
"analysis_setup": "分析设置…", "analysis_setup": "分析设置…",
"analysis_setup_tip": "为这段视频选择配置、标定与参考帧,然后跑(或重跑)分析。", "analysis_setup_tip": "为这段视频选择配置、标定与参考帧,然后跑(或重跑)分析。",
"run": "运行 / 停止", "run": "运行 / 停止",
@@ -78,8 +78,8 @@
"reset_defaults": "恢复默认", "reset_defaults": "恢复默认",
"plot_orientation_group": "手指 2D 显示方向(只影响 2D 画面;3D 视图仍用设计坐标系)", "plot_orientation_group": "手指 2D 显示方向(只影响 2D 画面;3D 视图仍用设计坐标系)",
"plot_orientation": "方向", "plot_orientation": "方向",
"plot_orientation_tip": "手指 2D 画布(展开域)的显示方向。默认 180° 是按展开图与设计坐标系推导出来的:画布的 +x 是设计系的 +X、屏幕向上是 −Z,与 3D 默认视图的 −X / +Z 正好都相反,因此需要旋转 180° 才能使箭头方向与 3D 视图一致。。只影响显示,不改变记录与存储的数组", "plot_orientation_tip": "只影响 2D 画面,默认旋转 180°。",
"plot_orientation_note": "只影响 2D 手指画布的显示:存储的光流、有效掩膜与录制数据始终保持域本身的朝向。3D 视图不受影响(它的坐标系是设计导出坐标系)", "plot_orientation_note": "只影响 2D 画面,默认旋转 180°。",
"orientation_identity": "原始(不旋转)", "orientation_identity": "原始(不旋转)",
"orientation_rotate180": "旋转 180°(默认)", "orientation_rotate180": "旋转 180°(默认)",
"orientation_mirror_h": "水平镜像", "orientation_mirror_h": "水平镜像",
@@ -89,7 +89,7 @@
"filter_preset_tip": "选择一档现成的滤波组合;改下面任一数值会变成“自定义”", "filter_preset_tip": "选择一档现成的滤波组合;改下面任一数值会变成“自定义”",
"filter_preset_custom": "自定义", "filter_preset_custom": "自定义",
"filter_flow_ema": "光流 EMA α", "filter_flow_ema": "光流 EMA α",
"filter_flow_ema_tip": "光流的时间平滑。1.00 = 关。它改变的是“测量本身”(接触统计与力值都会变),两个 α 不同的录制不可比 —— 只有确实需要压光流抖动时才调", "filter_flow_ema_tip": "1.00 为关闭。改它会改变测到的力。",
"filter_median": "中值窗口", "filter_median": "中值窗口",
"filter_median_tip": "力读数的滑动中值窗口(采样数)。1 = 关。去掉单点尖峰,代价是约半个窗口的延迟", "filter_median_tip": "力读数的滑动中值窗口(采样数)。1 = 关。去掉单点尖峰,代价是约半个窗口的延迟",
"filter_lowpass": "低通截止", "filter_lowpass": "低通截止",
@@ -116,7 +116,7 @@
"choose_video": "选择录制视频…", "choose_video": "选择录制视频…",
"choose_video_tip": "打开一段录制的视频:2D 面板放视频,曲线读同目录的 forces.csv(原始记录值)", "choose_video_tip": "打开一段录制的视频:2D 面板放视频,曲线读同目录的 forces.csv(原始记录值)",
"recompute": "分析视频…", "recompute": "分析视频…",
"recompute_tip": "有兼容的分析结果就直接用,没有就跑一遍分析:2D 面板可画箭头与深度,曲线可读结果中的力。分析进行中可随时「停止」。要改配置、标定或参考帧请用「分析 ▸ 分析设置…」。", "recompute_tip": "有结果就用,没有就分析。改配置用分析设置。",
"replay_view_only": "请在「历史回放」视图里打开录像", "replay_view_only": "请在「历史回放」视图里打开录像",
"preview": "预览", "preview": "预览",
"preview_stop": "停止预览", "preview_stop": "停止预览",
@@ -143,7 +143,7 @@
"reset_display": "复位显示", "reset_display": "复位显示",
"reset_display_tip": "清空累积的显示:曲线、游标与数字卡恢复默认,回放回到录像开头。本视图没有设备会话,不会改动设备;三维视角请用「视图 → 复位视图」(F8)。", "reset_display_tip": "清空累积的显示:曲线、游标与数字卡恢复默认,回放回到录像开头。本视图没有设备会话,不会改动设备;三维视角请用「视图 → 复位视图」(F8)。",
"pause": "⏸ 暂停", "pause": "⏸ 暂停",
"pause_tip": "暂停计算与显示(F7):不再做光流/深度/接触计算,画面停在最后一帧,设备保持连接,随时可继续。跨过暂停的记录会有一段空档。", "pause_tip": "暂停计算,画面停住,设备保持连接(F7)。",
"resume_tip": "继续计算与显示(F7),从最新一帧接着跑。", "resume_tip": "继续计算与显示(F7),从最新一帧接着跑。",
"resume_label": "▶ 继续", "resume_label": "▶ 继续",
"resume_label_tip": "继续计算与显示(F7):从最新一帧接着跑,设备保持连接。", "resume_label_tip": "继续计算与显示(F7):从最新一帧接着跑,设备保持连接。",
@@ -210,8 +210,8 @@
"view_mode_tip": "运行时的图像显示模式", "view_mode_tip": "运行时的图像显示模式",
"arrow_settings_tip": "手指 2D 显示方向(只影响画面)", "arrow_settings_tip": "手指 2D 显示方向(只影响画面)",
"replay_pipeline": "录制视频(原始相机图像)", "replay_pipeline": "录制视频(原始相机图像)",
"replay_pipeline_tip": "回放显示的是录制下来的原始相机图像,不含实时箭头叠加;要看分析得到的箭头与深度请用「视图 → 分析视频」", "replay_pipeline_tip": "这是录下的相机画面。箭头和深度用分析 ▸ 分析视频。",
"replay_raw_note": "尚无分析结果。打开录像会自动分析,或点顶部「分析视频…」。", "replay_raw_note": "尚无分析结果,请用分析 ▸ 分析视频。",
"frame_missing_note": "本帧没有{fields}:这个视图这一帧没有数据可画", "frame_missing_note": "本帧没有{fields}:这个视图这一帧没有数据可画",
"field_flow": "光流", "field_flow": "光流",
"field_depth": "深度", "field_depth": "深度",
@@ -222,9 +222,8 @@
"arrow_empty_tip": "运行或预览后在此显示形变箭头图", "arrow_empty_tip": "运行或预览后在此显示形变箭头图",
"arrow_render_failed": "本帧无法绘制形变箭头\n请换一种视图,或重新加载视频", "arrow_render_failed": "本帧无法绘制形变箭头\n请换一种视图,或重新加载视频",
"arrow_capture_unavailable": "当前无法取得采集图像用于叠加\n原始相机画面仍可在同一选择器中查看", "arrow_capture_unavailable": "当前无法取得采集图像用于叠加\n原始相机画面仍可在同一选择器中查看",
"depth_unavailable": "本帧没有深度数据\n(不是深度为零,也未回退显示普通形变图)", "depth_unavailable": "本帧没有深度数据",
"depth_scale": "深度色标 {low} – {high}", "depth_scale": "深度色标 {low} – {high}",
"depth_empty": "本帧深度全为 0({pixels} px 中 0 个非零):色标最低颜色是真实的零响应,不是缺数据",
"arrow_scale": "箭头按位移放大 ×{factor}", "arrow_scale": "箭头按位移放大 ×{factor}",
"arrow_scale_tip": "相对位移的显示放大倍率:箭头由原始采集图像经算法输出,长度按测量位移放大后绘制,上限为网格间距的 0.8 倍;触顶的箭头不再呈现真实倍率。", "arrow_scale_tip": "相对位移的显示放大倍率:箭头由原始采集图像经算法输出,长度按测量位移放大后绘制,上限为网格间距的 0.8 倍;触顶的箭头不再呈现真实倍率。",
"arrow_pipeline": "原始图像 → 算法输出", "arrow_pipeline": "原始图像 → 算法输出",
@@ -246,9 +245,9 @@
"mesh3d_unavailable_gl_reason": "本机无法使用三维显示:{reason}\n采集、记录与力曲线照常工作。", "mesh3d_unavailable_gl_reason": "本机无法使用三维显示:{reason}\n采集、记录与力曲线照常工作。",
"mesh3d_settings_tip": "三维显示设置(视角、外壳、色标、显示频率)", "mesh3d_settings_tip": "三维显示设置(视角、外壳、色标、显示频率)",
"mesh3d_reset_tip": "复位视角:回到场景自带的取景", "mesh3d_reset_tip": "复位视角:回到场景自带的取景",
"replay_analysis_running": "正在分析这个录像:跑完之前这里没有箭头/深度可画,进度与取消在分析面板上。", "replay_analysis_running": "正在分析,完成后显示箭头与深度。",
"replay_analysis_failed": "没有可画的分析结果:{reason}。这里只能显示原始相机图像;处理完之后可以点「分析视频」再跑一遍。", "replay_analysis_failed": "分析失败:{reason}。",
"replay_analysis_unavailable": "视频尚无分析配置。点顶部「分析视频…」选择配置并生成结果。" "replay_analysis_unavailable": "尚无分析配置,请用分析 ▸ 分析视频。"
}, },
"chart": { "chart": {
"fn": "FN", "fn": "FN",
@@ -267,7 +266,7 @@
"mode_replay": "回放录制(浏览)", "mode_replay": "回放录制(浏览)",
"mode_replay_tip": "曲线读的是打开的录制;回放期间采集与记录继续", "mode_replay_tip": "曲线读的是打开的录制;回放期间采集与记录继续",
"mode_recomputed": "从这段视频重算(浏览)", "mode_recomputed": "从这段视频重算(浏览)",
"mode_recomputed_tip": "曲线读的是分析过程从这段视频重算、并存入结果的力。这不是记录的 forces.csv——没有任何设备测过这些数字", "mode_recomputed_tip": "曲线来自这次分析,不是 forces.csv。",
"mode_no_trace": "没有力曲线 — {reason}", "mode_no_trace": "没有力曲线 — {reason}",
"mode_no_trace_tip": "没有可画的东西,而原因是输入本身的事实,不是一种加载状态", "mode_no_trace_tip": "没有可画的东西,而原因是输入本身的事实,不是一种加载状态",
"trace_not_analysed": "这段视频还没有分析过", "trace_not_analysed": "这段视频还没有分析过",
@@ -280,10 +279,7 @@
"box_zoom_tip": "切换为框选放大(拖动图表框选区域)", "box_zoom_tip": "切换为框选放大(拖动图表框选区域)",
"settings": "曲线图设置", "settings": "曲线图设置",
"settings_tip": "时间窗口、纵轴量程,以及采集端滤波(界面与记录用同一份数据)", "settings_tip": "时间窗口、纵轴量程,以及采集端滤波(界面与记录用同一份数据)",
"selection": "选区 {duration}s / {samples} 点 — {detail}",
"selection_summary": "选区 {duration}s / {samples} 点", "selection_summary": "选区 {duration}s / {samples} 点",
"selection_detail": "均值 {mean} · σ {std}",
"selection_peak_tip": "上方大号数字是选区的峰值;均值为选区均值",
"selection_empty": "选区无有效样本", "selection_empty": "选区无有效样本",
"value_unavailable": "不可用", "value_unavailable": "不可用",
"value_unavailable_tip": "{channel} 轴未加载标定曲线,因此没有该单位的数值(不会以原始值替代)", "value_unavailable_tip": "{channel} 轴未加载标定曲线,因此没有该单位的数值(不会以原始值替代)",
@@ -298,7 +294,7 @@
"video_filter": "录制的视频 (*.mp4 *.avi *.mov *.mkv);;所有文件 (*)", "video_filter": "录制的视频 (*.mp4 *.avi *.mov *.mkv);;所有文件 (*)",
"no_video": "这段录制没有可用的视频分段", "no_video": "这段录制没有可用的视频分段",
"open_failed": "无法打开该录制: {error}", "open_failed": "无法打开该录制: {error}",
"not_a_recording": "「{name}」不是一条记录:同目录没有 forces.csv(一条记录 = 录像 + forces.csv + session.json)。回放需要记录;只想看这段视频请直接打开视频,或用「分析视频」。", "not_a_recording": "旁边没有 forces.csv,不能当记录打开。",
"video_only_opened": "已打开「{name}」;尚无分析结果。点顶部「分析视频…」生成。", "video_only_opened": "已打开「{name}」;尚无分析结果。点顶部「分析视频…」生成。",
"video_only_notice": "这段视频没有配套记录(同目录无 forces.csv):可以播放,但力曲线与记录信息为空。", "video_only_notice": "这段视频没有配套记录(同目录无 forces.csv):可以播放,但力曲线与记录信息为空。",
"video_only_panel": "这不是一条记录,而且无法解码这段视频(没有 forces.csv,也不像可播放的视频文件)\n可以试试 分析 ▸ 分析视频", "video_only_panel": "这不是一条记录,而且无法解码这段视频(没有 forces.csv,也不像可播放的视频文件)\n可以试试 分析 ▸ 分析视频",
@@ -325,7 +321,7 @@
}, },
"video_analysis": { "video_analysis": {
"title": "分析这段视频", "title": "分析这段视频",
"video": "{name} — {frames} 帧 @ {fps} fps({seconds} 秒)。它旁边没有录制(没有 forces.csv、没有 frames.csv),因此这一遍会按为它生成的帧索引来跑。", "video": "{name} — {frames} 帧 @ {fps} fps",
"profile": "处理配置", "profile": "处理配置",
"calibration": "标定", "calibration": "标定",
"calibration_default": "配置默认 — {name}", "calibration_default": "配置默认 — {name}",
@@ -487,7 +483,7 @@
"run_failed": "启动失败: {error}", "run_failed": "启动失败: {error}",
"filters_saved": "滤波设置已保存:{describe}(下次采集生效)", "filters_saved": "滤波设置已保存:{describe}(下次采集生效)",
"rail_camera_fps": "摄像头 {fps}", "rail_camera_fps": "摄像头 {fps}",
"capture_slow": "摄像头交付的帧率低于配置值:{detail}。记录会按实际帧率保存。这是采集端(摄像头/驱动)的问题,软件改不了:换一个真正能达到 30 fps 的摄像头,或先确认线材/端口没有把它降速,然后再测。", "capture_slow": "摄像头帧率低于配置:{detail}。",
"video_ended": "视频已结束", "video_ended": "视频已结束",
"camera_disconnected": "摄像机已断开", "camera_disconnected": "摄像机已断开",
"reset_ok": "传感器追踪器已重置", "reset_ok": "传感器追踪器已重置",
@@ -510,7 +506,7 @@
"deform_tip": "只放大画面上的形变显示。它只改图像:记录的数据、标定与算法相机都不受影响。", "deform_tip": "只放大画面上的形变显示。它只改图像:记录的数据、标定与算法相机都不受影响。",
"deform_unavailable": "当前数据源未提供三维位移:本面板只有光流幅值的曲面着色,形变放大不可用。", "deform_unavailable": "当前数据源未提供三维位移:本面板只有光流幅值的曲面着色,形变放大不可用。",
"field": "颜色来源", "field": "颜色来源",
"field_tip": "三维表面用哪个量上色:深度响应(链路的相对形变响应,无量纲)或光流幅值(追踪器的像素位移大小)。两者都不是毫米,都是近似量;选中的那个若在这一帧画不出来,会自动改用另一个,并在这行下面说明。", "field_tip": "用深度或光流给表面着色。",
"field_depth": "深度响应", "field_depth": "深度响应",
"field_flow": "光流幅值", "field_flow": "光流幅值",
"field_substituted": "这一帧画的是 {shown},不是所选的 {chosen}。", "field_substituted": "这一帧画的是 {shown},不是所选的 {chosen}。",
@@ -537,7 +533,7 @@
"max_fps_tip": "三维视图每秒绘制多少帧。这只是显示预算:摄像头与记录不受影响,这里丢的帧永远不会变成丢的样本。", "max_fps_tip": "三维视图每秒绘制多少帧。这只是显示预算:摄像头与记录不受影响,这里丢的帧永远不会变成丢的样本。",
"reset_view": "复位视角", "reset_view": "复位视角",
"settings_title": "三维显示设置", "settings_title": "三维显示设置",
"settings_hint": "「每帧自适应」同时作用于二维深度图和三维表面;其余项只影响三维显示。采集、记录与力曲线不受影响,改完立即生效。", "settings_hint": "每帧自适应同时作用于二维和三维。",
"legend": "{quantity} [{unit}] · {quality}", "legend": "{quantity} [{unit}] · {quality}",
"legend_none": "屏幕上没有测量值 — 仅显示不着色的表面模型", "legend_none": "屏幕上没有测量值 — 仅显示不着色的表面模型",
"quality_calibrated": "已标定", "quality_calibrated": "已标定",
+8 -14
View File
@@ -161,25 +161,19 @@ class FrameView:
panel.set_shared_depth_scale(span) panel.set_shared_depth_scale(span)
def depth_scale_text(self, frame: ImageFrame | None = None) -> str: def depth_scale_text(self, frame: ImageFrame | None = None) -> str:
"""The colour scale, plus — when the frame's depth is exactly zero — why it is flat. """The colour scale for the depth now on screen.
A depth view of an all-zero map is a flat island in the ramp's low colour, which ``frame`` is kept so callers can pass the painted frame; the label is
looks the same as a broken view. The frame knows which it is: the pass stored the the latched / adaptive / fixed span. A frame with no depth channel is
channel, its non-zero count and its range, so "0 of 84,000 pixels" can be said handled by ``panels.depth_unavailable``.
instead of leaving the operator to wonder. It is not the same statement as
``panels.depth_unavailable``, which is for a frame that carries no depth at all.
""" """
_ = frame
span = self.active_depth_span() span = self.active_depth_span()
text = "" if span is None else t( if span is None:
return ""
return t(
"panels.depth_scale", low=f"{span[0]:.3f}", high=f"{span[1]:.3f}" "panels.depth_scale", low=f"{span[0]:.3f}", high=f"{span[1]:.3f}"
) )
if frame is None or frame.depth is None:
return text
array = np.asarray(frame.depth)
if array.size and not bool(np.any(array)):
empty = t("panels.depth_empty", pixels=int(array.size))
return f"{text} · {empty}" if text else empty
return text
def latched_domain_mask(self, frame: ImageFrame): def latched_domain_mask(self, frame: ImageFrame):
"""The 2D domain boundary to draw, latched for the session. """The 2D domain boundary to draw, latched for the session.
+6 -5
View File
@@ -1527,17 +1527,18 @@ def test_the_nothing_to_draw_line_names_the_action_that_produces_a_result():
assert seen["zh_CN"] != seen["en_US"] assert seen["zh_CN"] != seen["en_US"]
def test_the_empty_depth_note_is_not_the_missing_depth_note(): def test_depth_unavailable_is_a_short_absent_note():
"""Both locales keep the two apart: a flat map is a reading, an absent one is not.""" """Both locales say the frame has no depth, without explaining zero vs absent."""
from orisys_app_shared.i18n import set_locale from orisys_app_shared.i18n import set_locale
try: try:
for locale in ("zh_CN", "en_US"): for locale in ("zh_CN", "en_US"):
set_locale(locale) set_locale(locale)
empty = t("panels.depth_empty", pixels=84000)
absent = t("panels.depth_unavailable") absent = t("panels.depth_unavailable")
assert "84000" in empty or "84,000" in empty, empty assert absent
assert empty != absent assert "\n" not in absent, absent
scale = t("panels.depth_scale", low="0.000", high="1.000")
assert scale != absent
finally: finally:
set_locale("zh_CN") set_locale("zh_CN")
+6 -6
View File
@@ -345,9 +345,9 @@ def test_the_operator_sees_the_cached_views_over_the_recording(
# The depth view is the stored map, drawn through the window's own backend. This # The depth view is the stored map, drawn through the window's own backend. This
# result's map is exactly zero on every frame it holds — the box chain's depth is # result's map is exactly zero on every frame it holds — the box chain's depth is
# flat on this footage, which the panel has its own sentence for — and it is still a # flat on this footage — and it is still a stored array of the result's own shape,
# stored array of the result's own shape, so the pixels are checked against it rather # so the pixels are checked against it rather than against a flat placeholder.
# than against a flat placeholder. # The colour-scale caption names the range only.
_set_mode(window, DEPTH) _set_mode(window, DEPTH)
target = active_frame target = active_frame
assert target.depth is not None and target.depth_nonzero == 0 assert target.depth is not None and target.depth_nonzero == 0
@@ -365,9 +365,9 @@ def test_the_operator_sees_the_cached_views_over_the_recording(
"the depth view's pixels are not depth_map_to_bgr of the stored frame" "the depth view's pixels are not depth_map_to_bgr of the stored frame"
) )
assert not np.array_equal(shown, arrows_bgr), "the depth view drew the arrow view" assert not np.array_equal(shown, arrows_bgr), "the depth view drew the arrow view"
assert t("panels.depth_empty", pixels=int(target.depth.size)) in ( assert window.arrow_panel.depth_scale_label.text() == (
window.arrow_panel.depth_scale_label.text() window.frame_view.depth_scale_text(target)
), "an exactly-zero depth map is not stated as such" ), "the depth caption is not the colour-scale range"
# …and the recording's own picture is still reachable, at the same cursor. # …and the recording's own picture is still reachable, at the same cursor.
_set_mode(window, RECORDED) _set_mode(window, RECORDED)