42天前,我还坐在书桌前对着手机屏幕上一张放大的《多宝塔碑》照片发愣:字太小、纸面反光、手机拿近一点又挡光,想对临几笔,眼睛都快贴到屏幕上。那时我压根没想过,自己会从“拍字帖”这件事出发,最终把一套鸿蒙原生应用,做成了从手机到PC的完整临摹工作流。这42天里,我扔掉过两版方案、重写过三回图像处理流水线、在凌晨两点为一个像素偏移问题查了三个小时,也终于在一个周末下午,第一次在PC大屏上流畅写完一个“永”字,笔迹和底图严丝合缝地叠在一起。
这篇文章就是那42天的完整手记。我会把整个项目的来龙去脉、技术选型、核心算法思路、跨端架构细节、踩坑经历和心理实录都摊开来讲。如果你也是书法爱好者,又恰好对鸿蒙开发感兴趣,或者你纯粹好奇“把字帖数字化再跨端使用”这件事到底怎么落地,这篇文章都能给你一份可以直接抄作业的参考。放心,我不会只讲成功,那些让我抓耳挠腮的失败案例,反而是最值钱的部分。
1. 从一个痛点出发:字帖与屏幕的第一次握手
1.1 书法爱好者的三个崩溃瞬间
这个项目不是凭空想出来的。它源于三个特别具体的崩溃瞬间。
第一个崩溃,是字帖尺寸。纸质字帖一本本买回来,原大看还行,但想临单字时,字径往往只有两三厘米,我得凑到桌面上眯着眼看。手机拍下来放大倒是可以,可屏幕就那么大,看一笔要挪一下屏幕,很难建立整字的间架结构。第二个崩溃,是拍照质量。我拿手机拍字帖,屋里灯光不均匀,纸上不仅有反光,还有我茶杯压出来的弧形阴影。放大到全屏后,字口发灰、背景发黄,想看清起笔的运笔痕迹都费劲。第三个崩溃,是找不到“对照感”。传统临帖讲究读帖、摹帖、临帖、背帖,最好能原帖和练习面一左一右对照。手机屏幕太窄,没法把原帖、米字格、我写的笔迹同时铺开。
这三个崩溃叠加在一起,让我动了念头:能不能做一个应用,把手机拍下来的字帖“洗干净、切好字、排进米字格”,然后流转到大屏设备上去临摹?这个念头一开始只是想法,直到我了解鸿蒙的跨端能力后,才决定动手做。
1.2 为什么一定要做鸿蒙原生应用
有人问过我,这功能做个微信小程序不就行了吗?手机端拍照处理,电脑端开个网页,也完全可以跑。但仔细想,小程序的图像处理能力弱,相机权限和各种底层接口受限;而网页端虽然方便,却没法充分利用设备能力,也没有真正的“任务接续”体验。
我选择鸿蒙原生应用,核心原因是它的跨端流转能力。鸿蒙提供的是“同一套系统能力在不同设备之间的接续”:手机端正在处理一份字帖任务,可以一键流转到PC端,应用状态、数据上下文都跟着过去。这不是简单地把文件传输过去,而是把“编辑中”的状态搬过去。基于这个能力,我可以把整个流程有机地拆成两端:手机端负责采集和算法处理,PC端负责大屏渲染和临摹交互。
当然,现实层面也有条件:我自己的开发机就是鸿蒙生态的设备,熟悉ArkTS,也一直在关注声明式UI和一多适配。既然需求里天然有“跨端”二字,鸿蒙原生应用就是最顺手的载体。设计这套应用的轮廓也在我心里逐渐清晰:手机拍字帖、预处理、切单字、生成米字格、流转到PC、大屏临摹、笔迹回放、相似度评估。这就是“墨韵新生”的完整链路。
2. 手机拍字帖,怎么变成能临摹的“数字摹本”
2.1 图像预处理:先把照片拉到及格线
整个应用最核心的起点,是那张手机照片。书法字帖照片的常见问题我太熟悉了:透视变形、光照不均、纸张纹理、墨色深浅不一。如果把这种照片直接拿去切字,后期每个环节都会放大噪声。所以,图像预处理是我最早花心思的一步。
我的处理流水线是这样的:第一步做灰度化,直接把彩色图转成灰度图,一定要用加权公式而不是简单取平均值。加权公式是Y = 0.299R + 0.587G + 0.114B,这个权重符合人对亮度的感知,能保留更多笔画细节。第二步是降噪,用高斯模糊滤掉传感器噪点,但核不能太大,我实测5x5就够了,核太大会吃掉笔画边缘。第三步是光照校正,这一步特别关键:因为横竖撇捺的墨色变化其实不大,很多“看着字不清晰”的本质,是背景光照不均导致对比度忽高忽低。
我一开始用全局直方图均衡化,发现效果很糟:照片四角发暗,有时候背景比笔画还亮,处理完像一张脏抹布。后来改成顶帽变换做背景估计:用一个大核做形态学开运算,估算出背景光照,再用原图减去背景。这样处理后,纸张发黄、光照不均的问题基本消失,墨色和背景被拉成两个清晰的峰。这个步骤让我意识到,图像处理不是堆算法,而是先搞清楚照片到底是什么噪声占主导,再有针对性地处理。
2.2 透视矫正与纸张识别:手动自动结合的折中方案
字帖照片绝大多数不是正对着拍的。手机摆位稍微偏一点,纸的四边就会变成梯形,切出来的字全带着倾斜,后面根本没法对齐米字格。所以,透视矫正是绕不开的。
我最初尝试全自动方案:在图中检测纸张边界,用线段检测算法找到四条边,求交点得到四个角点,再算单应性矩阵映射到矩形。这个方案听起来很完美,实际测试下来惨不忍睹:字帖是深色纸面、米白字口,边缘和阴影混在一起,线段检测经常把地板缝、手指影子、甚至桌面木纹当成纸边。十次里只能成功两次。
后来我做了个妥协:自动检测结果作为默认值,但一定给用户一个手动调整的界面——照片拍进来后,字帖四角会显示四个可拖动的锚点,用户可以一秒矫正。这一步完成后再进入下一步处理。实测下来,这个“自动识别+一键手动校准”的组合,比单纯追求全自动靠谱得多。事实上,在真实字帖照片面前,全自动方案往往不如让用户介入几秒钟。这也成了我后面整个项目的设计观念:给用户随时可以介入的兜底。
透视矫正的参数也有讲究。映射后的长宽比应该按原始纸张估计,我会根据四个角点的欧几里得距离估算纸张原始长宽,而不是固定变成正方形,否则字会被拉伸或压扁。
2.3 二值化与单字切分:让每个字都有独立户口
矫正完成后,得到的是一张干净的灰度图。下一步要把它变成黑白分明的“摹本”。我先试了全局Otsu阈值,但效果不够好:书法拓本的墨色并不均匀,光照校正后仍有局部残留,全局阈值会把淡墨部分切掉。换成自适应阈值后情况好很多。我用的方法是局部均值法:对每个像素,计算它周围一个窗口内的灰度均值,再乘以一个系数作为阈值,系数在0.8到0.9之间。窗口大小很重要,太小会把笔画内部也当成背景,太大会失去局部自适应性。我的经验是取目标字径的三分之一到二分之一,测试几个值,效果差异非常大。
二值化之后还有一堆“小毛病”要修:纸纤维产生的椒盐噪声用中值滤波去掉;笔画断开和粘连的问题更棘手。正常拓本单字之间间隙足够,直接投影分割就可以。算法思路是:把二值图按列投影,统计每一列的黑色像素数量,连续多列都为0的那个区间,就是字与字之间的分界;同理,按行投影可以把一列字再切成单字。这里有个边界条件:字的高低参差不齐,行的切分不能只看绝对空白,我加了容许值——连续空白行数大于字高的十分之一才算行间隔。
投影分割不是万能的。有些字帖有界格,格子线会和笔画连在一起;有些拓本残破,墨迹溢出。我的解法是切完字之后,用连通域分析二次校验:如果某个“字”的面积突然特别大或者特别小,就标出来让用户手动确认。整个切分环节的最终输出,是一张张独立的白底黑字字卡,每张字卡都用原始坐标位置做了标记,方便后续归位到原帖。
2.4 米字格与描红模板生成:临摹前的最后一公里
切出来的单字,要放进米字格里才算真正可用。米字格不是简简单单画个田字框就行,它需要考虑字在格内的位置和留白。我先把每个字的非零像素边界算出来:循环找x和y方向的最小/最大坐标,拿到字的外接框;然后向外填充12%的空白作为安全边,再按照正方形画布居中。字太大顶到格子边缘会显得“撑”;字太小又没法练。12%是我反复试出来的,对楷书、行书都挺友好。
模板生成有两个模式。第一个是“描红模式”:把字卡变成半透明灰色,叠加在米字格上方,用户可以直接在上面描。第二个是“引导模式”:只显示米字格和淡淡的字心影,相当于把原帖放在余光里,逼着自己记住笔画位置后再下笔。这两个模式对应练字里的“摹”和“临”,开发时也顺手把原帖整页视图做成了对照栏。
到这一步,手机端的使命基本完成了:一张乱糟糟的字帖照片,变成了一个结构清晰的“字母库”,里面有原图、字卡、米字格参数和模板样式。接下来真正让人兴奋的部分来了:怎样把这些内容送到PC大屏上,让我能舒服地对临?
3. 从手机到PC:跨端流转不是传文件,是接续任务
3.1 为什么要大屏临摹:真实需求分析
我在设计时问过自己:是不是手机端处理完,直接在手机屏幕上临摹就行了?答案是否定的。练字这件事,屏幕大小直接决定“读帖”质量。手机屏幕窄,一个字的细节放大后只能显示局部,眼睛需要来回扫视;更关键的是,手写笔在手机上的活动范围太小,写一个字需要切换好几次手腕角度,运笔的提按顿挫根本施展不开。
PC端接入手写板或者数位屏后,书写面积大得多,眼睛和手的距离也正常,这和纸面练字的姿势接近。即便没有数位屏,PC上键鼠操作也至少能在米字格里练习笔画结构和位置关系,总比手机戳屏幕好。不过鼠标没有压感,写圆转笔画会显得僵硬,这我在评分设计时单独做了处理:没有压感的场景,不显示“笔锋分”,只显示结构分。
我认为这个需求真实存在,是因为我认识的好几位书友都面临同样的困难。我曾把原型拿给一位写了十几年楷书的朋友看,他说如果真能用手机拍字帖、流转到电脑大屏描红,他会天天用。他甚至当场指出一个细节:要保留原帖的灰色底图,练字的时候能看见墨色浓淡和笔顺走向,这比只留边界框有用得多。这条建议后来被直接做进了模板参数里。
3.2 鸿蒙跨端流转的架构思路
跨端流转是这个项目里最“鸿蒙”的部分,也是我最早认为会很简单、后来发现坑最多的部分。我一开始以为,把处理好的字卡传到PC端再重新加载,就算是跨端了。后来发现不对:如果只是传文件,那么手机和PC基本上是两个割裂的软件,用户在手机端调节的参数、当前看的第几个字、用了什么模板配置,PC端全不知道。
我要的是一个真正的“任务接续”:手机端把字帖处理到一半,流转到PC端后,PC端直接打开在同一个字的临摹界面;反过来,我在PC端写了几个字的笔迹,流转回手机端后,那些笔画也能继续回放。这其实是把一个工程文件挂在设备之间无缝迁移。
架构上我用了两层。第一层是流式文件同步:手机端的字库工程文件打包成自定义格式,通过分布式文件系统同步到PC端的沙箱。第二层是状态同步:用一个分布式数据对象保存当前状态,包括当前选中的字索引、模板透明度、笔迹数组的最新版本号。两端有一个简单的冲突处理规则:以最近有操作的设备为准,防止两端同时改一个状态导致数据错乱。这套方案在真机上跑通时,我整个人都松了口气,因为之前卡了很久:PC端接收文件总是慢半拍,后来才弄明白,我是先同步了大尺寸原图,那当然慢;改成先同步压缩后的模板和参数,再把原图作为后台低优先级异步补传,体验立刻流畅了。
3.3 PC端临摹工作台的界面与交互设计
PC端界面完全重新设计,而不是简单把手机界面拉宽。这一点特别重要。手机端是“短时任务型”界面,核心是拍照、确认、处理;PC端是“长时间沉浸型”界面,核心是看、练、调整。我把工作台分成四个区域:左侧是原帖缩略图和字列表,点击即可跳转;中间是最大的临摹画布,显示米字格、底图和笔迹层;右侧是参数栏,包含模板透明度、格线样式、笔迹粗细、评分开关;底部是操作栏,包含撤销、重做、清空、保存练习。
界面上我犯过一个低级错误:刚开始直接把米字格画布贴满了窗口,没想到要给左侧列表留位置。后来才发现,练字时用户需要频繁切换字帖中的不同字,左侧列表必须有;而原帖和临摹画布最好能同时可见。于是我增加了“分栏对照模式”:左侧原帖字卡,右侧临摹画布,中间用一条可拖动的分隔条调整比例。这个模式后来成了我练字时最常用的布局,因为我把临摹画的每一笔,都能瞬间对照到原帖的运笔细节上。
PC端的交互手势也要特殊处理:鼠标滚轮负责缩放画布,按住空格拖动画布平移,右键按住不进墨。对手写板用户,则要响应笔的悬停、压感、橡皮擦逻辑。这些交互细节看起来小而碎,但如果不在一开始建模,后面写临摹引擎时就会牵一发动全身。
4. 临摹引擎:这支数字笔到底是怎么工作的
4.1 笔迹采集与平滑拟合
到了PC端真正开始写“临摹引擎”时,我面对的核心问题很简单:怎么把用户在屏幕上的书写轨迹,变成像墨迹一样自然顺滑的笔画。
基础的数据层很直接:监听指针事件,按下时开始一笔,移动时不断追加点,抬起时结束。每个点记录x、y坐标、时间戳和压感值。裸点的折线的问题是转折处出现棱角,极不自然。我试过很多平滑算法:移动平均、一阶低通滤波、Catmull-Rom样条。移动平均会让笔迹变钝,跟手度差;低通滤波可以保留大致轮廓,但参数的调法太玄学;最终效果最好的是Catmull-Rom样条插值:在两个点之间生成两条中间点,用三次样条把轨迹变成光滑曲线,插值密度取2到4个点就行,再多就浪费性能了。
还有一个细节:起笔的顿挫。书法笔画不是均匀速度的,起笔往往有“按下去”的动作,收笔可能有回锋。如果只是把点连成线,所有笔画看起来都像钢笔线,没有毛笔的粗细变化。我的处理是:把压感值映射到线条宽度,同时根据两点间距和压感变化率做笔画宽度插值。没有压感设备时,则用速度模拟粗细:下笔慢时线条宽,快速扫过时线条细。实测下来效果还挺唬人,起码第一眼有毛笔的质感和节奏。
4.2 描红模式、引导模式与自由临摹
临摹引擎的三个模式是我在需求阶段就想清楚的。描红模式最简单也最直观:底图半透明地铺在米字格里,用户直接在上面画,画出来的笔画会叠在底图上面,用颜色区分开(我用的是一种沉稳的墨青色,和底图的淡灰色形成对比)。这个模式适合初学者,可以很快建立笔画位置的肌肉记忆。
引导模式更进一步:底图透明度被拉得很低,只留下一个特别淡的“字影”,米字格线稍微加强。用户要想写好,必须先读帖,记住横竖撇捺的相对位置,再凭记忆写。写完以后,用户可以把底图透明度调回来,比较自己的笔迹和原帖的差别。这个模式的用户体验最接近真实临帖——写的时候不要看着描,写完再对照。
自由临摹模式就干脆不显示底图,只有米字格,适合已经掌握了字形的用户。三个模式共用同一套笔迹采集和高精度渲染引擎,只是底图层的配置不同。为了让用户练得更投入,我还加了一个极简的“单字循环”功能:写完一个字可以一键清空重写,也可以用图层对照模式保留上一次的笔迹,观察两遍之间的差异。这个功能自己用起来非常上瘾,我经常一练就是一小时。
4.3 相似度评分:别迷信机器打分
提到“智能临摹”,很多人的第一反应是AI打分:我写一笔,系统告诉我像不像。但我做这个评分模块时,心态是谨慎的,因为机器评分很容易沦为一种玩具。真正有用的是给出“哪里不像”。
第一版评分我用了最粗暴的像素重合法:把用户笔迹栅格化成256x256的二进制图像,和模板图逐像素对比,计算交并比。测试下来,结果让人崩溃:同样的字,写快一点、写慢一点、写大一点、写小一点,分数差距巨大;甚至有些明显写得不好的笔画,反而因为线条更粗,覆盖面积大,得了高分。这个方法根本不能用。
第二版我换成了模板距离场:先对模板图做距离变换,得到每一点到最近笔画的距离;再把用户笔迹点映射到距离场上,计算所有点的平均距离,这就刻画了“我的笔画位置离应到位置有多远”。然后结合两个辅助指标:覆盖率,指用户笔迹落在模板笔画范围内的比例;以及越界率,指用户笔迹距离模板太远的比例。最后的评分是一个加权公式:score = 0.6 * coverage - 0.3 * averageDistanceNorm - 0.1 * outlierRate,其中averageDistanceNorm是平均距离除以字高做归一化。这个公式虽然不复杂,但比第一版靠谱得多。我还做了一个细节:评分结果不会显示成一个冰冷的百分比,而是分成“结构稳、基本到位、位置偏移较大、建议重新读帖”四档,每一档都配一句提示。
笔锋分则只在检测到压感设备时显示,因为键鼠输入根本没有压感数据,硬要评分只会误导用户。这里我也调整了心态:评分永远只是辅助,机器没法评价一个字的“神采”,真正练字的快乐,还是写完后肉眼对照原帖,发现这一笔比上一遍好了一点点。
4.4 数据存储与笔画回放
练完字不保存,等于白练。我设计了一个简单的练习记录格式:每个字保存模板标识、米字格参数、模式类型,以及完整笔画序列。笔画序列里不仅有点坐标,还有每笔的时间戳和压感,这样可以支持回放。回放功能其实做起来不难,按时间戳重放所有点,用同样的平滑算法重建笔迹,效果就和录制时一致。
更有意思的是“逐笔对照”功能:把用户当时写的每一笔,和原帖的对应笔画并排显示。这里其实碰到了笔画匹配的问题——机器并不知道我第一笔是横还是竖。我的简化方案是:不追求语义匹配,只按时间顺序,把用户第N笔和模板中的第N段骨架做重叠显示。楷书笔画有相对固定的顺序,这个方法大部分时候能用;遇到连笔、多笔确实匹配错开,但作为练字后的复盘已经足够。保存的练习记录还可以通过导出功能,生成一张带米字格的练习稿,分享给同样在练字的朋友。
5. 42天复盘:我踩过的坑和查错的实战记录
5.1 42天时间线:哪些事值得花时间
整个项目的开发周期是42天,但并不是一开始就照着最终方案走的。我实际经历了两次方案推倒重来,这让我对“先验证可行性、再埋头开发”这句话体会极深。
第1到5天,我做的是需求拆解和算法原型验证:用电脑上的Python脚本跑通图像处理流水线,判断哪些步骤可行、哪些要手动兜底。这一步非常关键,因为很多问题在手机上调试会非常慢,在电脑上先把算法跑通了,后面移植到鸿蒙才心里有底。第6到9天,搭建鸿蒙工程,把图像处理能力包成服务模块,此时只做最基本的界面:打开相机、拍一张图、显示处理结果。第10到18天,完善手机端的处理流水线,包括透视矫正、二值化、单字切分和模板生成。第19到28天,做跨端流转和PC端工作台。第29到36天,集中调优算法参数和笔迹渲染。第37到42天,开始在真实场景下整天使用,修掉了大量体验问题。
时间线里最值得反思的是:我在“自动透视矫正”上浪费了太多时间。我想追求全自动,结果反复调算法,性能却没上去。后来决定加入人工校准兜底,才算从这个坑里爬出来。如果重新来一次,我第一天就会把“自动识别+手动兜底”确立为设计原则,而不是等到第12天才想明白。
5.2 高频问题排查表
把我在这42天里遇到的高频问题整理成一张速查表,应该比长篇大论更有用。这些问题里没有一个是“玄学”,全是可复现、可排查的工程问题。我踩过一遍,希望后来者能直接跳过。
| 现象 | 根本原因 | 解决方式 |
|---|---|---|
| 字帖照片矫正后仍然歪 | 纸张边缘检测受阴影干扰,四角点算错 | 自动检测结果只做初值,增加四角手动微调;拍照引导中加入“保持纸边完整”提示 |
| 二值化后笔画中间发白、断成两截 | 光照校正不够,墨色中心被当成背景 | 改用顶帽变换做背景估计;窗口大小取字径的1/2到1/3 |
| 切出来的单字带着旁边的偏旁 | 界格线和笔画粘连,投影没有完全分开 | 增加连通域二次校验,异常小块标记出来给用户手动框选 |
| 字卡在米字格里位置歪斜 | 外接框计算时没有排除孤立噪点 | 先做中值滤波,再算非零像素边界,并做了2%的边界剔除 |
| PC端接收工程文件特别慢 | 直接同步大尺寸原图,占满带宽 | 先同步压缩模板和参数,原图降级为后台异步传输 |
| 流转后PC端界面状态丢失 | 状态同步只传了数据,没有同步当前设备ID | 用分布式数据对象同步当前阶段标记,两端以最近操作为准 |
| 描红笔迹整体偏移2像素 | PC端渲染层逻辑坐标和物理像素没有统一换算 | 统一改用逻辑像素建模,渲染层单独处理适配系数 |
| 笔迹绘制拖动明显卡顿 | 整个画布每次重画全部图层 | 笔迹层独立,静态米字格层缓存为离屏画布 |
| 评分结果忽高忽低 | 笔迹粗细不一致导致像素重合面积失真 | 换成距离场加权评分,结合覆盖率和越界率 |
| 鼠标写字没有粗细变化 | 鼠标没有压感事件 | 用速度模拟笔宽,慢时宽、快时窄 |
这张表里的每一个问题,都是我真实调试过的。有些问题的排查过程相当痛苦,比如那个“笔迹偏移2像素”的问题,我反复对比坐标值,最后发现是PC端的屏幕缩放比例和Canvas绘图接口的DPI换算没对齐。修完之后,笔迹稳稳落在米字格正中间,那一刻的满足感很难用语言形容。
5.3 算法调优的独家经验(手记)
除了问题表,我还想单独写几条算法调优时积累的独家经验,这些经验不一定写在教科书里,但对实际效果影响巨大。
第一条是阈值参数的调优要跟着“字径”走,而不是跟着“像素值”走。同一张照片,字帖上印的字大小不一样,如果固定用30像素作为自适应阈值窗口,大字和小字的效果完全不同。我把窗口设置成根据当前检测到的字径自动缩放,这个问题才彻底解决。这背后的逻辑很简单:图像处理的参数应该跟着图像中的目标尺寸走,而不是跟分辨率走。
第二条是“宁可不全自动,也不要让用户觉得不可控”。我在二值化、切分、矫正这些环节全部加入了手动微调入口。一开始担心这样会显得“不够智能”,后来发现用户并不反感手动微调,反而因为自己能控制每一步而感到安心。尤其是书法爱好者,对自己的字帖和想要的效果非常清楚,给用户权力其实是给产品兜底。
第三条是“评分算法一定要知道自己的局限”。像素重合度会骗人,距离场也会被小偏移骗过。我最后的做法是,同时展示几个维度:结构分显示在界面上,偏离度显示成米字格上的色块热力图,哪一笔偏了看颜色就知道。用户自己才是最好的评判者,评分只是辅助反馈。
还有一条关于性能:PC端绘制时,我一度把原帖大图直接铺在图层里,临摹手写时明显感觉画布重新合成有延迟。后来我把所有静态元素,包括米字格、底图、辅助线,预渲染到一张离屏画布,每次重绘只画动态笔迹层,帧率立刻从30帧提升到满帧。这个优化很小,但体验差异非常明显。
6. 这些经验,你可以直接用
6.1 工具链与性能建议
如果你也想做一个类似的项目,我强烈建议先不要碰界面,先用脚本语言把核心算法验证一遍。我是在电脑上用Python验证了灰度化、光照校正、Otsu二值化、投影切分、距离场评分这一整套流程,确认每一步效果可接受后,才开始写鸿蒙端代码。这样做的原因是:脚本环境调试快,算法迭代周期短;等流程确定后,再把同样的逻辑移植到真实设备上。事实证明,这在42天里省下至少一周的时间。
鸿蒙端的图像处理不需要从零造轮子。底层可以使用c++能力包装OpenCV核心操作,上层用ArkTS做界面和业务编排。如果不想引这么大一个库,简单的灰度化、二值化和投影分割完全可以用像素级循环自己实现,性能也不会差。我这里提供一段伪代码思路,方便理解投影分割的流程:
function splitChars(binaryImage): width = binaryImage.width height = binaryImage.height vProjection = new Array(width).fill(0) for y in 0..height-1: for x in 0..width-1: if binaryImage.getPixel(x, y) == BLACK: vProjection[x]++ // 找所有连续空白段,作为列切分边界 gaps = findZeroRuns(vProjection, minGap = width * 0.15) charBoxes = [] gapStart = 0 for gap in gaps: if gap.end - gap.start > threshold: if gap.start - gapStart > 5: charBoxes.push([gapStart, gap.start]) gapStart = gap.end return charBoxes跨端部分,我建议数据面尽量“窄”,不要什么东西都同步。同步一个包含所有坐标的文件是低效的,应该同步“当前正在做什么”的状态和“下次需要什么数据”的索引。我的经验是:控制面走分布式数据对象,文件面走异步文件同步,两个通道分离,性能问题直接减半。
6.2 给同样想跨界做应用的人几句实话
最后说点掏心窝的话。这个项目最打动我的,不是用了什么厉害技术,而是打通了“我日常练字”这个真实场景。以前我拍字帖、放大、凑近看,写一会儿就头晕;现在拍一张、处理一下、流转到PC,整个流程大概两三分钟,之后就是安心的大屏临摹。技术从来没有这么直接地改善过我的练字体验。
如果你也想做类似的“小而美”应用,我给三点建议。第一,一定要为自己而做:因为你自己就是最苛刻的需求方,也是一个永远不会倦怠的测试员。第二,不要完美主义:全自动处理固然好,但现实场景太复杂,给用户手动微调的空间往往更重要。第三,先用起来,再谈技术:很多坑不是看书能看出来的,而是真正写下那个字、看到笔迹和底图错位的一瞬间才知道该优化什么。
我记得完成核心功能的那天晚上,我在PC端临摹了一页《九成宫醴泉铭》的“九”字,写完最后一笔,点开逐笔对照,发现自己的起笔位置终于不再偏离米字格中心。那一刻的快乐,是这42天最好的结尾。这套工具现在已经成了我每天练字的固定起点,而我知道它还可以继续生长:未来也许能接上更多碑帖资源库,能把评分的维度拆得更细,能让练字记录形成一个完整的成长曲线。但即便没有这些后续,我也已经很知足了,因为它是从一个实际痛点出发,一步一个脚印长出来的东西,而这个过程本身,就是“墨韵新生”真正想表达的破界之意。