ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

BallonsTranslator 文本变换子系统(Text Transforms)源码级指南:可组合变换栈、编译管线与交互契约

BallonsTranslator 文本变换子系统(Text Transforms)源码级指南:可组合变换栈、编译管线与交互契约 AI 应用计算机视觉图像处理NLP桌面应用【免费下载链接】BallonsTranslator深度学习辅助漫画翻译工具, 支持一键机翻和简单的图像/文本编辑 | Yet another computer-aided comic/manga translation tool powered by deeplearning项目地址https://gitcode.com/gh_mirrors/ba/BallonsTranslator点击查看免费下载本篇指南以 BallonsTranslator 官方文档 doc/ui/text_transforms.md 为核心骨架结合 fontformat.py、registry.py、mapping.py 及 test_text_transform_undo.py 等源码与测试深入讲解漫画翻译工具文本引擎中的「可组合文本变换」子系统四种内置变体的模型与参数、矩阵/非线性两条编译路径、状态持久化与撤销、非线性映射器的完整契约、缓存边界、交互不变量以及如何安全地新增一个变换变体。阅读完本文你将具备在 BallonsTranslator 中定位、使用、调试乃至扩展文本变换功能的完整能力。前置阅读本文是 Text engine 文档的变换子模块专篇。在阅读前建议先理解文本引擎的整体架构布局、效果、几何、编辑的边界划分本指南是对该子系统的定位说明它处于何处、每个部分归谁所有、哪些契约容易被无意破坏。单个控件与算法的权威细节以代码与聚焦测试为准。整体心智模型从排版到最终变形变换子系统建立在QTextDocument SceneTextLayout的排版结果之上其处理顺序可以概括为如下管道QTextDocument SceneTextLayout - Glyph Slant around each shaped glyphs visible-space anchor - exterior Shadow/Glow, Stroke, foreground/Hollow, interior Shadow/Glow - ordered global stack: Projective / Bend / Sine Wave / Grid - QGraphicsItem position and rotation三个关键语义Glyph Slant 是排版级typography效果在全局变换栈之前应用不可重排不进入任何矩阵。从 mapping.py 的模块文档可以看到明确约束Glyph-local slant is deliberately rendered from shaped glyph runs and never enters the matrix in this module——即字形局部倾斜刻意从已排版的字形游程渲染绝不以矩阵形式混入本模块。全局栈变换的是完整的文本效果盒子completed text-and-effects box其顺序有意义且允许重复的变换类型。测试test_duplicate_stack_entries_and_glyph_slant_round_triptest_text_transform_undo.py即验证了两个连续的projective条目与glyph_slant_angle的持久化往返。Item 位置与内置旋转在栈之外。原生路径安装的是补偿矩阵见下文编译一节因此栈语义始终先于 Qt 内置旋转执行。关于直立字形upright glyph的倾斜锚点处理文档给出了一个重要细节直立字形围绕其映射后的基线mapped baseline倾斜而四分之一旋转的字形没有可见的水平基线因此其原始墨迹到基线距离会在可见的 y 轴上重建。这样既能保持预期的倾斜方向又不会把旋转后的标点从罗马字列中平移出去最终墨迹盒子ink box以该锚点居中保证镜像轮廓不会朝相反方向漂移。四种内置变体模型值与参数范围变换的模型值全部定义在 fontformat.py 中。所有TextTransform子类都是frozen dataclass不可变值对象带有稳定的transform_type、精确的中性态判定以及仅存在于运行期的is_nonlinear能力元数据is_nonlinear为ClassVar表示该操作无法用QTransform表达必须对完整文本表面做反采样变形。变体类型标识是否非线性参数默认值Scale / Slant / 3D透视projective否矩阵路径horizontal_scale(1.0)、vertical_scale(1.0)、horizontal_slant(0.0)、vertical_slant(0.0)、rotation_x(0.0)、rotation_y(0.0)、rotation_z(0.0)、perspective(0.0)Bend弯曲bend是bend(0.0)有符号圆弧弯曲Sine Wave正弦波sine是frequency_x(2)、frequency_y(0)、phase_x(0.0)、phase_y(0.0)、amplitude_x(0.1)、amplitude_y(0.1)Grid网格grid是horizontal_divisions(1)、vertical_divisions(1)、interpolation(bilinear)、control_points(归一化坐标元组)各变体的中性态判定来自 fontformat.pyProjectiveTextTransform.is_neutral()两个缩放均为 1.0 且三个斜切/旋转角均为 0 时中性注意perspective不参与中性判定。BendTextTransform.is_neutral()bend 0.0。SineTextTransform.is_neutral()x 轴频率为 0 或振幅为 0且 y 轴频率为 0 或振幅为 0同时中性。默认构造值frequency_x2, amplitude0.1是非中性的。GridTextTransform.is_neutral()control_points与默认规则网格完全一致时中性。__post_init__会在未提供控制点时自动生成默认网格with_value在修改 divisions 时会用双线性插值重采样既有控制点修改interpolation则走通用replace路径。控制范围UI 约束即真相文档明确UI 控件会把编辑约束到其支持范围与规范化精度内再产出模型值模型本身不 clamp、不做范围校验持久化参数。测试test_persisted_transform_values_are_not_range_validatedtest_text_transform_undo.py验证了horizontal_scale5.0、glyph_slant_angle50.0这类超出 UI 范围的持久化值可以无损往返。控件范围常量同样定义在 fontformat.py并被 registry.py 引用常量值TEXT_TRANSFORM_SCALE_MIN / MAX0.1 / 4.0TEXT_TRANSFORM_GLYPH_SLANT_MIN / MAX-45.0 / 45.0度TEXT_TRANSFORM_BEND_MIN / MAX-1.0 / 1.0TEXT_TRANSFORM_SINE_FREQUENCY_MIN / MAX0 / 64半波计数TEXT_TRANSFORM_SINE_PHASE_MIN / MAX0.0 / 1.0TEXT_TRANSFORM_SINE_AMPLITUDE_MIN / MAX0.0 / 1.0TEXT_TRANSFORM_GRID_DIVISION_MIN / MAX1 / 32单元格数非控制点数TEXT_TRANSFORM_PRECISION6网格控制点归一化坐标的舍入精度UI 侧的注册信息控件规格、显示标签、图标、stage 工厂在 registry.py 的TEXT_TRANSFORM_VARIANTS中集中定义ProjectiveScaleHorizontal/Vertical快捷键S → X/S → Y、SlantHorizontal/Vertical、RotationX/Y/Z快捷键R → X/R → Y/R或R → Z、Perspective共 8 个标量控件。BendAmount。Sine Wave按从左到右波与从上到下波两个分组各含 Segments频率整数、Shift相位、Height/Width振幅。GridDivisionsHorizontal/Vertical整数 Interpolation 下拉bilinear显示为 Straightcatmull_rom显示为 Smooth。面板的 Add 按钮菜单正是从该注册表生成的测试test_add_menu_and_hover_actions_are_generated_from_registry见 test_text_transform_undo.py断言菜单项为[Scale / Slant / 3D, Bend, Sine Wave, Grid]且图标非空。编译一条栈、两条路径compile_text_transform_stack()是变换栈编译器位于 registry.py。它接收已提交的TextTransformStack、当前逻辑边界logical bounds、带效果的源边界padded source bounds以及书写模式vertical 布尔值。注册的 stage 工厂把每个激活条目转成矩阵或非线性映射器编译器返回一个CompiledTextTransform供 geometry.py 中的TextItemGeometryController存储并安装。编译输出CompiledTextTransform定义于 mapping.py输出字段用途native_matrix纯矩阵栈的 Qt item 变换非线性路径下为恒等变换surface_mapper完整的非线性映射CompositeTextTransformMapper用于绘制与视觉/输入几何stages每个条目的输入上下文与可选映射器CompiledTransformStage用于在完整栈内定位与编辑被选中 stage 的控件该结果同时驱动绘制、边界、命中测试、光标/IME 映射、缩放resize与被选 stage 的叠加层。它有且仅有两个执行结果无非线性 stage所有矩阵 stage 折叠为一个原生QTransform编译器在逐个追加时即时折叠相邻矩阵见 registry.py 的注释Folding as stages are added keeps deep matrix runs cheap。存在任意非线性 stage所有激活 stage矩阵适配器 非线性映射器进入一个CompositeTextTransformMapper完整源表面只做一次最终变形inverse-map once。编译器校验registry.py拒绝非有限non-finite或不可逆non-invertible的矩阵_validate_matrix_stage检查 9 个系数全部math.isfinite且matrix.inverted()成功。拒绝越过其源地平线的 projective 变换对四个角计算齐次分母若某分母为 0、分母绝对值极小≤ 最大值 × 1e-9、或四个分母跨越正负号则抛出projective transform crosses its source horizon。拒绝映射到非有限坐标的矩阵。在非线性路径中强制非线性 stage 必须实现完整映射器契约_NONLINEAR_MAPPER_METHODS共 7 个方法见下文矩阵路径强制 stage 必须是QTransform。编译时Projective的缩放、斜切、旋转、透视会合并为单个单应矩阵homography。从projective_transform_matrixmapping.py的源码看其实现为先构造缩放 → 水平剪切 → 垂直剪切的仿射矩阵顺序剪切保持行列式为 1避免合法角度下奇异再与绕 x/y/z 轴的三维旋转矩阵rotate_z rotate_y rotate_x复合取前两列最后用矩形中心归一化深度系数depth coefficient使每个齐次分母在1 - perspective范围内。注释明确painting must not reconstruct those components——绘制阶段不得重建这些分量透视参数只存在于编译结果中。矩阵路径的旋转补偿文档强调Qt 应用其内置 item 旋转与 base transform 的顺序与栈语义不同。因此原生路径安装的是补偿矩阵compensated_native_transform_matrixmapping.py对于已编译原生变换S与内置旋转R安装C R⁻¹·S·R作为 base transformQt 组合成R·C S·R从而让 item 局部栈仍先于内置旋转执行。该函数对恒等矩阵、360° 整数倍旋转、以及同枢轴 各向同性缩放保持精确的规范矩阵快路径避免三角函数残差破坏 identity/cache 检查并在任何非有限输入、不可逆旋转时抛ValueError。文档同时要求不要在原生安装矩阵 stage 的同时又把它们放进复合映射器Never install a matrix stage natively as well as inside the composite mapper。Item 位置与内置旋转保持在栈之外。matrix-only stack stack with a nonlinear stage ----------------- ---------------------------- active matrix stages all active stages - one combined QTransform - matrix adapters nonlinear mappers - native item transform - one composite mapper - no surface warp - identity native stack transform - one final surface warp从源码结构看当前四个变体的归属为——Projective走矩阵路径其is_nonlinear为 FalseBend、Sine Wave、Grid走非线性路径Glyph Slant保持上图中的独立预处理效果不参与栈。测试test_grid_compiles_as_one_ordered_composable_surface_mappertest_text_transform_undo.py验证了projective → grid → projective混合栈在两种书写模式下都编译为 identity 原生矩阵 单一复合映射器且正反映射在点位与数组两条 API 上均精确往返。各模块职责谁拥有什么关注点归属模型值、栈与持久化utils/fontformat.pyStage 数学与复合映射ui/text_engine/transforms/变体注册与编译策略ui/text_engine/transforms/registry.pyItem 几何、已安装映射与渲染生命周期ui/text_engine/geometry.py选区范围内的预览与提交ui/text_engine/transforms/edit_session.py面板与变体控件ui/text_engine/transforms/panel.py、ui/text_engine/transforms/controls.py画布撤销与配对编辑器协调ui/text_engine/editing/commands.py、ui/text_engine/editing/manager.py共享橡皮筋与模态事件路由ui/canvas.py最终表面变形与 Glyph Slant 绘制ui/text_engine/rendering/被选 stage 的画布叠加层ui/text_engine/transforms/grid_control.py、ui/text_engine/transforms/projective_control.py扩展新变体的总则扩展模型、注册表与 stage 工厂不要在TextBlkItem或TextItemGeometryController中添加变体专用分支。这与 text_engine.md 的总体架构原则一致——Extend the existing owner。状态、持久化与撤销TextTransform子类是不可变模型值有稳定的transform_type、精确的中性态、仅运行期的is_nonlinear能力元数据。UI 控件在产出模型值前把编辑约束到支持范围与规范化精度模型不 clamp、不校验持久化参数的取值范围见上文测试证据。TextTransformStackfontformat.py把有序操作序列与固定的栈前glyph_slant_angle作为一个不可变值用于持久化与撤销。它实现了__iter__/__len__/__getitem__并提供has_active_stages任一条目非中性与has_nonlinear任一激活条目非线性两个只读属性。中性条目仍然保存在栈中编辑器可见但编译器会跳过它们矩阵、映射器、边界与预览全部属于派生状态从不持久化。持久化格式条目使用已注册类型与已知字段参数值视为有效省略字段使用变体默认值。被动加载passive loading时结构未知的变换条目会被忽略不会让整个项目加载失败——这符合 AGENTS.md 的被动加载数据安全约定。FontFormat.__post_init__fontformat.py会把 list/tuple 形态的text_transform逐项coerce_text_transform无法识别的条目记录 warning 后丢弃to_serializable_dict则把栈序列化为[asdict(transform) ...]的带类型字典数组并把glyph_slant_angle作为兼容字段写出同时保留glyph_slant_angle属性视图以兼容旧项目/配置。撤销与预览TextTransformEditSessionui/text_engine/transforms/edit_session.py遵循 text_engine.md 中描述的共享编辑生命周期。提交时它把 before/after 两个完整栈合并在一个SetTextTransformCommand中定义于 ui/text_engine/editing/commands.py并刷新几何与叠加层。文档给出两条实操警示在栈索引改变之前必须先 settle 挂起的控件Settle pending controls before stack indices change。多选时索引化控件indexed controls仅当所有目标具有相同变换类型序列时才有意义即使如此匹配索引仍可能显示混合值。当栈形状stack shapes不同时不要重新解释既有索引不过追加新 stage 始终是安全的。测试test_add_menu_and_hover_actions_are_generated_from_registry展示了面板向多选目标追加 stage 的信号路径。编译、几何与渲染非线性映射器契约每个 stage 都基于前序 stage 产出的边界构建因此重排会改变结果。编译依赖四件事不可变栈、书写模式、逻辑矩形、带效果的源矩形。富文本编辑可能暴露中间尺寸——推迟编译直到编辑 settle然后一次性发布已安装几何。延迟编译完成后只在安装 settled 几何之后才发出visual_geometry_changed否则形状与变换叠加层会观察到过期的边界。非线性映射器必须提供完整的交互与光栅契约方法签名来自文档实现于 mapping.py 的各 Mapper 类forward_point(source) forward_arrays(source_x, source_y) inverse_point(visual, previous_sourceNone, *, extrapolateFalse) inverse_arrays(visual_x, visual_y, *, return_validFalse) visual_bounds(source_rectNone) map_rect_path(source_rect) geometry_key各方法语义要点previous_source用于在折叠folds附近保持分支连续性CompositeTextTransformMapper.inverse_point会先把previous_source沿正向各 stage 逐级推进得到每级前序源点再按逆序逐级反解mapping.py。外推extrapolate仅供 reshape 超出可见映射表面时使用普通命中测试必须保持有界bounded。数组反解inverse_arrays返回有效性掩码return_validTrue时用于光栅与密集叠加层工作复合映射器会把各 stage 的掩码做逻辑与。边界必须包含内部极值interior extrema——例如 Sine 波谷波峰、被拖出网格单元格的控制点。geometry_key必须包含每一个会改变映射的输入。MatrixTransformMapper的 key 是 9 个矩阵系数CompositeTextTransformMapper的 key 是各 stage key、垂直标志、逻辑/源矩形的 x/y/宽/高mapping.py。正向 stage 按序执行、逆向 stage 按反序执行forward_point顺序遍历self.stagesinverse_point从len(stages)-1倒序。绘制、轮廓、手柄、命中测试、光标/IME 几何、resize 必须跨越同一个映射边界——这就是一条映射边界架构的核心。渲染路径总表状态布局绘制全局几何最终变形中性NativeIdentity无纯矩阵栈Native合并矩阵无Glyph Slant无非线性 stage自定义字形布局Identity 或矩阵无任意激活非线性 stageNative 或自定义字形布局复合映射器一次对非线性输出把完成的 typed-effect/Gradient Overlay/Hollow 输出及其块属 alpha 掩码捕获进一个带 padding 的源表面只做一次反采样映射inverse-map once必要时在映射后的目标上绘制编辑 UI。纯矩阵栈留在 Qt 原生路径。效果 padding 改变的是源矩形而不是持久化的逻辑文本矩形——与 text_engine.md 的坐标空间约定一致逻辑矩形排除效果 padding 与视觉溢出。Typed Stroke/Shadow/Hollow 输出在 neutral、Glyph Slant、matrix、nonlinear 四条路径上共用同一套有界、感知设备缩放的效果光栅策略。效果与委派给 Glyph Slant 的绘制会禁用 Qt 冗余的外层 item 缓存——效果渲染器是唯一的光栅缓存所有者因此能在当前视图或导出尺度下重建缓存这正是 ui/text_engine/rendering/ 中NonlinearTextSurfaceRenderer等类存在的意义。缓存边界子系统在四个边界上分别缓存因为几何与像素是独立变化的缓存内容复用与失效边界编译后的变换栈、书写模式、逻辑矩形、源矩形匹配时复用复用缓存输出前必须重新应用因为页面/布局生命周期代码可能已 detach 映射器或改动 Qt 矩阵字形几何已提交与预览几何分离且有界由布局生成与 Glyph Slant 渲染状态决定复用非线性反采样坐标文本、选区、IME 变化期间只要映射器几何、源/目标矩形、渲染缩放匹配即可复用最终非线性表面像素以布局、字形/效果状态、文档内容、选区为键参数或 resize 预览期间不得保留该缓存补充细节仅光标重绘可复用最终表面像素但必须运行透明源布局探测transparent source-layout probe让 Qt 刷新原生光标闪烁可见性选区属于表面键的一部分每个 IME 事件显式失效瞬态 preedit 像素。所有变换缓存命名空间与 item 持有的渲染缓存在 item/页面移除及回到中性态时必须可释放。Grid 的可选编译逆核compiled inverse kernels属于运行时加速而非变换结果缓存应在 Qt 线程之外预热并在其就绪前保留可用的回退实现fallback。交互不变量易被局部改动回归的细节Qt 的文本控件在字形塑造、光标、选区、IME 与文档历史上保持权威。视觉输入被反映射为源布局坐标输出矩形与叠加层被正向映射。以下是文档特别列出、局部改动时极易回归的点非线性变形会把编辑像素移出 Qt 的源局部脏矩形——因此光标、选区、IME 变化必须重绘整个TextBlkItem编辑 UI 必须与文本保持在同一源→视觉映射边界。Resize 时每个指针采样都通过拖动开始时冻结的几何映射复用被先前采样修改过的几何会产生反馈导致向外拖动反向或塌缩。面板与画布叠加层共享同一个选中栈索引。恰好一个文本块的选中 Grid 或 Projective stage 拥有变换叠加层并隐藏普通形状叠加层。二者互斥、跟随选区与编辑生命周期、绝不进入导出。Grid 拥有的右键选区选择会被消费不打开 Canvas 上下文菜单。Canvas 拥有唯一的场景空间橡皮筋手势与视觉激活的 Grid 复用它做手柄选择不得新增第二个矩形或鼠标生命周期。Grid 控制点是归一化的逻辑坐标 0..1见TEXT_TRANSFORM_PRECISION。文本、字体、书写模式或盒子变化时其 stage 会基于 settled 边界重建而不是保留过期的像素坐标。Glyph Slant 保持在QTextDocument布局之外因此 Qt 保有字形塑造、换行、光标索引与选区的所有权填充、效果与边界必须复用同一套倾斜字形几何对应 ui/text_engine/rendering/glyph_slant.py。激活与回到中性态是对称的生命周期转换必须恢复原生效果与几何并释放仅变换时持有的状态。如何新增一个变换变体按 registry.py 与 fontformat.py 的注册结构新增变体的标准步骤在 fontformat.py 中添加一个不可变TextTransform子类稳定的transform_type、精确的中性态is_neutral()、正确的is_nonlinear能力声明。在同一个稳定键下注册三样东西模型类型TEXT_TRANSFORM_TYPES、本地化控件registry.py 的TransformControlSpec、stage 工厂TEXT_TRANSFORM_VARIANTS。注册表末尾的运行时断言会强制持久化类型集合 UI/运行期变体集合raise RuntimeError(persisted and UI/runtime text-transform variants must be registered together)。stage 工厂返回校验过的矩阵或满足完整几何契约的映射器并使用传入的 stage 边界TransformStageContext逻辑边界、源边界、垂直标志。保持对无效可选项目数据的宽容加载并避免在 item/controller 新增类型分支。覆盖测试面持久化、中性/激活生命周期、栈顺序、预览与取消/提交、撤销隔离、两种书写模式、效果、交互、导出视适用性而定。架构红线没有稳定的单点反解point inversion与向量化数组反解vectorized array inversion的非线性变体不适合本架构。聚焦验证参考 text_engine.md 的验证指引。主测试套件 tests/test_text_transform_undo.py6153 行覆盖栈持久化、预览/撤销、几何与交互涵盖projective/bend 载荷往返、重复栈条目与 Glyph Slant 往返、projective 矩阵居中与可逆性、单 stage 原生矩阵编译、live 边界需要 typed stack、持久化值不做范围校验、Bend/Sine/Grid 各映射器在两种书写模式与极端参数下的正反映射、网格双线性与 Catmull-Rom 差异、逆解收敛与跨单元格重试、复合栈编译、面板注册表驱动菜单与可滚动高度、IME 提交传播等。运行命令与文档一致offscreen 平台可在无显示环境执行QT_QPA_PLATFORMoffscreen python -m unittest \ discover -s tests -p test_text_transform_undo.py此外文档要求在改动渲染/交互后做视觉检查覆盖中性、矩阵、非线性三种状态涉及布局生命周期、字形塑造、光标或绘制变更时还需要同时跑 PyQt5 与 PyQt6 两套绑定渲染/交互变更还需通过主题化应用themed-app检查或明确记录局限。赞分享AI 应用计算机视觉图像处理NLP桌面应用【免费下载链接】BallonsTranslator深度学习辅助漫画翻译工具, 支持一键机翻和简单的图像/文本编辑 | Yet another computer-aided comic/manga translation tool powered by deeplearning项目地址https://gitcode.com/gh_mirrors/ba/BallonsTranslator点击查看免费下载相关推荐TVM TIRx 编译器变换Compiler Transforms全指南tvm.tirx.transform 的 Pass 体系与降级管线TVM TIRx 编译器变换Compiler Transforms全指南tvm.tirx.transform 的 Pass 体系与降级管线 导读 本文是模型编译深度学习推理引擎PaddleSeg 数据变换transforms完全指南从 Compose 管线到源码级原理PaddleSeg 数据变换transforms完全指南从 Compose 管线到源码级原理 本文以 docs/apis/transforms.md ht人工智能计算机视觉预训练MLX 函数变换Transforms完全指南可组合的自动微分、自动向量化与计算图编译MLX 函数变换Transforms完全指南可组合的自动微分、自动向量化与计算图编译 导读 本文围绕 MLX 官方 API 文档中的函数变换Transf人工智能深度学习机器学习本地部署上一篇LiteDB 查询表达式与缓存机制源码级指南LINQ/SQL 翻译、可复用模板与缓存所有权下一篇3分钟解决Devika项目BERT模型加载难题从报错到流畅运行的实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进