我的Windows不可能这么的卡顿

Posted on

Bug 发现

前些天,在 bilibili 的首页刷到一个名为《修 BUG 的第一天,我给微软的 PowerToys 修了一个 BUG》的视频 。恰好 PowerToys 这款软件我也在使用,于是便点进去看了看视频,是一个文字识别输出方面的 Bug。但视频作者过于自信的语气,加之“标题的第一天”就是没有 C# 或者大型项目开发经验的代名词,对于他的代码,我怀疑有问题。

第二天早上起来,测试相关模块功能,果然,只是从一个错误转移成了另一个错误。

Bug 解释

Using TextExtractor to extract Chinese always contains spaces

最初的 Bug 源于此:中文的文字图片识别引擎返回内容时以“字”为单位,英文部分则以词为单位。最初的代码在所有识别单位中添加一个 U+0020(空格)作为间隔,如此便导致了中文汉字间包含错误空格。

[TextExtractor] Add space between CJK words and non-CJK

错误修复示范:遍历判断单位是否CJK文字,如果是则不加空格,不是的话就前后各加一个空格。也就是从这 里 是AO的blog , 你 好 ! 变成了这里是AO的blog , 你好 !

而实际正确的应该是这里是AO的blog,你好!,英文和中文之间最好没有空格,标点也不应该有空格。

这里就有一个新问题:标点怎么涵盖?既然是 CJK,那么日语和韩语的情况呢?

韩文引擎表现不同,并非单字识别。测试日文如下:

1そ の 後 、 2 人 の 住 む 関東 は 本格的 に 梅雨入 り し 、 雨 の 日 の 午前 た け の さ さ や か な 交流 が は し ま る 。

显然,日语的假名并没有算到 CJK words 里。观察一下源代码:

 1var cjkRegex = new Regex(@"\p{IsCJKUnifiedIdeographs}");
 2foreach (OcrLine ocrLine in ocrResult.Lines){
 3	bool isBeginning = true;
 4	bool isCJKPrev = false;
 5	foreach (OcrWord ocrWord in ocrLine.Words){
 6    	bool isCJK = cjkRegex.IsMatch(ocrWord.Text);
 7    	// Use spaces to separate non-CJK words.
 8    	if (!isBeginning && (!isCJK || !isCJKPrev)){
 9    	    _ = text.Append(' ');
10 	   }
11 	   _ = text.Append(ocrWord.Text);
12 	   isCJKPrev = isCJK;
13  	  isBeginning = false;
14}
15...
16}

问题就出现在代码最开头:CJKUnifiedIdeographs 只包含 CJK 的汉字部分。因此数字、假名、符号统统都有空格。

Patch

那么,是否把所有的 CJK 符号放到判断式里就万事大吉了呢?并不。考虑下面情况,空格依旧是重要的:

1韩文半角符号
2⑨ 记得给你的switch充电
3「卍 解」天锁斩月
4现在的B站都喜欢给标题加上空格——《反 复 横 跳》

文本的多样性决定了,空格的出现是难以预料的。引擎的输出格式丢失了一部分信息,而这部分信息难以通过代码复现(比如这个项目的处理 也有问题)。此问题应该交由引擎处理,或者分割开来,交给各语言的 native speaker 编写处理规则,而不是简单地加个残缺的范围了事。

考虑到这个模块也不是主要模块,引擎目前功能并不完善,不支持多语言识别,语言种类识别加之识别率一般,所以简单做个补丁。

1// var cjkRegex = new Regex(@"\p{IsCJKUnifiedIdeographs}|\p{IsHiragana}|\p{IsKatakana}|[\uFF61-\uFF9F]|[\u3000-\u3003]");
2var cjkRegex = new Regex(@"\p{IsCJKUnifiedIdeographs}|\p{IsHiragana}|\p{IsKatakana}|[\uFF61-\uFF9F]");
  • {IsCJKUnifiedIdeographs}:日文与中文中的汉字
  • {IsHiragana}:平假名
  • {IsKatakana}:片假名
  • [\uFF61-\uFF9F]:半角片假名
  • [\u3000-\u3003]:最常见的标点

考虑中文、日文、韩文,加入最常用的假名,半角假名。可供正常阅读。

1その後 、 2 人の住む関東は本格的に梅雨入りし 、 雨の日の午前たけのささやかな交流がはじまる 。 

吐槽

  1. 这个问题并没有得到很好的解决。最上策:有一个完备的引擎处理,准确的识别,具备以行为单位输出的功能。中策:分开处理,对每一种语言的符号进行详细的考证,编写规则,确保一般文本的准确性。(混合语言和新型新式可能会出错。)

  2. 微软,提高代码和审核的标准!(我说我电脑怎么越来越卡。这代码不卡才怪。) 如果没更好的解决办法,你就先把我的 patch 并进去或者把那个问题代码迁出来吧。

更新

上游程序通过更加复杂的筛选方案修复了此 Bug,并已合并到 PowerToys 中,此帖终结。哈哈,相比之下,自己写的代码也是一坨,彼此彼此啦。