解决了一个有趣的技术难题:如何让 PiP 不读取自己的文字 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
Maxwin

解决了一个有趣的技术难题:如何让 PiP 不读取自己的文字

  •  
  •   Maxwin 3 月 27 日 813 次点击

    做 TransPeek 的时候遇到一个很有意思的问题:PiP 悬浮窗显示翻译结果 → ReplayKit 捕获整个屏幕(包括 PiP )→ OCR 识别到 PiP 里的文字 → 翻译引擎把翻译结果再翻译一遍 → 循环。

    这个问题如果不解决,用户看到的就是一堆乱七八糟的重复翻译。

    思路

    核心问题:在 ReplayKit 捕获的帧中,找到 PiP 窗口的位置,把这块区域从 OCR 输入中排除。

    难点:

    1. Extension 是独立进程,不知道 PiP 的坐标
    2. PiP 可以被用户拖动到屏幕任意位置
    3. 不能用 accessibility API ( Extension 没有权限)

    所以只能通过图像本身来检测。

    方案一:帧差分检测

    给 PiP 加一个动画彩虹边框,色相每 4 秒旋转一周。这意味着连续两帧之间,PiP 边框的像素颜色一定会变化,而且颜色鲜艳(高饱和度)。

    for 每个像素 (x, y): diff = abs(frame_n[x,y] - frame_n-1[x,y]) if diff > threshold AND saturation(frame_n[x,y]) > 0.5: candidate_pixels.append((x, y)) // 对候选像素的 x 、y 坐标做统计 // 中位数确定中心,四分位数确定边界 bounds = computeBoundsFromQuantiles(candidate_pixels) 

    优点:只要边框在动,就能检测到。 缺点:需要两帧才能检测,启动时第一帧无法检测。

    方案二:模板匹配检测

    对于纯色边框(红、蓝、绿等),单帧就能检测。

    逻辑:

    1. 扫描帧中的特定颜色像素
    2. 找到连通区域
    3. 验证尺寸(约 800x400px @ 3x )
    4. 验证宽高比( 1.2-3.0 )
    5. 验证边缘像素浓度(边框的特征是颜色集中在边缘)
    候选区域面积验证 → 宽高比验证 → 边缘浓度验证 → 通过 

    稳定性

    两套算法的结果还需要做稳定性处理:

    • 位置抖动过滤:偏移 < 6px 视为不动
    • 丢失容忍:连续 3 次未检测到才清除位置( 1fps 就是 3 秒容忍)
    • 横竖屏切换:立即重置所有状态,重新检测

    实际效果

    调参花了不少时间,但最终效果还不错。彩虹边框在绝大多数背景下都能被检测到,纯色边框在颜色不冲突的场景也很稳定。用户可以根据使用场景选择边框样式。

    这个问题让我意识到,有些看起来简单的需求,背后的技术挑战其实很有趣。如果有更好的方案欢迎讨论。

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1480 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 32ms UTC 17:03 PVG 01:03 LAX 10:03 JFK 13:03
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86